From 6d2255968aecf296094c0727ffabea9b3d3d46aa Mon Sep 17 00:00:00 2001 From: Ovahlord Date: Thu, 21 Jul 2022 00:31:59 +0200 Subject: [PATCH] Core/Creatures: re-implement temporary summons (WIP) - reworked the summon slot assignment behavior - isolated controlled summons from charmed units - dropped old summon type API and replaced it with a all-new version based on the enumerated strings leak - made summon accessing type- and moronsafe - converted some pet-related packets to packet class - implement SPELL_EFFECT_ALLOW_CONTROL_PET - removed summon despawn behavior type arguments and fully determine it based on summon property flags and static flags - allow any summon to use levelstats and passive auras if available --- .../Implementation/CharacterDatabase.cpp | 36 +- .../Implementation/CharacterDatabase.h | 23 +- src/server/game/AI/CreatureAI.cpp | 56 +- src/server/game/AI/CreatureAISelector.cpp | 4 +- .../game/Battlegrounds/Battleground.cpp | 4 +- src/server/game/DataStores/DBCEnums.h | 43 +- src/server/game/DataStores/DBCStores.cpp | 4 - .../game/Entities/Creature/Creature.cpp | 16 +- .../Entities/Creature/TemporarySummon.cpp | 56 +- .../game/Entities/Creature/TemporarySummon.h | 3 - .../Creature/TemporarySummon/NewGuardian.cpp | 110 +++ .../Creature/TemporarySummon/NewGuardian.h | 51 ++ .../Creature/TemporarySummon/NewPet.cpp | 447 ++++++++++ .../Creature/TemporarySummon/NewPet.h | 84 ++ .../TemporarySummon/NewTemporarySummon.cpp | 263 ++++++ .../TemporarySummon/NewTemporarySummon.h | 77 ++ src/server/game/Entities/Object/Object.cpp | 99 +++ .../game/Entities/Object/ObjectDefines.h | 3 +- src/server/game/Entities/Pet/Pet.cpp | 39 +- src/server/game/Entities/Pet/Pet.h | 17 +- src/server/game/Entities/Pet/PetDefines.h | 17 + src/server/game/Entities/Player/Player.cpp | 813 ++++-------------- src/server/game/Entities/Player/Player.h | 89 +- src/server/game/Entities/Totem/Totem.cpp | 13 - src/server/game/Entities/Unit/StatSystem.cpp | 221 +++++ src/server/game/Entities/Unit/Unit.cpp | 747 ++++++---------- src/server/game/Entities/Unit/Unit.h | 102 ++- src/server/game/Entities/Unit/UnitDefines.h | 22 +- src/server/game/Entities/Vehicle/Vehicle.cpp | 4 +- src/server/game/Globals/ObjectMgr.cpp | 2 +- src/server/game/Handlers/CharacterHandler.cpp | 9 +- src/server/game/Handlers/MiscHandler.cpp | 1 - src/server/game/Handlers/MovementHandler.cpp | 4 +- src/server/game/Handlers/NPCHandler.cpp | 8 +- src/server/game/Handlers/PetHandler.cpp | 323 +++---- src/server/game/Handlers/SpellHandler.cpp | 21 +- src/server/game/Maps/Map.h | 8 +- src/server/game/Phasing/PhasingHandler.cpp | 2 + .../game/Server/Packets/CharacterPackets.cpp | 13 +- .../game/Server/Packets/PartyPackets.cpp | 4 + src/server/game/Server/Packets/PetPackets.cpp | 55 ++ src/server/game/Server/Packets/PetPackets.h | 71 ++ .../game/Server/Packets/QueryPackets.cpp | 14 + src/server/game/Server/Packets/QueryPackets.h | 14 + src/server/game/Server/WorldSession.cpp | 2 +- src/server/game/Server/WorldSession.h | 11 +- .../game/Spells/Auras/SpellAuraEffects.cpp | 19 +- src/server/game/Spells/Spell.cpp | 36 +- src/server/game/Spells/Spell.h | 1 + src/server/game/Spells/SpellEffects.cpp | 203 +---- src/server/game/Spells/SpellHistory.cpp | 30 + src/server/game/Spells/SpellHistory.h | 8 +- src/server/game/Tools/PlayerDump.cpp | 8 +- src/server/scripts/Commands/cs_npc.cpp | 10 - src/server/scripts/Commands/cs_pet.cpp | 8 - .../the_scarlet_enclave_chapter_1.cpp | 3 - .../boss_blackheart_the_inciter.cpp | 8 +- src/server/scripts/Spells/spell_dk.cpp | 12 +- src/server/scripts/Spells/spell_druid.cpp | 93 +- src/server/scripts/Spells/spell_generic.cpp | 12 +- src/server/scripts/Spells/spell_hunter.cpp | 3 - src/server/scripts/Spells/spell_item.cpp | 2 +- src/server/scripts/Spells/spell_mage.cpp | 4 +- src/server/scripts/Spells/spell_paladin.cpp | 6 +- src/server/scripts/Spells/spell_shaman.cpp | 6 +- src/server/scripts/Spells/spell_warlock.cpp | 2 +- src/server/scripts/World/npcs_special.cpp | 16 + 67 files changed, 2545 insertions(+), 1970 deletions(-) create mode 100644 src/server/game/Entities/Creature/TemporarySummon/NewGuardian.cpp create mode 100644 src/server/game/Entities/Creature/TemporarySummon/NewGuardian.h create mode 100644 src/server/game/Entities/Creature/TemporarySummon/NewPet.cpp create mode 100644 src/server/game/Entities/Creature/TemporarySummon/NewPet.h create mode 100644 src/server/game/Entities/Creature/TemporarySummon/NewTemporarySummon.cpp create mode 100644 src/server/game/Entities/Creature/TemporarySummon/NewTemporarySummon.h diff --git a/src/server/database/Database/Implementation/CharacterDatabase.cpp b/src/server/database/Database/Implementation/CharacterDatabase.cpp index 209149dca5f..3b8bb759e33 100644 --- a/src/server/database/Database/Implementation/CharacterDatabase.cpp +++ b/src/server/database/Database/Implementation/CharacterDatabase.cpp @@ -43,12 +43,12 @@ void CharacterDatabaseConnection::DoPrepareStatements() "subject, deliver_time, expire_time, money, has_items FROM mail WHERE receiver = ? ", CONNECTION_SYNCH); PrepareStatement(CHAR_SEL_MAIL_LIST_ITEMS, "SELECT itemEntry,count FROM item_instance WHERE guid = ?", CONNECTION_SYNCH); PrepareStatement(CHAR_SEL_ENUM, "SELECT c.guid, c.name, c.race, c.class, c.gender, c.skin, c.face, c.hairStyle, c.hairColor, c.facialStyle, c.level, c.zone, c.map, c.position_x, c.position_y, c.position_z, " - "gm.guildid, c.playerFlags, c.at_login, cp.entry, cp.modelid, cp.level, c.equipmentCache, cb.guid, c.slot " - "FROM characters AS c LEFT JOIN character_pet AS cp ON c.guid = cp.owner AND cp.active = 1 LEFT JOIN guild_member AS gm ON c.guid = gm.guid " + "gm.guildid, c.playerFlags, c.at_login, cp.CreatureId, cp.TamedCreatureId, cp.DisplayId, c.equipmentCache, cb.guid, c.slot " + "FROM characters AS c LEFT JOIN character_pet AS cp ON c.guid = cp.Guid AND cp.IsActive = 1 LEFT JOIN guild_member AS gm ON c.guid = gm.guid " "LEFT JOIN character_banned AS cb ON c.guid = cb.guid AND cb.active = 1 WHERE c.account = ? AND c.deleteInfos_Name IS NULL", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_ENUM_DECLINED_NAME, "SELECT c.guid, c.name, c.race, c.class, c.gender, c.skin, c.face, c.hairStyle, c.hairColor, c.facialStyle, c.level, c.zone, c.map, " - "c.position_x, c.position_y, c.position_z, gm.guildid, c.playerFlags, c.at_login, cp.entry, cp.modelid, cp.level, c.equipmentCache, " - "cb.guid, c.slot, cd.genitive FROM characters AS c LEFT JOIN character_pet AS cp ON c.guid = cp.owner AND cp.active = 1 " + "c.position_x, c.position_y, c.position_z, gm.guildid, c.playerFlags, c.at_login, cp.CreatureId, cp.TamedCreatureId, cp.DisplayId, c.equipmentCache, " + "cb.guid, c.slot, cd.genitive FROM characters AS c LEFT JOIN character_pet AS cp ON c.guid = cp.Guid AND cp.IsActive = 1 " "LEFT JOIN character_declinedname AS cd ON c.guid = cd.guid LEFT JOIN guild_member AS gm ON c.guid = gm.guid " "LEFT JOIN character_banned AS cb ON c.guid = cb.guid AND cb.active = 1 WHERE c.account = ? AND c.deleteInfos_Name IS NULL", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_FREE_NAME, "SELECT guid, name, at_login FROM characters WHERE guid = ? AND account = ? AND NOT EXISTS (SELECT NULL FROM characters WHERE name = ?)", CONNECTION_ASYNC); @@ -611,19 +611,13 @@ void CharacterDatabaseConnection::DoPrepareStatements() PrepareStatement(CHAR_DEL_CALENDAR_INVITE, "DELETE FROM calendar_invites WHERE id = ?", CONNECTION_ASYNC); // Pet - PrepareStatement(CHAR_SEL_PET_SLOTS, "SELECT owner, slot FROM character_pet WHERE owner = ? AND slot >= ? AND slot <= ? ORDER BY slot", CONNECTION_ASYNC); - PrepareStatement(CHAR_SEL_PET_SLOTS_DETAIL, "SELECT slot, id, entry, modelid, level, name FROM character_pet WHERE owner = ? AND slot >= ? AND slot <= ? ORDER BY slot", CONNECTION_ASYNC); - PrepareStatement(CHAR_SEL_PET_ENTRY, "SELECT entry FROM character_pet WHERE owner = ? AND id = ? AND slot >= ? AND slot <= ?", CONNECTION_ASYNC); - PrepareStatement(CHAR_SEL_PET_SLOT_BY_ID, "SELECT slot, id, entry FROM character_pet WHERE owner = ? AND id = ?", CONNECTION_ASYNC); - PrepareStatement(CHAR_SEL_PET_SPELL_LIST, "SELECT DISTINCT pet_spell.spell FROM pet_spell, character_pet WHERE character_pet.owner = ? AND character_pet.id = pet_spell.guid AND character_pet.id <> ?", CONNECTION_SYNCH); - PrepareStatement(CHAR_SEL_CHAR_PETS, "SELECT id FROM character_pet WHERE owner = ?", CONNECTION_SYNCH); - PrepareStatement(CHAR_DEL_CHAR_PET_DECLINEDNAME_BY_OWNER, "DELETE FROM character_pet_declinedname WHERE owner = ?", CONNECTION_ASYNC); - PrepareStatement(CHAR_DEL_CHAR_PET_DECLINEDNAME, "DELETE FROM character_pet_declinedname WHERE id = ?", CONNECTION_ASYNC); - PrepareStatement(CHAR_INS_CHAR_PET_DECLINEDNAME, "INSERT INTO character_pet_declinedname (id, owner, genitive, dative, accusative, instrumental, prepositional) VALUES (?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC); + //PrepareStatement(CHAR_DEL_CHAR_PET_DECLINEDNAME_BY_OWNER, "DELETE FROM character_pet_declinedname WHERE Guid = ?", CONNECTION_ASYNC); + //PrepareStatement(CHAR_DEL_CHAR_PET_DECLINEDNAME, "DELETE FROM character_pet_declinedname WHERE PetNumber = ?", CONNECTION_ASYNC); + //PrepareStatement(CHAR_INS_CHAR_PET_DECLINEDNAME, "INSERT INTO character_pet_declinedname (PetNumber, Guid, genitive, dative, accusative, instrumental, prepositional) VALUES (?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_PET_AURA, "SELECT casterGuid, spell, effectMask, recalculateMask, stackCount, amount0, amount1, amount2, base_amount0, base_amount1, base_amount2, maxDuration, remainTime, remainCharges, critChance, applyResilience FROM pet_aura WHERE guid = ?", CONNECTION_SYNCH); PrepareStatement(CHAR_SEL_PET_SPELL, "SELECT spell, active FROM pet_spell WHERE guid = ?", CONNECTION_SYNCH); PrepareStatement(CHAR_SEL_PET_SPELL_COOLDOWN, "SELECT spell, time, categoryId, categoryEnd FROM pet_spell_cooldown WHERE guid = ? AND time > UNIX_TIMESTAMP()", CONNECTION_SYNCH); - PrepareStatement(CHAR_SEL_PET_DECLINED_NAME, "SELECT genitive, dative, accusative, instrumental, prepositional FROM character_pet_declinedname WHERE owner = ? AND id = ?", CONNECTION_SYNCH); + PrepareStatement(CHAR_SEL_PET_DECLINED_NAME, "SELECT genitive, dative, accusative, instrumental, prepositional FROM character_pet_declinedname WHERE Guid = ? AND PetNumber = ?", CONNECTION_SYNCH); PrepareStatement(CHAR_DEL_PET_AURAS, "DELETE FROM pet_aura WHERE guid = ?", CONNECTION_BOTH); PrepareStatement(CHAR_DEL_PET_SPELLS, "DELETE FROM pet_spell WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_PET_SPELL_COOLDOWNS, "DELETE FROM pet_spell_cooldown WHERE guid = ?", CONNECTION_BOTH); @@ -632,14 +626,12 @@ void CharacterDatabaseConnection::DoPrepareStatements() PrepareStatement(CHAR_INS_PET_SPELL, "INSERT INTO pet_spell (guid, spell, active) VALUES (?, ?, ?)", CONNECTION_BOTH); PrepareStatement(CHAR_INS_PET_AURA, "INSERT INTO pet_aura (guid, casterGuid, spell, effectMask, recalculateMask, stackCount, amount0, amount1, amount2, " "base_amount0, base_amount1, base_amount2, maxDuration, remainTime, remainCharges, critChance, applyResilience) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_BOTH); - PrepareStatement(CHAR_DEL_CHAR_PET_BY_OWNER, "DELETE FROM character_pet WHERE owner = ?", CONNECTION_ASYNC); - PrepareStatement(CHAR_UPD_CHAR_PET_NAME, "UPDATE character_pet SET name = ?, renamed = 1 WHERE owner = ? AND id = ?", CONNECTION_ASYNC); - PrepareStatement(CHAR_UPD_CHAR_PET_SLOT_BY_SLOT, "UPDATE character_pet SET slot = ? WHERE owner = ? AND slot = ?", CONNECTION_ASYNC); - PrepareStatement(CHAR_UPD_CHAR_PET_SLOT_BY_ID, "UPDATE character_pet SET slot = ? WHERE owner = ? AND id = ?", CONNECTION_ASYNC); - PrepareStatement(CHAR_DEL_CHAR_PET_BY_ID, "DELETE FROM character_pet WHERE id = ?", CONNECTION_ASYNC); - PrepareStatement(CHAR_INS_PET, "INSERT INTO character_pet (id, entry, owner, modelid, level, exp, Reactstate, slot, name, renamed, active, curhealth, curmana, abdata, savetime, CreatedBySpell, PetType) " - "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC); - PrepareStatement(CHAR_SEL_CHAR_ALL_PETS_DETAIL, "SELECT id, entry, owner, modelid, level, exp, Reactstate, slot, name, renamed, active, curhealth, curmana, abdata, savetime, CreatedBySpell, PetType FROM character_pet WHERE owner = ? ORDER BY slot", CONNECTION_ASYNC); + + PrepareStatement(CHAR_DEL_CHAR_PETS, "DELETE FROM character_pet WHERE Guid = ?", CONNECTION_ASYNC); + PrepareStatement(CHAR_DEL_CHAR_PET, "DELETE FROM character_pet WHERE PetNumber = ?", CONNECTION_BOTH); + PrepareStatement(CHAR_SEL_CHAR_PET, "SELECT PetNumber, CreatureId, TamedCreatureId, DisplayId, SavedHealth, SavedPower, CreatedBySpellId, LastSaveTime, ReactState, Slot, HasBeenRenamed, IsActive, `Name`, ActionBar FROM character_pet WHERE Guid = ? ORDER BY Slot", CONNECTION_ASYNC); + PrepareStatement(CHAR_UPD_CHAR_PET, "UPDATE character_pet SET SavedHealth = ?, SavedPower = ?, LastSaveTime = ?, ReactState = ?, Slot = ?, HasBeenRenamed = ?, IsActive = ?, `Name` = ?, ActionBar = ? WHERE PetNumber = ?", CONNECTION_ASYNC); + PrepareStatement(CHAR_INS_CHAR_PET, "INSERT INTO character_pet (Guid, PetNumber, CreatureId, TamedCreatureId, DisplayId, SavedHealth, SavedPower, CreatedBySpellId, LastSaveTime, ReactState, Slot, HasBeenRenamed, IsActive, `Name`, ActionBar) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_BOTH); // PvPstats PrepareStatement(CHAR_SEL_PVPSTATS_MAXID, "SELECT MAX(id) FROM pvpstats_battlegrounds", CONNECTION_SYNCH); diff --git a/src/server/database/Database/Implementation/CharacterDatabase.h b/src/server/database/Database/Implementation/CharacterDatabase.h index db746cf3a59..3e8f7cf53b6 100644 --- a/src/server/database/Database/Implementation/CharacterDatabase.h +++ b/src/server/database/Database/Implementation/CharacterDatabase.h @@ -529,24 +529,13 @@ enum CharacterDatabaseStatements : uint32 CHAR_DEL_PET_SPELL_BY_SPELL, CHAR_INS_PET_SPELL, CHAR_INS_PET_AURA, - CHAR_DEL_PET_SPELLS, - CHAR_DEL_CHAR_PET_BY_OWNER, - CHAR_DEL_CHAR_PET_DECLINEDNAME_BY_OWNER, - CHAR_SEL_PET_SLOTS, - CHAR_SEL_PET_SLOTS_DETAIL, - CHAR_SEL_PET_ENTRY, - CHAR_SEL_PET_SLOT_BY_ID, - CHAR_SEL_PET_SPELL_LIST, - CHAR_SEL_CHAR_PETS, - CHAR_DEL_CHAR_PET_DECLINEDNAME, - CHAR_INS_CHAR_PET_DECLINEDNAME, - CHAR_UPD_CHAR_PET_NAME, - CHAR_UPD_CHAR_PET_SLOT_BY_SLOT, - CHAR_UPD_CHAR_PET_SLOT_BY_ID, - CHAR_DEL_CHAR_PET_BY_ID, - CHAR_INS_PET, - CHAR_SEL_CHAR_ALL_PETS_DETAIL, + + CHAR_INS_CHAR_PET, + CHAR_DEL_CHAR_PET, + CHAR_DEL_CHAR_PETS, + CHAR_UPD_CHAR_PET, + CHAR_SEL_CHAR_PET, CHAR_SEL_ITEMCONTAINER_ITEMS, CHAR_DEL_ITEMCONTAINER_ITEMS, diff --git a/src/server/game/AI/CreatureAI.cpp b/src/server/game/AI/CreatureAI.cpp index a7be872a2e1..40ad0edba8a 100644 --- a/src/server/game/AI/CreatureAI.cpp +++ b/src/server/game/AI/CreatureAI.cpp @@ -31,6 +31,7 @@ #include "SpellMgr.h" #include "SpellHistory.h" #include "TemporarySummon.h" +#include "NewTemporarySummon.h" #include "Vehicle.h" #include "World.h" @@ -93,9 +94,6 @@ void CreatureAI::DoZoneInCombat(Creature* creature /*= nullptr*/) creature->EngageWithTarget(player); - for (Unit* pet : player->m_Controlled) - creature->EngageWithTarget(pet); - if (Unit* vehicle = player->GetVehicleBase()) creature->EngageWithTarget(vehicle); } @@ -155,56 +153,20 @@ void CreatureAI::TriggerAlert(Unit const* who) const me->SetFacingTo(me->GetAngle(who)); } -// adapted from logic in Spell:EFfectSummonType before commit 8499434 -static bool ShouldFollowOnSpawn(SummonPropertiesEntry const* properties) -{ - // Summons without SummonProperties are generally scripted summons that don't belong to any owner - if (!properties) - return false; - - switch (properties->Control) - { - case SUMMON_CATEGORY_PET: - return true; - case SUMMON_CATEGORY_WILD: - case SUMMON_CATEGORY_ALLY: - case SUMMON_CATEGORY_UNK: - if (properties->Flags & 512) - return true; - - // Guides. They have their own movement - if (properties->Flags & SUMMON_PROP_FLAG_UNK14) - return false; - - switch (SummonTitle(properties->Title)) - { - case SummonTitle::Pet: - case SummonTitle::Guardian: - case SummonTitle::Runeblade: - case SummonTitle::Minion: - case SummonTitle::Companion: - return true; - default: - return false; - } - default: - return false; - } -} void CreatureAI::JustAppeared() { if (!IsEngaged()) { - if (TempSummon* summon = me->ToTempSummon()) + if (!me->IsSummon()) + return; + + NewTemporarySummon* summon = me->ToTemporarySummon(); + if (summon->ShouldJoinSummonerSpawnGroupAfterCreation() || summon->ShouldFollowSummonerAfterCreation() && !summon->GetVehicle()) { - // Only apply this to specific types of summons - if (!summon->GetVehicle() && ShouldFollowOnSpawn(summon->m_Properties)) + if (Unit* summoner = summon->GetSummoner()) { - if (Unit* owner = summon->GetCharmerOrOwner()) - { - summon->GetMotionMaster()->Clear(); - summon->FollowTarget(owner); - } + summon->GetMotionMaster()->Clear(); + summon->FollowTarget(summoner); // @todo: ShouldJoinSummonerSpawnGroupAfterCreation should actually make the creature join the target's formation } } } diff --git a/src/server/game/AI/CreatureAISelector.cpp b/src/server/game/AI/CreatureAISelector.cpp index 91c8cb792de..5e15135e773 100644 --- a/src/server/game/AI/CreatureAISelector.cpp +++ b/src/server/game/AI/CreatureAISelector.cpp @@ -81,8 +81,8 @@ namespace FactorySelector CreatureAI* SelectAI(Creature* creature) { // special pet case, if a tamed creature uses AIName (example SmartAI) we need to override it - if (creature->IsPet()) - return ASSERT_NOTNULL(sCreatureAIRegistry->GetRegistryItem("PetAI"))->Create(creature); + //if (creature->IsPet()) + // return ASSERT_NOTNULL(sCreatureAIRegistry->GetRegistryItem("PetAI"))->Create(creature); // scriptname in db try diff --git a/src/server/game/Battlegrounds/Battleground.cpp b/src/server/game/Battlegrounds/Battleground.cpp index b1cdab7683f..8b2e002f150 100644 --- a/src/server/game/Battlegrounds/Battleground.cpp +++ b/src/server/game/Battlegrounds/Battleground.cpp @@ -979,7 +979,7 @@ void Battleground::RemovePlayerAtLeave(ObjectGuid guid, bool Transport, bool Sen bgTypeId = BATTLEGROUND_AA; // set the bg type to all arenas (it will be used for queue refreshing) // unsummon current and summon old pet if there was one and there isn't a current pet - player->RemovePet(nullptr, PET_SAVE_DISMISS); player->ResummonPetTemporaryUnSummonedIfAny(); + //player->RemovePet(nullptr, PET_SAVE_DISMISS); player->ResummonPetTemporaryUnSummonedIfAny(); } if (SendPacket) @@ -1117,7 +1117,7 @@ void Battleground::AddPlayer(Player* player) { player->RemoveArenaEnchantments(TEMP_ENCHANTMENT_SLOT); player->DestroyConjuredItems(true); - player->UnsummonPetTemporaryIfAny(); + //player->UnsummonPetTemporaryIfAny(); if (GetStatus() == STATUS_WAIT_JOIN) // not started yet { diff --git a/src/server/game/DataStores/DBCEnums.h b/src/server/game/DataStores/DBCEnums.h index 6e30aaee2ee..0ecde24789b 100644 --- a/src/server/game/DataStores/DBCEnums.h +++ b/src/server/game/DataStores/DBCEnums.h @@ -679,31 +679,30 @@ enum class SummonPropertiesSlot : int8 AnyAvailableTotem = -1 }; -// SummonProperties.dbc, col 5 enum class SummonPropertiesFlags : uint32 { None = 0x000000, - AttackSummoner = 0x000001, - HelpWhenSummonedInCombat = 0x000002, - UseLevelOffset = 0x000004, - DespawnOnSummonerDeath = 0x000008, - OnlyVisibleToSummoner = 0x000010, - CannotDismissPet = 0x000020, - UseDemonTimeout = 0x000040, - UnlimitedSummons = 0x000080, - UseCreatureLevel = 0x000100, - JoinSummonerSpawnGroup = 0x000200, - DoNotToggle = 0x000400, - DespawnWhenExpired = 0x000800, - UseSummonerFaction = 0x001000, - DoNotFollowMountedSummoner = 0x002000, - SavePetAutocast = 0x004000, - IgnoreSummonerPhase = 0x008000, - OnlyVisibleToSummonerGroup = 0x010000, - DespawnOnSummonerLogout = 0x020000, - CastRideVehicleSpellOnSummoner = 0x040000, - GuardianActsLikePet = 0x080000, - DontSnapSessileToGround = 0x100000 + AttackSummoner = 0x000001, // Implemented in TemporarySummon::HandlePostSummonActions + HelpWhenSummonedInCombat = 0x000002, // Implemented in TemporarySummon::HandlePostSummonActions + UseLevelOffset = 0x000004, // NYI + DespawnOnSummonerDeath = 0x000008, // Implemented in Unit::UnsummonAllSummonsDueToDeath + OnlyVisibleToSummoner = 0x000010, // Implemented in Spell::EffectSummonType + CannotDismissPet = 0x000020, // Implemented in PetHandler.cpp HandlePetActionHelper + UseDemonTimeout = 0x000040, // NYI + UnlimitedSummons = 0x000080, // NYI + UseCreatureLevel = 0x000100, // Implemented in TemporarySummon::HandlePreSummonActions + JoinSummonerSpawnGroup = 0x000200, // Implemented in CreatureAI::JustAppeared + DoNotToggle = 0x000400, // NYI + DespawnWhenExpired = 0x000800, // Implemented in TemporarySummon::Update + UseSummonerFaction = 0x001000, // Implemented in TemporarySummon::HandlePreSummonActions + DoNotFollowMountedSummoner = 0x002000, // NYI + SavePetAutocast = 0x004000, // NYI + IgnoreSummonerPhase = 0x008000, // Wild Only - Implemented in Map::SummonCreature + OnlyVisibleToSummonerGroup = 0x010000, // Implemented in Spell::EffectSummonType + DespawnOnSummonerLogout = 0x020000, // Implemented in Unit::UnsummonAllSummonsOnLogout + CastRideVehicleSpellOnSummoner = 0x040000, // NYI + GuardianActsLikePet = 0x080000, // NYI - unused 4.3.4.15595 + DontSnapSessileToGround = 0x100000 // NYI }; DEFINE_ENUM_FLAG(SummonPropertiesFlags); diff --git a/src/server/game/DataStores/DBCStores.cpp b/src/server/game/DataStores/DBCStores.cpp index 6542d5dc9c2..f0340366402 100644 --- a/src/server/game/DataStores/DBCStores.cpp +++ b/src/server/game/DataStores/DBCStores.cpp @@ -637,10 +637,6 @@ void DBCManager::LoadStores(const std::string& dataPath, uint32 defaultLocale) if (!spellInfo) continue; - SpellLevelsEntry const* levels = sSpellLevelsStore.LookupEntry(spellInfo->LevelsID); - if (spellInfo->LevelsID && (!levels || levels->SpellLevel)) - continue; - if (spellInfo && spellInfo->Attributes & SPELL_ATTR0_PASSIVE) { for (CreatureFamilyEntry const* cFamily : sCreatureFamilyStore) diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index da81949dc6f..23f0f1dd36e 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -50,6 +50,7 @@ #include "SpellAuraEffects.h" #include "SpellMgr.h" #include "TemporarySummon.h" +#include "NewTemporarySummon.h" #include "Transport.h" #include "Util.h" #include "Vehicle.h" @@ -454,6 +455,12 @@ void Creature::RemoveCorpse(bool setSpawnTime, bool destroyForNearbyPlayers) SaveRespawnTime(); } + if (NewTemporarySummon* summon = ToTemporarySummon()) + { + summon->Unsummon(); + return; + } + if (TempSummon* summon = ToTempSummon()) summon->UnSummon(); else @@ -1083,15 +1090,6 @@ Unit* Creature::SelectVictim() target = owner->getAttackerForHelper(); if (!target) { - for (ControlList::const_iterator itr = owner->m_Controlled.begin(); itr != owner->m_Controlled.end(); ++itr) - { - if ((*itr)->IsInCombat()) - { - target = (*itr)->getAttackerForHelper(); - if (target) - break; - } - } } } } diff --git a/src/server/game/Entities/Creature/TemporarySummon.cpp b/src/server/game/Entities/Creature/TemporarySummon.cpp index b644a69faf1..78a82a44cdd 100644 --- a/src/server/game/Entities/Creature/TemporarySummon.cpp +++ b/src/server/game/Entities/Creature/TemporarySummon.cpp @@ -31,7 +31,7 @@ m_timer(0), m_lifetime(0) if (owner) m_summonerGUID = owner->GetGUID(); - m_unitTypeMask |= UNIT_MASK_SUMMON; + m_unitTypeMask |= UNIT_MASK_SUMMON_OLD; } Unit* TempSummon::GetSummoner() const @@ -183,16 +183,6 @@ void TempSummon::InitStats(uint32 duration) if (owner) { int32 slot = m_Properties->Slot; - if (slot > 0) - { - if (owner->m_SummonSlot[slot] && owner->m_SummonSlot[slot] != GetGUID()) - { - Creature* oldSummon = GetMap()->GetCreature(owner->m_SummonSlot[slot]); - if (oldSummon && oldSummon->IsSummon()) - oldSummon->ToTempSummon()->UnSummon(); - } - owner->m_SummonSlot[slot] = GetGUID(); - } if (m_Properties->Control != SUMMON_CATEGORY_WILD) { @@ -213,9 +203,6 @@ void TempSummon::InitStats(uint32 duration) } } } - - if (owner->IsTotem()) - owner->m_Controlled.insert(this); } // If property has a faction defined, use it. @@ -285,11 +272,6 @@ void TempSummon::RemoveFromWorld() if (m_Properties) { - int32 slot = m_Properties->Slot; - if (slot > 0) - if (Unit* owner = GetSummoner()) - if (owner->m_SummonSlot[slot] == GetGUID()) - owner->m_SummonSlot[slot].Clear(); } //if (GetOwnerGUID()) @@ -309,20 +291,6 @@ void Minion::InitStats(uint32 duration) { TempSummon::InitStats(duration); SetReactState(REACT_PASSIVE); - - // Controlable guardians and minions shall receive a summoner guid - if ((IsMinion() || IsControlableGuardian()) && !IsTotem() && !IsVehicle()) - GetOwner()->SetMinion(this, true); - else if (!IsPet() && !IsHunterPet()) - { - GetOwner()->m_Controlled.insert(this); - - // Store the totem elementals in players controlled list as well to trigger aggro mechanics - if (GetOwner()->IsTotem()) - if (Unit* totemOwner = GetOwner()->GetOwner()) - totemOwner->m_Controlled.insert(this); - } - if (m_Properties && m_Properties->Slot == SUMMON_SLOT_MINIPET) { SelectLevel(); // some summoned creaters have different from 1 DB data for level/hp @@ -338,21 +306,6 @@ void Minion::RemoveFromWorld() Unit* owner = GetOwner(); - if ((IsMinion() || IsControlableGuardian()) && !IsTotem() && !IsVehicle()) - owner->SetMinion(this, false); - else if (!IsPet() && !IsHunterPet()) - { - if (owner->m_Controlled.find(this) != owner->m_Controlled.end()) - owner->m_Controlled.erase(this); - else - TC_LOG_FATAL("entities.unit", "Minion::RemoveFromWorld: Owner %s tried to remove a non-existing controlled unit %s from controlled unit set.", owner->GetGUID().ToString().c_str(), GetGUID().ToString().c_str()); - - if (owner->IsTotem()) - if (Unit* totemOwner = owner->GetOwner()) - if (totemOwner->m_Controlled.find(this) != totemOwner->m_Controlled.end()) - totemOwner->m_Controlled.erase(this); - } - TempSummon::RemoveFromWorld(); } @@ -403,13 +356,6 @@ void Guardian::InitStats(uint32 duration) 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) diff --git a/src/server/game/Entities/Creature/TemporarySummon.h b/src/server/game/Entities/Creature/TemporarySummon.h index 39de736d898..0939aacee8e 100644 --- a/src/server/game/Entities/Creature/TemporarySummon.h +++ b/src/server/game/Entities/Creature/TemporarySummon.h @@ -63,9 +63,6 @@ enum PlayerPetSpells // Risen Ghoul SPELL_PET_RISEN_GHOUL_SPAWN_IN = 47448, SPELL_PET_RISEN_GHOUL_SELF_STUN = 47466, - - // Hunter Pets - SPELL_PET_ENERGIZE = 99289 }; class TC_GAME_API TempSummon : public Creature diff --git a/src/server/game/Entities/Creature/TemporarySummon/NewGuardian.cpp b/src/server/game/Entities/Creature/TemporarySummon/NewGuardian.cpp new file mode 100644 index 00000000000..9f3d6927436 --- /dev/null +++ b/src/server/game/Entities/Creature/TemporarySummon/NewGuardian.cpp @@ -0,0 +1,110 @@ + +#include "NewGuardian.h" +#include "DBCStores.h" +#include "ObjectMgr.h" +#include "SpellMgr.h" +#include "SpellInfo.h" + +NewGuardian::NewGuardian(SummonPropertiesEntry const* properties, Unit* summoner, bool isWorldObject) : + NewTemporarySummon(properties, summoner, isWorldObject), _isUsingRealStats(false) +{ + m_unitTypeMask |= UNIT_MASK_GUARDIAN; +} + +void NewGuardian::AddToWorld() +{ + // Setting the guid before adding to world to reduce building unnecessary object update packets + SetCreatorGUID(GetInternalSummonerGUID()); + + NewTemporarySummon::AddToWorld(); +} + +bool NewGuardian::HandlePreSummonActions(Unit const* summoner, uint8 creatureLevel, uint8 maxSummons) +{ + if (!NewTemporarySummon::HandlePreSummonActions(summoner, creatureLevel, maxSummons)) + return false; + + // Guardians inherit their summoner's faction and level unless a flag forbids it or the properties override it + if (summoner) + { + if (!_summonProperties || _summonProperties->Faction == 0) + SetFaction(summoner->GetFaction()); + + if (!_summonProperties || !_summonProperties->GetFlags().HasFlag(SummonPropertiesFlags::UseCreatureLevel)) + SetLevel(summoner->getLevel()); + } + + InitializeStats(); + + return true; +} + +void NewGuardian::InitializeStats() +{ + CreatureTemplate const* creatureInfo = GetCreatureTemplate(); + if (!creatureInfo) + return; + + SetMeleeDamageSchool(SpellSchools(creatureInfo->dmgschool)); + + if (PetLevelInfo const* petLevelInfo = sObjectMgr->GetPetLevelInfo(creatureInfo->Entry, getLevel())) + { + for (uint8 i = 0; i < MAX_STATS; ++i) + SetCreateStat(Stats(i), float(petLevelInfo->stats[i])); + + if (petLevelInfo->armor > 0) + SetStatFlatModifier(UNIT_MOD_ARMOR, BASE_VALUE, float(petLevelInfo->armor)); + + SetCreateHealth(petLevelInfo->health); + SetCreateMana(petLevelInfo->mana); + + _isUsingRealStats = true; + } + else + { + // Guardian has no pet level data, fall back to default creature behavior of Creature::UpdateEntry + uint32 previousHealth = GetHealth(); + UpdateLevelDependantStats(); + if (previousHealth > 0) + SetHealth(previousHealth); + + SetMeleeDamageSchool(SpellSchools(creatureInfo->dmgschool)); + SetStatFlatModifier(UNIT_MOD_RESISTANCE_HOLY, BASE_VALUE, float(creatureInfo->resistance[SPELL_SCHOOL_HOLY])); + SetStatFlatModifier(UNIT_MOD_RESISTANCE_FIRE, BASE_VALUE, float(creatureInfo->resistance[SPELL_SCHOOL_FIRE])); + SetStatFlatModifier(UNIT_MOD_RESISTANCE_NATURE, BASE_VALUE, float(creatureInfo->resistance[SPELL_SCHOOL_NATURE])); + SetStatFlatModifier(UNIT_MOD_RESISTANCE_FROST, BASE_VALUE, float(creatureInfo->resistance[SPELL_SCHOOL_FROST])); + SetStatFlatModifier(UNIT_MOD_RESISTANCE_SHADOW, BASE_VALUE, float(creatureInfo->resistance[SPELL_SCHOOL_SHADOW])); + SetStatFlatModifier(UNIT_MOD_RESISTANCE_ARCANE, BASE_VALUE, float(creatureInfo->resistance[SPELL_SCHOOL_ARCANE])); + + SetCanModifyStats(true); + } + + UpdateAllStats(); +} + +void NewGuardian::HandlePostSummonActions() +{ + NewTemporarySummon::HandlePostSummonActions(); + + //CastPassiveAuras(); +} + +void NewGuardian::CastPassiveAuras() +{ + CreatureTemplate const* creatureInfo = GetCreatureTemplate(); + if (!creatureInfo) + return; + + CreatureFamilyEntry const* creatureFamilyEntry = sCreatureFamilyStore.LookupEntry(creatureInfo->family); + if (!creatureFamilyEntry) + return; + + PetFamilySpellsStore::const_iterator petSpellStore = sPetFamilySpellsStore.find(creatureFamilyEntry->ID); + if (petSpellStore == sPetFamilySpellsStore.end()) + return; + + for (uint32 spellId : petSpellStore->second) + if (SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId)) + if (spellInfo->IsPassive()) + CastSpell(this, spellId); +} diff --git a/src/server/game/Entities/Creature/TemporarySummon/NewGuardian.h b/src/server/game/Entities/Creature/TemporarySummon/NewGuardian.h new file mode 100644 index 00000000000..6150dbe9b94 --- /dev/null +++ b/src/server/game/Entities/Creature/TemporarySummon/NewGuardian.h @@ -0,0 +1,51 @@ +/* + * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#ifndef Guardian_h__ +#define Guardian_h__ + +#include "NewTemporarySummon.h" + +class TC_GAME_API NewGuardian : public NewTemporarySummon +{ +public: + explicit NewGuardian(SummonPropertiesEntry const* properties, Unit* owner, bool isWorldObject); + + void AddToWorld() override; + + // Stats handling + bool UpdateAllStats() override; + bool UpdateStats(Stats stat) 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; + + bool HandlePreSummonActions(Unit const* summoner, uint8 creatureLevel, uint8 maxSummons) override; + void HandlePostSummonActions() override; + bool IsUsingRealStats() const { return _isUsingRealStats; } + void InitializeStats(); + +protected: + void CastPassiveAuras(); + + bool _isUsingRealStats; +}; + +#endif // Guardian_h__ diff --git a/src/server/game/Entities/Creature/TemporarySummon/NewPet.cpp b/src/server/game/Entities/Creature/TemporarySummon/NewPet.cpp new file mode 100644 index 00000000000..a644d70cd2e --- /dev/null +++ b/src/server/game/Entities/Creature/TemporarySummon/NewPet.cpp @@ -0,0 +1,447 @@ + +#include "NewPet.h" +#include "DBCStores.h" +#include "GameTime.h" +#include "Map.h" +#include "ObjectMgr.h" +#include "PetPackets.h" +#include "Player.h" +#include "SpellMgr.h" +#include "SpellInfo.h" + +NewPet::NewPet(SummonPropertiesEntry const* properties, Unit* summoner, bool isWorldObject, bool isClassPet) : + NewGuardian(properties, summoner, isWorldObject), _isClassPet(isClassPet) +{ + m_unitTypeMask |= UNIT_MASK_PET; + InitCharmInfo(); +} + +void NewPet::AddToWorld() +{ + // Setting the guid before adding to world to reduce building unnecessary object update packets + SetSummonerGUID(GetInternalSummonerGUID()); + + NewGuardian::AddToWorld(); + + if (Unit* summoner = GetInternalSummoner()) + summoner->SetActivelyControlledSummon(this, true); +} + +void NewPet::RemoveFromWorld() +{ + if (Unit* summoner = GetInternalSummoner()) + summoner->SetActivelyControlledSummon(this, false); + + NewGuardian::RemoveFromWorld(); +} + +bool NewPet::HandlePreSummonActions(Unit const* summoner, uint8 creatureLevel, uint8 maxSummons) +{ + if (!NewGuardian::HandlePreSummonActions(summoner, creatureLevel, maxSummons)) + return false; + + // Pets are initialized with REACT_ASSIST by default. Player pet data will override this in Unit::SummonPet + SetReactState(REACT_ASSIST); + + // Initialize the action bar after initializing the stats + if (IsClassPet()) + { + m_charmInfo->InitPetActionBar(); + LearnAvailableSpellsForCurrentLevel(); + } + else + m_charmInfo->InitCharmCreateSpells(); + + return true; +} + +void NewPet::HandlePostSummonActions() +{ + NewGuardian::HandlePostSummonActions(); +} + +bool NewPet::CanBeDismissed() const +{ + if (_summonProperties && _summonProperties->GetFlags().HasFlag(SummonPropertiesFlags::CannotDismissPet)) + return false; + + return true; +} + +PlayerPetData* NewPet::GetOrCreatePlayerPetData(Player* summoner, uint8 slot, uint32 creatureId) +{ + if (!summoner) + return nullptr; + + PlayerPetData* petData = summoner->GetPlayerPetData(slot, creatureId); + if (!petData && creatureId) // only create new pet data for non-hunter pets. Hunter pets do not have a creatureId so consequently we do not want to use it here. + petData = summoner->CreatePlayerPetData(slot, creatureId); + + return petData; +} + +bool NewPet::Create(ObjectGuid::LowType guidlow, Map* map, uint32 entry, uint32 petNumber) +{ + ASSERT(map); + SetMap(map); + + Object::_Create(guidlow, petNumber, HighGuid::Pet); + + m_spawnId = guidlow; + m_originalEntry = entry; + + if (!InitEntry(entry)) + return false; + + // Force regen flag for player pets, just like we do for players themselves + SetFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_REGENERATE_POWER); + SetSheath(SHEATH_STATE_MELEE); + SetUInt32Value(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_NONE); + SetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP, 0); + + // Patch 4.1.0 (2011-04-26): Pets will now level with hunters in the same way warlock pets currently do. + SetUInt32Value(UNIT_FIELD_PETEXPERIENCE, 0); + SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP, 0); + + GetThreatManager().Initialize(); + + return true; +} + +void NewPet::SynchronizeLevelWithSummoner() +{ + if (!IsClassPet()) + return; + + Unit const* summoner = GetInternalSummoner(); + if (!summoner || getLevel() == summoner->getLevel()) + return; + + SetLevel(summoner->getLevel()); + InitializeStats(); + + LearnAvailableSpellsForCurrentLevel(); +} + +bool NewPet::HasSpell(uint32 spellId) const +{ + auto itr = _spells.find(spellId); + return itr != _spells.end() && itr->second.State != PETSPELL_REMOVED; +} + +void NewPet::ToggleAutocast(SpellInfo const* spellInfo, bool apply) +{ + if (!spellInfo || !spellInfo->IsAutocastable()) + return; + + auto itr = _spells.find(spellInfo->Id); + if (itr == _spells.end()) + return; + + if (apply) + { + if (itr->second.Active != ACT_ENABLED) + { + itr->second.Active = ACT_ENABLED; + if (itr->second.State != PETSPELL_NEW) + itr->second.State = PETSPELL_CHANGED; + } + } + else + { + if (itr->second.Active != ACT_DISABLED) + { + itr->second.Active = ACT_DISABLED; + if (itr->second.State != PETSPELL_NEW) + itr->second.State = PETSPELL_CHANGED; + } + } +} + +void NewPet::SetPlayerPetDataKey(uint8 slot, uint32 creatureId) +{ + _playerPetDataKey = std::make_pair(slot, creatureId); +} + +void NewPet::UpdatePlayerPetData(Player* summoner, bool isActivePet) +{ + if (!summoner || !_playerPetDataKey.has_value()) + return; + + PlayerPetData* playerPetData = summoner->GetPlayerPetData(_playerPetDataKey->first, _playerPetDataKey->second); + if (!playerPetData || playerPetData->PetNumber != GetUInt32Value(UNIT_FIELD_PETNUMBER)) + return; + + playerPetData->IsActive = isActivePet; + playerPetData->SavedHealth = GetHealth(); + playerPetData->SavedPower = GetPower(GetPowerType()); + playerPetData->LastSaveTime = static_cast(GameTime::GetGameTime()); + playerPetData->ReactState = GetReactState(); + playerPetData->Slot = _playerPetDataKey->first; + playerPetData->Name = GetName(); + playerPetData->ActionBar = GenerateActionBarDataString(); + + if (playerPetData->Status != PlayerPetDataStatus::New) + playerPetData->Status = PlayerPetDataStatus::Changed; +} + +// ------------- private methods + +void NewPet::SendSpellLearnedToSummoner(uint32 spellId) +{ + Unit const* summoner = GetInternalSummoner(); + if (!summoner || !summoner->IsPlayer()) + return; + + summoner->ToPlayer()->SendDirectMessage(WorldPackets::Pet::PetLearnedSpell(spellId).Write()); +} + +void NewPet::SendSpellUnlearnedToSummoner(uint32 spellId) +{ + Unit const* summoner = GetInternalSummoner(); + if (!summoner || !summoner->IsPlayer()) + return; + + summoner->ToPlayer()->SendDirectMessage(WorldPackets::Pet::PetUnlearnedSpell(spellId).Write()); +} + +void NewPet::LearnAvailableSpellsForCurrentLevel() +{ + if (PetLevelupSpellSet const* levelupSpells = GetCreatureTemplate()->family ? sSpellMgr->GetPetLevelupSpellList(GetCreatureTemplate()->family) : nullptr) + { + // PetLevelupSpellSet ordered by levels + for (auto const& itr : *levelupSpells) + { + if (itr.first > getLevel()) + UnlearnSpell(itr.second, true); + else + LearnSpell(itr.second); + } + } + + int32 petSpellsId = GetCreatureTemplate()->PetSpellDataId ? -(int32)GetCreatureTemplate()->PetSpellDataId : GetEntry(); + + // default spells + if (PetDefaultSpellsEntry const* defSpells = sSpellMgr->GetPetDefaultSpellsEntry(petSpellsId)) + { + for (uint32 spellId : defSpells->spellid) + { + if (!spellId) + continue; + + SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId); + if (!spellInfo) + continue; + + if (spellInfo->SpellLevel > getLevel()) + UnlearnSpell(spellInfo->Id, true); + else + LearnSpell(spellInfo->Id); + } + } +} + +bool NewPet::LearnSpell(uint32 spellId) +{ + if (!AddSpell(spellId)) + return false; + + SendSpellLearnedToSummoner(spellId); + + return true; +} + +bool NewPet::UnlearnSpell(uint32 spellId, bool learnPreviousRank, bool clearActionbar /*= true*/) +{ + if (!RemoveSpell(spellId, learnPreviousRank, clearActionbar)) + return false; + + SendSpellUnlearnedToSummoner(spellId); + return true; +} + +bool NewPet::AddSpell(uint32 spellId, ActiveStates active /*= ACT_DECIDE*/, PetSpellState state /*= PETSPELL_NEW*/, PetSpellType type /*= PETSPELL_NORMAL*/) +{ + SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId); + if (!spellInfo) + return false; + + auto const itr = _spells.find(spellId); + if (itr != _spells.end()) + { + if (itr->second.State == PETSPELL_REMOVED) + state = PETSPELL_CHANGED; + else + { + if (state == PETSPELL_UNCHANGED && itr->second.State != PETSPELL_UNCHANGED) + { + // can be in case spell loading but learned at some previous spell loading + itr->second.State = PETSPELL_UNCHANGED; + + if (active == ACT_ENABLED) + ToggleAutocast(spellInfo, true); + else if (active == ACT_DISABLED) + ToggleAutocast(spellInfo, false); + } + + return false; + } + } + + PetSpell newspell; + newspell.State = state; + newspell.Type = type; + + if (active == ACT_DECIDE) // active was not used before, so we save it's autocast/passive state here + { + if (spellInfo->IsAutocastable()) + newspell.Active = ACT_ENABLED; + else + newspell.Active = ACT_PASSIVE; + } + else + newspell.Active = active; + + // talent: unlearn all other talent ranks (high and low) + if (TalentSpellPos const* talentPos = sDBCManager.GetTalentSpellPos(spellId)) + { + if (TalentEntry const* talentInfo = sTalentStore.LookupEntry(talentPos->talent_id)) + { + for (uint8 i = 0; i < MAX_TALENT_RANK; ++i) + { + // skip learning spell and no rank spell case + uint32 rankSpellId = talentInfo->SpellRank[i]; + if (!rankSpellId || rankSpellId == spellId) + continue; + + // skip unknown ranks + if (!HasSpell(rankSpellId)) + continue; + RemoveSpell(rankSpellId, false, false); + } + } + } + else if (spellInfo->IsRanked()) + { + for (auto const& itr : _spells) + { + if (itr.second.State == PETSPELL_REMOVED) + continue; + + SpellInfo const* oldRankSpellInfo = sSpellMgr->GetSpellInfo(itr.first); + + if (!oldRankSpellInfo) + continue; + + if (spellInfo->IsDifferentRankOf(oldRankSpellInfo)) + { + // replace by new high rank + if (spellInfo->IsHighRankOf(oldRankSpellInfo)) + { + newspell.Active = itr.second.Active; + + if (newspell.Active == ACT_ENABLED) + ToggleAutocast(oldRankSpellInfo, false); + + UnlearnSpell(itr.first, false, false); + break; + } + // ignore new lesser rank + else + return false; + } + } + } + + // Store pet scaling auras in a own vector to handle the updating more efficient + //if (spellInfo->HasAttribute(SPELL_ATTR4_OWNER_POWER_SCALING)) + //{ + // m_petScalingAuras.push_back(spellInfo->Id); + // return true; + //} + + _spells[spellId] = newspell; + + if (spellInfo->IsPassive() && (!spellInfo->CasterAuraState || HasAuraState(AuraStateType(spellInfo->CasterAuraState)))) + CastSpell(this, spellId, true); + else if (!spellInfo->HasAttribute(SPELL_ATTR4_NOT_IN_SPELLBOOK)) + m_charmInfo->AddSpellToActionBar(spellInfo); + + if (newspell.Active == ACT_ENABLED) + ToggleAutocast(spellInfo, true); + + /* + uint32 talentCost = sDBCManager.GetTalentSpellCost(spellId); + if (talentCost) + { + int32 free_points = GetMaxTalentPointsForLevel(getLevel()); + m_usedTalentCount += talentCost; + // update free talent points + free_points -= m_usedTalentCount; + SetFreeTalentPoints(free_points > 0 ? free_points : 0); + } + */ + return true; +} + +bool NewPet::RemoveSpell(uint32 spellId, bool learnPreviousRank, bool clearActionbar /*= true*/) +{ + auto itr = _spells.find(spellId); + if (itr == _spells.end()) + return false; + + if (itr->second.State == PETSPELL_REMOVED) + return false; + + if (itr->second.State == PETSPELL_NEW) + _spells.erase(itr); + else + itr->second.State = PETSPELL_REMOVED; + + RemoveAurasDueToSpell(spellId); + + /* + uint32 talentCost = sDBCManager.GetTalentSpellCost(spell_id); + if (talentCost > 0) + { + if (m_usedTalentCount > talentCost) + m_usedTalentCount -= talentCost; + else + m_usedTalentCount = 0; + // update free talent points + int32 free_points = GetMaxTalentPointsForLevel(getLevel()) - m_usedTalentCount; + SetFreeTalentPoints(free_points > 0 ? free_points : 0); + } + */ + + if (learnPreviousRank) + { + if (uint32 prev_id = sSpellMgr->GetPrevSpellInChain(spellId)) + LearnSpell(prev_id); + else + learnPreviousRank = false; + } + + // if remove last rank or non-ranked then update action bar at server and client if need + if (clearActionbar && !learnPreviousRank && m_charmInfo->RemoveSpellFromActionBar(spellId)) + { + if (Unit* summoner = GetInternalSummoner()) + if (summoner->IsPlayer()) + summoner->ToPlayer()->SendPetSpellsMessage(this); + } + + return true; +} + +std::string NewPet::GenerateActionBarDataString() const +{ + std::ostringstream ss; + + for (uint32 i = ACTION_BAR_INDEX_START; i < ACTION_BAR_INDEX_END; ++i) + { + ss << uint32(m_charmInfo->GetActionBarEntry(i)->GetType()) << ' ' + << uint32(m_charmInfo->GetActionBarEntry(i)->GetAction()) << ' '; + } + + return ss.str(); +} diff --git a/src/server/game/Entities/Creature/TemporarySummon/NewPet.h b/src/server/game/Entities/Creature/TemporarySummon/NewPet.h new file mode 100644 index 00000000000..8a1865c9f26 --- /dev/null +++ b/src/server/game/Entities/Creature/TemporarySummon/NewPet.h @@ -0,0 +1,84 @@ +/* + * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#ifndef NewPet_h__ +#define NewPet_h__ + +#include "NewGuardian.h" +#include "Optional.h" +#include "PetDefines.h" + +struct PetSpell +{ + ActiveStates Active = ACT_DECIDE; + PetSpellState State = PETSPELL_NEW; + PetSpellType Type = PETSPELL_NORMAL; +}; + +using PetSpellMap = std::unordered_map; +using PlayerPetDataKey = std::pair; + +class TC_GAME_API NewPet final : public NewGuardian +{ +public: + explicit NewPet(SummonPropertiesEntry const* properties, Unit* owner, bool isWorldObject, bool isClassPet); + + void AddToWorld() override; + void RemoveFromWorld() override; + + bool HandlePreSummonActions(Unit const* summoner, uint8 creatureLevel, uint8 maxSummons) override; + void HandlePostSummonActions() override; + + bool ShouldDespawnOnSummonerLogout() const override { return true; } + bool ShouldJoinSummonerSpawnGroupAfterCreation() const override { return true; } + + // Returns true if the pet is belongs to a specific class (Hunter Pets, Mage Water Elementals, DK Ghouls and Warlock Demons) + bool IsClassPet() const { return _isClassPet; } + // Returns true if the summoner is allowed to dismiss the pet via pet action + bool CanBeDismissed() const; + // Returns or creates player pet data. Does return nullptr when the summon is a hunter pet and no pet data is available + PlayerPetData* GetOrCreatePlayerPetData(Player* summoner, uint8 slot, uint32 creatureId); + + bool Create(ObjectGuid::LowType guidlow, Map* map, uint32 entry, uint32 petNumber); + + void SynchronizeLevelWithSummoner(); + bool HasSpell(uint32 spellID) const override; + void ToggleAutocast(SpellInfo const* spellInfo, bool apply); + + // Returns a map of a class pet's learned spells + PetSpellMap const& GetSpells() const { return _spells; } + // Sets the PlayerPetData map key for accessing the summoner's player pet data at any given time + void SetPlayerPetDataKey(uint8 slot, uint32 creatureId); + // Updates the PlayerPetData values of this pet for the specified summoner + void UpdatePlayerPetData(Player* summoner, bool isActivePet); + +private: + void SendSpellLearnedToSummoner(uint32 spellId); + void SendSpellUnlearnedToSummoner(uint32 spellId); + void LearnAvailableSpellsForCurrentLevel(); + bool LearnSpell(uint32 spellId); + bool UnlearnSpell(uint32 spellId, bool learnPreviousRank, bool clearActionbar = true); + bool AddSpell(uint32 spellId, ActiveStates active = ACT_DECIDE, PetSpellState state = PETSPELL_NEW, PetSpellType type = PETSPELL_NORMAL); + bool RemoveSpell(uint32 spellId, bool learnPreviousRank, bool clearActionbar = true); + std::string GenerateActionBarDataString() const; + + bool _isClassPet; + PetSpellMap _spells; + Optional _playerPetDataKey; +}; + +#endif // NewPet_h__ diff --git a/src/server/game/Entities/Creature/TemporarySummon/NewTemporarySummon.cpp b/src/server/game/Entities/Creature/TemporarySummon/NewTemporarySummon.cpp new file mode 100644 index 00000000000..0e9320aa192 --- /dev/null +++ b/src/server/game/Entities/Creature/TemporarySummon/NewTemporarySummon.cpp @@ -0,0 +1,263 @@ + +#include "NewTemporarySummon.h" +#include "DBCStores.h" +#include "CreatureAI.h" +#include "Log.h" +#include "ObjectAccessor.h" +#include "Player.h" +#include "TotemPackets.h" + +NewTemporarySummon::NewTemporarySummon(SummonPropertiesEntry const* properties, Unit* summoner, bool isWorldObject) : + Creature(isWorldObject), _summonProperties(properties), _summonDuration(0ms), _isPermanentSummon(false), _summonSlot(SummonPropertiesSlot::None) +{ + if (summoner) + _internalSummonerGUID = summoner->GetGUID(); + + m_unitTypeMask |= UNIT_MASK_SUMMON; +} + +void NewTemporarySummon::AddToWorld() +{ + if (IsInWorld()) + return; + + Creature::AddToWorld(); + + Unit* summoner = GetInternalSummoner(); + if (summoner) + { + summoner->AddSummonGUID(GetGUID()); + if (_summonSlot != SummonPropertiesSlot::None) + { + // Unsummon previous summon in the slot that we are about to occupy + if (NewTemporarySummon* summon = summoner->GetSummonInSlot(_summonSlot)) + summon->Unsummon(); + + summoner->AddSummonGUIDToSlot(GetGUID(), _summonSlot); + + if (_summonSlot == SummonPropertiesSlot::Critter) + summoner->SetCritterGUID(GetGUID()); + } + } +} + +void NewTemporarySummon::RemoveFromWorld() +{ + if (!IsInWorld()) + return; + + if (_summonProperties) + { + if (Unit* summoner = GetInternalSummoner()) + { + summoner->RemoveSummonGUID(GetGUID()); + if (_summonSlot != SummonPropertiesSlot::None) + { + summoner->RemoveSummonGUIDFromSlot(GetGUID(), _summonSlot); + if (_summonSlot == SummonPropertiesSlot::Critter && summoner->GetCritterGUID() == GetGUID()) + summoner->SetCritterGUID(ObjectGuid::Empty); + } + } + } + + Creature::RemoveFromWorld(); +} + +void NewTemporarySummon::Update(uint32 diff) +{ + Creature::Update(diff); + + if (_summonDuration >= Milliseconds(diff)) + _summonDuration -= Milliseconds(diff); + else + _summonDuration = 0s; + + // The summon has been marked as permanent so we will not proceed with any of the expiration mechanics + if (_isPermanentSummon) + return; + + if (_summonDuration <= 0s) + { + // When a summon expires it usually dies unless the summon property flags expects us to despawn the summon right away. + if (_summonProperties && _summonProperties->GetFlags().HasFlag(SummonPropertiesFlags::DespawnWhenExpired)) + { + Unsummon(); + return; + } + + KillSelf(false); + } +} + +bool NewTemporarySummon::HandlePreSummonActions(Unit const* summoner, uint8 creatureLevel, uint8 maxSummons) +{ + if (!_summonProperties) + return true; + + if (_summonProperties->Faction != 0) + SetFaction(_summonProperties->Faction); + + if (!_summonProperties->GetFlags().HasFlag(SummonPropertiesFlags::UseCreatureLevel) && creatureLevel > 0) + SetLevel(creatureLevel); + + if (!summoner) + return true; + + if (_summonProperties->GetFlags().HasFlag(SummonPropertiesFlags::UseSummonerFaction)) + SetFaction(summoner->GetFaction()); + + _summonSlot = static_cast(_summonProperties->Slot); + + // Only players are allowed to summon creatures in quest slots + if (_summonSlot == SummonPropertiesSlot::Quest && !summoner->IsPlayer()) + return false; + + if (_summonSlot == SummonPropertiesSlot::AnyAvailableTotem) + { + // We have to select an unoccupied totem slot that can be used. As of 4.3.4.15595 this mechanic is only being used by Wild Mushrooms + // If there is no empty slot, we are going to select the eldest summon of our kind and replace it + constexpr int8 firstTotemSlot = AsUnderlyingType(SummonPropertiesSlot::Totem1); + // first attempt: check for empty totem slots within the allowed range + for (uint8 i = 0; i < maxSummons; ++i) + { + SummonPropertiesSlot slot = static_cast(i + firstTotemSlot); + if (!summoner->HasSummonInSlot(slot)) + { + _summonSlot = slot; + break; + } + } + + // all eligible slots are occupied. So try to find a related summon and replace it if possible + if (_summonSlot == SummonPropertiesSlot::AnyAvailableTotem) + { + Optional> fallbackSlot; + for (uint8 i = 0; i < maxSummons; ++i) + { + SummonPropertiesSlot slot = static_cast(i + firstTotemSlot); + + NewTemporarySummon const* summon = summoner->GetSummonInSlot(slot); + if (summon->GetUInt32Value(UNIT_CREATED_BY_SPELL) == GetUInt32Value(UNIT_CREATED_BY_SPELL)) + if (!fallbackSlot.has_value() || fallbackSlot->second >= summon->GetRemainingSummonDuration()) + fallbackSlot = std::make_pair(slot, summon->GetRemainingSummonDuration()); + } + + if (fallbackSlot.has_value()) + _summonSlot = fallbackSlot->first; + else // There is no slot that we can use right now so skip summon creation + return false; + } + } + + // The summon slot is now fully initialized so we can proceed with the slot specific actions + switch (_summonSlot) + { + case SummonPropertiesSlot::Totem1: + case SummonPropertiesSlot::Totem2: + case SummonPropertiesSlot::Totem3: + case SummonPropertiesSlot::Totem4: + if (summoner->IsPlayer()) + { + // SMSG_TOTEM_CREATED must be sent to the client before adding the summon to world and destroying other totems + WorldPackets::Totem::TotemCreated totemCreated; + totemCreated.Duration = int32(_summonDuration.count()); + totemCreated.Slot = uint8(_summonProperties->Slot - AsUnderlyingType(SummonPropertiesSlot::Totem1)); + totemCreated.SpellID = GetUInt32Value(UNIT_CREATED_BY_SPELL); + totemCreated.Totem = GetGUID(); + summoner->ToPlayer()->SendDirectMessage(totemCreated.Write()); + } + // There are some creatures which also go into totem slot but are no real totems. In this case we do not want to override the display Id + if (uint32 totemModel = summoner->GetModelForTotem(PlayerTotemType(_summonProperties->ID))) + SetDisplayId(totemModel); + break; + default: + break; + } + + return true; +} + +void NewTemporarySummon::HandlePostSummonActions() +{ + if (Unit* summoner = GetInternalSummoner()) + { + if (summoner->IsCreature() && summoner->IsAIEnabled()) + if (CreatureAI* ai = summoner->ToCreature()->AI()) + ai->JustSummoned(this); + + if (IsAIEnabled()) + AI()->IsSummonedBy(summoner); + + if (_summonProperties) + { + if (_summonProperties->GetFlags().HasFlag(SummonPropertiesFlags::AttackSummoner)) + EngageWithTarget(summoner); + + if (_summonProperties->GetFlags().HasFlag(SummonPropertiesFlags::HelpWhenSummonedInCombat) && summoner->IsInCombat()) + { + Unit* victim = nullptr; + if (summoner->IsPlayer()) + victim = summoner->GetVictim(); + else if (summoner->IsCreature() && summoner->IsAIEnabled()) + victim = summoner->GetThreatManager().GetCurrentVictim(); + + if (victim) + EngageWithTarget(victim); + } + } + } +} + +void NewTemporarySummon::Unsummon(Milliseconds timeUntilDespawn /*= 0ms*/) +{ + if (timeUntilDespawn > 0ms) + { + m_Events.AddEventAtOffset([&]() { Unsummon(); }, timeUntilDespawn); + return; + } + + if (Unit* summoner = GetInternalSummoner()) + if (summoner->IsCreature() && summoner->IsAIEnabled()) + if (CreatureAI* ai = summoner->ToCreature()->AI()) + ai->SummonedCreatureDespawn(this); + + AddObjectToRemoveList(); +} + +Unit* NewTemporarySummon::GetInternalSummoner() const +{ + if (_internalSummonerGUID.IsEmpty()) + return nullptr; + + return ObjectAccessor::GetUnit(*this, _internalSummonerGUID); +} + +bool NewTemporarySummon::ShouldDespawnOnSummonerDeath() const +{ + return _summonProperties && _summonProperties->GetFlags().HasFlag(SummonPropertiesFlags::DespawnOnSummonerDeath); +} + +bool NewTemporarySummon::ShouldDespawnOnSummonerLogout() const +{ + if (_summonProperties && _summonProperties->GetFlags().HasFlag(SummonPropertiesFlags::DespawnOnSummonerLogout)) + return true; + + // Summons that are stored in a slot will be despawned when the summoner logs out + return _summonSlot != SummonPropertiesSlot::None; +} + +bool NewTemporarySummon::ShouldJoinSummonerSpawnGroupAfterCreation() const +{ + if (!_summonProperties) + return false; + + if (_summonProperties->GetFlags().HasFlag(SummonPropertiesFlags::JoinSummonerSpawnGroup)) + return true; + + return false; +} + +bool NewTemporarySummon::ShouldFollowSummonerAfterCreation() const +{ + return _summonSlot == SummonPropertiesSlot::Critter; +} diff --git a/src/server/game/Entities/Creature/TemporarySummon/NewTemporarySummon.h b/src/server/game/Entities/Creature/TemporarySummon/NewTemporarySummon.h new file mode 100644 index 00000000000..88ad140723a --- /dev/null +++ b/src/server/game/Entities/Creature/TemporarySummon/NewTemporarySummon.h @@ -0,0 +1,77 @@ +/* + * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#ifndef TempoarySummon_h__ +#define TempoarySummon_h__ + +#include "Creature.h" + +struct SummonPropertiesEntry; + +// This time sepcifies when a summon will despawn after dying from expiration +static constexpr Milliseconds SummonExpirationCorpseDespawnTime = 15s; + +class TC_GAME_API NewTemporarySummon : public Creature +{ +public: + explicit NewTemporarySummon(SummonPropertiesEntry const* properties, Unit* summoner, bool isWorldObject); + virtual ~NewTemporarySummon() { } + + // Overriden methods of Creature class + void AddToWorld() override; + void RemoveFromWorld() override; + void Update(uint32 diff) override; + + // Handles everything that needs to be done before the summon is being added to the world (assigning slot, selecting level, etc) + virtual bool HandlePreSummonActions(Unit const* summoner, uint8 creatureLevel, uint8 maxSummons); + // Handles everything that needs to be done after the summon has been added to the world (casting passive auras, calling AI hooks, etc) + virtual void HandlePostSummonActions(); + + // Sets the summon duration before it will expire and either die or despawn + void SetSummonDuration(Milliseconds duration) { _summonDuration = duration; } + // Returns the remaining duration in milliseconds until the summon expires and dies or despawns + Milliseconds GetRemainingSummonDuration() const { return _summonDuration; } + + // Marks the summon as permanent so it will longer die or despawn upon expiration + void SetPermanentSummon(bool apply) { _isPermanentSummon = apply; } + + // Unsummons the summon after a specified amount of time. + void Unsummon(Milliseconds timeUntilDespawn = 0ms); + + // Returns a pointer to the internally stored summoner that has summoned this creature. Unrelated to UNIT_FIELD_SUMMONEDBY. If the creature has not been added to a map yet this method will return nullptr! + Unit* GetInternalSummoner() const; + // Returns the internally stored ObjectGuid of the summoner. Unrelated to UNIT_FIELD_SUMMONEDBY + ObjectGuid GetInternalSummonerGUID() const { return _internalSummonerGUID; } + + // Returns true if the summon is suposed to despawn when its summoner has died + bool ShouldDespawnOnSummonerDeath() const; + // Returns true if the summon is suposed to despawn when its summoner is a player and has logged out + virtual bool ShouldDespawnOnSummonerLogout() const; + // Returns true if the summon is suposed to follow the summoner. SpawnGroups are Blizzard's internal term for our CreatureGroups which are currently not supported for players + virtual bool ShouldJoinSummonerSpawnGroupAfterCreation() const; + // Returns true if the summon is suposed to follow the summoner without joining its SpawnGroup. This mechanic is often being used by companions (formerly known as minipets) + bool ShouldFollowSummonerAfterCreation() const; + +protected: + SummonPropertiesEntry const* _summonProperties; + ObjectGuid _internalSummonerGUID; + Milliseconds _summonDuration; + bool _isPermanentSummon; + SummonPropertiesSlot _summonSlot; +}; + +#endif // PhasingHandler_h__ diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp index 15bb571b1c1..8095d0ecaa6 100644 --- a/src/server/game/Entities/Object/Object.cpp +++ b/src/server/game/Entities/Object/Object.cpp @@ -48,6 +48,9 @@ #include "SpellDefines.h" #include "SpellMgr.h" #include "TemporarySummon.h" +#include "NewTemporarySummon.h" +#include "NewGuardian.h" +#include "NewPet.h" #include "Totem.h" #include "Transport.h" #include "Unit.h" @@ -2124,6 +2127,102 @@ TempSummon* Map::SummonCreature(uint32 entry, Position const& pos, SummonCreatur return summon; } +NewTemporarySummon* Map::SummonCreatureNew(uint32 entry, Position const& pos, SummonCreatureExtraArgs const& summonArgs /*= { }*/) +{ + NewTemporarySummon* summon = nullptr; + if (summonArgs.SummonProperties) + { + switch (SummonPropertiesControl(summonArgs.SummonProperties->Control)) + { + case SummonPropertiesControl::None: + summon = new NewTemporarySummon(summonArgs.SummonProperties, summonArgs.Summoner, false); + break; + case SummonPropertiesControl::Guardian: + summon = new NewGuardian(summonArgs.SummonProperties, summonArgs.Summoner, false); + break; + case SummonPropertiesControl::Pet: + summon = new NewPet(summonArgs.SummonProperties, summonArgs.Summoner, false, false); + break; + default: + break; + } + } + else + summon = new NewTemporarySummon(summonArgs.SummonProperties, summonArgs.Summoner, false); + + if (!summon) + return nullptr; + + // Create creature entity + if (!summon->Create(GenerateLowGuid(), this, entry, pos, nullptr, summonArgs.VehicleRecID, true)) + { + delete summon; + return nullptr; + } + + // Inherit summoner's Phaseshift + bool ignorePhaseShift = false; + if (summonArgs.SummonProperties && summonArgs.SummonProperties->GetFlags().HasFlag(SummonPropertiesFlags::IgnoreSummonerPhase) + && SummonPropertiesControl(summonArgs.SummonProperties->Control) == SummonPropertiesControl::None) + ignorePhaseShift = true; + + if (!ignorePhaseShift && summonArgs.Summoner) + PhasingHandler::InheritPhaseShift(summon, summonArgs.Summoner); + + TransportBase* transport = summonArgs.Summoner ? summonArgs.Summoner->GetTransport() : nullptr; + if (transport) + { + float x, y, z, o; + pos.GetPosition(x, y, z, o); + transport->CalculatePassengerOffset(x, y, z, &o); + summon->m_movementInfo.transport.pos.Relocate(x, y, z, o); + + // This object must be added to transport before adding to map for the client to properly display it + transport->AddPassenger(summon); + } + + // Initialize tempsummon fields + summon->SetUInt32Value(UNIT_CREATED_BY_SPELL, summonArgs.SummonSpellId); + summon->SetHomePosition(pos); + summon->SetPrivateObjectOwner(summonArgs.PrivateObjectOwner); + + if (summonArgs.SummonDuration >= 0) + summon->SetSummonDuration(Milliseconds(summonArgs.SummonDuration)); + else + summon->SetPermanentSummon(true); + + if (!summon->HandlePreSummonActions(summonArgs.Summoner, summonArgs.CreatureLevel, summonArgs.MaxSummons)) + { + delete summon; + return nullptr; + } + + // Handle health argument + if (summonArgs.SummonHealth > 0) + { + summon->SetMaxHealth(summonArgs.SummonHealth); + summon->SetHealth(summonArgs.SummonHealth); + } + + if (!AddToMap(summon->ToCreature())) + { + // Returning false will cause the object to be deleted - remove from transport + if (transport) + transport->RemovePassenger(summon); + + delete summon; + return nullptr; + } + + summon->HandlePostSummonActions(); + + // call MoveInLineOfSight for nearby creatures + Trinity::AIRelocationNotifier notifier(*summon); + Cell::VisitAllObjects(summon, notifier, GetVisibilityRange()); + + return summon; +} + /** * Summons group of creatures. * diff --git a/src/server/game/Entities/Object/ObjectDefines.h b/src/server/game/Entities/Object/ObjectDefines.h index 28ab831bac7..884a18e4c8b 100644 --- a/src/server/game/Entities/Object/ObjectDefines.h +++ b/src/server/game/Entities/Object/ObjectDefines.h @@ -63,7 +63,8 @@ enum TempSummonType TEMPSUMMON_CORPSE_DESPAWN = 5, // despawns instantly after death TEMPSUMMON_CORPSE_TIMED_DESPAWN = 6, // despawns after a specified time after death TEMPSUMMON_DEAD_DESPAWN = 7, // despawns when the creature disappears - TEMPSUMMON_MANUAL_DESPAWN = 8 // despawns when UnSummon() is called + TEMPSUMMON_MANUAL_DESPAWN = 8, // despawns when UnSummon() is called + TEMPSUMMON_DIE_UPON_EXPIRE = 9 // dies after a specified time. Corpse will despawn just like any regular creature. }; enum PhaseMasks diff --git a/src/server/game/Entities/Pet/Pet.cpp b/src/server/game/Entities/Pet/Pet.cpp index 54b71c2cc5c..833dbd581e0 100644 --- a/src/server/game/Entities/Pet/Pet.cpp +++ b/src/server/game/Entities/Pet/Pet.cpp @@ -96,6 +96,7 @@ void Pet::RemoveFromWorld() bool Pet::LoadPetData(Player* owner, uint32 petEntry, uint32 petnumber, bool current) { + /** m_loading = true; PlayerPetData* playerPetData; @@ -269,7 +270,6 @@ bool Pet::LoadPetData(Player* owner, uint32 petEntry, uint32 petnumber, bool cur owner->SendMessageToSet(&data, true); } - owner->SetMinion(this, true); map->AddToMap(ToCreature()); InitTalentForLevel(); // set original talents points before spell loading @@ -344,12 +344,15 @@ bool Pet::LoadPetData(Player* owner, uint32 petEntry, uint32 petnumber, bool cur // must be after SetMinion (owner guid check) LoadTemplateImmunities(); m_loading = false; + */ return true; } void Pet::SavePetToDB(PetSaveMode mode) { + return; + if (!GetEntry()) return; @@ -372,11 +375,11 @@ void Pet::SavePetToDB(PetSaveMode mode) GetSpellHistory()->SaveToDB(trans); CharacterDatabase.CommitTransaction(trans); - PlayerPetData* playerPetData = GetOwner()->GetPlayerPetDataById(m_charmInfo->GetPetNumber()); + //PlayerPetData* playerPetData = GetOwner()->GetPlayerPetDataById(m_charmInfo->GetPetNumber()); // save as new if no data for Pet in PlayerPetDataStore - if (mode < PET_SAVE_NEW_PET && !playerPetData) - mode = PET_SAVE_NEW_PET; + //if (mode < PET_SAVE_NEW_PET && !playerPetData) + // mode = PET_SAVE_NEW_PET; if (mode == PET_SAVE_NEW_PET) { @@ -385,7 +388,7 @@ void Pet::SavePetToDB(PetSaveMode mode) if (slot) { SetSlot(*slot); - playerPetData = new PlayerPetData(); + //playerPetData = new PlayerPetData(); } else mode = PET_SAVE_AS_DELETED; @@ -394,6 +397,7 @@ void Pet::SavePetToDB(PetSaveMode mode) if (mode == PET_SAVE_DISMISS || mode == PET_SAVE_LOGOUT) RemoveAllAuras(); + /* // whole pet is saved to DB if (mode >= PET_SAVE_CURRENT_STATE) { @@ -468,10 +472,12 @@ void Pet::SavePetToDB(PetSaveMode mode) GetOwner()->DeleteFromPlayerPetDataStore(m_charmInfo->GetPetNumber()); GetOwner()->GetSession()->SendStablePet(ObjectGuid::Empty); } + */ } void Pet::DeleteFromDB(ObjectGuid::LowType guidlow) { + /* CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_PET_BY_ID); @@ -495,6 +501,7 @@ void Pet::DeleteFromDB(ObjectGuid::LowType guidlow) trans->Append(stmt); CharacterDatabase.CommitTransaction(trans); + */ } void Pet::setDeathState(DeathState s) // overwrite virtual Creature::setDeathState and Unit::setDeathState @@ -541,7 +548,7 @@ void Pet::Update(uint32 diff) { // unsummon pet that lost owner Player* owner = GetOwner(); - if ((!IsWithinDistInMap(owner, GetMap()->GetVisibilityRange()) && !isPossessed()) || (isControlled() && !owner->GetPetGUID())) + if ((!IsWithinDistInMap(owner, GetMap()->GetVisibilityRange()) && !isPossessed()) || (isControlled() && !owner->GetSummonGUID())) //if (!owner || (!IsWithinDistInMap(owner, GetMap()->GetVisibilityDistance()) && (owner->GetCharmGUID() && (owner->GetCharmGUID() != GetGUID()))) || (isControlled() && !owner->GetPetGUID())) { Remove(PET_SAVE_DISMISS, true); @@ -550,7 +557,7 @@ void Pet::Update(uint32 diff) if (isControlled()) { - if (owner->GetPetGUID() != GetGUID()) + if (owner->GetSummonGUID() != GetGUID()) { TC_LOG_ERROR("entities.pet", "Pet %u is not pet of owner %s, removed", GetEntry(), GetOwner()->GetName().c_str()); Remove(IsHunterPet() ? PET_SAVE_AS_DELETED : PET_SAVE_DISMISS); @@ -579,7 +586,7 @@ void Pet::Update(uint32 diff) void Pet::Remove(PetSaveMode mode, bool returnreagent) { - GetOwner()->RemovePet(this, mode, returnreagent); + //GetOwner()->RemovePet(this, mode, returnreagent); } void Pet::GivePetXP(uint32 xp) @@ -1071,7 +1078,7 @@ void Pet::_LoadSpells() void Pet::_SaveSpells(CharacterDatabaseTransaction& trans) { - for (PetSpellMap::iterator itr = m_spells.begin(), next = m_spells.begin(); itr != m_spells.end(); itr = next) + for (PetSpellMapOld::iterator itr = m_spells.begin(), next = m_spells.begin(); itr != m_spells.end(); itr = next) { ++next; @@ -1284,7 +1291,7 @@ bool Pet::addSpell(uint32 spellId, ActiveStates active /*= ACT_DECIDE*/, PetSpel return false; } - PetSpellMap::iterator itr = m_spells.find(spellId); + PetSpellMapOld::iterator itr = m_spells.find(spellId); if (itr != m_spells.end()) { if (itr->second.state == PETSPELL_REMOVED) @@ -1306,7 +1313,7 @@ bool Pet::addSpell(uint32 spellId, ActiveStates active /*= ACT_DECIDE*/, PetSpel } } - PetSpell newspell; + PetSpellOld newspell; newspell.state = state; newspell.type = type; @@ -1341,7 +1348,7 @@ bool Pet::addSpell(uint32 spellId, ActiveStates active /*= ACT_DECIDE*/, PetSpel } else if (spellInfo->IsRanked()) { - for (PetSpellMap::const_iterator itr2 = m_spells.begin(); itr2 != m_spells.end(); ++itr2) + for (PetSpellMapOld::const_iterator itr2 = m_spells.begin(); itr2 != m_spells.end(); ++itr2) { if (itr2->second.state == PETSPELL_REMOVED) continue; @@ -1476,7 +1483,7 @@ bool Pet::unlearnSpell(uint32 spell_id, bool learn_prev, bool clear_ab) bool Pet::removeSpell(uint32 spell_id, bool learn_prev, bool clear_ab) { - PetSpellMap::iterator itr = m_spells.find(spell_id); + PetSpellMapOld::iterator itr = m_spells.find(spell_id); if (itr == m_spells.end()) return false; @@ -1591,7 +1598,7 @@ bool Pet::resetTalents() for (uint8 j = 0; j < MAX_TALENT_RANK; ++j) { - for (PetSpellMap::const_iterator itr = m_spells.begin(); itr != m_spells.end();) + for (PetSpellMapOld::const_iterator itr = m_spells.begin(); itr != m_spells.end();) { if (itr->second.state == PETSPELL_REMOVED) { @@ -1729,7 +1736,7 @@ void Pet::ToggleAutocast(SpellInfo const* spellInfo, bool apply) uint32 spellid = spellInfo->Id; - PetSpellMap::iterator itr = m_spells.find(spellid); + PetSpellMapOld::iterator itr = m_spells.find(spellid); if (itr == m_spells.end()) return; @@ -1818,7 +1825,7 @@ bool Pet::Create(ObjectGuid::LowType guidlow, Map* map, uint32 Entry, uint32 pet bool Pet::HasSpell(uint32 spell) const { - PetSpellMap::const_iterator itr = m_spells.find(spell); + PetSpellMapOld::const_iterator itr = m_spells.find(spell); return itr != m_spells.end() && itr->second.state != PETSPELL_REMOVED; } diff --git a/src/server/game/Entities/Pet/Pet.h b/src/server/game/Entities/Pet/Pet.h index 796ee3aa3cb..e34471bbaeb 100644 --- a/src/server/game/Entities/Pet/Pet.h +++ b/src/server/game/Entities/Pet/Pet.h @@ -21,31 +21,20 @@ #include "PetDefines.h" #include "TemporarySummon.h" -enum StableResultCode -{ - STABLE_ERR_MONEY = 0x01, // "you don't have enough money" - STABLE_ERR_INVALID_SLOT = 0x03, // "That slot is locked" - STABLE_SUCCESS_STABLE = 0x08, // stable success - STABLE_SUCCESS_UNSTABLE = 0x09, // unstable/swap success - STABLE_SUCCESS_BUY_SLOT = 0x0A, // buy slot success - STABLE_ERR_EXOTIC = 0x0B, // "you are unable to control exotic creatures" - STABLE_ERR_STABLE = 0x0C // "Internal pet error" -}; - enum PetStableInfo { PET_STABLE_ACTIVE = 1, PET_STABLE_INACTIVE = 2 }; -struct PetSpell +struct PetSpellOld { ActiveStates active; PetSpellState state; PetSpellType type; }; -typedef std::unordered_map PetSpellMap; +typedef std::unordered_map PetSpellMapOld; typedef std::vector AutoSpellList; typedef std::vector PetScalingAuraList; @@ -137,7 +126,7 @@ class TC_GAME_API Pet : public Guardian void CleanupActionBar(); std::string GenerateActionBarData() const; - PetSpellMap m_spells; + PetSpellMapOld m_spells; AutoSpellList m_autospells; PetScalingAuraList m_petScalingAuras; diff --git a/src/server/game/Entities/Pet/PetDefines.h b/src/server/game/Entities/Pet/PetDefines.h index 8eba4649f30..cc9e56deba7 100644 --- a/src/server/game/Entities/Pet/PetDefines.h +++ b/src/server/game/Entities/Pet/PetDefines.h @@ -78,6 +78,23 @@ enum PetTalk PET_TALK_ATTACK = 1 }; +enum StableResultCode : uint8 +{ + STABLE_ERR_MONEY = 0x01, // "you don't have enough money" + STABLE_ERR_INVALID_SLOT = 0x03, // "That slot is locked" + STABLE_SUCCESS_STABLE = 0x08, // stable success + STABLE_SUCCESS_UNSTABLE = 0x09, // unstable/swap success + STABLE_SUCCESS_BUY_SLOT = 0x0A, // buy slot success + STABLE_ERR_EXOTIC = 0x0B, // "you are unable to control exotic creatures" + STABLE_ERR_STABLE = 0x0C // "Internal pet error" +}; + + +enum PetSummonSpellIds +{ + // Hunter Pets + SPELL_PET_ENERGIZE = 99289 +}; // Used by companions (minipets) and quest slot summons constexpr float DEFAULT_FOLLOW_DISTANCE = 2.5f; constexpr float DEFAULT_FOLLOW_DISTANCE_PET = 3.f; diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 9101fea1da1..4261d26015a 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -79,7 +79,9 @@ #include "Opcodes.h" #include "OutdoorPvP.h" #include "OutdoorPvPMgr.h" +#include "NewPet.h" #include "Pet.h" +#include "NewPet.h" #include "PetPackets.h" #include "PetitionMgr.h" #include "PhasingHandler.h" @@ -251,13 +253,6 @@ Player::Player(WorldSession* session): Unit(true) m_canTitanGrip = false; m_titanGripPenaltySpellId = 0; - m_temporaryUnsummonedPetNumber = 0; - //cache for UNIT_CREATED_BY_SPELL to allow - //returning reagents for temporarily removed pets - //when dying/logging out - m_oldpetspell = 0; - m_lastpetnumber = 0; - ////////////////////Rest System///////////////////// _restTime = 0; inn_triggerId = 0; @@ -354,8 +349,9 @@ Player::Player(WorldSession* session): Unit(true) m_reputationMgr = std::make_unique(this); _hasValidLFGLeavePoint = false; _archaeology = std::make_unique(this); - m_petScalingSynchTimer.Reset(1000); m_groupUpdateTimer.Reset(5000); + + _canControlClassPets = false; } Player::~Player() @@ -380,9 +376,6 @@ Player::~Player() for (size_t i = 0; i < _voidStorageItems.size(); ++i) delete _voidStorageItems[i]; - for (uint8 i = 0; i < PlayerPetDataStore.size(); i++) - delete PlayerPetDataStore[i]; - ClearResurrectRequestData(); sWorld->DecreasePlayerCount(); @@ -1276,23 +1269,6 @@ void Player::Update(uint32 p_time) m_groupUpdateTimer.Reset(5000); } - Pet* pet = GetPet(); - if (pet && !pet->IsWithinDistInMap(this, GetMap()->GetVisibilityRange()) && !pet->isPossessed()) - //if (pet && !pet->IsWithinDistInMap(this, GetMap()->GetVisibilityDistance()) && (GetCharmGUID() && (pet->GetGUID() != GetCharmGUID()))) - RemovePet(pet, PET_SAVE_DISMISS, true); - - m_petScalingSynchTimer.Update(p_time); - if (m_petScalingSynchTimer.Passed()) - { - if (pet) - { - pet->UpdatePetScalingAuras(); - pet->UpdateAllStats(); - } - - m_petScalingSynchTimer.Reset(1000); - } - if (IsAlive()) { if (m_hostileReferenceCheckTimer <= p_time) @@ -1333,9 +1309,6 @@ void Player::setDeathState(DeathState s) ClearResurrectRequestData(); - //FIXME: is pet dismissed at dying or releasing spirit? if second, add setDeathState(DEAD) to HandleRepopRequestOpcode and define pet unsummon here with (s == DEAD) - RemovePet(nullptr, PET_SAVE_DISMISS, true); - // save value before aura remove in Unit::setDeathState ressSpellId = GetUInt32Value(PLAYER_SELF_RES_SPELL); @@ -1410,9 +1383,6 @@ bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientati return false; } - // preparing unsummon pet if lost (we must get pet before teleportation or will not find it later) - Pet* pet = GetPet(); - MapEntry const* mEntry = sMapStore.LookupEntry(mapid); // don't let enter battlegrounds without assigned battleground id (for example through areatrigger)... @@ -1477,13 +1447,6 @@ bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientati return true; } - if (!(options & TELE_TO_NOT_UNSUMMON_PET)) - { - //same map, only remove pet if out of range for new position - if (pet && !pet->IsWithinDist3d(x, y, z, GetMap()->GetVisibilityRange())) - UnsummonPetTemporaryIfAny(); - } - if (!(options & TELE_TO_NOT_LEAVE_COMBAT)) CombatStop(); @@ -1577,14 +1540,8 @@ bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientati { RemoveArenaSpellCooldowns(true); RemoveArenaAuras(); - if (pet) - pet->RemoveArenaAuras(); } - // remove pet on map change - if (pet) - UnsummonPetTemporaryIfAny(); - // remove all dyn objects RemoveAllDynObjects(); @@ -1747,10 +1704,10 @@ void Player::RemoveFromWorld() // cleanup if (IsInWorld()) { - ///- Release charmed creatures, unsummon totems and remove pets/guardians + ///- Release charmed creatures StopCastingCharm(); StopCastingBindSight(); - UnsummonPetTemporaryIfAny(); + UnsummonAllSummonsOnLogout(); ClearComboPoints(); ClearComboPointHolders(); ObjectGuid lootGuid = GetLootGUID(); @@ -2084,9 +2041,6 @@ void Player::SetGameMaster(bool on) SetFlag(PLAYER_FLAGS, PLAYER_FLAGS_GM); SetFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_ALLOW_CHEAT_SPELLS); - if (Pet* pet = GetPet()) - pet->SetFaction(FACTION_FRIENDLY); - RemoveByteFlag(UNIT_FIELD_BYTES_2, UNIT_BYTES_2_OFFSET_PVP_FLAG, UNIT_BYTE2_FLAG_FFA_PVP); ResetContestedPvP(); @@ -2104,9 +2058,6 @@ void Player::SetGameMaster(bool on) RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_GM); RemoveFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_ALLOW_CHEAT_SPELLS); - if (Pet* pet = GetPet()) - pet->SetFaction(GetFaction()); - // restore FFA PvP Server state if (sWorld->IsFFAPvPRealm()) SetByteFlag(UNIT_FIELD_BYTES_2, UNIT_BYTES_2_OFFSET_PVP_FLAG, UNIT_BYTE2_FLAG_FFA_PVP); @@ -2325,10 +2276,6 @@ void Player::GiveLevel(uint8 level) SetFullHealth(); SetFullPower(POWER_MANA); - // update level to hunter/summon pet - if (Pet* pet = GetPet()) - pet->SynchronizeLevelWithOwner(); - //Update QuestGivers SendQuestGiverStatusMultiple(); @@ -2353,6 +2300,10 @@ void Player::GiveLevel(uint8 level) SetByteFlag(PLAYER_FIELD_BYTES, PLAYER_FIELD_BYTES_OFFSET_RAF_GRANTABLE_LEVEL, 0x01); } + if (NewPet* pet = GetActivelyControlledSummon()) + if (pet->IsClassPet()) + pet->SynchronizeLevelWithSummoner(); + sScriptMgr->OnPlayerLevelChanged(this, oldLevel); } @@ -2574,10 +2525,6 @@ void Player::InitStatsForLevel(bool reapplyMods) SetFullPower(POWER_RAGE); SetFullPower(POWER_FOCUS); SetPower(POWER_RUNIC_POWER, 0); - - // update level to hunter/summon pet - if (Pet* pet = GetPet()) - pet->SynchronizeLevelWithOwner(); } void Player::SendKnownSpells(bool firstLogin /*= false*/) @@ -3178,9 +3125,6 @@ void Player::LearnSpell(uint32 spell_id, bool dependent, uint32 fromSkill /*= 0* } } - if (CanControlPet(spell_id) && GetPet()) - PetSpellInitialize(); - if (Guild* guild = GetGuild()) guild->UpdateMemberData(this, GUILD_MEMBER_DATA_PROFESSIONS, 0); } @@ -3384,11 +3328,6 @@ void Player::RemoveArenaSpellCooldowns(bool removeActivePetCooldowns) && spellInfo->CategoryRecoveryTime < 10 * MINUTE * IN_MILLISECONDS && !spellInfo->HasAttribute(SPELL_ATTR6_DO_NOT_RESET_COOLDOWN_IN_ARENA); }, true); - - // pet cooldowns - if (removeActivePetCooldowns) - if (Pet* pet = GetPet()) - pet->GetSpellHistory()->ResetAllCooldowns(); } uint32 Player::GetNextResetTalentsCost() const @@ -3453,8 +3392,6 @@ bool Player::ResetTalents(bool no_cost) } } - RemovePet(nullptr, PET_SAVE_DISMISS, true); - for (uint32 talentId = 0; talentId < sTalentStore.GetNumRows(); ++talentId) { TalentEntry const* talentInfo = sTalentStore.LookupEntry(talentId); @@ -3766,21 +3703,6 @@ void Player::DeleteFromDB(ObjectGuid playerguid, uint32 accountId, bool updateRe while (resultMail->NextRow()); } - // Unsummon and delete for pets in world is not required: player deleted from CLI or character list with not loaded pet. - // NOW we can finally clear other DB data related to character - stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_PETS); - stmt->setUInt32(0, guid); - PreparedQueryResult resultPets = CharacterDatabase.Query(stmt); - - if (resultPets) - { - do - { - ObjectGuid::LowType petguidlow = (*resultPets)[0].GetUInt32(); - Pet::DeleteFromDB(petguidlow); - } while (resultPets->NextRow()); - } - // Delete char from social list of online chars stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_SOCIAL); stmt->setUInt32(0, guid); @@ -3902,13 +3824,13 @@ void Player::DeleteFromDB(ObjectGuid playerguid, uint32 accountId, bool updateRe stmt->setUInt32(0, guid); trans->Append(stmt); - stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_PET_BY_OWNER); + stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_PET); stmt->setUInt32(0, guid); trans->Append(stmt); - stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_PET_DECLINEDNAME_BY_OWNER); - stmt->setUInt32(0, guid); - trans->Append(stmt); + //stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_PET_DECLINEDNAME_BY_OWNER); + //stmt->setUInt32(0, guid); + //trans->Append(stmt); stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_ACHIEVEMENTS); stmt->setUInt32(0, guid); @@ -7046,8 +6968,8 @@ void Player::UpdateZone(uint32 newZone, uint32 newArea) if (GetGroup()) { SetGroupUpdateFlag(GROUP_UPDATE_FULL); - if (GetPet()) - SetGroupUpdateFlag(GROUP_UPDATE_PET); + //if (GetPet()) + // SetGroupUpdateFlag(GROUP_UPDATE_PET); } // zone changed, so area changed as well, update it @@ -7279,12 +7201,12 @@ void Player::DuelComplete(DuelCompleteType type) // cleanup combo points if (GetComboTarget() == duel->opponent->GetGUID()) ClearComboPoints(); - else if (GetComboTarget() == duel->opponent->GetPetGUID()) + else if (GetComboTarget() == duel->opponent->GetSummonGUID()) ClearComboPoints(); if (duel->opponent->GetComboTarget() == GetGUID()) duel->opponent->ClearComboPoints(); - else if (duel->opponent->GetComboTarget() == GetPetGUID()) + else if (duel->opponent->GetComboTarget() == GetSummonGUID()) duel->opponent->ClearComboPoints(); //cleanups @@ -8759,21 +8681,6 @@ void Player::SendTalentWipeConfirm(ObjectGuid guid) const void Player::ResetPetTalents() { - // This needs another gossip option + NPC text as a confirmation. - // The confirmation gossip listid has the text: "Yes, please do." - Pet* pet = GetPet(); - - if (!pet || pet->getPetType() != HUNTER_PET || pet->m_usedTalentCount == 0) - return; - - CharmInfo* charmInfo = pet->GetCharmInfo(); - if (!charmInfo) - { - TC_LOG_ERROR("entities.player", "Object (GUID: %u TypeId: %u) is considered pet-like, but doesn't have charm info!", pet->GetGUID().GetCounter(), pet->GetTypeId()); - return; - } - pet->resetTalents(); - SendTalentsInfoData(true); } /*********************************************************/ @@ -13333,8 +13240,8 @@ void Player::PrepareGossipMenu(WorldObject* source, uint32 menuId /*= 0*/, bool canTalk = false; break; case GOSSIP_OPTION_UNLEARNPETTALENTS: - if (!GetPet() || GetPet()->getPetType() != HUNTER_PET || GetPet()->m_spells.size() <= 1 || !creature->CanResetTalents(this)) - canTalk = false; + //if (!GetPet() || GetPet()->getPetType() != HUNTER_PET || GetPet()->m_spells.size() <= 1 || !creature->CanResetTalents(this)) + // canTalk = false; break; case GOSSIP_OPTION_TAXIVENDOR: if (GetSession()->SendLearnNewTaxiNode(creature)) @@ -16945,7 +16852,7 @@ bool Player::LoadFromDB(ObjectGuid guid, CharacterDatabaseQueryHolder const& hol SetUInt32Value(UNIT_CHANNEL_SPELL, 0); // clear charm/summon related fields - SetOwnerGUID(ObjectGuid::Empty); + SetSummonerGUID(ObjectGuid::Empty); SetGuidValue(UNIT_FIELD_CHARMEDBY, ObjectGuid::Empty); SetGuidValue(UNIT_FIELD_CHARM, ObjectGuid::Empty); SetGuidValue(UNIT_FIELD_SUMMON, ObjectGuid::Empty); @@ -17007,6 +16914,8 @@ bool Player::LoadFromDB(ObjectGuid guid, CharacterDatabaseQueryHolder const& hol if (HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GHOST)) m_deathState = DEAD; + _LoadPets(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_ALL_PETS)); + // after spell load, learn rewarded spell if need also _LoadQuestStatus(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_QUEST_STATUS)); _LoadQuestStatusRewarded(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_QUEST_STATUS_REW)); @@ -17832,143 +17741,97 @@ void Player::_LoadMail(PreparedQueryResult mailsResult, PreparedQueryResult mail UpdateNextMailTimeAndUnreads(); } -void Player::LoadPet() +void Player::_LoadPets(PreparedQueryResult result) { - //fixme: the pet should still be loaded if the player is not in world - // just not added to the map - if (IsInWorld()) - { - Pet* pet = new Pet(this); - if (!pet->LoadPetData(this, 0, 0, true)) - delete pet; - } -} + // "SELECT PetNumber, CreatureId, TamedCreatureId, DisplayId, SavedHealth, SavedPower, CreatedBySpellId, LastSaveTime, ReactState, Slot, HasBeenRenamed, IsActive, `Name`, ActionBar FROM character_pet WHERE Guid = ? ORDER BY Slot" -void Player::LoadPetsFromDB(PreparedQueryResult result) -{ if (!result) return; do { Field* fields = result->Fetch(); - - PlayerPetData* playerPetData = new PlayerPetData(); - - uint8 slot = fields[7].GetUInt8(); - uint32 petId = fields[0].GetUInt32(); - - if (slot > PET_SLOT_LAST) - { - TC_LOG_ERROR("sql.sql", "Player::LoadPetsFromDB: bad slot %u for pet %u!", slot, petId); - continue; - } - - playerPetData->PetId = petId; - playerPetData->CreatureId = fields[1].GetUInt32(); - playerPetData->Owner = fields[2].GetUInt64(); - playerPetData->DisplayId = fields[3].GetUInt32(); - playerPetData->Petlevel = fields[4].GetUInt16(); - playerPetData->PetExp = fields[5].GetUInt32(); - playerPetData->Reactstate = ReactStates(fields[6].GetUInt8()); - playerPetData->Slot = slot; - playerPetData->Name = fields[8].GetString(); - playerPetData->Renamed = fields[9].GetBool(); - playerPetData->Active = fields[10].GetBool(); - playerPetData->SavedHealth = fields[11].GetUInt32(); - playerPetData->SavedMana = fields[12].GetUInt32(); - playerPetData->Actionbar = fields[13].GetString(); - playerPetData->Timediff = fields[14].GetUInt32(); - playerPetData->SummonSpellId = fields[15].GetUInt32(); - playerPetData->Type = PetType(fields[16].GetUInt8()); - - PlayerPetDataStore.push_back(playerPetData); + std::unique_ptr petData = std::make_unique(); + + petData->PetNumber = fields[0].GetUInt32(); + petData->CreatureId = fields[1].GetUInt32(); + petData->TamedCreatureId = fields[2].GetUInt32(); + petData->DisplayId = fields[3].GetUInt32(); + petData->SavedHealth = fields[4].GetUInt32(); + petData->SavedPower = fields[5].GetUInt32(); + petData->CreatedBySpellId = fields[6].GetUInt32(); + petData->LastSaveTime = fields[7].GetUInt32(); + petData->ReactState = static_cast(fields[8].GetUInt8()); + petData->Slot = fields[9].GetUInt8(); + petData->HasBeenRenamed = fields[10].GetBool(); + petData->IsActive = fields[11].GetBool(); + petData->Name = fields[12].GetString(); + petData->ActionBar = fields[13].GetString(); + petData->Status = PlayerPetDataStatus::UpToDate; + + _playerPetDataMap.emplace(std::make_pair(petData->Slot, petData->CreatureId), std::move(petData)); } while (result->NextRow()); } -PlayerPetData* Player::GetPlayerPetDataById(uint32 petId) +PlayerPetData* Player::GetPlayerPetData(uint8 slot, uint32 creatureId) const { - for (PlayerPetData* p : PlayerPetDataStore) - if (p->PetId == petId) - return p; + auto itr = _playerPetDataMap.find(std::make_pair(slot, creatureId)); + if (itr == _playerPetDataMap.end()) + return nullptr; - return nullptr; + return itr->second.get(); } -PlayerPetData* Player::GetPlayerPetDataBySlot(uint8 slot) +PlayerPetData* Player::CreatePlayerPetData(uint8 slot, uint32 creatureId) { - for (PlayerPetData* p : PlayerPetDataStore) - if (p->Slot == slot) - return p; + // Make sure that we do not attempt to create data for already existing pets + if (_playerPetDataMap.find(std::make_pair(slot, creatureId)) != _playerPetDataMap.end()) + { + TC_LOG_ERROR("entities.player", "Player::CreatePlayerPetData: Player(GUID: % u) tried to create pet data (slot %u, creatureId %u) that already exsist.", GetGUID().GetCounter(), slot, creatureId); + return nullptr; + } - return nullptr; -} + CreatureTemplate const* cInfo = sObjectMgr->GetCreatureTemplate(creatureId); + if (!cInfo) + { + TC_LOG_ERROR("entities.player", "Player::CreatePlayerPetData: Player(GUID: % u) tried to create pet data (slot %u, creatureId %u) which does not have a creature_template entry.", GetGUID().GetCounter(), slot, creatureId); + return nullptr; + } -PlayerPetData* Player::GetPlayerPetDataByCreatureId(uint32 creatureId) -{ - for (PlayerPetData* p : PlayerPetDataStore) - if (p->CreatureId == creatureId) - return p; + std::unique_ptr petData = std::make_unique(); + petData->Slot = slot; + petData->CreatureId = creatureId; + petData->PetNumber = sObjectMgr->GeneratePetNumber(); + petData->Name = sObjectMgr->GeneratePetName(creatureId); + petData->Status = PlayerPetDataStatus::New; + petData->ReactState = REACT_ASSIST; - return nullptr; + return _playerPetDataMap.emplace(std::make_pair(slot, creatureId), std::move(petData)).first->second.get(); } PlayerPetData* Player::GetPlayerPetDataCurrent() { - for (PlayerPetData* p : PlayerPetDataStore) - if (p->Active == true) - return p; - return nullptr; } Optional Player::GetFirstUnusedActivePetSlot() { std::set unusedActiveSlot = { 0, 1, 2, 3, 4 }; //unfiltered - - for (PlayerPetData* p : PlayerPetDataStore) - if (unusedActiveSlot.find(p->Slot) != unusedActiveSlot.end()) - unusedActiveSlot.erase(p->Slot); - - if (!unusedActiveSlot.empty()) - return *unusedActiveSlot.begin(); - return Optional{}; } Optional Player::GetFirstUnusedPetSlot() { - std::set unusedSlot; - - for (uint8 i = 0; i < PET_SLOT_LAST; i++) // 4 MAX StableSlots (256 theoretically) - unusedSlot.insert(i); - - for (PlayerPetData* p : PlayerPetDataStore) - if (unusedSlot.find(p->Slot) != unusedSlot.end()) - unusedSlot.erase(p->Slot); - - if (!unusedSlot.empty()) - return *unusedSlot.begin(); - return Optional{}; } void Player::DeleteFromPlayerPetDataStore(uint32 petNumber) { - for (uint8 i = 0; i < PlayerPetDataStore.size(); ++i) - { - if (PlayerPetDataStore[i]->PetId == petNumber) - { - delete PlayerPetDataStore[i]; - PlayerPetDataStore.erase(PlayerPetDataStore.begin() + (i--)); - } - } } -void Player::AddToPlayerPetDataStore(PlayerPetData* playerPetData) +void Player::AddToPlayerPetDataStore(PlayerPetData&& playerPetData) { - PlayerPetDataStore.push_back(playerPetData); } void Player::_LoadQuestStatus(PreparedQueryResult result) @@ -19160,15 +19023,12 @@ void Player::SaveToDB(CharacterDatabaseTransaction trans, bool create /* = false _SaveInstanceTimeRestrictions(trans); _SaveCurrency(trans); _SaveCUFProfiles(trans); + _SavePets(trans); // check if stats should only be saved on logout // save stats can be out of transaction if (m_session->isLogingOut() || !sWorld->getBoolConfig(CONFIG_STATS_SAVE_ONLY_ON_LOGOUT)) _SaveStats(trans); - - // save pet (hunter pet level and experience and all type pets health/mana). - if (Pet* pet = GetPet()) - pet->SavePetToDB(PET_SAVE_CURRENT_STATE); } // fast save function for item/money cheating preventing - save only inventory and money state @@ -19505,7 +19365,6 @@ void Player::_SaveCUFProfiles(CharacterDatabaseTransaction& trans) } } - void Player::_SaveMail(CharacterDatabaseTransaction& trans) { CharacterDatabasePreparedStatement* stmt; @@ -19916,6 +19775,72 @@ void Player::_SaveStats(CharacterDatabaseTransaction& trans) const trans->Append(stmt); } +void Player::_SavePets(CharacterDatabaseTransaction& trans) +{ + CharacterDatabasePreparedStatement* stmt = nullptr; + uint32 lowGuid = GetGUID().GetCounter(); + + // Make sure that the currently active pet is up to date before we save everything. + if (NewPet* pet = GetActivelyControlledSummon()) + pet->UpdatePlayerPetData(this, true); + + for (auto const& pair : _playerPetDataMap) + { + PlayerPetData* petData = pair.second.get(); + if (petData->Status == PlayerPetDataStatus::UpToDate) + continue; + + if (petData->Status == PlayerPetDataStatus::New) + { + // INSERT INTO character_pet (Guid, PetNumber, CreatureId, TamedCreatureId, DisplayId, SavedHealth, SavedPower, CreatedBySpellId, LastSaveTime, ReactState, Slot, HasBeenRenamed, IsActive, `Name`, ActionBar) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHAR_PET); + stmt->setUInt32(0, lowGuid); + stmt->setUInt32(1, petData->PetNumber); + stmt->setUInt32(2, petData->CreatureId); + stmt->setUInt32(3, petData->TamedCreatureId); + stmt->setUInt32(4, petData->DisplayId); + stmt->setUInt32(5, petData->SavedHealth); + stmt->setUInt32(6, petData->SavedPower); + stmt->setUInt32(7, petData->CreatedBySpellId); + stmt->setUInt32(8, petData->LastSaveTime); + stmt->setUInt8(9, petData->ReactState); + stmt->setUInt8(10, petData->Slot); + stmt->setUInt8(11, petData->HasBeenRenamed); + stmt->setUInt8(12, petData->IsActive); + stmt->setString(13, petData->Name); + stmt->setString(14, petData->ActionBar); + } + else if (petData->Status == PlayerPetDataStatus::Changed) + { + // UPDATE character_pet SET SavedHealth = ?, SavedPower = ?, LastSaveTime = ?, ReactState = ?, Slot = ?, HasBeenRenamed = ?, IsActive = ?, `Name` = ?, ActionBar = ? WHERE PetNumber = ? + stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHAR_PET); + stmt->setUInt32(0, petData->SavedHealth); + stmt->setUInt32(1, petData->SavedPower); + stmt->setUInt32(2, petData->LastSaveTime); + stmt->setUInt8(3, petData->ReactState); + stmt->setUInt8(4, petData->Slot); + stmt->setUInt8(5, petData->HasBeenRenamed); + stmt->setUInt8(6, petData->IsActive); + stmt->setString(7, petData->Name); + stmt->setString(8, petData->ActionBar); + stmt->setUInt32(9, petData->PetNumber); + } + + petData->Status = PlayerPetDataStatus::UpToDate; + + trans->Append(stmt); + } + + for (uint32 deletedPetNumber : _deletedPlayerPetDataSet) + { + stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_PET); + stmt->setUInt32(0, deletedPetNumber); + trans->Append(stmt); + } + + _deletedPlayerPetDataSet.clear(); +} + void Player::outDebugValues() const { if (!sLog->ShouldLog("entities.unit", LOG_LEVEL_DEBUG)) @@ -20187,15 +20112,6 @@ void Player::SetContestedPvP(Player* attackedPlayer) Trinity::AIRelocationNotifier notifier(*this); Cell::VisitWorldObjects(this, notifier, GetVisibilityRange()); } - for (Unit* unit : m_Controlled) - { - if (!unit->HasUnitState(UNIT_STATE_ATTACK_PLAYER)) - { - unit->AddUnitState(UNIT_STATE_ATTACK_PLAYER); - Trinity::AIRelocationNotifier notifier(*unit); - Cell::VisitWorldObjects(this, notifier, GetVisibilityRange()); - } - } } void Player::UpdateContestedPvP(uint32 diff) @@ -20243,129 +20159,18 @@ void Player::UpdateDuelFlag(time_t currTime) duel->opponent->duel->startTime = currTime; } -Pet* Player::GetPet() const -{ - if (ObjectGuid pet_guid = GetPetGUID()) - { - if (!pet_guid.IsPet()) - return nullptr; - - Pet* pet = ObjectAccessor::GetPet(*this, pet_guid); - - if (!pet) - return nullptr; - - if (IsInWorld()) - return pet; - - // there may be a guardian in this slot - //TC_LOG_ERROR("entities.player", "Player::GetPet: Pet %u does not exist.", GUID_LOPART(pet_guid)); - //const_cast(this)->SetPetGUID(0); - } - - return nullptr; -} - -void Player::RemovePet(Pet* pet, PetSaveMode mode, bool returnreagent) -{ - if (!pet) - pet = GetPet(); - - if (pet) - { - TC_LOG_DEBUG("entities.pet", "Player::RemovePet: Player '%s' (%s), Pet (Entry: %u, Mode: %u, ReturnReagent: %u)", - GetName().c_str(), GetGUID().ToString().c_str(), pet->GetEntry(), mode, returnreagent); - - if (pet->m_removed) - return; - } - - if (returnreagent && (pet || m_temporaryUnsummonedPetNumber) && !InBattleground()) - { - //returning of reagents only for players, so best done here - uint32 spellId = pet ? pet->GetUInt32Value(UNIT_CREATED_BY_SPELL) : m_oldpetspell; - SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId); - - if (spellInfo) - { - for (uint32 i = 0; i < MAX_SPELL_REAGENTS; ++i) - { - if (spellInfo->Reagent[i] > 0) - { - ItemPosCountVec dest; //for succubus, voidwalker, felhunter and felguard credit soulshard when despawn reason other than death (out of range, logout) - InventoryResult msg = CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, spellInfo->Reagent[i], spellInfo->ReagentCount[i]); - if (msg == EQUIP_ERR_OK) - { - Item* item = StoreNewItem(dest, spellInfo->Reagent[i], true); - if (IsInWorld()) - SendNewItem(item, spellInfo->ReagentCount[i], true, false); - } - } - } - } - m_temporaryUnsummonedPetNumber = 0; - } - - if (!pet || pet->GetOwnerOrCreatorGUID() != GetGUID()) - return; - - pet->CombatStop(); - - if (returnreagent) - { - switch (pet->GetEntry()) - { - //warlock pets except imp are removed(?) when logging out - case 1860: - case 1863: - case 417: - case 17252: - mode = PET_SAVE_DISMISS; - break; - } - } - - pet->SavePetToDB(mode); - - SetMinion(pet, false); - - pet->AddObjectToRemoveList(); - pet->m_removed = true; - - if (pet->isControlled()) - { - WorldPacket data(SMSG_PET_SPELLS, 8); - data << uint64(0); - SendDirectMessage(&data); - - if (GetGroup()) - SetGroupUpdateFlag(GROUP_UPDATE_PET); - - if (mode == PET_SAVE_DISMISS && getClass() == CLASS_WARLOCK) - { - if (CreatureDisplayInfoEntry const* creatureDisplay = sCreatureDisplayInfoStore.LookupEntry(pet->GetDisplayId())) - { - WorldPackets::Pet::PetDismissSound dismissSound; - dismissSound.ModelID = creatureDisplay->ModelID; - dismissSound.ModelPosition = pet->GetPosition(); - pet->SendMessageToSet(dismissSound.Write(), false); - } - } - } -} - void Player::AddPetAura(PetAura const* petSpell) { m_petAuras.insert(petSpell); - if (Pet* pet = GetPet()) - pet->CastPetAura(petSpell); + //if (Pet* pet = GetPet()) + // pet->CastPetAura(petSpell); } void Player::RemovePetAura(PetAura const* petSpell) { m_petAuras.erase(petSpell); - if (Pet* pet = GetPet()) - pet->RemoveAurasDueToSpell(petSpell->GetAura(pet->GetEntry())); + //if (Pet* pet = GetPet()) + // pet->RemoveAurasDueToSpell(petSpell->GetAura(pet->GetEntry())); } void Player::StopCastingCharm() @@ -20540,6 +20345,45 @@ bool Player::RemoveMItem(uint32 id) return mMitems.erase(id) ? true : false; } +void Player::SendPetSpellsMessage(NewPet* pet) +{ + // Warlocks and Hunters cannot control their pets until they have learned their respective control spell + if (pet->IsClassPet() && !CanControlClassPets()) + return; + + CharmInfo* charmInfo = pet->GetCharmInfo(); + if (!charmInfo) + { + TC_LOG_ERROR("entities.pet", "Attempted to send pet spells message for a pet without charmInfo."); + return; + } + + WorldPackets::Pet::PetSpellsMessage packet; + packet.PetGUID = pet->GetGUID(); + packet._CreatureFamily = pet->GetCreatureTemplate()->family; // creature family (required for pet talents) + packet.TimeLimit = pet->GetRemainingSummonDuration().count(); + packet.ReactState = pet->GetReactState(); + packet.CommandState = charmInfo->GetCommandState(); + + charmInfo->BuildActionBar(packet.ActionButtons); + if (pet->IsClassPet()) + { + for (auto const& itr : pet->GetSpells()) + { + if (itr.second.State == PETSPELL_REMOVED) + continue; + + packet.Actions.push_back(MAKE_UNIT_ACTION_BUTTON(itr.first, itr.second.Active)); + } + + // Cooldowns + pet->GetSpellHistory()->WritePetSpellHistory(packet); + } + + SendDirectMessage(packet.Write()); +} + + void Player::SendOnCancelExpectedVehicleRideAura() const { WorldPacket data(SMSG_ON_CANCEL_EXPECTED_RIDE_VEHICLE_AURA, 0); @@ -20605,7 +20449,7 @@ void Player::PetSpellInitialize() if (pet->IsPermanentPetFor(this)) { // spells loop - for (PetSpellMap::iterator itr = pet->m_spells.begin(); itr != pet->m_spells.end(); ++itr) + for (PetSpellMapOld::iterator itr = pet->m_spells.begin(); itr != pet->m_spells.end(); ++itr) { if (itr->second.state == PETSPELL_REMOVED) continue; @@ -20708,7 +20552,7 @@ void Player::VehicleSpellInitialize() void Player::CharmSpellInitialize() { - Unit* charm = GetFirstControlled(); + Unit* charm = nullptr; if (!charm) return; @@ -21918,15 +21762,11 @@ void Player::UpdatePvPState(bool onlyFFA) if (!IsFFAPvP()) { SetByteFlag(UNIT_FIELD_BYTES_2, UNIT_BYTES_2_OFFSET_PVP_FLAG, UNIT_BYTE2_FLAG_FFA_PVP); - for (ControlList::iterator itr = m_Controlled.begin(); itr != m_Controlled.end(); ++itr) - (*itr)->SetByteValue(UNIT_FIELD_BYTES_2, UNIT_BYTES_2_OFFSET_PVP_FLAG, UNIT_BYTE2_FLAG_FFA_PVP); } } else if (IsFFAPvP()) { RemoveByteFlag(UNIT_FIELD_BYTES_2, UNIT_BYTES_2_OFFSET_PVP_FLAG, UNIT_BYTE2_FLAG_FFA_PVP); - for (ControlList::iterator itr = m_Controlled.begin(); itr != m_Controlled.end(); ++itr) - (*itr)->RemoveByteFlag(UNIT_FIELD_BYTES_2, UNIT_BYTES_2_OFFSET_PVP_FLAG, UNIT_BYTE2_FLAG_FFA_PVP); } if (onlyFFA) @@ -21947,8 +21787,6 @@ void Player::UpdatePvPState(bool onlyFFA) void Player::SetPvP(bool state) { Unit::SetPvP(state); - for (ControlList::iterator itr = m_Controlled.begin(); itr != m_Controlled.end(); ++itr) - (*itr)->SetPvP(state); } void Player::UpdatePvP(bool state, bool _override) @@ -22421,7 +22259,7 @@ inline void BeforeVisibilityDestroy(T* /*t*/, Player* /*p*/) { } template<> inline void BeforeVisibilityDestroy(Creature* t, Player* p) { - if (p->GetPetGUID() == t->GetGUID() && t->IsPet()) + if (p->GetSummonGUID() == t->GetGUID() && t->IsPet()) t->ToPet()->Remove(PET_SAVE_DISMISS, true); } @@ -22849,8 +22687,8 @@ void Player::SendUpdateToOutOfRangeGroupMembers() m_groupUpdateMask = GROUP_UPDATE_FLAG_NONE; m_auraRaidUpdateMask = 0; - if (Pet* pet = GetPet()) - pet->ResetAuraUpdateMaskForRaid(); + //if (Pet* pet = GetPet()) + // pet->ResetAuraUpdateMaskForRaid(); } void Player::SendTransferAborted(uint32 mapid, TransferAbortReason reason, uint8 arg) const @@ -25492,137 +25330,6 @@ bool Player::LearnTalent(uint32 talentId, uint32 talentRank) void Player::LearnPetTalent(ObjectGuid petGuid, uint32 talentId, uint32 talentRank) { - Pet* pet = GetPet(); - - if (!pet) - return; - - if (petGuid != pet->GetGUID()) - return; - - uint32 CurTalentPoints = pet->GetFreeTalentPoints(); - - if (CurTalentPoints == 0) - return; - - if (talentRank >= MAX_PET_TALENT_RANK) - return; - - TalentEntry const* talentInfo = sTalentStore.LookupEntry(talentId); - - if (!talentInfo) - return; - - TalentTabEntry const* talentTabInfo = sTalentTabStore.LookupEntry(talentInfo->TabID); - - if (!talentTabInfo) - return; - - CreatureTemplate const* ci = pet->GetCreatureTemplate(); - - if (!ci) - return; - - CreatureFamilyEntry const* pet_family = sCreatureFamilyStore.LookupEntry(ci->family); - - if (!pet_family) - return; - - if (pet_family->PetTalentType < 0) // not hunter pet - return; - - // prevent learn talent for different family (cheating) - if (!((1 << pet_family->PetTalentType) & talentTabInfo->CategoryEnumID)) - return; - - // find current max talent rank (0~5) - uint8 curtalent_maxrank = 0; // 0 = not learned any rank - for (int8 rank = MAX_TALENT_RANK-1; rank >= 0; --rank) - { - if (talentInfo->SpellRank[rank] && pet->HasSpell(talentInfo->SpellRank[rank])) - { - curtalent_maxrank = (rank + 1); - break; - } - } - - // we already have same or higher talent rank learned - if (curtalent_maxrank >= (talentRank + 1)) - return; - - // check if we have enough talent points - if (CurTalentPoints < (talentRank - curtalent_maxrank + 1)) - return; - - // Check if it requires another talent - if (talentInfo->PrereqTalent[0] > 0) - { - if (TalentEntry const* depTalentInfo = sTalentStore.LookupEntry(talentInfo->PrereqTalent[0])) - { - bool hasEnoughRank = false; - for (uint8 rank = talentInfo->PrereqRank[0]; rank < MAX_TALENT_RANK; rank++) - { - if (depTalentInfo->SpellRank[rank] != 0) - if (pet->HasSpell(depTalentInfo->SpellRank[rank])) - hasEnoughRank = true; - } - if (!hasEnoughRank) - return; - } - } - - // Find out how many points we have in this field - uint32 spentPoints = 0; - - uint32 tTab = talentInfo->TabID; - if (talentInfo->TierID > 0) - { - uint32 numRows = sTalentStore.GetNumRows(); - for (uint32 i = 0; i < numRows; ++i) // Loop through all talents. - { - // Someday, someone needs to revamp - TalentEntry const* tmpTalent = sTalentStore.LookupEntry(i); - if (tmpTalent) // the way talents are tracked - { - if (tmpTalent->TabID == tTab) - { - for (uint8 rank = 0; rank < MAX_TALENT_RANK; rank++) - { - if (tmpTalent->SpellRank[rank] != 0) - { - if (pet->HasSpell(tmpTalent->SpellRank[rank])) - { - spentPoints += (rank + 1); - } - } - } - } - } - } - } - - // not have required min points spent in talent tree - if (spentPoints < (talentInfo->TierID * MAX_PET_TALENT_RANK)) - return; - - // spell not set in talent.dbc - uint32 spellid = talentInfo->SpellRank[talentRank]; - if (spellid == 0) - { - TC_LOG_ERROR("entities.player", "Talent.dbc contains talent: %u Rank: %u spell id = 0", talentId, talentRank); - return; - } - - // already known - if (pet->HasSpell(spellid)) - return; - - // learn! (other talent ranks will unlearned at learning) - pet->learnSpell(spellid); - TC_LOG_DEBUG("entities.player", "PetTalentID: %u Rank: %u Spell: %u\n", talentId, talentRank, spellid); - - // update free talent points - pet->SetFreeTalentPoints(CurTalentPoints - (talentRank - curtalent_maxrank + 1)); } void Player::AddKnownCurrency(uint32 itemId) @@ -25637,45 +25344,6 @@ void Player::UpdateFallInformationIfNeed(MovementInfo const& minfo, uint16 opcod SetFallInformation(minfo.jump.fallTime, minfo.pos.GetPositionZ()); } -void Player::UnsummonPetTemporaryIfAny() -{ - Pet* pet = GetPet(); - if (!pet) - return; - - if (!m_temporaryUnsummonedPetNumber && pet->isControlled() && !pet->isTemporarySummoned()) - { - m_temporaryUnsummonedPetNumber = pet->GetCharmInfo()->GetPetNumber(); - m_oldpetspell = pet->GetUInt32Value(UNIT_CREATED_BY_SPELL); - } - - RemovePet(pet, PET_SAVE_TEMP_UNSUMMON); -} - -void Player::ResummonPetTemporaryUnSummonedIfAny() -{ - if (!m_temporaryUnsummonedPetNumber) - return; - - // not resummon in not appropriate state - if (IsPetNeedBeTemporaryUnsummoned()) - return; - - if (GetPetGUID()) - return; - - Pet* NewPet = new Pet(this); - if (!NewPet->LoadPetData(this, 0, m_temporaryUnsummonedPetNumber, false)) - delete NewPet; - - m_temporaryUnsummonedPetNumber = 0; -} - -bool Player::IsPetNeedBeTemporaryUnsummoned() const -{ - return !IsInWorld() || !IsAlive() || IsMounted() /*+in flight*/; -} - bool Player::CanSeeSpellClickOn(Creature const* c) const { if (!c->HasFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_SPELLCLICK)) @@ -25780,6 +25448,7 @@ void Player::BuildPlayerTalentsInfoData(WorldPacket* data) void Player::BuildPetTalentsInfoData(WorldPacket* data) { + /* uint32 unspentTalentPoints = 0; size_t pointsPos = data->wpos(); *data << uint32(unspentTalentPoints); // [PH], unspentTalentPoints @@ -25848,6 +25517,7 @@ void Player::BuildPetTalentsInfoData(WorldPacket* data) break; } + */ } void Player::SendTalentsInfoData(bool pet) @@ -26241,14 +25911,8 @@ void Player::ActivateSpec(uint8 spec) _SaveActions(trans); CharacterDatabase.CommitTransaction(trans); - // TO-DO: We need more research to know what happens with warlock's reagent - if (Pet* pet = GetPet()) - RemovePet(pet, PET_SAVE_DISMISS); - ClearAllReactives(); - UnsummonAllTotems(); ExitVehicle(); - RemoveAllControlled(); // remove limited target auras at other targets AuraList& scAuras = GetSingleCastAuras(); @@ -26264,10 +25928,6 @@ void Player::ActivateSpec(uint8 spec) ++iter; } - /*RemoveAllAurasOnDeath(); - if (GetPet()) - GetPet()->RemoveAllAurasOnDeath();*/ - //RemoveAllAuras(GetGUID(), nullptr, false, true); // removes too many auras //ExitVehicle(); // should be impossible to switch specs from inside a vehicle.. @@ -27090,103 +26750,6 @@ Guild* Player::GetGuild() return guildId ? sGuildMgr->GetGuildById(guildId) : nullptr; } -Pet* Player::SummonPet(uint32 entry, float x, float y, float z, float ang, PetType petType, uint32 duration) -{ - Pet* pet = new Pet(this, petType); - - pet->Relocate(x, y, z, ang); - if (!pet->IsPositionValid()) - { - TC_LOG_ERROR("misc", "Player::SummonPet: Pet (%s, Entry: %d) not summoned. Suggested coordinates aren't valid (X: %f Y: %f)", pet->GetGUID().ToString().c_str(), pet->GetEntry(), pet->GetPositionX(), pet->GetPositionY()); - delete pet; - return nullptr; - } - - bool hasPetData = pet->LoadPetData(this, entry); - - if (!hasPetData && petType == HUNTER_PET) - { - delete pet; - return nullptr; - } - - if (!hasPetData) - { - Map* map = GetMap(); - uint32 pet_number = sObjectMgr->GeneratePetNumber(); - if (!pet->Create(map->GenerateLowGuid(), map, entry, pet_number)) - { - TC_LOG_ERROR("misc", "Player::SummonPet: No such creature entry %u", entry); - delete pet; - return nullptr; - } - - // generate new name for summon pet - std::string new_name = sObjectMgr->GeneratePetName(entry); - if (!new_name.empty()) - pet->SetName(new_name); - - pet->SetCreatorGUID(GetGUID()); - pet->SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE, GetFaction()); - - pet->SetUInt64Value(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_NONE); - pet->SetUInt32Value(UNIT_FIELD_BYTES_1, 0); - pet->InitStatsForLevel(getLevel()); - pet->SetReactState(REACT_ASSIST); - - SetMinion(pet, true); - - switch (petType) - { - case SUMMON_PET: - pet->GetCharmInfo()->SetPetNumber(pet_number, true); - pet->SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_CLASS, CLASS_MAGE); - pet->SetUInt32Value(UNIT_FIELD_PETEXPERIENCE, 0); - pet->SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP, 1000); - pet->SetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP, uint32(GameTime::GetGameTime())); // cast can't be helped in this case - break; - default: - break; - } - - map->AddToMap(pet->ToCreature()); - - switch (petType) - { - case SUMMON_PET: - pet->InitPetCreateSpells(); - pet->SavePetToDB(PET_SAVE_NEW_PET); - PetSpellInitialize(); - GetSession()->SendPetAdded(pet->GetSlot(), pet->GetCharmInfo()->GetPetNumber(), pet->GetEntry(), pet->getLevel(), pet->GetName()); - break; - default: - break; - } - - // Update all stats after we have applied pet scaling auras to make sure we have all fields initialized properly - pet->UpdateAllStats(); - pet->SetFullHealth(); - pet->SetPower(POWER_MANA, pet->GetMaxPower(POWER_MANA)); - - if (pet->IsHunterPet()) - pet->CastSpell(pet, SPELL_PET_ENERGIZE, true); - else if (pet->IsPetGhoul()) - { - pet->CastSpell(pet, SPELL_PET_RISEN_GHOUL_SPAWN_IN, true); - pet->CastSpell(pet, SPELL_PET_RISEN_GHOUL_SELF_STUN, true); - } - } - - PhasingHandler::InheritPhaseShift(pet, this); - - if (duration > 0) - pet->SetDuration(duration); - - //ObjectAccessor::UpdateObjectVisibility(pet); - - return pet; -} - bool Player::CanUseMastery() const { return HasSpell(MasterySpells[getClass()]); diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index 18ac79b60b7..ab559c621af 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -891,27 +891,36 @@ enum PlayerLogXPReason : uint8 LOG_XP_REASON_NO_KILL = 1 }; +enum class PlayerPetDataStatus : uint8 +{ + UpToDate = 0, + New = 1, + Changed = 2 +}; + struct PlayerPetData { - uint32 PetId; - uint32 CreatureId; - uint64 Owner; - uint32 DisplayId; - uint32 Petlevel; - uint32 PetExp; - ReactStates Reactstate; - uint8 Slot; + PlayerPetData() { } + + uint32 PetNumber = 0; + uint32 CreatureId = 0; + uint32 TamedCreatureId = 0; + uint32 DisplayId = 0; + uint32 SavedHealth = 0; + uint32 SavedPower = 0; + uint32 CreatedBySpellId = 0; + uint32 LastSaveTime = 0; + ReactStates ReactState = REACT_ASSIST; + uint8 Slot = 0; + bool HasBeenRenamed = false; + bool IsActive = false; std::string Name; - bool Renamed; - bool Active; - uint32 SavedHealth; - uint32 SavedMana; - std::string Actionbar; - uint32 Timediff; - uint32 SummonSpellId; - PetType Type; + std::string ActionBar; + PlayerPetDataStatus Status = PlayerPetDataStatus::New; }; +using PlayerPetDataMap = std::unordered_map, std::unique_ptr>; + class Player; /// Holder for Battleground data @@ -1125,14 +1134,11 @@ class TC_GAME_API Player : public Unit, public GridObject uint32 GetXPRestBonus(uint32 xp); uint32 GetInnTriggerId() const { return inn_triggerId; } - Pet* GetPet() const; - Pet* SummonPet(uint32 entry, float x, float y, float z, float ang, PetType petType, uint32 despwtime); - void RemovePet(Pet* pet, PetSaveMode mode, bool returnreagent = false); - // pet auras std::unordered_set m_petAuras; void AddPetAura(PetAura const* petSpell); void RemovePetAura(PetAura const* petSpell); + Pet* GetPet() { return nullptr; } /// Handles said message in regular chat based on declared language and in config pre-defined Range. void Say(std::string const& text, Language language, WorldObject const* = nullptr) override; @@ -1304,8 +1310,6 @@ class TC_GAME_API Player : public Unit, public GridObject void RemoveItemDurations(Item* item); void SendItemDurations(); void LoadCorpse(PreparedQueryResult result); - void LoadPet(); - void LoadPetsFromDB(PreparedQueryResult result); bool AddItem(uint32 itemId, uint32 count); @@ -1557,9 +1561,16 @@ class TC_GAME_API Player : public Unit, public GridObject void AddMItem(Item* it); bool RemoveMItem(uint32 id); + // Pets + void SendPetSpellsMessage(NewPet* pet); + void SetCanControlClassPets() { _canControlClassPets = true; } + bool CanControlClassPets() const { return _canControlClassPets; } + + public: void SendOnCancelExpectedVehicleRideAura() const; bool CanControlPet(uint32 spellId = 0) const; void PetSpellInitialize(); + void CharmSpellInitialize(); void PossessSpellInitialize(); void VehicleSpellInitialize(); @@ -2213,13 +2224,6 @@ class TC_GAME_API Player : public Unit, public GridObject bool HasValidLFGLeavePoint(uint32 mapid); void SetLFGLeavePoint(); - // Temporarily removed pet cache - uint32 GetTemporaryUnsummonedPetNumber() const { return m_temporaryUnsummonedPetNumber; } - void SetTemporaryUnsummonedPetNumber(uint32 petnumber) { m_temporaryUnsummonedPetNumber = petnumber; } - void UnsummonPetTemporaryIfAny(); - void ResummonPetTemporaryUnSummonedIfAny(); - bool IsPetNeedBeTemporaryUnsummoned() const; - void SendCinematicStart(uint32 cinematicId); void SendMovieStart(uint32 movieId); @@ -2254,10 +2258,6 @@ class TC_GAME_API Player : public Unit, public GridObject bool CheckInstanceCount(uint32 instanceId) const; void AddInstanceEnterTime(uint32 instanceId, time_t enterTime); - // last used pet number (for BG's) - uint32 GetLastPetNumber() const { return m_lastpetnumber; } - void SetLastPetNumber(uint32 petnumber) { m_lastpetnumber = petnumber; } - /*********************************************************/ /*** GROUP SYSTEM ***/ /*********************************************************/ @@ -2380,14 +2380,14 @@ class TC_GAME_API Player : public Unit, public GridObject VoidStorageItem* GetVoidStorageItem(uint64 id, uint8& slot) const; // Pets - PlayerPetData* GetPlayerPetDataById(uint32 petId); - PlayerPetData* GetPlayerPetDataBySlot(uint8 slot); - PlayerPetData* GetPlayerPetDataByCreatureId(uint32 creatureId); + PlayerPetData* GetPlayerPetData(uint8 slot, uint32 creatureId) const; + PlayerPetData* CreatePlayerPetData(uint8 slot, uint32 creatureId); + PlayerPetData* GetPlayerPetDataCurrent(); Optional GetFirstUnusedActivePetSlot(); Optional GetFirstUnusedPetSlot(); void DeleteFromPlayerPetDataStore(uint32 petNumber); - void AddToPlayerPetDataStore(PlayerPetData* playerPetData); + void AddToPlayerPetDataStore(PlayerPetData&& playerPetData); protected: // Gamemaster whisper whitelist @@ -2477,6 +2477,7 @@ class TC_GAME_API Player : public Unit, public GridObject void _LoadCurrency(PreparedQueryResult result); void _LoadCUFProfiles(PreparedQueryResult result); void _LoadLFGRewardStatus(PreparedQueryResult result); + void _LoadPets(PreparedQueryResult result); /*********************************************************/ /*** SAVE SYSTEM ***/ @@ -2503,6 +2504,7 @@ class TC_GAME_API Player : public Unit, public GridObject void _SaveCurrency(CharacterDatabaseTransaction& trans); void _SaveCUFProfiles(CharacterDatabaseTransaction& trans); void _SaveLFGRewardStatus(CharacterDatabaseTransaction& trans); + void _SavePets(CharacterDatabaseTransaction& trans); /*********************************************************/ /*** ENVIRONMENTAL SYSTEM ***/ @@ -2649,9 +2651,6 @@ class TC_GAME_API Player : public Unit, public GridObject uint64 m_auraRaidUpdateMask; bool m_bPassOnGroupLoot; - // last used pet number (for BG's) - uint32 m_lastpetnumber; - // Player summoning time_t m_summon_expire; WorldLocation m_summon_location; @@ -2731,10 +2730,6 @@ class TC_GAME_API Player : public Unit, public GridObject bool m_bCanDelayTeleport; bool m_bHasDelayedTeleport; - // Temporary removed pet cache - uint32 m_temporaryUnsummonedPetNumber; - uint32 m_oldpetspell; - std::unique_ptr> m_achievementMgr; std::unique_ptr m_reputationMgr; @@ -2757,10 +2752,12 @@ class TC_GAME_API Player : public Unit, public GridObject std::unique_ptr _archaeology; - std::vector PlayerPetDataStore; + PlayerPetDataMap _playerPetDataMap; + std::unordered_set _deletedPlayerPetDataSet; - TimeTrackerSmall m_petScalingSynchTimer; TimeTrackerSmall m_groupUpdateTimer; + + bool _canControlClassPets; }; TC_GAME_API void AddItemsSetItem(Player* player, Item* item); diff --git a/src/server/game/Entities/Totem/Totem.cpp b/src/server/game/Entities/Totem/Totem.cpp index 0913356517e..75ef6fc399a 100644 --- a/src/server/game/Entities/Totem/Totem.cpp +++ b/src/server/game/Entities/Totem/Totem.cpp @@ -104,16 +104,6 @@ void Totem::UnSummon(uint32 msTime) CombatStop(); RemoveAurasDueToSpell(GetSpell(), GetGUID()); - // clear owner's totem slot - for (uint8 i = SUMMON_SLOT_TOTEM_FIRE; i < MAX_TOTEM_SLOT; ++i) - { - if (GetOwner()->m_SummonSlot[i] == GetGUID()) - { - GetOwner()->m_SummonSlot[i].Clear(); - break; - } - } - GetOwner()->RemoveAurasDueToSpell(GetSpell(), GetGUID()); // remove aura all party members too @@ -135,9 +125,6 @@ void Totem::UnSummon(uint32 msTime) } } - // Despawn elementals - RemoveAllControlled(); - // any totem unsummon look like as totem kill, req. for proper animation if (IsAlive()) setDeathState(DEAD); diff --git a/src/server/game/Entities/Unit/StatSystem.cpp b/src/server/game/Entities/Unit/StatSystem.cpp index 35763d1978d..668210fea6d 100644 --- a/src/server/game/Entities/Unit/StatSystem.cpp +++ b/src/server/game/Entities/Unit/StatSystem.cpp @@ -18,6 +18,7 @@ #include "Unit.h" #include "Creature.h" #include "DBCStores.h" +#include "NewGuardian.h" #include "Item.h" #include "Pet.h" #include "Player.h" @@ -1286,3 +1287,223 @@ void Guardian::SetBonusDamage(int32 damage) if (GetOwner()->GetTypeId() == TYPEID_PLAYER) GetOwner()->SetUInt32Value(PLAYER_PET_SPELL_POWER, damage); } + +bool NewGuardian::UpdateAllStats() +{ + // Guardians without real level data use regular stat updates + if (!IsUsingRealStats()) + return Creature::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; +} + +bool NewGuardian::UpdateStats(Stats stat) +{ + if (stat >= MAX_STATS) + return false; + + // Guardians without real level data use regular stat updates + if (!IsUsingRealStats()) + return Creature::UpdateStats(stat); + + float value = GetTotalStatValue(stat); + + SetStat(stat, int32(value)); + 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; +} + +void NewGuardian::UpdateResistances(uint32 school) +{ + if (!IsUsingRealStats()) + { + Creature::UpdateResistances(school); + return; + } + + if (school > SPELL_SCHOOL_NORMAL) + { + float value = GetTotalAuraModValue(UnitMods(UNIT_MOD_RESISTANCE_START + school)); + SetResistance(SpellSchools(school), int32(value)); + } + else + UpdateArmor(); +} + +void NewGuardian::UpdateArmor() +{ + if (!IsUsingRealStats()) + { + Creature::UpdateArmor(); + return; + } + + float value = 0.0f; + UnitMods unitMod = UNIT_MOD_ARMOR; + + value = GetFlatModifierValue(unitMod, BASE_VALUE); + value *= GetPctModifierValue(unitMod, BASE_PCT); + value += GetFlatModifierValue(unitMod, TOTAL_VALUE); + value *= GetPctModifierValue(unitMod, TOTAL_PCT); + + SetArmor(int32(value)); +} + +void NewGuardian::UpdateMaxHealth() +{ + if (!IsUsingRealStats()) + { + Creature::UpdateMaxHealth(); + return; + } + + 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 NewGuardian::UpdateMaxPower(Powers power) +{ + if (GetPowerIndex(power) == MAX_POWERS) + return; + + if (!IsUsingRealStats()) + { + Creature::UpdateMaxPower(power); + 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) + GetCreatePowers(power); + value *= GetPctModifierValue(unitMod, BASE_PCT); + value += GetFlatModifierValue(unitMod, TOTAL_VALUE) + addValue * multiplicator; + value *= GetPctModifierValue(unitMod, TOTAL_PCT); + + SetMaxPower(power, int32(value)); +} + +void NewGuardian::UpdateAttackPowerAndDamage(bool ranged /*= false*/) +{ + if (ranged) + return; + + if (!IsUsingRealStats()) + { + Creature::UpdateAttackPowerAndDamage(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); +} + +void NewGuardian::UpdateDamagePhysical(WeaponAttackType attType) +{ + if (attType > BASE_ATTACK) + return; + + if (!IsUsingRealStats()) + { + Creature::UpdateDamagePhysical(attType); + return; + } + + UnitMods unitMod = UNIT_MOD_DAMAGE_MAINHAND; + + float att_speed = float(GetAttackTime(BASE_ATTACK)) / 1000.0f; + + float base_value = GetFlatModifierValue(unitMod, BASE_VALUE) + GetTotalAttackPowerValue(attType) / 14.0f * att_speed; + 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); +} diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index 4e36c7f7cec..a22f97910bb 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -59,6 +59,7 @@ #include "Opcodes.h" #include "OutdoorPvP.h" #include "PassiveAI.h" +#include "NewPet.h" #include "Pet.h" #include "PetPackets.h" #include "PetAI.h" @@ -76,6 +77,7 @@ #include "SpellMgr.h" #include "SpellPackets.h" #include "TemporarySummon.h" +#include "NewTemporarySummon.h" #include "Totem.h" #include "Transport.h" #include "UnitAI.h" @@ -397,7 +399,6 @@ Unit::~Unit() ASSERT(!m_attacking); ASSERT(m_attackers.empty()); ASSERT(m_sharedVision.empty()); - ASSERT(m_Controlled.empty()); ASSERT(m_appliedAuras.empty()); ASSERT(m_ownedAuras.empty()); ASSERT(m_removedAuras.empty()); @@ -754,19 +755,8 @@ bool Unit::HasBreakableByDamageCrowdControlAura(Unit* excludeCasterChannel) cons sScriptMgr->OnDamage(attacker, victim, damage); if (victim->GetTypeId() == TYPEID_PLAYER) - { - // Signal to pets that their owner was attacked - except when DOT. - if (attacker != victim && damagetype != DOT) - { - for (Unit* controlled : victim->m_Controlled) - if (Creature* cControlled = controlled->ToCreature()) - if (CreatureAI* controlledAI = cControlled->AI()) - controlledAI->OwnerAttackedBy(attacker); - } - if (victim->ToPlayer()->GetCommandStatus(CHEAT_GOD)) return 0; - } if (damagetype != NODAMAGE) { @@ -5394,16 +5384,6 @@ bool Unit::Attack(Unit* victim, bool meleeAttack) if (meleeAttack) SendMeleeAttackStart(victim); - // Let the pet know we've started attacking someting. Handles melee attacks only - // Spells such as auto-shot and others handled in WorldSession::HandleCastSpellOpcode - if (GetTypeId() == TYPEID_PLAYER) - { - for (Unit* controlled : m_Controlled) - if (Creature* cControlled = controlled->ToCreature()) - if (CreatureAI* controlledAI = cControlled->AI()) - controlledAI->OwnerAttacked(victim); - } - return true; } @@ -5473,9 +5453,6 @@ void Unit::CombatStop(bool includingCast, bool mutualPvP) void Unit::CombatStopWithPets(bool includingCast) { CombatStop(includingCast); - - for (Unit* minion : m_Controlled) - minion->CombatStop(includingCast); } bool Unit::isAttackingPlayer() const @@ -5483,16 +5460,6 @@ bool Unit::isAttackingPlayer() const if (HasUnitState(UNIT_STATE_ATTACK_PLAYER)) return true; - for (ControlList::const_iterator itr = m_Controlled.begin(); itr != m_Controlled.end(); ++itr) - if ((*itr)->isAttackingPlayer()) - return true; - - for (uint8 i = 0; i < MAX_SUMMON_SLOT; ++i) - if (m_SummonSlot[i]) - if (Creature* summon = GetMap()->GetCreature(m_SummonSlot[i])) - if (summon->isAttackingPlayer()) - return true; - return false; } @@ -5530,6 +5497,7 @@ void Unit::ModifyAuraState(AuraStateType flag, bool apply) CastSpell(this, itr->first, true); } } + /* else if (Pet* pet = ToCreature()->ToPet()) { for (PetSpellMap::const_iterator itr = pet->m_spells.begin(); itr != pet->m_spells.end(); ++itr) @@ -5543,6 +5511,7 @@ void Unit::ModifyAuraState(AuraStateType flag, bool apply) CastSpell(this, itr->first, true); } } + */ } } else @@ -5602,29 +5571,20 @@ bool Unit::HasAuraState(AuraStateType flag, SpellInfo const* spellProto, Unit co return HasFlag(UNIT_FIELD_AURASTATE, 1 << (flag - 1)); } -void Unit::SetOwnerGUID(ObjectGuid owner) +void Unit::SetActivelyControlledSummon(NewPet* pet, bool apply) { - if (GetOwnerGUID() == owner) - return; - - SetGuidValue(UNIT_FIELD_SUMMONEDBY, owner); - if (!owner) - return; - - // Update owner dependent fields - Player* player = ObjectAccessor::GetPlayer(*this, owner); - if (!player || !player->HaveAtClient(this)) // if player cannot see this unit yet, he will receive needed data with create object + if (apply) + { + if (GetSummonGUID() == pet->GetGUID()) + return; + } + else if (GetSummonGUID() != pet->GetGUID()) return; - SetFieldNotifyFlag(UF_FLAG_OWNER); - - UpdateData udata(GetMapId()); - WorldPacket packet; - BuildValuesUpdateBlockForPlayer(&udata, player); - udata.BuildPacket(&packet); - player->SendDirectMessage(&packet); + SetSummonGUID(apply ? pet->GetGUID() : ObjectGuid::Empty); - RemoveFieldNotifyFlag(UF_FLAG_OWNER); + if (IsPlayer()) + ToPlayer()->SendPetSpellsMessage(pet); } Player* Unit::GetControllingPlayer() const @@ -5639,196 +5599,6 @@ Player* Unit::GetControllingPlayer() const return const_cast(ToPlayer()); } -Minion* Unit::GetFirstMinion() const -{ - if (ObjectGuid pet_guid = GetMinionGUID()) - { - if (Creature* pet = ObjectAccessor::GetCreatureOrPetOrVehicle(*this, pet_guid)) - if (pet->HasUnitTypeMask(UNIT_MASK_MINION)) - return (Minion*)pet; - - TC_LOG_ERROR("entities.unit", "Unit::GetFirstMinion: Minion %s not exist.", pet_guid.ToString().c_str()); - const_cast(this)->SetMinionGUID(ObjectGuid::Empty); - } - - return nullptr; -} - -Guardian* Unit::GetGuardianPet() const -{ - if (ObjectGuid pet_guid = GetPetGUID()) - { - if (Creature* pet = ObjectAccessor::GetCreatureOrPetOrVehicle(*this, pet_guid)) - if (pet->HasUnitTypeMask(UNIT_MASK_GUARDIAN)) - return (Guardian*)pet; - - TC_LOG_FATAL("entities.unit", "Unit::GetGuardianPet: Guardian %s not exist.", pet_guid.ToString().c_str()); - const_cast(this)->SetPetGUID(ObjectGuid::Empty); - } - - return nullptr; -} - -void Unit::SetMinion(Minion* minion, bool apply) -{ - if (!minion) - { - TC_LOG_ERROR("entities.unit", "Unit::SetMinion: Unit %s tried to reference a non existing minion", GetGUID().ToString().c_str()); - return; - } - - TC_LOG_DEBUG("entities.unit", "SetMinion %u for %u, apply %u", minion->GetEntry(), GetEntry(), apply); - - if (apply) - { - if (minion->GetOwnerGUID()) - { - TC_LOG_FATAL("entities.unit", "SetMinion: Minion %u is not the minion of owner %u", minion->GetEntry(), GetEntry()); - return; - } - - minion->SetOwnerGUID(GetGUID()); - - m_Controlled.insert(minion); - - if (GetTypeId() == TYPEID_PLAYER) - { - minion->m_ControlledByPlayer = true; - minion->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED); - } - - // Can only have one pet. If a new one is summoned, dismiss the old one. - if (minion->IsGuardianPet()) - { - if (Guardian* oldPet = GetGuardianPet()) - { - if (oldPet != minion && (oldPet->IsPet() || minion->IsPet() || oldPet->GetEntry() != minion->GetEntry())) - { - // remove existing minion pet - if (oldPet->IsPet()) - ((Pet*)oldPet)->Remove(PET_SAVE_DISMISS); - else - oldPet->UnSummon(); - SetPetGUID(minion->GetGUID()); - SetMinionGUID(ObjectGuid::Empty); - } - } - else - { - SetPetGUID(minion->GetGUID()); - SetMinionGUID(ObjectGuid::Empty); - } - } - - if (minion->HasUnitTypeMask(UNIT_MASK_CONTROLABLE_GUARDIAN)) - AddGuidValue(UNIT_FIELD_SUMMON, minion->GetGUID()); - - if (minion->m_Properties && SummonTitle(minion->m_Properties->Title) == SummonTitle::Companion) - SetCritterGUID(minion->GetGUID()); - - // PvP, FFAPvP - minion->SetByteValue(UNIT_FIELD_BYTES_2, UNIT_BYTES_2_OFFSET_PVP_FLAG, GetByteValue(UNIT_FIELD_BYTES_2, UNIT_BYTES_2_OFFSET_PVP_FLAG)); - - // Send infinity cooldown - client does that automatically but after relog cooldown needs to be set again - SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(minion->GetUInt32Value(UNIT_CREATED_BY_SPELL)); - - if (spellInfo && spellInfo->IsCooldownStartedOnEvent()) - GetSpellHistory()->StartCooldown(spellInfo, 0, nullptr, true); - } - else - { - if (minion->GetOwnerGUID() != GetGUID()) - { - TC_LOG_FATAL("entities.unit", "SetMinion: Minion %u is not the minion of owner %u", minion->GetEntry(), GetEntry()); - return; - } - - if (m_Controlled.find(minion) != m_Controlled.end()) - m_Controlled.erase(minion); - - if (minion->m_Properties && SummonTitle(minion->m_Properties->Title) == SummonTitle::Companion) - if (GetCritterGUID() == minion->GetGUID()) - SetCritterGUID(ObjectGuid::Empty); - - if (minion->IsGuardianPet()) - { - if (GetPetGUID() == minion->GetGUID()) - SetPetGUID(ObjectGuid::Empty); - } - - SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(minion->GetUInt32Value(UNIT_CREATED_BY_SPELL)); - // Remove infinity cooldown - if (spellInfo && (spellInfo->IsCooldownStartedOnEvent())) - GetSpellHistory()->SendCooldownEvent(spellInfo); - - //if (minion->HasUnitTypeMask(UNIT_MASK_GUARDIAN)) - { - if (RemoveGuidValue(UNIT_FIELD_SUMMON, minion->GetGUID())) - { - // Check if there is another minion - for (Unit const* controlled : m_Controlled) - { - // do not use this check, creature do not have charm guid - //if (GetCharmedGUID() == (*itr)->GetGUID()) - if (GetGUID() == controlled->GetCharmerGUID()) - continue; - - //ASSERT((*itr)->GetOwnerGUID() == GetGUID()); - if (controlled->GetOwnerOrCreatorGUID() != GetGUID()) - { - OutDebugInfo(); - controlled->OutDebugInfo(); - ABORT(); - } - ASSERT(controlled->IsCreature()); - - if (!controlled->HasUnitTypeMask(UNIT_MASK_CONTROLABLE_GUARDIAN)) - continue; - - if (AddGuidValue(UNIT_FIELD_SUMMON, controlled->GetGUID())) - { - // show another pet bar if there is no charm bar - if (IsPlayer() && !GetCharmedGUID()) - { - if (controlled->IsPet()) - ToPlayer()->PetSpellInitialize(); - else - ToPlayer()->CharmSpellInitialize(); - } - } - break; - } - } - } - } - UpdatePetCombatState(); -} - -void Unit::GetAllMinionsByEntry(std::list& Minions, uint32 entry) -{ - for (Unit::ControlList::iterator itr = m_Controlled.begin(); itr != m_Controlled.end();) - { - Unit* unit = *itr; - ++itr; - if (unit->GetEntry() == entry && unit->GetTypeId() == TYPEID_UNIT - && unit->IsSummon()) // minion, actually - Minions.push_back(unit->ToCreature()); - } -} - -void Unit::RemoveAllMinionsByEntry(uint32 entry) -{ - for (Unit::ControlList::iterator itr = m_Controlled.begin(); itr != m_Controlled.end();) - { - Unit* unit = *itr; - ++itr; - if (unit->GetEntry() == entry && unit->GetTypeId() == TYPEID_UNIT - && unit->IsSummon()) // minion, actually - unit->ToTempSummon()->UnSummon(); - // i think this is safe because i have never heard that a despawned minion will trigger a same minion - } -} - void Unit::SetCharm(Unit* charm, bool apply) { if (apply) @@ -5857,7 +5627,7 @@ void Unit::SetCharm(Unit* charm, bool apply) if (_isWalkingBeforeCharm) charm->SetWalk(false); - m_Controlled.insert(charm); + //m_Controlled.insert(charm); } else { @@ -5900,7 +5670,7 @@ void Unit::SetCharm(Unit* charm, bool apply) || !charm->ToCreature()->HasUnitTypeMask(UNIT_MASK_MINION) || charm->GetOwnerOrCreatorGUID() != GetGUID()) { - m_Controlled.erase(charm); + //m_Controlled.erase(charm); } } UpdatePetCombatState(); @@ -5984,44 +5754,6 @@ Unit* Unit::GetMeleeHitRedirectTarget(Unit* victim, SpellInfo const* spellInfo) return victim; } -Unit* Unit::GetFirstControlled() const -{ - // Sequence: charmed, pet, other guardians - Unit* unit = GetCharmed(); - if (!unit) - if (ObjectGuid guid = GetMinionGUID()) - unit = ObjectAccessor::GetUnit(*this, guid); - - return unit; -} - -void Unit::RemoveAllControlled() -{ - // possessed pet and vehicle - if (GetTypeId() == TYPEID_PLAYER) - ToPlayer()->StopCastingCharm(); - - while (!m_Controlled.empty()) - { - Unit* target = *m_Controlled.begin(); - m_Controlled.erase(m_Controlled.begin()); - if (target->GetCharmerGUID() == GetGUID()) - target->RemoveCharmAuras(); - else if (target->GetOwnerOrCreatorGUID() == GetGUID() && target->IsSummon()) - target->ToTempSummon()->UnSummon(); - else - TC_LOG_ERROR("entities.unit", "Unit %u is trying to release unit %u which is neither charmed nor owned by it", GetEntry(), target->GetEntry()); - } - if (GetPetGUID()) - TC_LOG_FATAL("entities.unit", "Unit %u is not able to release its pet %s", GetEntry(), GetPetGUID().ToString().c_str()); - if (GetMinionGUID()) - TC_LOG_FATAL("entities.unit", "Unit %u is not able to release its minion %s", GetEntry(), GetMinionGUID().ToString().c_str()); - if (GetCharmedGUID()) - TC_LOG_FATAL("entities.unit", "Unit %u is not able to release its charm %s", GetEntry(), GetCharmedGUID().ToString().c_str()); - if (!IsPet()) // pets don't use the flag for this - RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PET_IN_COMBAT); // m_controlled is now empty, so we know none of our minions are in combat -} - bool Unit::isPossessedByPlayer() const { return HasUnitState(UNIT_STATE_POSSESSED) && GetCharmerGUID().IsPlayer(); @@ -6048,56 +5780,6 @@ Unit* Unit::GetCharmerOrSelf() const return const_cast(this); } -Unit* Unit::GetNextRandomRaidMemberOrPet(float radius) -{ - Player* player = nullptr; - if (GetTypeId() == TYPEID_PLAYER) - player = ToPlayer(); - // Should we enable this also for charmed units? - else if (GetTypeId() == TYPEID_UNIT && IsPet()) - player = GetOwner()->ToPlayer(); - - if (!player) - return nullptr; - Group* group = player->GetGroup(); - // When there is no group check pet presence - if (!group) - { - // We are pet now, return owner - if (player != this) - return IsWithinDistInMap(player, radius) ? player : nullptr; - Unit* pet = GetGuardianPet(); - // No pet, no group, nothing to return - if (!pet) - return nullptr; - // We are owner now, return pet - return IsWithinDistInMap(pet, radius) ? pet : nullptr; - } - - std::vector nearMembers; - // reserve place for players and pets because resizing vector every unit push is unefficient (vector is reallocated then) - nearMembers.reserve(group->GetMembersCount() * 2); - - for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next()) - if (Player* Target = itr->GetSource()) - { - // IsHostileTo check duel and controlled by enemy - if (Target != this && IsWithinDistInMap(Target, radius) && Target->IsAlive() && !IsHostileTo(Target)) - nearMembers.push_back(Target); - - // Push player's pet to vector - if (Unit* pet = Target->GetGuardianPet()) - if (pet != this && IsWithinDistInMap(pet, radius) && pet->IsAlive() && !IsHostileTo(pet)) - nearMembers.push_back(pet); - } - - if (nearMembers.empty()) - return nullptr; - - uint32 randTarget = urand(0, nearMembers.size()-1); - return nearMembers[randTarget]; -} - // only called in Player::SetSeer // so move it to Player? void Unit::AddPlayerToVision(Player* player) @@ -6133,19 +5815,6 @@ void Unit::RemoveCharmAuras() RemoveAurasByType(SPELL_AURA_AOE_CHARM); } -void Unit::UnsummonAllTotems() -{ - for (uint8 i = 0; i < MAX_SUMMON_SLOT; ++i) - { - if (!m_SummonSlot[i]) - continue; - - if (Creature* OldTotem = GetMap()->GetCreature(m_SummonSlot[i])) - if (OldTotem->IsSummon()) - OldTotem->ToTempSummon()->UnSummon(); - } -} - void Unit::SendHealSpellLog(HealInfo& healInfo, bool critical /*= false*/) { // we guess size @@ -7824,18 +7493,6 @@ void Unit::Mount(uint32 mount, uint32 VehicleId, uint32 creatureEntry) } } - // unsummon pet - Pet* pet = player->GetPet(); - if (pet) - { - Battleground* bg = ToPlayer()->GetBattleground(); - // don't unsummon pet in arena but SetFlag UNIT_FLAG_STUNNED to disable pet's interface - if (bg && bg->isArena()) - pet->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_STUNNED); - else - player->UnsummonPetTemporaryIfAny(); - } - // if we have charmed npc, stun him also (everywhere) if (Unit* charm = player->GetCharmed()) if (charm->GetTypeId() == TYPEID_UNIT) @@ -7874,14 +7531,6 @@ void Unit::Dismount() // (it could probably happen when logging in after a previous crash) if (Player* player = ToPlayer()) { - if (Pet* pPet = player->GetPet()) - { - if (pPet->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_STUNNED) && !pPet->HasUnitState(UNIT_STATE_STUNNED)) - pPet->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_STUNNED); - } - else - player->ResummonPetTemporaryUnSummonedIfAny(); - // if we have charmed npc, remove stun also if (Unit* charm = player->GetCharmed()) if (charm->GetTypeId() == TYPEID_UNIT && charm->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_STUNNED) && !charm->HasUnitState(UNIT_STATE_STUNNED)) @@ -8459,10 +8108,6 @@ void Unit::SetSpeedRate(UnitMoveType mtype, float rate) void Unit::SetSpeedRateReal(UnitMoveType mtype, float rate) { - if (!IsInCombat() && ToPlayer()) - if (Pet* pet = ToPlayer()->GetPet()) - pet->SetSpeedRate(mtype, rate); - m_speed_rate[mtype] = rate; PropagateSpeedChange(); } @@ -8481,34 +8126,19 @@ void Unit::FollowTarget(Unit* target) bool faceTarget = false; // unit will face its target with every spline float distance = DEFAULT_FOLLOW_DISTANCE_PET; - if (TempSummon* summon = ToTempSummon()) + if (IsSummon()) { - if (SummonPropertiesEntry const* properties = summon->m_Properties) + if (NewTemporarySummon* summon = ToTemporarySummon()) { - // Allied summons, pet summons join a formation unless the following exceptions are being met. - if (properties->Control == SUMMON_CATEGORY_ALLY || properties->Control == SUMMON_CATEGORY_PET) - joinFormation = true; - - // Companion minipets will always be able to catch up to their target - if (properties->Slot == SUMMON_SLOT_MINIPET) + joinFormation = summon->ShouldJoinSummonerSpawnGroupAfterCreation(); + if (summon->ShouldFollowSummonerAfterCreation()) { - joinFormation = false; catchUpToTarget = true; faceTarget = true; distance = DEFAULT_FOLLOW_DISTANCE; } - - // Quest npcs follow their target outside of formations - if (properties->Slot == SUMMON_SLOT_QUEST) - { - joinFormation = false; - distance = DEFAULT_FOLLOW_DISTANCE; - } } - - // Pets and minions alwys move in a formation of their target - if (summon->IsPet()) - joinFormation = true; + } else if (IsCharmed()) joinFormation = true; @@ -8572,9 +8202,8 @@ void Unit::setDeathState(DeathState s) ExitVehicle(); // Exit vehicle before calling RemoveAllControlled // vehicles use special type of charm that is not removed by the next function // triggering an assert - UnsummonAllTotems(); - RemoveAllControlled(); RemoveAllAurasOnDeath(); + UnsummonAllSummonsDueToDeath(); } if (s == JUST_DIED) @@ -8611,6 +8240,48 @@ void Unit::setDeathState(DeathState s) //====================================================================== +Unit* Unit::GetCreator() const +{ + if (GetCreatorGUID().IsEmpty()) + return nullptr; + + return ObjectAccessor::GetUnit(*this, GetCreatorGUID()); +} + +Unit* Unit::GetSummoner() const +{ + if (GetSummonerGUID().IsEmpty()) + return nullptr; + + return ObjectAccessor::GetUnit(*this, GetSummonerGUID()); +} + +NewTemporarySummon* Unit::GetCritter() const +{ + if (GetCritterGUID().IsEmpty()) + return nullptr; + + Creature* creature = ObjectAccessor::GetCreature(*this, GetCritterGUID()); + if (!creature || !creature->IsSummon()) + return nullptr; + + return creature->ToTemporarySummon(); +} + +NewPet* Unit::GetActivelyControlledSummon() const +{ + if (GetSummonGUID().IsEmpty()) + return nullptr; + + Creature* creature = ObjectAccessor::GetCreature(*this, GetSummonGUID()); + if (!creature || !creature->IsPet()) + return nullptr; + + return creature->ToNewPet(); +} + +//====================================================================== + void Unit::AtEnterCombat() { if (Spell* spell = m_currentSpells[CURRENT_GENERIC_SPELL]) @@ -8648,20 +8319,6 @@ void Unit::AtTargetAttacked(Unit* target, bool canInitialAggro) void Unit::UpdatePetCombatState() { - ASSERT(!IsPet()); // player pets do not use UNIT_FLAG_PET_IN_COMBAT for this purpose - but player pets should also never have minions of their own to call this - - bool state = false; - for (Unit* minion : m_Controlled) - if (minion->IsInCombat()) - { - state = true; - break; - } - - if (state) - SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PET_IN_COMBAT); - else - RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PET_IN_COMBAT); } //====================================================================== @@ -9247,14 +8904,11 @@ void Unit::SetMaxHealth(uint32 val) if (ToPlayer()->GetGroup()) ToPlayer()->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_MAX_HP); } - else if (Pet* pet = ToCreature()->ToPet()) + else if (NewPet* pet = GetActivelyControlledSummon()) { - if (pet->isControlled()) - { - Unit* owner = GetOwner(); - if (owner && (owner->GetTypeId() == TYPEID_PLAYER) && owner->ToPlayer()->GetGroup()) - owner->ToPlayer()->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_PET_MAX_HP); - } + Unit* owner = pet->GetInternalSummoner(); + if (owner && owner->IsPlayer() && owner->ToPlayer()->GetGroup()) + owner->ToPlayer()->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_PET_MAX_HP); } if (val < health) @@ -9579,8 +9233,6 @@ void Unit::RemoveFromWorld() RemoveAllDynObjects(); ExitVehicle(); // Remove applied auras with SPELL_AURA_CONTROL_VEHICLE - UnsummonAllTotems(); - RemoveAllControlled(); RemoveAreaAurasDueToLeaveWorld(); @@ -9592,16 +9244,6 @@ void Unit::RemoveFromWorld() RemoveCharmedBy(nullptr); ASSERT(!GetCharmedGUID(), "Unit %u has charmed guid when removed from world", GetEntry()); - ASSERT(!GetCharmerGUID(), "Unit %u has charmer guid when removed from world", GetEntry()); - - if (Unit* owner = GetOwner()) - { - if (owner->m_Controlled.find(this) != owner->m_Controlled.end()) - { - TC_LOG_FATAL("entities.unit", "Unit %u is in controlled list of %u when removed from world", GetEntry(), owner->GetEntry()); - ABORT(); - } - } WorldObject::RemoveFromWorld(); m_duringRemoveFromWorld = false; @@ -9919,13 +9561,13 @@ void CharmInfo::SetPetNumber(uint32 petnumber, bool statwindow) void CharmInfo::LoadPetActionBar(const std::string& data) { - InitPetActionBar(); - Tokenizer tokens(data, ' '); if (tokens.size() != (ACTION_BAR_INDEX_END-ACTION_BAR_INDEX_START) * 2) return; // non critical, will reset to default + InitPetActionBar(); + uint8 index = ACTION_BAR_INDEX_START; Tokenizer::const_iterator iter = tokens.begin(); for (; index < ACTION_BAR_INDEX_END; ++iter, ++index) @@ -9955,6 +9597,12 @@ void CharmInfo::BuildActionBar(WorldPacket* data) *data << uint32(PetActionBar[i].packedData); } +void CharmInfo::BuildActionBar(std::array& actionButtons) +{ + for (uint8 i = 0; i < MAX_UNIT_ACTION_BAR_INDEX; ++i) + actionButtons[i] = PetActionBar[i].packedData; +} + void CharmInfo::SetSpellAutocast(SpellInfo const* spellInfo, bool state) { for (uint8 i = 0; i < MAX_UNIT_ACTION_BAR_INDEX; ++i) @@ -10891,42 +10539,12 @@ float Unit::GetAPMultiplier(WeaponAttackType attType, bool normalized) const Pet* Unit::CreateTamedPetFrom(Creature* creatureTarget, uint32 spell_id) { - if (GetTypeId() != TYPEID_PLAYER) - return nullptr; - - Pet* pet = new Pet(ToPlayer(), HUNTER_PET); - - if (!pet->CreateBaseAtCreature(creatureTarget)) - { - delete pet; - return nullptr; - } - - uint8 level = getLevel(); - - InitTamedPet(pet, level, spell_id); - - return pet; + return nullptr; } Pet* Unit::CreateTamedPetFrom(uint32 creatureEntry, uint32 spell_id) { - if (GetTypeId() != TYPEID_PLAYER) - return nullptr; - - CreatureTemplate const* creatureInfo = sObjectMgr->GetCreatureTemplate(creatureEntry); - if (!creatureInfo) - return nullptr; - - Pet* pet = new Pet(ToPlayer(), HUNTER_PET); - - if (!pet->CreateBaseAtCreatureInfo(creatureInfo, this) || !InitTamedPet(pet, getLevel(), spell_id)) - { - delete pet; - return nullptr; - } - - return pet; + return nullptr; } bool Unit::InitTamedPet(Pet* pet, uint8 level, uint32 spell_id) @@ -11120,21 +10738,6 @@ void Unit::PlayOneShotAnimKitId(uint16 animKitId) TC_LOG_DEBUG("entities.unit", "SET JUST_DIED"); victim->setDeathState(JUST_DIED); - // Inform pets (if any) when player kills target) - // MUST come after victim->setDeathState(JUST_DIED); or pet next target - // selection will get stuck on same target and break pet react state - if (player) - { - Pet* pet = player->GetPet(); - if (pet && pet->IsAlive() && pet->isControlled()) - { - if (pet->IsAIEnabled()) - pet->AI()->KilledUnit(victim); - else - TC_LOG_ERROR("entities.unit", "Pet doesn't have any AI in Unit::Kill(). %s", pet->GetGUID().ToString().c_str()); - } - } - // 10% durability loss on death if (Player* plrVictim = victim->ToPlayer()) { @@ -11740,8 +11343,8 @@ void Unit::RemoveCharmedBy(Unit* charmer) EngageWithTarget(charmer); - if (playerCharmer && this != charmer->GetFirstControlled()) - playerCharmer->SendRemoveControlBar(); + //if (playerCharmer && this != charmer->GetFirstControlled()) + // playerCharmer->SendRemoveControlBar(); // a guardian should always have charminfo if (!IsGuardian()) @@ -11905,10 +11508,6 @@ void Unit::GetPartyMembers(std::list &TagUnitMap) { if (Target->IsAlive()) TagUnitMap.push_back(Target); - - if (Guardian* pet = Target->GetGuardianPet()) - if (pet->IsAlive()) - TagUnitMap.push_back(pet); } } } @@ -11916,9 +11515,6 @@ void Unit::GetPartyMembers(std::list &TagUnitMap) { if ((owner == this || IsInMap(owner)) && owner->IsAlive()) TagUnitMap.push_back(owner); - if (Guardian* pet = owner->GetGuardianPet()) - if ((pet == this || IsInMap(pet)) && pet->IsAlive()) - TagUnitMap.push_back(pet); } } @@ -12556,7 +12152,7 @@ uint32 Unit::GetModelForForm(ShapeshiftForm form, uint32 spellId) const return modelid; } -uint32 Unit::GetModelForTotem(PlayerTotemType totemType) +uint32 Unit::GetModelForTotem(PlayerTotemType totemType) const { switch (getRace()) { @@ -12946,9 +12542,6 @@ void Unit::_ExitVehicle(Position const* exitPosition) init.SetTransportExit(); GetMotionMaster()->LaunchMoveSpline(std::move(init), EVENT_VEHICLE_EXIT, MOTION_SLOT_CONTROLLED); - if (player) - player->ResummonPetTemporaryUnSummonedIfAny(); - if (vehicle->GetBase()->HasUnitTypeMask(UNIT_MASK_MINION) && vehicle->GetBase()->GetTypeId() == TYPEID_UNIT) if (((Minion*)vehicle->GetBase())->GetOwner() == this) vehicle->GetBase()->ToCreature()->DespawnOrUnsummon(1); @@ -13487,34 +13080,17 @@ void Unit::StopAttackFaction(uint32 faction_id) refsToEnd.push_back(pair.second); for (CombatReference* ref : refsToEnd) ref->EndCombat(); - - for (Unit* minion : m_Controlled) - minion->StopAttackFaction(faction_id); } void Unit::OutDebugInfo() const { TC_LOG_ERROR("entities.unit", "Unit::OutDebugInfo"); TC_LOG_DEBUG("entities.unit", "%s name %s", GetGUID().ToString().c_str(), GetName().c_str()); - TC_LOG_DEBUG("entities.unit", "Owner %s, Minion %s, Charmer %s, Charmed %s", GetOwnerOrCreatorGUID().ToString().c_str(), GetMinionGUID().ToString().c_str(), GetCharmerGUID().ToString().c_str(), GetCharmedGUID().ToString().c_str()); TC_LOG_DEBUG("entities.unit", "In world %u, unit type mask %u", (uint32)(IsInWorld() ? 1 : 0), m_unitTypeMask); if (IsInWorld()) TC_LOG_DEBUG("entities.unit", "Mapid %u", GetMapId()); std::ostringstream o; - o << "Summon Slot: "; - for (uint32 i = 0; i < MAX_SUMMON_SLOT; ++i) - o << m_SummonSlot[i].ToString() << ", "; - - TC_LOG_DEBUG("entities.unit", "%s", o.str().c_str()); - o.str(""); - - o << "Controlled List: "; - for (ControlList::const_iterator itr = m_Controlled.begin(); itr != m_Controlled.end(); ++itr) - o << (*itr)->GetGUID().ToString() << ", "; - TC_LOG_DEBUG("entities.unit", "%s", o.str().c_str()); - o.str(""); - o << "Aura List: "; for (AuraApplicationMap::const_iterator itr = m_appliedAuras.begin(); itr != m_appliedAuras.end(); ++itr) o << itr->first << ", "; @@ -14577,3 +14153,164 @@ void Unit::SetGameClientMovingMe(GameClient* gameClientMovingMe) { _gameClientMovingMe = gameClientMovingMe; } + +void Unit::AddSummonGUIDToSlot(ObjectGuid summonGuid, SummonPropertiesSlot slot) +{ + _summonGUIDsInSlot[AsUnderlyingType(slot)] = summonGuid; +} + +void Unit::AddSummonGUID(ObjectGuid summonGuid) +{ + _summonGUIDs.insert(summonGuid); +} + +void Unit::RemoveSummonGUIDFromSlot(ObjectGuid summonGuid, SummonPropertiesSlot slot) +{ + if (_summonGUIDsInSlot[AsUnderlyingType(slot)] == summonGuid) + _summonGUIDsInSlot[AsUnderlyingType(slot)] = ObjectGuid::Empty; +} + +void Unit::RemoveSummonGUID(ObjectGuid summonGuid) +{ + _summonGUIDs.erase(summonGuid); +} + +bool Unit::HasSummonInSlot(SummonPropertiesSlot slot) const +{ + return _summonGUIDsInSlot[AsUnderlyingType(slot)] != ObjectGuid::Empty; +} + +void Unit::UnsummonAllSummonsDueToDeath() +{ + std::unordered_set summons = _summonGUIDs; + for (std::unordered_set::const_iterator itr = summons.begin(); itr != summons.end(); ++itr) + if (NewTemporarySummon* summon = GetSummonByGUID(*itr)) + if (summon->ShouldDespawnOnSummonerDeath()) + summon->Unsummon(); +} + +void Unit::UnsummonAllSummonsOnLogout() +{ + std::unordered_set summons = _summonGUIDs; + for (std::unordered_set::const_iterator itr = summons.begin(); itr != summons.end(); ++itr) + if (NewTemporarySummon* summon = GetSummonByGUID(*itr)) + if (summon->ShouldDespawnOnSummonerLogout()) + summon->Unsummon(); +} + +NewTemporarySummon* Unit::GetSummonInSlot(SummonPropertiesSlot slot) const +{ + if (_summonGUIDsInSlot[AsUnderlyingType(slot)].IsEmpty()) + return nullptr; + + Creature* summon = ObjectAccessor::GetCreature(*this, _summonGUIDsInSlot[AsUnderlyingType(slot)]); + if (!summon || !summon->IsSummon()) + return nullptr; + + return summon->ToTemporarySummon(); +} + +NewTemporarySummon* Unit::GetSummonByGUID(ObjectGuid guid) const +{ + if (_summonGUIDs.empty() || _summonGUIDs.find(guid) == _summonGUIDs.end()) + return nullptr; + + Creature* summon = ObjectAccessor::GetCreature(*this, guid); + if (!summon || !summon->IsSummon()) + return nullptr; + + return summon->ToTemporarySummon(); +} + +NewPet* Unit::SummonPet(uint32 creatureId, uint8 slot, uint32 spellId, bool asClassPet, Position const& position) +{ + NewPet* pet = new NewPet(nullptr, this, false, asClassPet); + pet->Relocate(position); + if (!pet->IsPositionValid()) + { + delete pet; + return nullptr; + } + + PlayerPetData* playerPetData = nullptr; + if (Player* player = ToPlayer()) + playerPetData = pet->GetOrCreatePlayerPetData(player, slot, creatureId); + + // Hunter pets must always have existing pet data + if (!playerPetData && !creatureId) + { + delete pet; + return nullptr; + } + + uint32 creatureTemplateEntry = creatureId; + if (playerPetData && playerPetData->TamedCreatureId) + creatureTemplateEntry = playerPetData->TamedCreatureId; + + Map* map = GetMap(); + if (!pet->Create(map->GenerateLowGuid(), map, creatureTemplateEntry, playerPetData != nullptr ? playerPetData->PetNumber : 0)) + { + delete pet; + return nullptr; + } + + TransportBase* transport = GetTransport(); + if (transport) + { + float x, y, z, o; + position.GetPosition(x, y, z, o); + transport->CalculatePassengerOffset(x, y, z, &o); + pet->m_movementInfo.transport.pos.Relocate(x, y, z, o); + + // This object must be added to transport before adding to map for the client to properly display it + transport->AddPassenger(pet); + } + + PhasingHandler::InheritPhaseShift(pet, this); + pet->SetUInt32Value(UNIT_CREATED_BY_SPELL, spellId); + pet->SetPermanentSummon(true); + + bool successfullyCreated = [&]() + { + if (!pet->HandlePreSummonActions(this, 0, 0)) + return false; + + // initialize pet behavior + if (playerPetData) + { + pet->SetPlayerPetDataKey(slot, creatureId); + playerPetData->IsActive = true; + playerPetData->DisplayId = pet->GetNativeDisplayId(); + playerPetData->CreatedBySpellId = spellId; + pet->GetCharmInfo()->SetPetNumber(playerPetData->PetNumber, pet->IsClassPet()); + pet->GetCharmInfo()->LoadPetActionBar(playerPetData->ActionBar); + pet->SetName(playerPetData->Name); + pet->SetReactState(playerPetData->ReactState); + + if (!creatureId) + SetByteFlag(UNIT_FIELD_BYTES_2, UNIT_BYTES_2_OFFSET_PET_FLAGS, playerPetData->HasBeenRenamed ? UNIT_CAN_BE_ABANDONED : (UNIT_CAN_BE_RENAMED | UNIT_CAN_BE_ABANDONED)); + else + SetByteFlag(UNIT_FIELD_BYTES_2, UNIT_BYTES_2_OFFSET_PET_FLAGS, 0); + } + + return map->AddToMap(pet->ToCreature()); + }(); + + if (!successfullyCreated) + { + // Returning false will cause the object to be deleted - remove from transport + if (transport) + transport->RemovePassenger(pet); + + delete pet; + return nullptr; + } + + pet->HandlePostSummonActions(); + + // call MoveInLineOfSight for nearby creatures + Trinity::AIRelocationNotifier notifier(*pet); + Cell::VisitAllObjects(pet, notifier, GetVisibilityRange()); + + return pet; +} diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index 01bff114d97..33d059754e2 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -82,10 +82,13 @@ class Item; class Minion; class MotionMaster; class Pet; +class Player; +class NewPet; class Spell; class SpellCastTargets; class SpellHistory; class SpellInfo; +class NewTemporarySummon; class Totem; class Transport; class TransportBase; @@ -95,6 +98,7 @@ class Vehicle; class VehicleJoinEvent; enum ZLiquidStatus : uint32; +enum class SummonPropertiesSlot : int8; namespace Movement { @@ -365,16 +369,20 @@ enum DamageEffectType : uint8 enum UnitTypeMask { UNIT_MASK_NONE = 0x00000000, - UNIT_MASK_SUMMON = 0x00000001, - UNIT_MASK_MINION = 0x00000002, - UNIT_MASK_GUARDIAN = 0x00000004, - UNIT_MASK_TOTEM = 0x00000008, - UNIT_MASK_PET = 0x00000010, - UNIT_MASK_VEHICLE = 0x00000020, - UNIT_MASK_PUPPET = 0x00000040, - UNIT_MASK_HUNTER_PET = 0x00000080, - UNIT_MASK_CONTROLABLE_GUARDIAN = 0x00000100, - UNIT_MASK_ACCESSORY = 0x00000200 + UNIT_MASK_SUMMON = 0x00000001, // SummonPropertiesControl = 0 + UNIT_MASK_GUARDIAN = 0x00000002, // SummonPropertiesControl = 1 + UNIT_MASK_PET = 0x00000004, // SummonPropertiesControl = 2 + UNIT_MASK_POSSESSED = 0x00000008, // SummonPropertiesControl = 3 + UNIT_MASK_POSSESSED_VEHICLE = 0x00000010, // SummonPropertiesControl = 4 + UNIT_MASK_VEHICLE = 0x00000020, // SummonPropertiesControl = 5 and Wild entities + + UNIT_MASK_MINION = 0x00000040, + UNIT_MASK_TOTEM = 0x00000080, + UNIT_MASK_PUPPET = 0x00000200, + UNIT_MASK_HUNTER_PET = 0x00000400, + UNIT_MASK_CONTROLABLE_GUARDIAN = 0x00000800, + UNIT_MASK_ACCESSORY = 0x00001000, + UNIT_MASK_SUMMON_OLD = 0x00002000, }; struct DiminishingReturn @@ -695,6 +703,8 @@ struct TC_GAME_API CharmInfo bool RemoveSpellFromActionBar(uint32 spell_id); void LoadPetActionBar(const std::string& data); void BuildActionBar(WorldPacket* data); + void BuildActionBar(std::array& actionButtons); + void SetSpellAutocast(SpellInfo const* spellInfo, bool state); void SetActionBar(uint8 index, uint32 spellOrAction, ActiveStates type) { @@ -804,7 +814,6 @@ class TC_GAME_API Unit : public WorldObject friend class WorldSession; public: typedef std::set AttackerSet; - typedef std::set ControlList; typedef std::vector UnitVector; typedef std::multimap AuraMap; @@ -1225,16 +1234,28 @@ class TC_GAME_API Unit : public WorldObject DeathState getDeathState() const { return m_deathState; } virtual void setDeathState(DeathState s); // overwrited in Creature/Player/Pet - ObjectGuid GetOwnerGUID() const override { return GetGuidValue(UNIT_FIELD_SUMMONEDBY); } - void SetOwnerGUID(ObjectGuid owner); ObjectGuid GetCreatorGUID() const { return GetGuidValue(UNIT_FIELD_CREATEDBY); } - void SetCreatorGUID(ObjectGuid creator) { SetGuidValue(UNIT_FIELD_CREATEDBY, creator); } - ObjectGuid GetMinionGUID() const { return GetGuidValue(UNIT_FIELD_SUMMON); } - void SetMinionGUID(ObjectGuid guid) { SetGuidValue(UNIT_FIELD_SUMMON, guid); } - void SetPetGUID(ObjectGuid guid) { m_SummonSlot[SUMMON_SLOT_PET] = guid; } - ObjectGuid GetPetGUID() const { return m_SummonSlot[SUMMON_SLOT_PET]; } - void SetCritterGUID(ObjectGuid guid) { SetGuidValue(UNIT_FIELD_CRITTER, guid); } + void SetCreatorGUID(ObjectGuid guid) { SetGuidValue(UNIT_FIELD_CREATEDBY, guid); } + // Returns the unit that has been referenced by the guid value of UNIT_FIELD_CREATEDBY + Unit* GetCreator() const; + + ObjectGuid GetSummonerGUID() const { return GetGuidValue(UNIT_FIELD_SUMMONEDBY); } + void SetSummonerGUID(ObjectGuid guid) { SetGuidValue(UNIT_FIELD_SUMMONEDBY, guid); } + // Returns the unit that has been referenced by the guid value of UNIT_FIELD_SUMMONEDBY + Unit* GetSummoner() const; + ObjectGuid GetCritterGUID() const { return GetGuidValue(UNIT_FIELD_CRITTER); } + void SetCritterGUID(ObjectGuid guid) { SetGuidValue(UNIT_FIELD_CRITTER, guid); } + // Returns the temporary summon that has been referenced by the guid value of UNIT_FIELD_CRITTER + NewTemporarySummon* GetCritter() const; + + ObjectGuid GetSummonGUID() const { return GetGuidValue(UNIT_FIELD_SUMMON); } + void SetSummonGUID(ObjectGuid guid) { SetGuidValue(UNIT_FIELD_SUMMON, guid); } + NewPet* GetActivelyControlledSummon() const; + + void SetActivelyControlledSummon(NewPet* pet, bool apply); + + ObjectGuid GetOwnerGUID() const override { return GetGuidValue(UNIT_FIELD_SUMMONEDBY); } ObjectGuid GetOwnerOrCreatorGUID() const { return GetOwnerGUID() ? GetOwnerGUID() : GetCreatorGUID(); } ObjectGuid GetCharmerGUID() const { return GetGuidValue(UNIT_FIELD_CHARMEDBY); } @@ -1248,23 +1269,13 @@ class TC_GAME_API Unit : public WorldObject ObjectGuid GetCharmerOrOwnerGUID() const override { return IsCharmed() ? GetCharmerGUID() : GetOwnerGUID(); } bool IsCharmedOwnedByPlayerOrPlayer() const { return GetCharmerOrOwnerOrOwnGUID().IsPlayer(); } - Guardian* GetGuardianPet() const; - Minion* GetFirstMinion() const; Unit* GetCharmerOrOwner() const { return IsCharmed() ? GetCharmer() : GetOwner(); } - void SetMinion(Minion *minion, bool apply); - void GetAllMinionsByEntry(std::list& Minions, uint32 entry); - void RemoveAllMinionsByEntry(uint32 entry); void SetCharm(Unit* target, bool apply); - Unit* GetNextRandomRaidMemberOrPet(float radius); bool SetCharmedBy(Unit* charmer, CharmType type, AuraApplication const* aurApp = nullptr); void RemoveCharmedBy(Unit* charmer); void RestoreFaction(); - ControlList m_Controlled; - Unit* GetFirstControlled() const; - void RemoveAllControlled(); - bool IsCharmed() const { return !GetCharmerGUID().IsEmpty(); } bool IsCharming() const { return !GetCharmedGUID().IsEmpty(); } bool isPossessed() const { return HasUnitState(UNIT_STATE_POSSESSED); } @@ -1470,7 +1481,6 @@ class TC_GAME_API Unit : public WorldObject SpellHistory* GetSpellHistory() { return m_spellHistory.get(); } SpellHistory const* GetSpellHistory() const { return m_spellHistory.get(); } - std::array m_SummonSlot; std::array m_ObjectSlot; ShapeshiftForm GetShapeshiftForm() const { return ShapeshiftForm(GetByteValue(UNIT_FIELD_BYTES_2, UNIT_BYTES_2_OFFSET_SHAPESHIFT_FORM)); } @@ -1587,7 +1597,6 @@ class TC_GAME_API Unit : public WorldObject void ModifyAuraState(AuraStateType flag, bool apply); uint32 BuildAuraStateUpdateForTarget(Unit* target) const; bool HasAuraState(AuraStateType flag, SpellInfo const* spellProto = nullptr, Unit const* Caster = nullptr) const; - void UnsummonAllTotems(); bool IsMagnet() const; Unit* GetMeleeHitRedirectTarget(Unit* victim, SpellInfo const* spellInfo = nullptr); @@ -1710,7 +1719,7 @@ class TC_GAME_API Unit : public WorldObject void SetCantProc(bool apply); uint32 GetModelForForm(ShapeshiftForm form, uint32 spellId) const; - uint32 GetModelForTotem(PlayerTotemType totemType); + uint32 GetModelForTotem(PlayerTotemType totemType) const; friend class VehicleJoinEvent; ObjectGuid LastCharmerGUID; @@ -1775,6 +1784,12 @@ class TC_GAME_API Unit : public WorldObject TempSummon* ToTempSummon() { if (IsSummon()) return reinterpret_cast(this); else return nullptr; } TempSummon const* ToTempSummon() const { if (IsSummon()) return reinterpret_cast(this); else return nullptr; } + NewTemporarySummon* ToTemporarySummon() { if (IsSummon()) return reinterpret_cast(this); else return nullptr; } + NewTemporarySummon const* ToTemporarySummon() const { if (IsSummon()) return reinterpret_cast(this); else return nullptr; } + + NewPet* ToNewPet() { if (IsPet()) return reinterpret_cast(this); else return nullptr; } + NewPet const* ToNewPet() const { if (IsPet()) return reinterpret_cast(this); else return nullptr; } + ObjectGuid GetTarget() const { return GetGuidValue(UNIT_FIELD_TARGET); } virtual void SetTarget(ObjectGuid /*guid*/) = 0; @@ -1969,6 +1984,29 @@ class TC_GAME_API Unit : public WorldObject std::unordered_map m_pendingMovementChanges; /* Player Movement fields END*/ + + /*Temporary Summons*/ + std::array _summonGUIDsInSlot; + std::unordered_set _summonGUIDs; + + public: + void AddSummonGUIDToSlot(ObjectGuid summonGuid, SummonPropertiesSlot slot); + void AddSummonGUID(ObjectGuid summonGuid); + void RemoveSummonGUIDFromSlot(ObjectGuid summonGuid, SummonPropertiesSlot slot); + void RemoveSummonGUID(ObjectGuid summonGuid); + bool HasSummonInSlot(SummonPropertiesSlot slot) const; + // Despawns all summons that are meant to be despawned when the unit has died + void UnsummonAllSummonsDueToDeath(); + // Despawns all summons that are meant to be despawned when the player leaves the world. Despite its name and summon property flag handling, this method is meant to trigger on world leaving instead of logouts only + void UnsummonAllSummonsOnLogout(); // we are using this method in the unit class to restrict direct access of the _summonGUIDs container + std::unordered_set const& GetSummonGUIDs() const { return _summonGUIDs; } + + NewTemporarySummon* GetSummonInSlot(SummonPropertiesSlot slot) const; + NewTemporarySummon* GetSummonByGUID(ObjectGuid guid) const; + + // Summons a pet that is being summoned by SPELL_EFFECT_SUMMON_PET. If creatureId is 0, we assume that the player attempts to summon a hunter pet. + NewPet* SummonPet(uint32 creatureId, uint8 slot, uint32 spellId, bool asClassPet, Position const& position); + /*End of Tempoary Summons*/ }; namespace Trinity diff --git a/src/server/game/Entities/Unit/UnitDefines.h b/src/server/game/Entities/Unit/UnitDefines.h index 2a6c4871bdf..097587b21a8 100644 --- a/src/server/game/Entities/Unit/UnitDefines.h +++ b/src/server/game/Entities/Unit/UnitDefines.h @@ -354,7 +354,7 @@ struct DeclinedName std::string name[MAX_DECLINED_NAME_CASES]; }; -enum ActiveStates +enum ActiveStates : uint8 { ACT_PASSIVE = 0x01, // 0x01 - passive ACT_DISABLED = 0x81, // 0x80 - castable @@ -364,7 +364,7 @@ enum ActiveStates ACT_DECIDE = 0x00 // custom }; -enum ReactStates +enum ReactStates : uint8 { REACT_PASSIVE = 0, REACT_DEFENSIVE = 1, @@ -381,4 +381,22 @@ enum CommandStates : uint8 COMMAND_MOVE_TO = 4 }; +enum class PetModeFlags : uint16 +{ + Unknown1 = 0x001, + Unknown2 = 0x002, + Unknown3 = 0x004, + Unknown4 = 0x008, + Unknown5 = 0x010, + Unknown6 = 0x020, + Unknown7 = 0x040, + Unknown8 = 0x080, + Unknown9 = 0x100, + Unknown10 = 0x200, + Unknown11 = 0x400, + DisableActions = 0x800 +}; + +DEFINE_ENUM_FLAG(PetModeFlags); + #endif // UnitDefines_h__ diff --git a/src/server/game/Entities/Vehicle/Vehicle.cpp b/src/server/game/Entities/Vehicle/Vehicle.cpp index 0bb2e2d8fd2..560cdfa6533 100644 --- a/src/server/game/Entities/Vehicle/Vehicle.cpp +++ b/src/server/game/Entities/Vehicle/Vehicle.cpp @@ -893,8 +893,8 @@ bool VehicleJoinEvent::Execute(uint64, uint32) player->StopCastingCharm(); player->StopCastingBindSight(); player->SendOnCancelExpectedVehicleRideAura(); - if (!veSeat->HasFlag(VEHICLE_SEAT_FLAG_B_KEEP_PET)) - player->UnsummonPetTemporaryIfAny(); + //if (!veSeat->HasFlag(VEHICLE_SEAT_FLAG_B_KEEP_PET)) + // player->UnsummonPetTemporaryIfAny(); } if (veSeat->HasFlag(VEHICLE_SEAT_FLAG_DISABLE_GRAVITY) || Target->GetBase()->CanFly()) diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index 1c88ae219f3..a77ef8cb6d2 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -7377,7 +7377,7 @@ void ObjectMgr::LoadPetNumber() { uint32 oldMSTime = getMSTime(); - QueryResult result = CharacterDatabase.Query("SELECT MAX(id) FROM character_pet"); + QueryResult result = CharacterDatabase.Query("SELECT MAX(PetNumber) FROM character_pet"); if (result) { Field* fields = result->Fetch(); diff --git a/src/server/game/Handlers/CharacterHandler.cpp b/src/server/game/Handlers/CharacterHandler.cpp index 6e75fa7063e..267e10f58ed 100644 --- a/src/server/game/Handlers/CharacterHandler.cpp +++ b/src/server/game/Handlers/CharacterHandler.cpp @@ -229,8 +229,8 @@ bool LoginQueryHolder::Initialize() stmt->setUInt32(0, lowGuid); res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_CORPSE_LOCATION, stmt); - stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_ALL_PETS_DETAIL); - stmt->setUInt64(0, lowGuid); + stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_PET); + stmt->setUInt32(0, lowGuid); res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_ALL_PETS, stmt); stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_REWARDSTATUS_LFG); @@ -969,11 +969,6 @@ void WorldSession::HandlePlayerLogin(LoginQueryHolder const& holder) if (pCurrChar->HasAtLoginFlag(AT_LOGIN_RESET_PET_TALENTS)) Pet::resetTalentsForAllPetsOf(pCurrChar); - pCurrChar->LoadPetsFromDB(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_ALL_PETS)); - - // Load pet if any (if player not alive and in taxi flight or another then pet will remember as temporary unsummoned) - pCurrChar->LoadPet(); - // Set FFA PvP for non GM in non-rest mode if (sWorld->IsFFAPvPRealm() && !pCurrChar->IsGameMaster() && !pCurrChar->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING)) pCurrChar->SetByteFlag(UNIT_FIELD_BYTES_2, UNIT_BYTES_2_OFFSET_PVP_FLAG, UNIT_BYTE2_FLAG_FFA_PVP); diff --git a/src/server/game/Handlers/MiscHandler.cpp b/src/server/game/Handlers/MiscHandler.cpp index 2290c9c4252..0aa16015bd5 100644 --- a/src/server/game/Handlers/MiscHandler.cpp +++ b/src/server/game/Handlers/MiscHandler.cpp @@ -88,7 +88,6 @@ void WorldSession::HandleRepopRequestOpcode(WorldPacket& recvData) } //this is spirit release confirm? - GetPlayer()->RemovePet(nullptr, PET_SAVE_DISMISS, true); GetPlayer()->BuildPlayerRepop(); GetPlayer()->RepopAtGraveyard(); } diff --git a/src/server/game/Handlers/MovementHandler.cpp b/src/server/game/Handlers/MovementHandler.cpp index 1294c99f207..9f4377d6483 100644 --- a/src/server/game/Handlers/MovementHandler.cpp +++ b/src/server/game/Handlers/MovementHandler.cpp @@ -210,7 +210,7 @@ void WorldSession::HandleMoveWorldportAck() GetPlayer()->UpdatePvP(false, false); // resummon pet - GetPlayer()->ResummonPetTemporaryUnSummonedIfAny(); + //GetPlayer()->ResummonPetTemporaryUnSummonedIfAny(); //lets process all delayed operations on successful teleport GetPlayer()->ProcessDelayedOperations(); @@ -274,7 +274,7 @@ void WorldSession::HandleMoveTeleportAck(WorldPackets::Movement::MoveTeleportAck } // resummon pet - GetPlayer()->ResummonPetTemporaryUnSummonedIfAny(); + //GetPlayer()->ResummonPetTemporaryUnSummonedIfAny(); //lets process all delayed operations on successful teleport GetPlayer()->ProcessDelayedOperations(); diff --git a/src/server/game/Handlers/NPCHandler.cpp b/src/server/game/Handlers/NPCHandler.cpp index 4d555cd4272..eb57e1ab1ea 100644 --- a/src/server/game/Handlers/NPCHandler.cpp +++ b/src/server/game/Handlers/NPCHandler.cpp @@ -319,13 +319,14 @@ void WorldSession::HandleListStabledPetsOpcode(WorldPacket& recvData) void WorldSession::SendStablePet(ObjectGuid guid) { + /* WorldPacket data(MSG_LIST_STABLED_PETS, 200); data << uint64(guid); // Stablemaster - data << uint8(_player->PlayerPetDataStore.size()); + data << uint8(_player->_playerPetDataStore.size()); data << uint8(PET_SLOT_LAST_STABLE_SLOT); // Stable Slots - for (PlayerPetData* p : _player->PlayerPetDataStore) + for (PlayerPetData* p : _player->_playerPetDataStore) { uint32 petSlot = p->Slot; uint8 flags = PET_STABLE_ACTIVE; @@ -342,6 +343,7 @@ void WorldSession::SendStablePet(ObjectGuid guid) } SendPacket(&data); + */ } void WorldSession::SendStableResult(uint8 res) @@ -353,6 +355,7 @@ void WorldSession::SendStableResult(uint8 res) void WorldSession::HandleSetPetSlot(WorldPacket& recvData) { + /* TC_LOG_DEBUG("network", "WORLD: Recv CMSG_STABLE_PET"); ObjectGuid guid; uint32 petId; @@ -436,6 +439,7 @@ void WorldSession::HandleSetPetSlot(WorldPacket& recvData) { UpdatePetSlot(petId, playerPetData->Slot, new_slot); } + */ } void WorldSession::HandleStableRevivePet(WorldPacket &/* recvData */) diff --git a/src/server/game/Handlers/PetHandler.cpp b/src/server/game/Handlers/PetHandler.cpp index 1a3d67269b3..bd63bee296b 100644 --- a/src/server/game/Handlers/PetHandler.cpp +++ b/src/server/game/Handlers/PetHandler.cpp @@ -26,9 +26,11 @@ #include "ObjectMgr.h" #include "Opcodes.h" #include "Pet.h" +#include "NewPet.h" #include "PetPackets.h" #include "PetAI.h" #include "Player.h" +#include "QueryPackets.h" #include "Spell.h" #include "SpellHistory.h" #include "SpellInfo.h" @@ -36,133 +38,100 @@ #include "Util.h" #include "WorldPacket.h" -void WorldSession::HandleDismissCritter(WorldPacket& recvData) +void WorldSession::HandleDismissCritter(WorldPackets::Pet::DismissCritter& packet) { - ObjectGuid guid; - recvData >> guid; - - TC_LOG_DEBUG("network.opcode", "WORLD: Received CMSG_DISMISS_CRITTER for %s", guid.ToString().c_str()); - - Unit* pet = ObjectAccessor::GetCreatureOrPetOrVehicle(*_player, guid); - - if (!pet) - { - TC_LOG_DEBUG("entities.pet", "Vanitypet (%s) does not exist - player '%s' (guid: %u / account: %u) attempted to dismiss it (possibly lagged out)", - guid.ToString().c_str(), GetPlayer()->GetName().c_str(), GetPlayer()->GetGUID().GetCounter(), GetAccountId()); - return; - } - - if (_player->GetCritterGUID() == pet->GetGUID()) - { - if (pet->GetTypeId() == TYPEID_UNIT && pet->IsSummon()) - pet->ToTempSummon()->UnSummon(); - } -} - -void WorldSession::HandlePetAction(WorldPacket& recvData) -{ - ObjectGuid guid1; - uint32 data; - ObjectGuid guid2; - float x, y, z; - recvData >> guid1; //pet guid - recvData >> data; - recvData >> guid2; //tag guid - // Position - recvData >> x; - recvData >> y; - recvData >> z; - - uint32 spellid = UNIT_ACTION_BUTTON_ACTION(data); - uint8 flag = UNIT_ACTION_BUTTON_TYPE(data); //delete = 0x07 CastSpell = C1 - - // used also for charmed creature - Unit* pet = ObjectAccessor::GetUnit(*_player, guid1); - TC_LOG_DEBUG("entities.pet", "HandlePetAction: %s - flag: %u, spellid: %u, target: %s.", guid1.ToString().c_str(), uint32(flag), spellid, guid2.ToString().c_str()); - - if (!pet) - { - TC_LOG_DEBUG("entities.pet", "HandlePetAction: %s doesn't exist for %s %s", guid1.ToString().c_str(), GetPlayer()->GetGUID().ToString().c_str(), GetPlayer()->GetName().c_str()); - return; - } - - if (pet != GetPlayer()->GetFirstControlled()) - { - TC_LOG_DEBUG("entities.pet", "HandlePetAction: %s does not belong to %s %s", guid1.ToString().c_str(), GetPlayer()->GetGUID().ToString().c_str(), GetPlayer()->GetName().c_str()); - return; - } - - if (!pet->IsAlive()) - { - SpellInfo const* spell = (flag == ACT_ENABLED || flag == ACT_PASSIVE) ? sSpellMgr->GetSpellInfo(spellid) : nullptr; - if (!spell) - return; - if (!spell->HasAttribute(SPELL_ATTR0_ALLOW_CAST_WHILE_DEAD)) - return; - } - - /// @todo allow control charmed player? - if (pet->GetTypeId() == TYPEID_PLAYER && !(flag == ACT_COMMAND && spellid == COMMAND_ATTACK)) - return; - - if (GetPlayer()->m_Controlled.size() == 1) - HandlePetActionHelper(pet, guid1, spellid, flag, guid2, x, y, z); - else - { - //If a pet is dismissed, m_Controlled will change - std::vector controlled; - for (Unit::ControlList::iterator itr = GetPlayer()->m_Controlled.begin(); itr != GetPlayer()->m_Controlled.end(); ++itr) - if ((*itr)->GetEntry() == pet->GetEntry() && (*itr)->IsAlive()) - controlled.push_back(*itr); - for (std::vector::iterator itr = controlled.begin(); itr != controlled.end(); ++itr) - HandlePetActionHelper(*itr, guid1, spellid, flag, guid2, x, y, z); - } -} - -void WorldSession::HandlePetStopAttack(WorldPacket &recvData) -{ - ObjectGuid guid; - recvData >> guid; - - TC_LOG_DEBUG("network.opcode", "WORLD: Received CMSG_PET_STOP_ATTACK for %s", guid.ToString().c_str()); - - Unit* pet = ObjectAccessor::GetCreatureOrPetOrVehicle(*_player, guid); - - if (!pet) - { - TC_LOG_ERROR("entities.pet", "HandlePetStopAttack: %s does not exist", guid.ToString().c_str()); - return; - } - - if (pet != GetPlayer()->GetPet() && pet != GetPlayer()->GetCharmed()) - { - TC_LOG_ERROR("entities.pet", "HandlePetStopAttack: %s isn't a pet or charmed creature of player %s", - guid.ToString().c_str(), GetPlayer()->GetName().c_str()); - return; - } - - if (!pet->IsAlive()) + if (_player->GetCritterGUID() != packet.CritterGUID) return; - pet->AttackStop(); + if (NewTemporarySummon* summon = _player->GetSummonInSlot(SummonPropertiesSlot::Critter)) + if (summon->GetGUID() == packet.CritterGUID) + summon->Unsummon(); } -void WorldSession::HandlePetActionHelper(Unit* pet, ObjectGuid guid1, uint32 spellid, uint16 flag, ObjectGuid guid2, float x, float y, float z) +void HandlePetActionHelper(NewPet* pet, Player* owner, ObjectGuid targetGuid, uint32 actionValue, uint16 actionFlag, Position const& actionPosition) { CharmInfo* charmInfo = pet->GetCharmInfo(); if (!charmInfo) - { - TC_LOG_DEBUG("entities.pet", "WorldSession::HandlePetAction(petGuid: %s, tagGuid: %s, spellId: %u, flag: %u): object (GUID: %u Entry: %u TypeId: %u) is considered pet-like but doesn't have a charminfo!", - guid1.ToString().c_str(), guid2.ToString().c_str(), spellid, flag, pet->GetGUID().GetCounter(), pet->GetEntry(), pet->GetTypeId()); return; + + switch (actionFlag) + { + case ACT_COMMAND: + switch (actionValue) + { + case COMMAND_STAY: + printf("COMMAND_STAY -- %u\n", actionValue); + break; + case COMMAND_FOLLOW: + printf("COMMAND_FOLLOW -- %u\n", actionValue); + pet->AttackStop(); + pet->InterruptNonMeleeSpells(false); + pet->FollowTarget(pet->GetSummoner()); + charmInfo->SetCommandState(COMMAND_FOLLOW); + charmInfo->SetIsCommandAttack(false); + charmInfo->SetIsAtStay(false); + charmInfo->SetIsReturning(true); + charmInfo->SetIsCommandFollow(true); + charmInfo->SetIsFollowing(false); + break; + case COMMAND_ATTACK: + printf("COMMAND_ATTACK -- %u\n", actionValue); + break; + case COMMAND_ABANDON: + printf("COMMAND_ABANDON -- %u\n", actionValue); + if (pet->CanBeDismissed()) + { + pet->UpdatePlayerPetData(owner, false); + pet->Unsummon(); + } + break; + case COMMAND_MOVE_TO: + printf("COMMAND_MOVE_TO -- %u\n", actionValue); + pet->GetMotionMaster()->Clear(MOTION_SLOT_IDLE); + pet->GetMotionMaster()->MoveIdle(); + pet->GetMotionMaster()->MovePoint(0, actionPosition); + charmInfo->SetCommandState(COMMAND_MOVE_TO); + charmInfo->SetIsCommandAttack(false); + charmInfo->SetIsAtStay(true); + charmInfo->SetIsCommandFollow(false); + charmInfo->SetIsFollowing(false); + charmInfo->SetIsReturning(false); + charmInfo->SaveStayPosition(); + break; + } + break; + case ACT_REACTION: + switch (actionValue) + { + case REACT_PASSIVE: + case REACT_DEFENSIVE: + case REACT_ASSIST: + pet->SetReactState(static_cast(actionValue)); + break; + // case REACT_AGGRESSIVE: // no longer available since 4.x + default: + break; + } + break; + case ACT_DISABLED: + printf("ACT_DISABLED -- %u\n", actionValue); + break; + case ACT_PASSIVE: + printf("ACT_PASSIVE -- %u\n", actionValue); + break; + case ACT_ENABLED: + printf("ACT_ENABLED -- %u\n", actionValue); + break; + default: + break; } - switch (flag) + /* { - case ACT_COMMAND: //0x07 - switch (spellid) + case ACT_COMMAND: //0x07 + switch (actionValue) { - case COMMAND_STAY: //flat=1792 //STAY + case COMMAND_STAY: //flat=1792 //STAY if (pet->GetMotionMaster()->GetCurrentSlot() != MOTION_SLOT_CONTROLLED) pet->StopMoving(); @@ -181,7 +150,7 @@ void WorldSession::HandlePetActionHelper(Unit* pet, ObjectGuid guid1, uint32 spe case COMMAND_FOLLOW: //spellid=1792 //FOLLOW pet->AttackStop(); pet->InterruptNonMeleeSpells(false); - pet->FollowTarget(_player); + pet->FollowTarget(pet->GetSummoner()); charmInfo->SetCommandState(COMMAND_FOLLOW); charmInfo->SetIsCommandAttack(false); @@ -190,7 +159,7 @@ void WorldSession::HandlePetActionHelper(Unit* pet, ObjectGuid guid1, uint32 spe charmInfo->SetIsCommandFollow(true); charmInfo->SetIsFollowing(false); break; - case COMMAND_ATTACK: //spellid=1792 //ATTACK + case COMMAND_ATTACK: //spellid=1792 //ATTACK { // Can't attack if owner is pacified if (_player->HasAuraType(SPELL_AURA_MOD_PACIFY)) @@ -417,6 +386,63 @@ void WorldSession::HandlePetActionHelper(Unit* pet, ObjectGuid guid1, uint32 spe default: TC_LOG_ERROR("entities.pet", "WORLD: unknown PET flag Action %i and spellid %i.", uint32(flag), spellid); } + */ +} + +void WorldSession::HandlePetAction(WorldPackets::Pet::PetAction& packet) +{ + if (packet.PetGUID.IsEmpty() || packet.Action == 0) + return; + + uint32 actionValue = packet.Action & 0xFFFFFF; + uint8 actionFlags = (packet.Action >> 24) & 0xFF; + + NewTemporarySummon* pet = _player->GetSummonByGUID(packet.PetGUID); + if (!pet || !pet->IsPet()) + return; + + // There are pet summons that consist of more than a single summon (Force of Nature for example) so we make sure that all related summons perform the action + std::vector pets; + pets.push_back(pet->ToNewPet()); + for (ObjectGuid const& guid : _player->GetSummonGUIDs()) + { + NewTemporarySummon* summon = _player->GetSummonByGUID(guid); + if (!summon || !summon->IsPet() || summon == pet || summon->GetUInt32Value(UNIT_CREATED_BY_SPELL) != pet->GetUInt32Value(UNIT_CREATED_BY_SPELL)) + continue; + + pets.push_back(summon->ToNewPet()); + } + + for (NewPet* actionPet : pets) + HandlePetActionHelper(actionPet, _player, packet.TargetGUID, actionValue, actionFlags, packet.ActionPosition); +} + +void WorldSession::HandlePetStopAttack(WorldPacket &recvData) +{ + ObjectGuid guid; + recvData >> guid; + + TC_LOG_DEBUG("network.opcode", "WORLD: Received CMSG_PET_STOP_ATTACK for %s", guid.ToString().c_str()); + + Unit* pet = ObjectAccessor::GetCreatureOrPetOrVehicle(*_player, guid); + + if (!pet) + { + TC_LOG_ERROR("entities.pet", "HandlePetStopAttack: %s does not exist", guid.ToString().c_str()); + return; + } + + if (pet != GetPlayer()->GetPet() && pet != GetPlayer()->GetCharmed()) + { + TC_LOG_ERROR("entities.pet", "HandlePetStopAttack: %s isn't a pet or charmed creature of player %s", + guid.ToString().c_str(), GetPlayer()->GetName().c_str()); + return; + } + + if (!pet->IsAlive()) + return; + + pet->AttackStop(); } void WorldSession::HandlePetNameQuery(WorldPacket& recvData) @@ -432,25 +458,22 @@ void WorldSession::HandlePetNameQuery(WorldPacket& recvData) SendPetNameQuery(petguid, petnumber); } -void WorldSession::SendPetNameQuery(ObjectGuid petguid, uint32 petnumber) +void WorldSession::SendPetNameQuery(ObjectGuid petGuid, uint32 petNumber) { - Creature* pet = ObjectAccessor::GetCreatureOrPetOrVehicle(*_player, petguid); - if (!pet) + WorldPackets::Query::QueryPetNameResponse packet; + packet.PetID = petNumber; + + NewPet* pet = _player->GetActivelyControlledSummon(); + if (!pet || pet->GetGUID() != petGuid || pet->GetUInt32Value(UNIT_FIELD_PETNUMBER) != petNumber) { - WorldPacket data(SMSG_PET_NAME_QUERY_RESPONSE, (4+1+4+1)); - data << uint32(petnumber); - data << uint8(0); - data << uint32(0); - data << uint8(0); - _player->SendDirectMessage(&data); + _player->SendDirectMessage(packet.Write()); return; } - WorldPacket data(SMSG_PET_NAME_QUERY_RESPONSE, (4+4+pet->GetName().size()+1)); - data << uint32(petnumber); - data << pet->GetName(); - data << uint32(pet->GetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP)); + packet.Timestamp = pet->GetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP); + packet.Name = pet->GetName(); + /* if (pet->IsPet() && ((Pet*)pet)->GetDeclinedNames()) { data << uint8(1); @@ -459,8 +482,9 @@ void WorldSession::SendPetNameQuery(ObjectGuid petguid, uint32 petnumber) } else data << uint8(0); + */ - _player->SendDirectMessage(&data); + _player->SendDirectMessage(packet.Write()); } bool WorldSession::CheckStableMaster(ObjectGuid guid) @@ -497,7 +521,7 @@ void WorldSession::HandlePetSetAction(WorldPacket& recvData) Unit* pet = ObjectAccessor::GetUnit(*_player, petguid); - if (!pet || pet != _player->GetFirstControlled()) + if (!pet) { TC_LOG_ERROR("entities.pet", "HandlePetSetAction: Unknown %s or owner (%s)", petguid.ToString().c_str(), _player->GetGUID().ToString().c_str()); return; @@ -581,20 +605,12 @@ void WorldSession::HandlePetSetAction(WorldPacket& recvData) { if (pet->GetTypeId() == TYPEID_UNIT && pet->IsPet()) ((Pet*)pet)->ToggleAutocast(spellInfo, true); - else - for (Unit::ControlList::iterator itr = GetPlayer()->m_Controlled.begin(); itr != GetPlayer()->m_Controlled.end(); ++itr) - if ((*itr)->GetEntry() == pet->GetEntry()) - (*itr)->GetCharmInfo()->ToggleCreatureAutocast(spellInfo, true); } //sign for no/turn off autocast else if (act_state == ACT_DISABLED) { if (pet->GetTypeId() == TYPEID_UNIT && pet->IsPet()) ((Pet*)pet)->ToggleAutocast(spellInfo, false); - else - for (Unit::ControlList::iterator itr = GetPlayer()->m_Controlled.begin(); itr != GetPlayer()->m_Controlled.end(); ++itr) - if ((*itr)->GetEntry() == pet->GetEntry()) - (*itr)->GetCharmInfo()->ToggleCreatureAutocast(spellInfo, false); } } @@ -662,6 +678,7 @@ void WorldSession::HandlePetRename(WorldPacket& recvData) } } + /* CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); if (isdeclined) { @@ -687,6 +704,7 @@ void WorldSession::HandlePetRename(WorldPacket& recvData) CharacterDatabase.CommitTransaction(trans); pet->SetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP, uint32(GameTime::GetGameTime())); // cast can't be helped + */ } void WorldSession::HandlePetAbandon(WorldPacket& recvData) @@ -698,6 +716,7 @@ void WorldSession::HandlePetAbandon(WorldPacket& recvData) if (!_player->IsInWorld()) return; + /* // pet/charmed Creature* pet = ObjectAccessor::GetCreatureOrPetOrVehicle(*_player, guid); if (pet) @@ -707,6 +726,7 @@ void WorldSession::HandlePetAbandon(WorldPacket& recvData) else if (pet->GetGUID() == _player->GetCharmedGUID()) _player->StopCastingCharm(); } + */ } void WorldSession::HandlePetSpellAutocastOpcode(WorldPacket& recvPacket) @@ -717,20 +737,11 @@ void WorldSession::HandlePetSpellAutocastOpcode(WorldPacket& recvPacket) uint8 state; //1 for on, 0 for off recvPacket >> guid >> spellid >> state; - if (!_player->GetGuardianPet() && !_player->GetCharmed()) - return; - if (guid.IsPlayer()) return; Creature* pet=ObjectAccessor::GetCreatureOrPetOrVehicle(*_player, guid); - if (!pet || (pet != _player->GetGuardianPet() && pet != _player->GetCharmed())) - { - TC_LOG_ERROR("entities.pet", "HandlePetSpellAutocastOpcode. %s isn't pet of player %s (%s).", guid.ToString().c_str(), GetPlayer()->GetName().c_str(), GetPlayer()->GetGUID().ToString().c_str()); - return; - } - SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellid); if (!spellInfo) { @@ -770,18 +781,8 @@ void WorldSession::HandlePetCastSpellOpcode(WorldPacket& recvPacket) TC_LOG_DEBUG("entities.pet", "WORLD: CMSG_PET_CAST_SPELL, %s, castCount: %u, spellId %u, castFlags %u", guid.ToString().c_str(), castCount, spellId, castFlags); - // This opcode is also sent from charmed and possessed units (players and creatures) - if (!_player->GetGuardianPet() && !_player->GetCharmed()) - return; - Unit* caster = ObjectAccessor::GetUnit(*_player, guid); - if (!caster || (caster != _player->GetGuardianPet() && caster != _player->GetCharmed())) - { - TC_LOG_ERROR("entities.pet", "HandlePetCastSpellOpcode: %s isn't pet of player %s (%s).", guid.ToString().c_str(), GetPlayer()->GetName().c_str(), GetPlayer()->GetGUID().ToString().c_str()); - return; - } - SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId); if (!spellInfo) { @@ -888,6 +889,7 @@ void WorldSession::HandleLearnPreviewTalentsPet(WorldPacket& recvData) void WorldSession::UpdatePetSlot(uint32 petNumberA, uint8 oldPetSlot, uint8 newPetSlot) { + /* uint32 petNumberB = 0; Pet* pet = _player->GetPet(); @@ -942,6 +944,7 @@ void WorldSession::UpdatePetSlot(uint32 petNumberA, uint8 oldPetSlot, uint8 newP SendPetSlotUpdated(petNumberA, newPetSlot, petNumberB, oldPetSlot); SendStableResult(STABLE_SUCCESS_STABLE); + */ } void WorldSession::SendPetSlotUpdated(int32 petNumberA, int32 petSlotA, int32 petNumberB, int32 petSlotB) @@ -979,8 +982,8 @@ void WorldSession::HandleRequestPetInfoOpcode(WorldPacket& /*recvData*/) * You're possessing a unit (Player::PossessSpellInitialize) */ - if (_player->GetPet() && _player->CanControlPet()) - _player->PetSpellInitialize(); + if (NewPet* pet = _player->GetActivelyControlledSummon()) + _player->SendPetSpellsMessage(pet); if (Unit* charm = _player->GetCharmed()) { diff --git a/src/server/game/Handlers/SpellHandler.cpp b/src/server/game/Handlers/SpellHandler.cpp index a81c963115a..a77993bbba1 100644 --- a/src/server/game/Handlers/SpellHandler.cpp +++ b/src/server/game/Handlers/SpellHandler.cpp @@ -19,6 +19,7 @@ #include "Archaeology.h" #include "Common.h" #include "Config.h" +#include "Creature.h" #include "DatabaseEnv.h" #include "DBCStores.h" #include "GameClient.h" @@ -38,7 +39,9 @@ #include "SpellAuraEffects.h" #include "SpellMgr.h" #include "SpellPackets.h" -#include "Totem.h" +#include "TemporarySummon.h" +#include "NewTemporarySummon.h" +#include "TotemPackets.h" #include "TotemPackets.h" #include "World.h" #include "WorldPacket.h" @@ -366,12 +369,6 @@ void WorldSession::HandlePetCancelAuraOpcode(WorldPacket& recvPacket) return; } - if (pet != GetPlayer()->GetGuardianPet() && pet != GetPlayer()->GetCharmed()) - { - TC_LOG_ERROR("network", "HandlePetCancelAura: %s is not a pet of player '%s'", guid.ToString().c_str(), GetPlayer()->GetName().c_str()); - return; - } - if (!pet->IsAlive()) { pet->SendPetActionFeedback(FEEDBACK_PET_DEAD); @@ -422,15 +419,15 @@ void WorldSession::HandleTotemDestroyed(WorldPackets::Totem::TotemDestroyed& pac if (_player->IsCharming()) return; - if (packet.Slot +1 >= MAX_TOTEM_SLOT) + SummonPropertiesSlot slot = SummonPropertiesSlot(packet.Slot + 1); + if (slot < SummonPropertiesSlot::Totem1 || slot > SummonPropertiesSlot::Totem4) return; - if (!_player->m_SummonSlot[packet.Slot + 1]) + NewTemporarySummon* totem = _player->GetSummonInSlot(slot); + if (!totem || totem->GetGUID() != packet.TotemGUID) return; - Creature* totem = ObjectAccessor::GetCreature(*GetPlayer(), _player->m_SummonSlot[packet.Slot + 1]); - if (totem && totem->IsTotem() && totem->GetGUID() == packet.TotemGUID) - totem->ToTotem()->UnSummon(); + totem->Unsummon(); } void WorldSession::HandleSelfResOpcode(WorldPacket& /*recvData*/) diff --git a/src/server/game/Maps/Map.h b/src/server/game/Maps/Map.h index b134b538e94..8ecb4517477 100644 --- a/src/server/game/Maps/Map.h +++ b/src/server/game/Maps/Map.h @@ -52,6 +52,7 @@ class PhaseShift; class Player; class SpawnedPoolData; class TempSummon; +class NewTemporarySummon; class TerrainInfo; class Transport; class Unit; @@ -149,17 +150,18 @@ struct TC_GAME_API SummonCreatureExtraArgs public: SummonCreatureExtraArgs() { } - SummonCreatureExtraArgs& SetSummonDuration(uint32 duration) { SummonDuration = duration; return *this; } + SummonCreatureExtraArgs& SetSummonDuration(int32 duration) { SummonDuration = duration; return *this; } SummonPropertiesEntry const* SummonProperties = nullptr; Unit* Summoner = nullptr; - uint32 SummonDuration = 0; + int32 SummonDuration = 0; uint32 SummonSpellId = 0; uint32 VehicleRecID = 0; uint32 SummonHealth = 0; uint32 RideSpell = 0; uint8 SeatNumber = 0; uint8 CreatureLevel = 0; + uint8 MaxSummons = 0; ObjectGuid PrivateObjectOwner; }; @@ -352,6 +354,8 @@ class TC_GAME_API Map : public GridRefManager void UpdateIteratorBack(Player* player); TempSummon* SummonCreature(uint32 entry, Position const& pos, SummonCreatureExtraArgs const& summonArgs = { }); + NewTemporarySummon* SummonCreatureNew(uint32 entry, Position const& pos, SummonCreatureExtraArgs const& summonArgs = { }); + void SummonCreatureGroup(uint8 group, std::list* list = nullptr); Player* GetPlayer(ObjectGuid const& guid); AreaTrigger* GetAreaTrigger(ObjectGuid const& guid); diff --git a/src/server/game/Phasing/PhasingHandler.cpp b/src/server/game/Phasing/PhasingHandler.cpp index 84bf8db57f5..47676193ade 100644 --- a/src/server/game/Phasing/PhasingHandler.cpp +++ b/src/server/game/Phasing/PhasingHandler.cpp @@ -52,6 +52,7 @@ inline PhaseFlags GetPhaseFlags(uint32 phaseId) template inline void ForAllControlled(Unit* unit, Func&& func) { + /* for (Unit* controlled : unit->m_Controlled) if (controlled->GetTypeId() != TYPEID_PLAYER) func(controlled); @@ -60,6 +61,7 @@ inline void ForAllControlled(Unit* unit, Func&& func) if (!unit->m_SummonSlot[i].IsEmpty()) if (Creature* summon = ObjectAccessor::GetCreature(*unit, unit->m_SummonSlot[i])) func(summon); + */ } } diff --git a/src/server/game/Server/Packets/CharacterPackets.cpp b/src/server/game/Server/Packets/CharacterPackets.cpp index 4d64e561789..14cbcfdd419 100644 --- a/src/server/game/Server/Packets/CharacterPackets.cpp +++ b/src/server/game/Server/Packets/CharacterPackets.cpp @@ -28,8 +28,8 @@ WorldPackets::Character::EnumCharactersResult::CharacterInfo::CharacterInfo(Fiel // SELECT characters.guid, characters.name, characters.race, characters.class, characters.gender, characters.skin, characters.face, characters.hairStyle, // 8 9 10 11 12 13 14 15 // characters.hairColor, characters.facialStyle, characters.level, characters.zone, characters.map, characters.position_x, characters.position_y, characters.position_z, - // 16 17 18 19 20 21 22 - // guild_member.guildid, characters.playerFlags, characters.at_login, character_pet.entry, character_pet.modelid, character_pet.level, characters.data, + // 16 17 18 19 20 21 22 + // guild_member.guildid, characters.playerFlags, characters.at_login, character_pet.CreatureId, character_pet.TamedCreatureId character_pet.DisplayId, characters.data, // 23 24 25 // character_banned.guid, characters.slot, character_declinedname.genitive @@ -87,10 +87,13 @@ WorldPackets::Character::EnumCharactersResult::CharacterInfo::CharacterInfo(Fiel // show pet at selection character in character list only for non-ghost character if (!(playerFlags & PLAYER_FLAGS_GHOST) && (ClassID == CLASS_WARLOCK || ClassID == CLASS_HUNTER || ClassID == CLASS_DEATH_KNIGHT)) { - if (CreatureTemplate const* creatureInfo = sObjectMgr->GetCreatureTemplate(fields[19].GetUInt32())) + uint32 creatureId = fields[19].GetUInt32(); + uint32 tamedCreatureId = fields[20].GetUInt32(); + + if (CreatureTemplate const* creatureInfo = sObjectMgr->GetCreatureTemplate(creatureId != 0 ? creatureId : tamedCreatureId)) { - PetCreatureDisplayID = fields[20].GetUInt32(); - PetExperienceLevel = fields[21].GetUInt16(); + PetCreatureDisplayID = fields[21].GetUInt32(); + PetExperienceLevel = ExperienceLevel; PetCreatureFamilyID = creatureInfo->family; } } diff --git a/src/server/game/Server/Packets/PartyPackets.cpp b/src/server/game/Server/Packets/PartyPackets.cpp index 841ca66bd9a..caa27e1c081 100644 --- a/src/server/game/Server/Packets/PartyPackets.cpp +++ b/src/server/game/Server/Packets/PartyPackets.cpp @@ -44,10 +44,12 @@ void WorldPackets::Party::PartyMemberState::Initialize(Player const* player) if (player->GetPowerType() != POWER_MANA) ChangeMask |= GROUP_UPDATE_FLAG_POWER_TYPE; + /* if (player->GetPet()) ChangeMask |= GROUP_UPDATE_FLAG_PET_GUID | GROUP_UPDATE_FLAG_PET_NAME | GROUP_UPDATE_FLAG_PET_MODEL_ID | GROUP_UPDATE_FLAG_PET_CUR_HP | GROUP_UPDATE_FLAG_PET_MAX_HP | GROUP_UPDATE_FLAG_PET_POWER_TYPE | GROUP_UPDATE_FLAG_PET_CUR_POWER | GROUP_UPDATE_FLAG_PET_MAX_POWER; + */ if (player->GetVehicle()) ChangeMask |= GROUP_UPDATE_FLAG_VEHICLE_SEAT; @@ -144,6 +146,7 @@ void WorldPackets::Party::PartyMemberState::Initialize(Player const* player) PhasingHandler::FillPartyMemberPhase(&MemberStats.Phases, player->GetPhaseShift()); // Pet + /* if (player->GetPet()) { ::Pet* pet = player->GetPet(); @@ -190,6 +193,7 @@ void WorldPackets::Party::PartyMemberState::Initialize(Player const* player) MemberStats.PetStats->Auras.push_back(aura); } } + */ } ByteBuffer& operator<<(ByteBuffer& data, WorldPackets::Party::PartyMemberPhaseStates const& phases) diff --git a/src/server/game/Server/Packets/PetPackets.cpp b/src/server/game/Server/Packets/PetPackets.cpp index 3469659fcbd..1bb4b4749d9 100644 --- a/src/server/game/Server/Packets/PetPackets.cpp +++ b/src/server/game/Server/Packets/PetPackets.cpp @@ -80,3 +80,58 @@ WorldPacket const* WorldPackets::Pet::PetAdded::Write() return &_worldPacket; } + +WorldPacket const* WorldPackets::Pet::PetSpellsMessage::Write() +{ + _worldPacket << PetGUID; + _worldPacket << uint16(_CreatureFamily); + _worldPacket << uint32(TimeLimit); + _worldPacket << uint8(ReactState); + _worldPacket << uint8(CommandState); + _worldPacket << uint16(Flag); + + for (uint32 button : ActionButtons) + _worldPacket << uint32(button); + + _worldPacket << uint8(Actions.size()); + for (uint32 action : Actions) + _worldPacket << uint32(action); + + _worldPacket << uint8(Cooldowns.size()); + for (PetSpellCooldown const& cooldown : Cooldowns) + { + _worldPacket << uint32(cooldown.SpellID); + _worldPacket << uint16(cooldown.Category); + _worldPacket << int32(cooldown.Duration); + _worldPacket << int32(cooldown.CategoryDuration); + } + + return &_worldPacket; +} + +void WorldPackets::Pet::PetAction::Read() +{ + _worldPacket >> PetGUID; + _worldPacket >> Action; + _worldPacket >> TargetGUID; + _worldPacket >> ActionPosition; +} + +void WorldPackets::Pet::DismissCritter::Read() +{ + _worldPacket >> CritterGUID; +} + +WorldPacket const* WorldPackets::Pet::PetLearnedSpell::Write() +{ + _worldPacket << uint32(SpellID); + + return &_worldPacket; +} + +WorldPacket const* WorldPackets::Pet::PetUnlearnedSpell::Write() +{ + _worldPacket << uint32(SpellID); + + return &_worldPacket; +} diff --git a/src/server/game/Server/Packets/PetPackets.h b/src/server/game/Server/Packets/PetPackets.h index bd9f9989eff..8b2ad72c870 100644 --- a/src/server/game/Server/Packets/PetPackets.h +++ b/src/server/game/Server/Packets/PetPackets.h @@ -94,6 +94,77 @@ namespace WorldPackets uint32 PetNumber = 0; uint8 Flags = 0; }; + + struct PetSpellCooldown + { + int32 SpellID = 0; + int32 Duration = 0; + int32 CategoryDuration = 0; + uint16 Category = 0; + }; + + class PetSpellsMessage final : public ServerPacket + { + public: + PetSpellsMessage() : ServerPacket(SMSG_PET_SPELLS) { } + + WorldPacket const* Write() override; + + ObjectGuid PetGUID; + uint16 _CreatureFamily = 0; ///< @see enum CreatureFamily + uint32 TimeLimit = 0; + uint8 ReactState = 0; + uint8 CommandState = 0; + uint8 Flag = 0; + + std::array ActionButtons = { }; + std::vector Actions; + std::vector Cooldowns; + }; + + class PetAction final : public ClientPacket + { + public: + PetAction(WorldPacket&& packet) : ClientPacket(CMSG_PET_ACTION, std::move(packet)) { } + + void Read() override; + + ObjectGuid PetGUID; + ObjectGuid TargetGUID; + TaggedPosition ActionPosition; + uint32 Action = 0; + }; + + + class DismissCritter final : public ClientPacket + { + public: + DismissCritter(WorldPacket&& packet) : ClientPacket(CMSG_DISMISS_CRITTER, std::move(packet)) { } + + void Read() override; + + ObjectGuid CritterGUID; + }; + + class PetLearnedSpell final : public ServerPacket + { + public: + PetLearnedSpell(uint32 spellId) : ServerPacket(SMSG_PET_LEARNED_SPELL, 4), SpellID(spellId) { } + + WorldPacket const* Write() override; + + uint32 SpellID = 0; + }; + + class PetUnlearnedSpell final : public ServerPacket + { + public: + PetUnlearnedSpell(uint32 spellId) : ServerPacket(SMSG_PET_REMOVED_SPELL, 4), SpellID(spellId) { } + + WorldPacket const* Write() override; + + uint32 SpellID = 0; + }; } } diff --git a/src/server/game/Server/Packets/QueryPackets.cpp b/src/server/game/Server/Packets/QueryPackets.cpp index 2772de5d823..633c6e92345 100644 --- a/src/server/game/Server/Packets/QueryPackets.cpp +++ b/src/server/game/Server/Packets/QueryPackets.cpp @@ -217,3 +217,17 @@ WorldPacket const* WorldPackets::Query::QueryPlayerNameResponse::Write() return &_worldPacket; } + +WorldPacket const* WorldPackets::Query::QueryPetNameResponse::Write() +{ + _worldPacket << uint32(PetID); + _worldPacket << Name; + _worldPacket << uint32(Timestamp); + _worldPacket << uint8(HasDeclined); + + if (HasDeclined) + for (std::string declinedName : DeclinedNames) + _worldPacket << declinedName; + + return &_worldPacket; +} diff --git a/src/server/game/Server/Packets/QueryPackets.h b/src/server/game/Server/Packets/QueryPackets.h index c2e2b22969b..aad485b4a64 100644 --- a/src/server/game/Server/Packets/QueryPackets.h +++ b/src/server/game/Server/Packets/QueryPackets.h @@ -188,6 +188,20 @@ namespace WorldPackets uint8 Result = 0; // 0 - full packet, != 0 - only guid PlayerGuidLookupData Data; }; + + class QueryPetNameResponse final : public ServerPacket + { + public: + QueryPetNameResponse() : ServerPacket(SMSG_PET_NAME_QUERY_RESPONSE) { } + + WorldPacket const* Write() override; + + uint32 PetID = 0; + uint32 Timestamp = 0; + bool HasDeclined = false; + std::array DeclinedNames = { }; + std::string Name; + }; } } diff --git a/src/server/game/Server/WorldSession.cpp b/src/server/game/Server/WorldSession.cpp index d076f5c2fa0..980b815598a 100644 --- a/src/server/game/Server/WorldSession.cpp +++ b/src/server/game/Server/WorldSession.cpp @@ -604,7 +604,7 @@ void WorldSession::LogoutPlayer(bool save) guild->HandleMemberLogout(this); ///- Remove pet - _player->RemovePet(nullptr, PET_SAVE_LOGOUT, true); + //_player->RemovePet(nullptr, PET_SAVE_LOGOUT, true); ///- Clear whisper whitelist _player->ClearWhisperWhiteList(); diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h index aa56cce85b8..d6529f1773d 100644 --- a/src/server/game/Server/WorldSession.h +++ b/src/server/game/Server/WorldSession.h @@ -198,6 +198,12 @@ namespace WorldPackets class PartyInviteResponse; } + namespace Pet + { + class DismissCritter; + class PetAction; + } + namespace Quest { class QuestGiverAcceptQuest; @@ -1054,9 +1060,8 @@ class TC_GAME_API WorldSession void HandleTutorialReset(WorldPacket& recvData); //Pet - void HandlePetAction(WorldPacket& recvData); + void HandlePetAction(WorldPackets::Pet::PetAction& packet); void HandlePetStopAttack(WorldPacket& recvData); - void HandlePetActionHelper(Unit* pet, ObjectGuid guid1, uint32 spellid, uint16 flag, ObjectGuid guid2, float x, float y, float z); void HandlePetNameQuery(WorldPacket& recvData); void HandlePetSetAction(WorldPacket& recvData); void HandlePetAbandon(WorldPacket& recvData); @@ -1070,7 +1075,7 @@ class TC_GAME_API WorldSession void HandleSetActionBarToggles(WorldPacket& recvData); void HandleTotemDestroyed(WorldPackets::Totem::TotemDestroyed& packet); - void HandleDismissCritter(WorldPacket& recvData); + void HandleDismissCritter(WorldPackets::Pet::DismissCritter& packet); //Battleground void HandleBattlemasterHelloOpcode(WorldPacket& recvData); diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.cpp b/src/server/game/Spells/Auras/SpellAuraEffects.cpp index a39127147ab..3c669ae5c05 100644 --- a/src/server/game/Spells/Auras/SpellAuraEffects.cpp +++ b/src/server/game/Spells/Auras/SpellAuraEffects.cpp @@ -4364,9 +4364,6 @@ void AuraEffect::HandleModDamageDone(AuraApplication const* aurApp, uint8 mode, for (uint16 i = 0; i < MAX_SPELL_SCHOOL; ++i) if (GetMiscValue() & (1 << i)) target->ApplyModUInt32Value(baseField + i, GetAmount(), apply); - - if (Guardian* pet = target->ToPlayer()->GetGuardianPet()) - pet->UpdateAttackPowerAndDamage(); } } @@ -4564,23 +4561,13 @@ void AuraEffect::HandleAuraDummy(AuraApplication const* aurApp, uint8 mode, bool break; case 34026: // kill command { - Unit* pet = target->GetGuardianPet(); - if (!pet) - break; - target->CastSpell(target, 34027, this); // set 3 stacks and 3 charges (to make all auras not disappear at once) Aura* owner_aura = target->GetAura(34027, GetCasterGUID()); - Aura* pet_aura = pet->GetAura(58914, GetCasterGUID()); if (owner_aura) { owner_aura->SetStackAmount(owner_aura->GetSpellInfo()->StackAmount); - if (pet_aura) - { - pet_aura->SetCharges(0); - pet_aura->SetStackAmount(owner_aura->GetSpellInfo()->StackAmount); - } } break; } @@ -6103,6 +6090,7 @@ void AuraEffect::HandlePeriodicManaLeechAuraTick(Unit* target, Unit* caster) con } // Drain Mana - Mana Feed effect + /* if (caster->GetGuardianPet() && m_spellInfo->SpellFamilyName == SPELLFAMILY_WARLOCK && m_spellInfo->SpellFamilyFlags[0] & 0x00000010) { int32 manaFeedVal = 0; @@ -6118,6 +6106,7 @@ void AuraEffect::HandlePeriodicManaLeechAuraTick(Unit* target, Unit* caster) con caster->CastSpell(caster, 32554, args); } } + */ } void AuraEffect::HandleObsModPowerAuraTick(Unit* target, Unit* caster) const @@ -6346,12 +6335,14 @@ void AuraEffect::HandleRaidProcFromChargeAuraProc(AuraApplication* aurApp, ProcE { float radius = GetSpellInfo()->Effects[GetEffIndex()].CalcRadius(caster); + /* if (Unit* triggerTarget = target->GetNextRandomRaidMemberOrPet(radius)) { target->CastSpell(triggerTarget, GetId(), { this, GetCasterGUID() }); if (Aura* aura = triggerTarget->GetAura(GetId(), GetCasterGUID())) aura->SetCharges(jumps); } + */ } } @@ -6388,12 +6379,14 @@ void AuraEffect::HandleRaidProcFromChargeWithValueAuraProc(AuraApplication* aurA { float radius = GetSpellInfo()->Effects[GetEffIndex()].CalcRadius(caster); + /* if (Unit* triggerTarget = target->GetNextRandomRaidMemberOrPet(radius)) { target->CastSpell(triggerTarget, GetId(), args); if (Aura* aura = triggerTarget->GetAura(GetId(), GetCasterGUID())) aura->SetCharges(jumps); } + */ } } diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index 46dcb95c99c..c65d9dbc9ce 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -41,6 +41,7 @@ #include "Opcodes.h" #include "PathGenerator.h" #include "Pet.h" +#include "NewPet.h" #include "Player.h" #include "ScriptMgr.h" #include "SharedDefines.h" @@ -1670,7 +1671,7 @@ void Spell::SelectImplicitCasterObjectTargets(SpellEffIndex effIndex, SpellImpli break; case TARGET_UNIT_PET: if (Unit* unitCaster = m_caster->ToUnit()) - target = unitCaster->GetGuardianPet(); + target = unitCaster->GetActivelyControlledSummon(); break; case TARGET_UNIT_SUMMONER: if (Unit* unitCaster = m_caster->ToUnit()) @@ -3540,16 +3541,6 @@ void Spell::_cast(bool skipCheck) // now that we've done the basic check, now run the scripts // should be done before the spell is actually executed sScriptMgr->OnPlayerSpellCast(playerCaster, this, skipCheck); - - // As of 3.0.2 pets begin attacking their owner's target immediately - // Let any pets know we've attacked something. Check DmgClass for harmful spells only - // This prevents spells such as Hunter's Mark from triggering pet attack - if (this->GetSpellInfo()->DmgClass != SPELL_DAMAGE_CLASS_NONE) - if (Unit* unitTarget = m_targets.GetUnitTarget()) - for (Unit* controlled : playerCaster->m_Controlled) - if (Creature* cControlled = controlled->ToCreature()) - if (CreatureAI* controlledAI = cControlled->AI()) - controlledAI->OwnerAttacked(unitTarget); } SetExecutedCurrently(true); @@ -3652,10 +3643,10 @@ void Spell::_cast(bool skipCheck) return; } - if (Unit* unitCaster = m_caster->ToUnit()) - if (m_spellInfo->HasAttribute(SPELL_ATTR1_DISMISS_PET_FIRST)) - if (Creature* pet = ObjectAccessor::GetCreature(*m_caster, unitCaster->GetPetGUID())) - pet->DespawnOrUnsummon(); + if (m_spellInfo->HasAttribute(SPELL_ATTR1_DISMISS_PET_FIRST)) + if (Unit* unitCaster = m_caster->ToUnit()) + if (NewPet* pet = unitCaster->GetActivelyControlledSummon()) + pet->Unsummon(); PrepareTriggersExecutedOnHit(); @@ -5854,14 +5845,14 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint if (Unit* unitCaster = m_caster->ToUnit()) { if (m_spellInfo->HasAttribute(SPELL_ATTR2_NO_ACTIVE_PETS)) - if (!unitCaster->GetPetGUID().IsEmpty()) + if (!unitCaster->GetSummonGUID().IsEmpty()) return SPELL_FAILED_ALREADY_HAVE_PET; for (uint8 j = 0; j < MAX_SPELL_EFFECTS; ++j) { if (m_spellInfo->Effects[j].TargetA.GetTarget() == TARGET_UNIT_PET) { - if (!unitCaster->GetGuardianPet()) + if (!unitCaster->GetActivelyControlledSummon()) { if (m_triggeredByAuraSpell) // not report pet not existence for triggered spells return SPELL_FAILED_DONT_REPORT; @@ -6219,10 +6210,9 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint if (!unitCaster) return SPELL_FAILED_BAD_TARGETS; - Creature* pet = unitCaster->GetGuardianPet(); + NewPet* pet = unitCaster->GetActivelyControlledSummon(); if (pet && pet->IsAlive()) return SPELL_FAILED_ALREADY_HAVE_SUMMON; - break; } // This is generic summon effect @@ -6238,7 +6228,7 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint switch (SummonProperties->Control) { case SUMMON_CATEGORY_PET: - if (!m_spellInfo->HasAttribute(SPELL_ATTR1_DISMISS_PET_FIRST) && unitCaster->GetPetGUID()) + if (!m_spellInfo->HasAttribute(SPELL_ATTR1_DISMISS_PET_FIRST) && unitCaster->GetSummonGUID()) return SPELL_FAILED_ALREADY_HAVE_SUMMON; [[fallthrough]]; // check both GetPetGUID() and GetCharmGUID for SUMMON_CATEGORY_PET*/ case SUMMON_CATEGORY_PUPPET: @@ -6254,7 +6244,7 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint { if (m_targets.GetUnitTarget()->GetTypeId() != TYPEID_PLAYER) return SPELL_FAILED_BAD_TARGETS; - if (!m_spellInfo->HasAttribute(SPELL_ATTR1_DISMISS_PET_FIRST) && m_targets.GetUnitTarget()->GetPetGUID()) + if (!m_spellInfo->HasAttribute(SPELL_ATTR1_DISMISS_PET_FIRST) && m_targets.GetUnitTarget()->GetSummonGUID()) return SPELL_FAILED_ALREADY_HAVE_SUMMON; } break; @@ -6265,7 +6255,7 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint if (!unitCaster) return SPELL_FAILED_BAD_TARGETS; - if (unitCaster->GetPetGUID()) //let warlock do a replacement summon + if (unitCaster->GetSummonGUID()) //let warlock do a replacement summon { if (unitCaster->GetTypeId() == TYPEID_PLAYER) { @@ -6405,7 +6395,7 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint if (m_spellInfo->Effects[i].ApplyAuraName == SPELL_AURA_MOD_CHARM || m_spellInfo->Effects[i].ApplyAuraName == SPELL_AURA_MOD_POSSESS) { - if (!m_spellInfo->HasAttribute(SPELL_ATTR1_DISMISS_PET_FIRST) && unitCaster->GetPetGUID()) + if (!m_spellInfo->HasAttribute(SPELL_ATTR1_DISMISS_PET_FIRST) && unitCaster->GetSummonGUID()) return SPELL_FAILED_ALREADY_HAVE_SUMMON; if (unitCaster->GetCharmedGUID()) diff --git a/src/server/game/Spells/Spell.h b/src/server/game/Spells/Spell.h index 8ab0b132bde..202eb65c04d 100644 --- a/src/server/game/Spells/Spell.h +++ b/src/server/game/Spells/Spell.h @@ -409,6 +409,7 @@ class TC_GAME_API Spell void EffectResurrectWithAura(SpellEffIndex effIndex); void EffectCreateAreaTrigger(SpellEffIndex effIndex); void EffectUpdatePlayerPhase(SpellEffIndex effIndex); + void EffectAllowControlPet(SpellEffIndex effIndex); void EffectUpdateZoneAurasAndPhases(SpellEffIndex effIndex); typedef std::unordered_set UsedSpellMods; diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index 49c726be26a..1b6e3c91fe6 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -44,6 +44,7 @@ #include "MapManager.h" #include "MiscPackets.h" #include "MotionMaster.h" +#include "NewPet.h" #include "ObjectAccessor.h" #include "ObjectMgr.h" #include "Opcodes.h" @@ -64,6 +65,7 @@ #include "SpellHistory.h" #include "SpellMgr.h" #include "TemporarySummon.h" +#include "NewTemporarySummon.h" #include "Totem.h" #include "Transport.h" #include "Unit.h" @@ -243,7 +245,7 @@ SpellEffectHandlerFn SpellEffectHandlers[TOTAL_SPELL_EFFECTS] = &Spell::EffectDamageFromMaxHealthPCT, //165 SPELL_EFFECT_DAMAGE_FROM_MAX_HEALTH_PCT &Spell::EffectGiveCurrency, //166 SPELL_EFFECT_GIVE_CURRENCY &Spell::EffectUpdatePlayerPhase, //167 SPELL_EFFECT_UPDATE_PLAYER_PHASE - &Spell::EffectNULL, //168 SPELL_EFFECT_168 + &Spell::EffectAllowControlPet, //168 SPELL_EFFECT_ALLOW_CONTROL_PET &Spell::EffectNULL, //169 SPELL_EFFECT_DESTROY_ITEM &Spell::EffectUpdateZoneAurasAndPhases, //170 SPELL_EFFECT_UPDATE_ZONE_AURAS_AND_PHASES &Spell::EffectSummonPersonalGameObject, //171 SPELL_EFFECT_SUMMON_PERSONAL_GAMEOBJECT @@ -1942,13 +1944,13 @@ void Spell::EffectSummonType(SpellEffIndex effIndex) ObjectGuid privateObjectOwner = [&]() { - if (!(properties->Flags & (SUMMON_PROP_FLAG_PERSONAL_SPAWN | SUMMON_PROP_FLAG_PERSONAL_GROUP_SPAWN))) + if (!(properties->GetFlags().HasFlag(SummonPropertiesFlags::OnlyVisibleToSummoner | SummonPropertiesFlags::OnlyVisibleToSummonerGroup))) return ObjectGuid::Empty; if (caster->IsPrivateObject()) return caster->GetPrivateObjectOwner(); - if (properties->Flags & SUMMON_PROP_FLAG_PERSONAL_GROUP_SPAWN) + if (properties->GetFlags().HasFlag(SummonPropertiesFlags::OnlyVisibleToSummonerGroup)) if (caster->IsPlayer() && caster->ToPlayer()->GetGroup()) return caster->ToPlayer()->GetGroup()->GetGUID(); @@ -1997,7 +1999,8 @@ void Spell::EffectSummonType(SpellEffIndex effIndex) case SummonPropertiesParamType::CreatureLevel: extraArgs.CreatureLevel = damage; break; - case SummonPropertiesParamType::MaxSummons: // @todo: handle and research (number of allowed units in one summon slot?) + case SummonPropertiesParamType::MaxSummons: // restricts the amount of totem slots that may be checked for SummonPropertiesSlot::AnyAvailableTotem + extraArgs.MaxSummons = damage; break; case SummonPropertiesParamType::NumUnitsMax: // Fails if less than 1 if (damage <= 0) @@ -2019,7 +2022,7 @@ void Spell::EffectSummonType(SpellEffIndex effIndex) dest = caster->GetRandomPoint(*destTarget, radius); } - if (TempSummon* summon = caster->GetMap()->SummonCreature(entry, *destTarget, extraArgs)) + if (NewTemporarySummon* summon = caster->GetMap()->SummonCreatureNew(entry, *destTarget, extraArgs)) { ExecuteLogEffectSummonObject(effIndex, summon); @@ -2530,7 +2533,7 @@ void Spell::EffectTameCreature(SpellEffIndex /*effIndex*/) if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET) return; - if (!unitCaster || unitCaster->GetPetGUID()) + if (!unitCaster || unitCaster->GetSummonGUID()) return; if (!unitTarget) @@ -2563,9 +2566,6 @@ void Spell::EffectTameCreature(SpellEffIndex /*effIndex*/) // add to world pet->GetMap()->AddToMap(pet->ToCreature()); - // caster have pet now - unitCaster->SetMinion(pet, true); - pet->InitTalentForLevel(); if (unitCaster->GetTypeId() == TYPEID_PLAYER) { @@ -2593,95 +2593,16 @@ void Spell::EffectSummonPet(SpellEffIndex effIndex) if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT) return; - Player* owner = nullptr; - if (unitCaster) - { - owner = unitCaster->ToPlayer(); - if (!owner && unitCaster->IsTotem()) - owner = unitCaster->GetCharmerOrOwnerPlayerOrPlayerItself(); - } - - // SUMMON_PET SummonPet's entries are at MiscValue, HunterPetSlot at BasePoints - uint32 petentry = (m_spellInfo->Effects[effIndex].MiscValue == 0 && m_spellInfo->Effects[effIndex].BasePoints <= PET_SLOT_LAST_ACTIVE_SLOT) ? - m_spellInfo->Effects[effIndex].BasePoints : m_spellInfo->Effects[effIndex].MiscValue; - - PetType petType = (m_spellInfo->Effects[effIndex].MiscValue == 0 && m_spellInfo->Effects[effIndex].BasePoints <= PET_SLOT_LAST_ACTIVE_SLOT) ? HUNTER_PET : SUMMON_PET; - - // Pet is summoned by a npc - if (!owner) - { - if (SummonPropertiesEntry const* properties = sSummonPropertiesStore.LookupEntry(67)) - { - SummonCreatureExtraArgs extraArgs; - extraArgs.SummonProperties = properties; - extraArgs.SummonDuration = m_spellInfo->GetDuration(); - extraArgs.Summoner = m_originalCaster; - extraArgs.SummonSpellId = m_spellInfo->Id; - - if (TempSummon* summon = m_caster->GetMap()->SummonCreature(petentry, *destTarget, extraArgs)) - ExecuteLogEffectSummonObject(effIndex, summon); - } - return; - } - - Pet* OldSummon = owner->GetPet(); - - // if pet requested type already exist - if (OldSummon) - { - if (petentry == 0 || OldSummon->GetEntry() == petentry) - { - // pet in corpse state can't be summoned - if (OldSummon->isDead()) - return; - - ASSERT(OldSummon->GetMap() == owner->GetMap()); + int32 petCreatureId = m_spellInfo->Effects[effIndex].MiscValue; + int32 petSlotIndex = m_spellInfo->Effects[effIndex].BasePoints; - //OldSummon->GetMap()->Remove(OldSummon->ToCreature(), false); - - float px, py, pz; - owner->GetClosePoint(px, py, pz, OldSummon->GetCombatReach()); - - OldSummon->NearTeleportTo(px, py, pz, OldSummon->GetOrientation()); - //OldSummon->Relocate(px, py, pz, OldSummon->GetOrientation()); - //OldSummon->SetMap(owner->GetMap()); - //owner->GetMap()->Add(OldSummon->ToCreature()); - if (OldSummon->getPetType() == SUMMON_PET) - { - OldSummon->SetHealth(OldSummon->GetMaxHealth()); - OldSummon->SetPower(OldSummon->GetPowerType(), - OldSummon->GetMaxPower(OldSummon->GetPowerType())); - } - - if (owner->GetTypeId() == TYPEID_PLAYER && OldSummon->isControlled()) - owner->ToPlayer()->PetSpellInitialize(); - - return; - } - - if (owner->GetTypeId() == TYPEID_PLAYER) - owner->ToPlayer()->RemovePet(OldSummon, (OldSummon->getPetType() == HUNTER_PET ? PET_SAVE_AS_DELETED : PET_SAVE_DISMISS), false); - else - return; - } - - float x, y, z; - owner->GetClosePoint(x, y, z, owner->GetCombatReach()); - Pet* pet = owner->SummonPet(petentry, x, y, z, owner->GetOrientation(), petType, 0); - if (!pet) + if (!unitCaster) return; - if (m_caster->GetTypeId() == TYPEID_UNIT) - { - if (m_caster->ToCreature()->IsTotem()) - pet->SetReactState(REACT_AGGRESSIVE); - else - pet->SetReactState(REACT_DEFENSIVE); - } - - pet->SetUInt32Value(UNIT_CREATED_BY_SPELL, m_spellInfo->Id); + bool isClassPet = (m_spellInfo->SpellFamilyName != SPELLFAMILY_GENERIC || (!petCreatureId && petSlotIndex != 0)); - ExecuteLogEffectSummonObject(effIndex, pet); + if (NewPet* summon = unitCaster->SummonPet(petCreatureId, petSlotIndex , m_spellInfo->Id, isClassPet, *destTarget)) + ExecuteLogEffectSummonObject(effIndex, summon); } void Spell::EffectLearnPetSpell(SpellEffIndex effIndex) @@ -3490,41 +3411,6 @@ void Spell::EffectScriptEffect(SpellEffIndex effIndex) return; } - // Stoneclaw Totem - case 55328: // Rank 1 - case 55329: // Rank 2 - case 55330: // Rank 3 - case 55332: // Rank 4 - case 55333: // Rank 5 - case 55335: // Rank 6 - case 55278: // Rank 7 - case 58589: // Rank 8 - case 58590: // Rank 9 - case 58591: // Rank 10 - { - // Cast Absorb on totems - for (uint8 slot = SUMMON_SLOT_TOTEM_FIRE; slot < MAX_TOTEM_SLOT; ++slot) - { - if (!unitTarget->m_SummonSlot[slot]) - continue; - - Creature* totem = unitTarget->GetMap()->GetCreature(unitTarget->m_SummonSlot[slot]); - if (totem && totem->IsTotem()) - { - CastSpellExtraArgs args(TRIGGERED_FULL_MASK); - args.AddSpellMod(SPELLVALUE_BASE_POINT0, damage); - m_caster->CastSpell(totem, 55277, args); - } - } - // Glyph of Stoneclaw Totem - if (AuraEffect* aur=unitTarget->GetAuraEffect(63298, 0)) - { - CastSpellExtraArgs args(TRIGGERED_FULL_MASK); - args.AddSpellMod(SPELLVALUE_BASE_POINT0, damage* aur->GetAmount()); - m_caster->CastSpell(unitTarget, 55277, args); - } - break; - } case 45668: // Ultra-Advanced Proto-Typical Shortening Blaster { if (!unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT) @@ -4078,10 +3964,12 @@ void Spell::EffectDismissPet(SpellEffIndex effIndex) if (!unitTarget || !unitTarget->IsPet()) return; + /* Pet* pet = unitTarget->ToPet(); ExecuteLogEffectUnsummonObject(effIndex, pet); pet->GetOwner()->RemovePet(pet, PET_SAVE_DISMISS); + */ } void Spell::EffectSummonObject(SpellEffIndex effIndex) @@ -4734,7 +4622,7 @@ void Spell::EffectResurrectPet(SpellEffIndex /*effIndex*/) { // Position passed to SummonPet is irrelevant with current implementation, // pet will be relocated without using these coords in Pet::LoadPetData - player->SummonPet(0, 0.0f, 0.0f, 0.0f, 0.0f, SUMMON_PET, 0); + //player->SummonPet(0, 0.0f, 0.0f, 0.0f, 0.0f, SUMMON_PET, 0); hadPet = false; } @@ -4776,40 +4664,27 @@ void Spell::EffectResurrectPet(SpellEffIndex /*effIndex*/) pet->SavePetToDB(PET_SAVE_CURRENT_STATE); } +static constexpr uint32 SPELL_TOTEMIC_RECALL_ENERGIZE = 39104; void Spell::EffectDestroyAllTotems(SpellEffIndex /*effIndex*/) { - if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT) - return; - - if (!unitCaster) + if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT || !m_caster->IsUnit()) return; - int32 mana = 0; - for (uint8 slot = SUMMON_SLOT_TOTEM_FIRE; slot < MAX_TOTEM_SLOT; ++slot) + int32 refundedMana = 0; + for (uint8 i = AsUnderlyingType(SummonPropertiesSlot::Totem1); i <= AsUnderlyingType(SummonPropertiesSlot::Totem4); ++i) { - if (!unitCaster->m_SummonSlot[slot]) + NewTemporarySummon* summon = m_caster->ToUnit()->GetSummonInSlot(SummonPropertiesSlot(i)); + if (!summon) continue; - Creature* totem = m_caster->GetMap()->GetCreature(unitCaster->m_SummonSlot[slot]); - if (totem && totem->IsTotem()) - { - uint32 spell_id = totem->GetUInt32Value(UNIT_CREATED_BY_SPELL); - SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spell_id); - if (spellInfo) - { - mana += spellInfo->ManaCost; - mana += int32(CalculatePct(unitCaster->GetCreateMana(), spellInfo->ManaCostPercentage)); - } - totem->ToTotem()->UnSummon(); - } - } - ApplyPct(mana, damage); - if (mana) - { - CastSpellExtraArgs args(TRIGGERED_FULL_MASK); - args.AddSpellMod(SPELLVALUE_BASE_POINT0, mana); - unitCaster->CastSpell(unitCaster, 39104, args); + if (SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(summon->GetUInt32Value(UNIT_CREATED_BY_SPELL))) + refundedMana += CalculatePct(spellInfo->CalcPowerCost(m_caster, m_spellInfo->GetSchoolMask()), damage); + + summon->Unsummon(); } + + if (refundedMana > 0) + m_caster->CastSpell(m_caster, SPELL_TOTEMIC_RECALL_ENERGIZE, CastSpellExtraArgs(TRIGGERED_FULL_MASK).AddSpellBP0(refundedMana)); } void Spell::EffectDurabilityDamage(SpellEffIndex effIndex) @@ -5336,7 +5211,7 @@ void Spell::EffectCreateTamedPet(SpellEffIndex effIndex) if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET) return; - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER || unitTarget->GetPetGUID() || unitTarget->getClass() != CLASS_HUNTER) + if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER || unitTarget->getClass() != CLASS_HUNTER) return; uint32 creatureEntry = m_spellInfo->Effects[effIndex].MiscValue; @@ -5352,9 +5227,6 @@ void Spell::EffectCreateTamedPet(SpellEffIndex effIndex) // add to world pet->GetMap()->AddToMap(pet->ToCreature()); - // unitTarget has pet now - unitTarget->SetMinion(pet, true); - pet->InitTalentForLevel(); if (unitTarget->GetTypeId() == TYPEID_PLAYER) @@ -5806,6 +5678,17 @@ void Spell::EffectUpdatePlayerPhase(SpellEffIndex /*effIndex*/) PhasingHandler::OnConditionChange(unitTarget); } +void Spell::EffectAllowControlPet(SpellEffIndex /*effIndex*/) +{ + if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET) + return; + + if (!unitTarget || !unitTarget->IsPlayer()) + return; + + unitTarget->ToPlayer()->SetCanControlClassPets(); +} + void Spell::EffectUpdateZoneAurasAndPhases(SpellEffIndex /*effIndex*/) { if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET) diff --git a/src/server/game/Spells/SpellHistory.cpp b/src/server/game/Spells/SpellHistory.cpp index 61ee3ad4d8f..e10392458cf 100644 --- a/src/server/game/Spells/SpellHistory.cpp +++ b/src/server/game/Spells/SpellHistory.cpp @@ -22,6 +22,7 @@ #include "ObjectMgr.h" #include "Opcodes.h" #include "Pet.h" +#include "PetPackets.h" #include "Player.h" #include "Spell.h" #include "SpellInfo.h" @@ -238,6 +239,35 @@ void SpellHistory::WritePacket(WorldPacket& packet) const } } +void SpellHistory::WritePetSpellHistory(WorldPackets::Pet::PetSpellsMessage& petSpellsMessage) const +{ + Clock::time_point now = GameTime::GetGameTimeSystemPoint(); + + petSpellsMessage.Cooldowns.reserve(_spellCooldowns.size()); + for (auto const& p : _spellCooldowns) + { + WorldPackets::Pet::PetSpellCooldown petSpellCooldown; + petSpellCooldown.SpellID = p.first; + petSpellCooldown.Category = p.second.CategoryId; + + if (!p.second.OnHold) + { + Milliseconds cooldownDuration = std::chrono::duration_cast(p.second.CooldownEnd - now); + if (cooldownDuration.count() <= 0) + continue; + + petSpellCooldown.Duration = uint32(cooldownDuration.count()); + Milliseconds categoryDuration = std::chrono::duration_cast(p.second.CategoryEnd - now); + if (categoryDuration.count() > 0) + petSpellCooldown.CategoryDuration = uint32(categoryDuration.count()); + } + else + petSpellCooldown.CategoryDuration = 0x80000000; + + petSpellsMessage.Cooldowns.push_back(petSpellCooldown); + } +} + template<> void SpellHistory::WriteSpellHistoryEntries(std::vector& spellHistoryEntries) const { diff --git a/src/server/game/Spells/SpellHistory.h b/src/server/game/Spells/SpellHistory.h index c9a4974394b..3acda7b5091 100644 --- a/src/server/game/Spells/SpellHistory.h +++ b/src/server/game/Spells/SpellHistory.h @@ -35,6 +35,11 @@ struct SpellCategoryEntry; namespace WorldPackets { + namespace Pet + { + class PetSpellsMessage; + } + namespace Spells { struct SpellHistoryEntry; @@ -82,7 +87,8 @@ class TC_GAME_API SpellHistory void HandleCooldowns(SpellInfo const* spellInfo, uint32 itemID, Spell* spell = nullptr); bool IsReady(SpellInfo const* spellInfo, uint32 itemId = 0, bool ignoreCategoryCooldown = false) const; template - void WritePacket(WorldPacket& packet) const; + void WritePacket(WorldPacket& data) const; + void WritePetSpellHistory(WorldPackets::Pet::PetSpellsMessage& petSpellsMessage) const; template void WriteSpellHistoryEntries(std::vector& spellHistoryEntries) const; diff --git a/src/server/game/Tools/PlayerDump.cpp b/src/server/game/Tools/PlayerDump.cpp index 7f5a18c51ab..fe8513480f7 100644 --- a/src/server/game/Tools/PlayerDump.cpp +++ b/src/server/game/Tools/PlayerDump.cpp @@ -70,7 +70,7 @@ struct BaseTable BaseTable const BaseTables[] = { - { "character_pet", "id", "owner", GUID_TYPE_PET }, + { "character_pet", "PetNumber", "Guid", GUID_TYPE_PET }, { "mail", "id", "receiver", GUID_TYPE_MAIL }, { "item_instance", "guid", "owner_guid", GUID_TYPE_ITEM }, @@ -347,10 +347,10 @@ void PlayerDump::InitializeTables() MarkDependentColumn(t, "item_guid", GUID_TYPE_ITEM); break; case DTT_PET: - MarkWhereField(t, "owner"); + MarkWhereField(t, "Guid"); - MarkDependentColumn(t, "id", GUID_TYPE_PET); - MarkDependentColumn(t, "owner", GUID_TYPE_CHAR); + MarkDependentColumn(t, "PetNumber", GUID_TYPE_PET); + MarkDependentColumn(t, "Guid", GUID_TYPE_CHAR); break; case DTT_PET_TABLE: MarkWhereField(t, "guid"); diff --git a/src/server/scripts/Commands/cs_npc.cpp b/src/server/scripts/Commands/cs_npc.cpp index d2bdb4a7adc..395363690d5 100644 --- a/src/server/scripts/Commands/cs_npc.cpp +++ b/src/server/scripts/Commands/cs_npc.cpp @@ -1483,13 +1483,6 @@ class npc_commandscript : public CommandScript Player* player = handler->GetSession()->GetPlayer(); - if (player->GetPetGUID()) - { - handler->SendSysMessage (LANG_YOU_ALREADY_HAVE_PET); - handler->SetSentErrorMessage (true); - return false; - } - CreatureTemplate const* cInfo = creatureTarget->GetCreatureTemplate(); if (!cInfo->IsTameable (player->CanTameExoticPets())) @@ -1528,9 +1521,6 @@ class npc_commandscript : public CommandScript // visual effect for levelup pet->SetUInt32Value(UNIT_FIELD_LEVEL, level); - // caster have pet now - player->SetMinion(pet, true); - pet->SavePetToDB(PET_SAVE_NEW_PET); player->PetSpellInitialize(); diff --git a/src/server/scripts/Commands/cs_pet.cpp b/src/server/scripts/Commands/cs_pet.cpp index f6937dff699..d8c355d31e6 100644 --- a/src/server/scripts/Commands/cs_pet.cpp +++ b/src/server/scripts/Commands/cs_pet.cpp @@ -83,13 +83,6 @@ class pet_commandscript : public CommandScript return false; } - if (player->GetPetGUID()) - { - handler->PSendSysMessage("You already have a pet"); - handler->SetSentErrorMessage(true); - return false; - } - // Everything looks OK, create new pet Pet* pet = new Pet(player, HUNTER_PET); if (!pet->CreateBaseAtCreature(creatureTarget)) @@ -126,7 +119,6 @@ class pet_commandscript : public CommandScript // visual effect for levelup pet->SetUInt32Value(UNIT_FIELD_LEVEL, creatureTarget->getLevel()); - player->SetMinion(pet, true); pet->SavePetToDB(PET_SAVE_NEW_PET); player->PetSpellInitialize(); diff --git a/src/server/scripts/EasternKingdoms/ScarletEnclave/the_scarlet_enclave_chapter_1.cpp b/src/server/scripts/EasternKingdoms/ScarletEnclave/the_scarlet_enclave_chapter_1.cpp index 959e072d13d..a6c81304180 100644 --- a/src/server/scripts/EasternKingdoms/ScarletEnclave/the_scarlet_enclave_chapter_1.cpp +++ b/src/server/scripts/EasternKingdoms/ScarletEnclave/the_scarlet_enclave_chapter_1.cpp @@ -951,8 +951,6 @@ class npc_dkc1_gothik : public CreatureScript // and dig into the ground. creature->DespawnOrUnsummon(); - if (player->GetQuestStatus(12698) == QUEST_STATUS_COMPLETE) - owner->RemoveAllMinionsByEntry(NPC_GHOSTS); } } } @@ -980,7 +978,6 @@ struct npc_scarlet_ghoul : public ScriptedAI void FindMinions(Unit* owner) { std::list MinionList; - owner->GetAllMinionsByEntry(MinionList, NPC_GHOULS); if (!MinionList.empty()) { diff --git a/src/server/scripts/Outland/Auchindoun/ShadowLabyrinth/boss_blackheart_the_inciter.cpp b/src/server/scripts/Outland/Auchindoun/ShadowLabyrinth/boss_blackheart_the_inciter.cpp index cab3678f7ee..60d46b7ce73 100644 --- a/src/server/scripts/Outland/Auchindoun/ShadowLabyrinth/boss_blackheart_the_inciter.cpp +++ b/src/server/scripts/Outland/Auchindoun/ShadowLabyrinth/boss_blackheart_the_inciter.cpp @@ -177,18 +177,12 @@ struct boss_blackheart_the_inciter_mc_dummy : public NullCreatureAI { me->GetThreatManager().AddThreat(trigger, 0.0f); trigger->GetThreatManager().AddThreat(who, 0.0f); - for (Unit* other : trigger->m_Controlled) - { - me->GetThreatManager().AddThreat(other, 0.0f); - other->GetThreatManager().AddThreat(who, 0.0f); - } } } void UpdateAI(uint32 /*diff*/) override { - if (me->m_Controlled.empty()) - me->DespawnOrUnsummon(); } + PlayerAI* GetAIForCharmedPlayer(Player* player) override { return new BlackheartCharmedPlayerAI(player); diff --git a/src/server/scripts/Spells/spell_dk.cpp b/src/server/scripts/Spells/spell_dk.cpp index fd4cc4d6128..1e8ebdbea11 100644 --- a/src/server/scripts/Spells/spell_dk.cpp +++ b/src/server/scripts/Spells/spell_dk.cpp @@ -415,16 +415,6 @@ class spell_dk_death_pact : public SpellScript { SpellCastResult CheckCast() { - // Check if we have valid targets, otherwise skip spell casting here - if (Player* player = GetCaster()->ToPlayer()) - for (Unit::ControlList::const_iterator itr = player->m_Controlled.begin(); itr != player->m_Controlled.end(); ++itr) - if (Creature* undeadPet = (*itr)->ToCreature()) - if (undeadPet->IsAlive() && - undeadPet->GetOwnerOrCreatorGUID() == player->GetGUID() && - undeadPet->GetCreatureType() == CREATURE_TYPE_UNDEAD && - undeadPet->IsWithinDist(player, 100.0f, false)) - return SPELL_CAST_OK; - return SPELL_FAILED_NO_PET; } @@ -1784,7 +1774,7 @@ class spell_dk_dancing_rune_weapon : public AuraScript { PreventDefaultAction(); std::list runeWeapon; - GetTarget()->GetAllMinionsByEntry(runeWeapon, GetSpellInfo()->Effects[EFFECT_0].MiscValue); + //GetTarget()->GetAllMinionsByEntry(runeWeapon, GetSpellInfo()->Effects[EFFECT_0].MiscValue); if (runeWeapon.empty()) return; diff --git a/src/server/scripts/Spells/spell_druid.cpp b/src/server/scripts/Spells/spell_druid.cpp index 55916d8d476..96c1245a7f3 100644 --- a/src/server/scripts/Spells/spell_druid.cpp +++ b/src/server/scripts/Spells/spell_druid.cpp @@ -33,6 +33,7 @@ #include "SpellMgr.h" #include "SpellScript.h" #include "TemporarySummon.h" +#include "NewTemporarySummon.h" enum DruidSpells { @@ -63,8 +64,6 @@ enum DruidSpells SPELL_DRUID_FERAL_SWIFTNESS = 17002, SPELL_DRUID_FERAL_SWIFTNESS_CLEAR_ROAR = 97993, SPELL_DRUID_FERAL_SWIFTNESS_CLEAR_CAT = 97985, - SPELL_DRUID_FUNGAL_GROWTH_R1 = 78788, - SPELL_DRUID_FUNGAL_GROWTH_R2 = 78789, SPELL_DRUID_FUNGAL_GROWTH_SUMMON_R1 = 81291, SPELL_DRUID_FUNGAL_GROWTH_SUMMON_R2 = 81283, SPELL_DRUID_FRENZIED_REGENERATION_HEAL = 22845, @@ -120,8 +119,8 @@ enum DruidSpells SPELL_DRUID_T13_FERAL_2P_BONUS = 105725, SPELL_DRUID_WILD_MUSHROOM = 88747, SPELL_DRUID_WILD_MUSHROOM_DAMAGE = 78777, - SPELL_DRUID_WILD_MUSHROOM_SUICIDE = 92853, - SPELL_DRUID_WILD_MUSHROOM_VISUAL = 92701, + SPELL_DRUID_WILD_MUSHROOM_DETONATE_SUICIDE = 92853, + SPELL_DRUID_WILD_MUSHROOM_DETONATE_DEATH_VISUAL = 92701, SPELL_DRUID_FIREBLOOM = 99017 }; @@ -136,7 +135,8 @@ enum DruidSpellIconIds SPELL_ICON_ID_GLYPH_OF_FEROCIOUS_BITE = 1680, SPELL_ICON_ID_GLYPH_OF_FRENZIED_REGENERATION = 50, SPELL_ICON_ID_GIFT_OF_THE_EARTHMOTHER = 3186, - SPELL_ICON_ID_STAMPEDE = 3930 + SPELL_ICON_ID_STAMPEDE = 3930, + SPELL_ICON_ID_FUNGAL_GROWTH = 2681 }; enum MiscSpells @@ -1323,46 +1323,7 @@ class spell_dru_leader_of_the_pack : public AuraScript } }; -class spell_dru_wild_mushroom : public SpellScript -{ - void HandleSummon() - { - Unit* caster = GetCaster(); - if (!caster) - return; - - std::list mushrooms; - caster->GetAllMinionsByEntry(mushrooms, GetSpellInfo()->Effects[EFFECT_0].MiscValue); - if (mushrooms.empty()) - return; - - TempSummon* oldestMushroom = nullptr; - - if (mushrooms.size() > uint32(GetSpellInfo()->Effects[EFFECT_0].BasePoints)) - { - for (auto itr : mushrooms) - { - if (TempSummon* mushroomToCheck = itr->ToTempSummon()) - { - if (!oldestMushroom) - oldestMushroom = mushroomToCheck; - else - { - if (mushroomToCheck->GetTimer() < oldestMushroom->GetTimer()) - oldestMushroom = mushroomToCheck; - } - } - } - if (oldestMushroom) - oldestMushroom->UnSummon(); - } - } - - void Register() override - { - AfterCast.Register(&spell_dru_wild_mushroom::HandleSummon); - } -}; +static constexpr uint32 const NPC_WILD_MUSHROOM = 47649; class spell_dru_wild_mushroom_detonate : public SpellScript { @@ -1370,12 +1331,9 @@ class spell_dru_wild_mushroom_detonate : public SpellScript { return ValidateSpellInfo( { - SPELL_DRUID_WILD_MUSHROOM, SPELL_DRUID_WILD_MUSHROOM_DAMAGE, - SPELL_DRUID_WILD_MUSHROOM_SUICIDE, - SPELL_DRUID_WILD_MUSHROOM_VISUAL, - SPELL_DRUID_FUNGAL_GROWTH_R1, - SPELL_DRUID_FUNGAL_GROWTH_R2, + SPELL_DRUID_WILD_MUSHROOM_DETONATE_SUICIDE, + SPELL_DRUID_WILD_MUSHROOM_DETONATE_DEATH_VISUAL, SPELL_DRUID_FUNGAL_GROWTH_SUMMON_R1, SPELL_DRUID_FUNGAL_GROWTH_SUMMON_R2 }); @@ -1383,33 +1341,19 @@ class spell_dru_wild_mushroom_detonate : public SpellScript void HandleDummy(SpellEffIndex /*effIndex*/) { - Unit* caster = GetCaster(); - if (!caster) - return; - SpellInfo const* spell = sSpellMgr->GetSpellInfo(SPELL_DRUID_WILD_MUSHROOM); - if (!spell) - return; + for (SummonPropertiesSlot slot : { SummonPropertiesSlot::Totem1, SummonPropertiesSlot::Totem2, SummonPropertiesSlot::Totem3, SummonPropertiesSlot::Totem4 }) + { + NewTemporarySummon* summon = GetHitUnit()->GetSummonInSlot(slot); + if (!summon || summon->GetEntry() != NPC_WILD_MUSHROOM) + continue; - std::list mushrooms; - caster->GetAllMinionsByEntry(mushrooms, spell->Effects[EFFECT_0].MiscValue); - if (mushrooms.empty()) - return; + if (AuraEffect const* fungalGrowth = GetHitUnit()->GetDummyAuraEffect(SPELLFAMILY_DRUID, SPELL_ICON_ID_FUNGAL_GROWTH, EFFECT_0)) + GetHitUnit()->CastSpell(summon, fungalGrowth->GetSpellInfo()->GetRank() == 1 ? SPELL_DRUID_FUNGAL_GROWTH_SUMMON_R1 : SPELL_DRUID_FUNGAL_GROWTH_SUMMON_R2, fungalGrowth); - for (auto itr : mushrooms) - { - if (TempSummon* mushroom = itr->ToTempSummon()) - { - if (caster->HasAura(SPELL_DRUID_FUNGAL_GROWTH_R1)) - caster->CastSpell(mushroom, SPELL_DRUID_FUNGAL_GROWTH_SUMMON_R1, true); - else if (caster->HasAura(SPELL_DRUID_FUNGAL_GROWTH_R2)) - caster->CastSpell(mushroom, SPELL_DRUID_FUNGAL_GROWTH_SUMMON_R2, true); - - caster->CastSpell(mushroom, SPELL_DRUID_WILD_MUSHROOM_DAMAGE, true); - mushroom->CastSpell(mushroom, SPELL_DRUID_WILD_MUSHROOM_VISUAL, true); - mushroom->CastSpell(mushroom, SPELL_DRUID_WILD_MUSHROOM_SUICIDE, true); - mushroom->UnSummon(1500); - } + GetHitUnit()->CastSpell(summon, SPELL_DRUID_WILD_MUSHROOM_DAMAGE, true); + summon->CastSpell(nullptr, SPELL_DRUID_WILD_MUSHROOM_DETONATE_DEATH_VISUAL); + summon->CastSpell(nullptr, SPELL_DRUID_WILD_MUSHROOM_DETONATE_SUICIDE); } } @@ -2240,7 +2184,6 @@ void AddSC_druid_spell_scripts() RegisterSpellScript(spell_dru_t12_restoration_4p_bonus); RegisterSpellScript(spell_dru_item_t11_feral_4p_bonus); RegisterSpellScript(spell_dru_wild_growth); - RegisterSpellScript(spell_dru_wild_mushroom); RegisterSpellScript(spell_dru_wild_mushroom_detonate); RegisterSpellScript(spell_dru_stampeding_roar); RegisterSpellScript(spell_dru_feral_swiftness_clear); diff --git a/src/server/scripts/Spells/spell_generic.cpp b/src/server/scripts/Spells/spell_generic.cpp index 508a22b3462..7cf63f2f6bc 100644 --- a/src/server/scripts/Spells/spell_generic.cpp +++ b/src/server/scripts/Spells/spell_generic.cpp @@ -1705,7 +1705,7 @@ class spell_ethereal_pet_aura : public AuraScript PreventDefaultAction(); std::list minionList; - GetUnitOwner()->GetAllMinionsByEntry(minionList, NPC_ETHEREAL_SOUL_TRADER); + //GetUnitOwner()->GetAllMinionsByEntry(minionList, NPC_ETHEREAL_SOUL_TRADER); for (Creature* minion : minionList) { if (minion->IsAIEnabled()) @@ -2651,6 +2651,7 @@ class spell_gen_pet_summoned : public SpellScriptLoader void HandleScript(SpellEffIndex /*effIndex*/) { + /* Player* player = GetCaster()->ToPlayer(); if (player->GetLastPetNumber()) { @@ -2678,6 +2679,7 @@ class spell_gen_pet_summoned : public SpellScriptLoader else delete newPet; } + */ } void Register() override @@ -3168,10 +3170,6 @@ class spell_gen_summon_elemental : public SpellScriptLoader void AfterRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) { - if (GetCaster()) - if (Unit* owner = GetCaster()->GetOwner()) - if (owner->GetTypeId() == TYPEID_PLAYER) /// @todo this check is maybe wrong - owner->ToPlayer()->RemovePet(nullptr, PET_SAVE_DISMISS, true); } void Register() override @@ -3785,8 +3783,8 @@ class spell_gen_gm_freeze : public SpellScriptLoader { pet->SavePetToDB(PET_SAVE_CURRENT_STATE); // not let dismiss dead pet - if (pet->IsAlive()) - player->RemovePet(pet, PET_SAVE_DISMISS); + //if (pet->IsAlive()) + // player->RemovePet(pet, PET_SAVE_DISMISS); } } } diff --git a/src/server/scripts/Spells/spell_hunter.cpp b/src/server/scripts/Spells/spell_hunter.cpp index 843c72557b8..77a2eeb1d3a 100644 --- a/src/server/scripts/Spells/spell_hunter.cpp +++ b/src/server/scripts/Spells/spell_hunter.cpp @@ -761,9 +761,6 @@ class spell_hun_tame_beast : public SpellScript if (!target->GetCreatureTemplate()->IsTameable(player->ToPlayer()->CanTameExoticPets())) return SendTameFailResult(PET_TAME_FAILURE_CANNOT_TAME_EXOTIC); - if (player->GetPetGUID()) - return SendTameFailResult(PET_TAME_FAILURE_ACTIVE_SUMMON); - if (player->GetCharmedGUID()) return SendTameFailResult(PET_TAME_FAILURE_CREATURE_CONTROLLED); } diff --git a/src/server/scripts/Spells/spell_item.cpp b/src/server/scripts/Spells/spell_item.cpp index 01a0c3fc7c7..eab12beece4 100644 --- a/src/server/scripts/Spells/spell_item.cpp +++ b/src/server/scripts/Spells/spell_item.cpp @@ -2491,7 +2491,7 @@ class spell_item_gift_of_the_harvester : public SpellScriptLoader SpellCastResult CheckRequirement() { std::list ghouls; - GetCaster()->GetAllMinionsByEntry(ghouls, NPC_GHOUL); + //GetCaster()->GetAllMinionsByEntry(ghouls, NPC_GHOUL); if (ghouls.size() >= MAX_GHOULS) { SetCustomCastResultMessage(SPELL_CUSTOM_ERROR_TOO_MANY_GHOULS); diff --git a/src/server/scripts/Spells/spell_mage.cpp b/src/server/scripts/Spells/spell_mage.cpp index f4cb972bd18..6e6f122c91f 100644 --- a/src/server/scripts/Spells/spell_mage.cpp +++ b/src/server/scripts/Spells/spell_mage.cpp @@ -827,7 +827,7 @@ class spell_mage_permafrost : public AuraScript bool DoCheck(ProcEventInfo& eventInfo) { - return GetTarget()->GetGuardianPet() && eventInfo.GetDamageInfo()->GetDamage() && eventInfo.GetProcTarget(); + return false; } void HandleEffectProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) @@ -967,7 +967,7 @@ class spell_mage_ring_of_frost : public AuraScript void Apply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) { std::list MinionList; - GetTarget()->GetAllMinionsByEntry(MinionList, GetSpellInfo()->Effects[EFFECT_0].MiscValue); + //GetTarget()->GetAllMinionsByEntry(MinionList, GetSpellInfo()->Effects[EFFECT_0].MiscValue); // Get the last summoned RoF, save it and despawn older ones for (std::list::iterator itr = MinionList.begin(); itr != MinionList.end(); itr++) diff --git a/src/server/scripts/Spells/spell_paladin.cpp b/src/server/scripts/Spells/spell_paladin.cpp index 62d5c570c10..e3360c324a3 100644 --- a/src/server/scripts/Spells/spell_paladin.cpp +++ b/src/server/scripts/Spells/spell_paladin.cpp @@ -1428,9 +1428,9 @@ class spell_pal_ancient_healer : public AuraScript int32 bp1 = CalculatePct(heal->GetEffectiveHeal(), 10); - for (Unit* guardian : GetTarget()->m_Controlled) - if (guardian->GetUInt32Value(UNIT_CREATED_BY_SPELL) == SPELL_PALADIN_GUARDIAN_OF_ANCIENT_KINGS_HOLY) - guardian->CastSpell(heal->GetTarget(), SPELL_PALADIN_LIGHT_OF_THE_ANCIENT_KINGS, CastSpellExtraArgs(aurEff).AddSpellBP0(bp0).AddSpellMod(SPELLVALUE_BASE_POINT1, bp1)); + //for (Unit* guardian : GetTarget()->m_Controlled) + // if (guardian->GetUInt32Value(UNIT_CREATED_BY_SPELL) == SPELL_PALADIN_GUARDIAN_OF_ANCIENT_KINGS_HOLY) + // guardian->CastSpell(heal->GetTarget(), SPELL_PALADIN_LIGHT_OF_THE_ANCIENT_KINGS, CastSpellExtraArgs(aurEff).AddSpellBP0(bp0).AddSpellMod(SPELLVALUE_BASE_POINT1, bp1)); _procCount++; } diff --git a/src/server/scripts/Spells/spell_shaman.cpp b/src/server/scripts/Spells/spell_shaman.cpp index 097fe03e85a..fb176a26124 100644 --- a/src/server/scripts/Spells/spell_shaman.cpp +++ b/src/server/scripts/Spells/spell_shaman.cpp @@ -1012,9 +1012,9 @@ class spell_sha_totemic_mastery : public AuraScript void HandleDummy(AuraEffect const* /*aurEff*/) { Unit* target = GetTarget(); - for (uint8 i = SUMMON_SLOT_TOTEM_FIRE; i < MAX_TOTEM_SLOT; ++i) - if (!target->m_SummonSlot[i]) - return; + //for (uint8 i = SUMMON_SLOT_TOTEM_FIRE; i < MAX_TOTEM_SLOT; ++i) + // if (!target->m_SummonSlot[i]) + // return; target->CastSpell(target, SPELL_SHAMAN_TOTEMIC_MASTERY, true); PreventDefaultAction(); diff --git a/src/server/scripts/Spells/spell_warlock.cpp b/src/server/scripts/Spells/spell_warlock.cpp index 8b3b0b08de8..3445ad0f25e 100644 --- a/src/server/scripts/Spells/spell_warlock.cpp +++ b/src/server/scripts/Spells/spell_warlock.cpp @@ -507,7 +507,7 @@ class spell_warl_fel_synergy : public AuraScript bool CheckProc(ProcEventInfo& eventInfo) { - return GetTarget()->GetGuardianPet() && eventInfo.GetDamageInfo()->GetDamage(); + return false; } void OnProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) diff --git a/src/server/scripts/World/npcs_special.cpp b/src/server/scripts/World/npcs_special.cpp index b2dd7ab4bb9..187d6f57716 100644 --- a/src/server/scripts/World/npcs_special.cpp +++ b/src/server/scripts/World/npcs_special.cpp @@ -3031,6 +3031,21 @@ class npc_druid_treant : public CreatureScript } }; +enum WildMushroom +{ + SPELL_WILD_MUSHROOM_BIRTH_VISUAL = 94081 +}; + +struct npc_druid_wild_mushroom : public NullCreatureAI +{ + npc_druid_wild_mushroom(Creature* creature) : NullCreatureAI(creature) { } + + void JustAppeared() override + { + DoCastSelf(SPELL_WILD_MUSHROOM_BIRTH_VISUAL); + } +}; + enum WhackAGnoll { NPC_DARKMOON_FAIRE_GNOLL = 54444, @@ -3173,5 +3188,6 @@ void AddSC_npcs_special() new npc_bountiful_table(); RegisterCreatureAI(npc_mage_orb); new npc_druid_treant(); + RegisterCreatureAI(npc_druid_wild_mushroom); RegisterCreatureAI(npc_darkmoon_island_gnoll); }