diff --git a/src/server/database/Database/Implementation/CharacterDatabase.cpp b/src/server/database/Database/Implementation/CharacterDatabase.cpp index b80b851d95..db4217f222 100644 --- a/src/server/database/Database/Implementation/CharacterDatabase.cpp +++ b/src/server/database/Database/Implementation/CharacterDatabase.cpp @@ -620,6 +620,10 @@ void CharacterDatabaseConnection::DoPrepareStatements() 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_SEL_PET_SPELLS, "SELECT guid, spell, active FROM pet_spell WHERE guid IN (SELECT PetNumber FROM character_pet WHERE Guid = ?)", CONNECTION_ASYNC); + PrepareStatement(CHAR_DEL_PET_SPELLS, "DELETE FROM pet_spell WHERE guid = ?", CONNECTION_ASYNC); + PrepareStatement(CHAR_INS_PET_SPELL, "INSERT INTO pet_spell (guid, spell, active) VALUES (?, ?, ?)", CONNECTION_BOTH); + 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, Talents FROM character_pet WHERE Guid = ? ORDER BY Slot", CONNECTION_ASYNC); diff --git a/src/server/database/Database/Implementation/CharacterDatabase.h b/src/server/database/Database/Implementation/CharacterDatabase.h index 39e49dc254..d0eb3b7bfb 100644 --- a/src/server/database/Database/Implementation/CharacterDatabase.h +++ b/src/server/database/Database/Implementation/CharacterDatabase.h @@ -523,6 +523,10 @@ enum CharacterDatabaseStatements : uint32 CHAR_DEL_PET_AURAS, CHAR_INS_PET_AURA, + CHAR_SEL_PET_SPELLS, + CHAR_DEL_PET_SPELLS, + CHAR_INS_PET_SPELL, + CHAR_SEL_PET_SPELL_COOLDOWNS, CHAR_DEL_PET_SPELL_COOLDOWNS, CHAR_INS_PET_SPELL_COOLDOWN, diff --git a/src/server/game/Entities/Creature/TemporarySummon/NewPet.cpp b/src/server/game/Entities/Creature/TemporarySummon/NewPet.cpp index 5c772f8a11..13e0653ade 100644 --- a/src/server/game/Entities/Creature/TemporarySummon/NewPet.cpp +++ b/src/server/game/Entities/Creature/TemporarySummon/NewPet.cpp @@ -77,6 +77,19 @@ bool NewPet::HandlePreSummonActions(Unit const* summoner, uint8 creatureLevel, u GetCharmInfo()->LoadPetActionBar(playerPetData->ActionBar, this); + for (auto const& itr : playerPetData->SpellStates) + { + if (!HasSpell(itr.SpellId)) + continue; + + SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(itr.SpellId); + if (!spellInfo) + continue; + + if (itr.ActiveState == ACT_ENABLED || itr.ActiveState == ACT_DISABLED) + ToggleAutocast(spellInfo, itr.ActiveState == ACT_ENABLED); + } + for (auto const& itr : playerPetData->Cooldowns) GetSpellHistory()->AddCooldown(itr.SpellId, 0, itr.CooldownEnd, itr.CategoryId, itr.CategoryEnd); @@ -331,6 +344,17 @@ void NewPet::UpdatePlayerPetData(PlayerPetData* petData) StoreAppliedAuras(petData); } + petData->SpellStates.clear(); + for (auto const& itr : _spells) + { + if (itr.second.Active == ACT_ENABLED || itr.second.Active == ACT_DISABLED) + { + PlayerPetDataSpellState& spellState = petData->SpellStates.emplace_back(); + spellState.SpellId = itr.first; + spellState.ActiveState = itr.second.Active; + } + } + if (petData->Status != PlayerPetDataStatus::New) petData->Status = PlayerPetDataStatus::Changed; } diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 6910a4e896..dac7e7c40a 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -16927,6 +16927,7 @@ bool Player::LoadFromDB(ObjectGuid guid, CharacterDatabaseQueryHolder const& hol _LoadPetCooldowns(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_PET_COOLDOWNS)); _LoadPetAuras(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_PET_AURAS)); _LoadPetDeclinedNames(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_PET_DECLINED_NAMES)); + _LoadPetSpellStates(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_PET_SPELL_STATES)); // after spell load, learn rewarded spell if need also _LoadQuestStatus(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_QUEST_STATUS)); @@ -17874,6 +17875,28 @@ void Player::_LoadPetDeclinedNames(PreparedQueryResult result) } while (result->NextRow()); } +void Player::_LoadPetSpellStates(PreparedQueryResult result) +{ + // "SELECT guid, spell, active FROM pet_spell WHERE guid IN (SELECT PetNumber FROM character_pet WHERE Guid = ?)" + if (!result) + return; + + do + { + Field* fields = result->Fetch(); + + uint32 petNumber = fields[0].GetUInt32(); + PlayerPetData* petData = GetPlayerPetDataByPetNumber(petNumber); + if (!petData) + continue; + + PlayerPetDataSpellState& state = petData->SpellStates.emplace_back(); + state.SpellId = fields[1].GetUInt32(); + state.ActiveState = fields[2].GetUInt8(); + + } while (result->NextRow()); +} + void Player::SendPetSpellsMessage(NewPet* pet, bool remove /*= false*/) { CharmInfo* charmInfo = pet->GetCharmInfo(); @@ -20146,6 +20169,20 @@ void Player::_SavePets(CharacterDatabaseTransaction& trans) trans->Append(stmt); } + // Pet spell states + stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_PET_SPELLS); + stmt->setUInt32(0, petData->PetNumber); + trans->Append(stmt); + + for (auto const& itr : petData->SpellStates) + { + stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_PET_SPELL); + stmt->setUInt32(0, petData->PetNumber); + stmt->setUInt32(1, itr.SpellId); + stmt->setUInt8(2, itr.ActiveState); + trans->Append(stmt); + } + // Declined names stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_PET_DECLINEDNAME); stmt->setUInt32(0, petData->PetNumber); @@ -20163,6 +20200,7 @@ void Player::_SavePets(CharacterDatabaseTransaction& trans) } } + // Deleted pets for (uint32 deletedPetNumber : _deletedPlayerPetDataSet) { stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_PET); @@ -20180,6 +20218,10 @@ void Player::_SavePets(CharacterDatabaseTransaction& trans) stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_PET_DECLINEDNAME); stmt->setUInt32(0, deletedPetNumber); trans->Append(stmt); + + stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_PET_SPELLS); + stmt->setUInt32(0, deletedPetNumber); + trans->Append(stmt); } _deletedPlayerPetDataSet.clear(); diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index 9356fc5c40..8a1407e76a 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -804,7 +804,8 @@ enum PlayerLoginQueryIndex PLAYER_LOGIN_QUERY_LOAD_PET_COOLDOWNS = 38, PLAYER_LOGIN_QUERY_LOAD_PET_AURAS = 39, PLAYER_LOGIN_QUERY_LOAD_PET_DECLINED_NAMES = 40, - PLAYER_LOGIN_QUERY_LOAD_LFG_REWARD_STATUS = 41, + PLAYER_LOGIN_QUERY_LOAD_PET_SPELL_STATES = 41, + PLAYER_LOGIN_QUERY_LOAD_LFG_REWARD_STATUS = 42, MAX_PLAYER_LOGIN_QUERY }; @@ -2468,6 +2469,7 @@ class TC_GAME_API Player : public Unit, public GridObject void _LoadPetCooldowns(PreparedQueryResult result); void _LoadPetAuras(PreparedQueryResult result); void _LoadPetDeclinedNames(PreparedQueryResult result); + void _LoadPetSpellStates(PreparedQueryResult result); /*********************************************************/ /*** SAVE SYSTEM ***/ diff --git a/src/server/game/Entities/Player/PlayerPetData.h b/src/server/game/Entities/Player/PlayerPetData.h index 674b46d188..cc9f8dd791 100644 --- a/src/server/game/Entities/Player/PlayerPetData.h +++ b/src/server/game/Entities/Player/PlayerPetData.h @@ -22,6 +22,12 @@ #include "PetDefines.h" #include "UnitDefines.h" +struct PlayerPetDataSpellState +{ + uint32 SpellId = 0; + uint8 ActiveState = 0; +}; + struct PlayerPetDataAura { std::array Amount = { }; @@ -59,6 +65,7 @@ struct PlayerPetData std::string Talents; std::vector Cooldowns; std::vector Auras; + std::vector SpellStates; Optional DeclinedNames; PlayerPetDataStatus Status = PlayerPetDataStatus::New; }; diff --git a/src/server/game/Handlers/CharacterHandler.cpp b/src/server/game/Handlers/CharacterHandler.cpp index 3ac8254212..fdb1c63140 100644 --- a/src/server/game/Handlers/CharacterHandler.cpp +++ b/src/server/game/Handlers/CharacterHandler.cpp @@ -245,6 +245,10 @@ bool LoginQueryHolder::Initialize() stmt->setUInt32(0, lowGuid); res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_PET_DECLINED_NAMES, stmt); + stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PET_SPELLS); + stmt->setUInt32(0, lowGuid); + res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_PET_SPELL_STATES, stmt); + stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_REWARDSTATUS_LFG); stmt->setUInt32(0, lowGuid); res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_LFG_REWARD_STATUS, stmt); diff --git a/src/server/scripts/Spells/spell_pet.cpp b/src/server/scripts/Spells/spell_pet.cpp index 24b5eded2e..f22e263842 100644 --- a/src/server/scripts/Spells/spell_pet.cpp +++ b/src/server/scripts/Spells/spell_pet.cpp @@ -70,9 +70,9 @@ enum DKPetCalculate enum ShamanPetCalculate { - SPELL_FERAL_SPIRIT_PET_UNK_01 = 35674, - SPELL_FERAL_SPIRIT_PET_UNK_02 = 35675, - SPELL_FERAL_SPIRIT_PET_UNK_03 = 35676, + SPELL_FERAL_SPIRIT_PET_SCALING_01 = 35674, // Serverside spell + SPELL_FERAL_SPIRIT_PET_SCALING_02 = 35675, // Serverside spell + SPELL_FERAL_SPIRIT_PET_SCALING_03 = 35676, // Serverside spell SPELL_FERAL_SPIRIT_PET_SCALING_04 = 61783, }; @@ -85,6 +85,7 @@ enum MiscPetCalculate enum WarlockSpellIconId { SPELL_ICON_ID_GLYPH_OF_VOID_WALKER = 217, + SPELL_ICON_ID_GLYPH_OF_FERAL_SPIRIT = 3081 }; class spell_warl_pet_scaling_01 : public AuraScript @@ -800,11 +801,7 @@ class spell_dk_rune_weapon_scaling_02 : public AuraScript { canBeRecalculated = true; - NewGuardian* guardian = GetUnitOwner()->ToNewGuardian(); - if (!guardian) - return; - - Unit* summoner = guardian->GetInternalSummoner(); + Unit* summoner = GetUnitOwner()->GetSummoner(); if (!summoner) return; diff --git a/src/server/scripts/Spells/spell_shaman.cpp b/src/server/scripts/Spells/spell_shaman.cpp index 9ab711f4fa..332985d4eb 100644 --- a/src/server/scripts/Spells/spell_shaman.cpp +++ b/src/server/scripts/Spells/spell_shaman.cpp @@ -85,6 +85,7 @@ enum ShamanSpells SPELL_SHAMAN_RIPTIDE = 61295, SPELL_SHAMAN_SATED = 57724, SPELL_SHAMAN_SEARING_FLAMES_DAMAGE = 77661, + SPELL_SHAMAN_SPIRIT_HUNT_HEAL = 58879, SPELL_SHAMAN_STORM_EARTH_AND_FIRE = 51483, SPELL_SHAMAN_TELLURIC_CURRENTS = 82987, SPELL_SHAMAN_TOTEM_EARTHBIND_EARTHGRAB = 64695, @@ -1775,6 +1776,34 @@ class spell_sha_clearcasting : public AuraScript } }; +// 58877 - Spirit Hunt +class spell_sha_spirit_hunt : public AuraScript +{ + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_SHAMAN_SPIRIT_HUNT_HEAL }); + } + + bool CheckProc(ProcEventInfo& eventInfo) + { + return eventInfo.GetDamageInfo(); + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + int32 bp = CalculatePct(eventInfo.GetDamageInfo()->GetDamage(), aurEff->GetAmount()); + if (Unit* summoner = GetTarget()->GetSummoner()) + GetTarget()->CastSpell(summoner, SPELL_SHAMAN_SPIRIT_HUNT_HEAL, CastSpellExtraArgs(aurEff).AddSpellBP0(bp)); + } + + void Register() override + { + DoCheckProc.Register(&spell_sha_spirit_hunt::CheckProc); + OnEffectProc.Register(&spell_sha_spirit_hunt::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } +}; + void AddSC_shaman_spell_scripts() { RegisterSpellScript(spell_sha_ancestral_awakening); @@ -1820,6 +1849,7 @@ void AddSC_shaman_spell_scripts() RegisterSpellScript(spell_sha_resurgence); RegisterSpellScript(spell_sha_rolling_thunder); RegisterSpellScript(spell_sha_searing_bolt); + RegisterSpellScript(spell_sha_spirit_hunt); RegisterSpellScript(spell_sha_static_shock); RegisterSpellScript(spell_sha_telluric_currents); RegisterSpellScript(spell_sha_thunderstorm); diff --git a/src/server/scripts/World/npcs_special.cpp b/src/server/scripts/World/npcs_special.cpp index e40cee8aeb..626c946b4c 100644 --- a/src/server/scripts/World/npcs_special.cpp +++ b/src/server/scripts/World/npcs_special.cpp @@ -3055,6 +3055,26 @@ struct npc_darkmoon_island_gnoll : public ScriptedAI bool _hit; }; +enum ShamanSpiritWolf +{ + SPELL_SPAWN_SMOKE = 36747, + SPELL_FERAL_SPIRIT_SCALING_4 = 61783, + SPELL_SPIRIT_HUNT = 58877 +}; + +struct npc_shaman_spirit_wolf : public PetAI +{ + npc_shaman_spirit_wolf(Creature* creature) : PetAI(creature) { } + + void JustAppeared() override + { + PetAI::JustAppeared(); + DoCastSelf(SPELL_SPAWN_SMOKE); + DoCastSelf(SPELL_FERAL_SPIRIT_SCALING_4); // @todo: this should be updated like other scalings + DoCastSelf(SPELL_SPIRIT_HUNT); + } +}; + void AddSC_npcs_special() { new npc_air_force_bots(); @@ -3084,4 +3104,5 @@ void AddSC_npcs_special() RegisterCreatureAI(npc_druid_treant); RegisterCreatureAI(npc_druid_wild_mushroom); RegisterCreatureAI(npc_darkmoon_island_gnoll); + RegisterCreatureAI(npc_shaman_spirit_wolf); }