diff --git a/src/game/Level3.cpp b/src/game/Level3.cpp index 83f1ad0be..1b9f35566 100644 --- a/src/game/Level3.cpp +++ b/src/game/Level3.cpp @@ -4402,8 +4402,10 @@ bool ChatHandler::HandleResetLevelCommand(const char * args) // reset level to summoned pet Pet* pet = player->GetPet(); if(pet && pet->getPetType()==SUMMON_PET) + { pet->InitStatsForLevel(1); - + pet->InitTalentForLevel(); + } return true; } @@ -4515,13 +4517,6 @@ bool ChatHandler::HandleResetTalentsCommand(const char * args) else player = getSelectedPlayer(); - if(!player && !playerGUID) - { - SendSysMessage(LANG_NO_CHAR_SELECTED); - SetSentErrorMessage(true); - return false; - } - if(player) { player->resetTalents(true); @@ -4530,14 +4525,33 @@ bool ChatHandler::HandleResetTalentsCommand(const char * args) if(m_session->GetPlayer()!=player) PSendSysMessage(LANG_RESET_TALENTS_ONLINE,player->GetName()); + return true; } - else + else if (playerGUID) { CharacterDatabase.PExecute("UPDATE characters SET at_login = at_login | '%u' WHERE guid = '%u'",uint32(AT_LOGIN_RESET_TALENTS), GUID_LOPART(playerGUID) ); PSendSysMessage(LANG_RESET_TALENTS_OFFLINE,pName); + return true; + } + // Try reset talenents as Hunter Pet + Creature* creature = getSelectedCreature(); + if (creature && creature->isPet() && ((Pet *)creature)->getPetType() == HUNTER_PET) + { + ((Pet *)creature)->resetTalents(true); + Unit *owner = creature->GetOwner(); + if (owner && owner->GetTypeId() == TYPEID_PLAYER) + { + player = (Player *)owner; + ChatHandler(player).SendSysMessage(LANG_RESET_TALENTS); + if(m_session->GetPlayer()!=player) + PSendSysMessage(LANG_RESET_TALENTS_ONLINE,player->GetName()); + } + return true; } - return true; + SendSysMessage(LANG_NO_CHAR_SELECTED); + SetSentErrorMessage(true); + return false; } bool ChatHandler::HandleResetAllCommand(const char * args) diff --git a/src/game/Pet.cpp b/src/game/Pet.cpp index df8042603..8c92cd6ec 100644 --- a/src/game/Pet.cpp +++ b/src/game/Pet.cpp @@ -53,6 +53,7 @@ Pet::Pet(PetType type) : Creature() m_resetTalentsCost = 0; m_resetTalentsTime = 0; + m_usedTalentCount = 0; m_auraUpdateMask = 0; @@ -716,6 +717,7 @@ void Pet::GivePetLevel(uint32 level) return; InitStatsForLevel(level); + InitTalentForLevel(); } bool Pet::CreateBaseAtCreature(Creature* creature) @@ -1291,22 +1293,42 @@ bool Pet::addSpell(uint16 spell_id, uint16 active, PetSpellState state, PetSpell else newspell->active = active; - uint32 chainstart = spellmgr.GetFirstSpellInChain(spell_id); - - for (PetSpellMap::iterator itr = m_spells.begin(); itr != m_spells.end(); ++itr) + // talent: unlearn all other talent ranks (high and low) + if(TalentSpellPos const* talentPos = GetTalentSpellPos(spell_id)) { - if(itr->second->state == PETSPELL_REMOVED) continue; - - if(spellmgr.GetFirstSpellInChain(itr->first) == chainstart) + if(TalentEntry const *talentInfo = sTalentStore.LookupEntry( talentPos->talent_id )) { - newspell->active = itr->second->active; + for(int i=0; i <5; ++i) + { + // skip learning spell and no rank spell case + uint32 rankSpellId = talentInfo->RankID[i]; + if(!rankSpellId || rankSpellId==spell_id) + continue; - if(newspell->active == ACT_ENABLED) - ToggleAutocast(itr->first, false); + // skip unknown ranks + if(!HasSpell(rankSpellId)) + continue; + removeSpell(rankSpellId); + } + } + } + else if(uint32 chainstart = spellmgr.GetFirstSpellInChain(spell_id)) + { + for (PetSpellMap::iterator itr = m_spells.begin(); itr != m_spells.end(); ++itr) + { + if(itr->second->state == PETSPELL_REMOVED) continue; - oldspell_id = itr->first; - unlearnSpell(itr->first); - break; + if(spellmgr.GetFirstSpellInChain(itr->first) == chainstart) + { + newspell->active = itr->second->active; + + if(newspell->active == ACT_ENABLED) + ToggleAutocast(itr->first, false); + + oldspell_id = itr->first; + unlearnSpell(itr->first); + break; + } } } @@ -1320,6 +1342,15 @@ bool Pet::addSpell(uint16 spell_id, uint16 active, PetSpellState state, PetSpell if(newspell->active == ACT_ENABLED) ToggleAutocast(spell_id, true); + uint32 talentCost = GetTalentSpellCost(spell_id); + 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; } @@ -1329,19 +1360,17 @@ bool Pet::learnSpell(uint16 spell_id) if (!addSpell(spell_id)) return false; - if(GetOwner()->GetTypeId() == TYPEID_PLAYER) + Unit* owner = GetOwner(); + if(owner && owner->GetTypeId() == TYPEID_PLAYER) { if(!m_loading) { WorldPacket data(SMSG_PET_LEARNED_SPELL, 2); data << uint16(spell_id); - ((Player*)GetOwner())->GetSession()->SendPacket(&data); + ((Player*)owner)->GetSession()->SendPacket(&data); } - } - - Unit* owner = GetOwner(); - if(owner->GetTypeId() == TYPEID_PLAYER) ((Player*)owner)->PetSpellInitialize(); + } return true; } @@ -1399,6 +1428,18 @@ bool Pet::removeSpell(uint16 spell_id) RemoveAurasDueToSpell(spell_id); + uint32 talentCost = 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); + } + return true; } @@ -1478,6 +1519,110 @@ void Pet::CheckLearning(uint32 spellid) } } +bool Pet::resetTalents(bool no_cost) +{ + Unit *owner = GetOwner(); + if (!owner || owner->GetTypeId()!=TYPEID_PLAYER) + return false; + + CreatureInfo const * ci = GetCreatureInfo(); + if(!ci) + return false; + // Check pet talent type + CreatureFamilyEntry const *pet_family = sCreatureFamilyStore.LookupEntry(ci->family); + if(!pet_family || pet_family->petTalentType < 0) + return false; + + Player *player = (Player *)owner; + + uint32 level = getLevel(); + uint32 talentPointsForLevel = GetMaxTalentPointsForLevel(level); + + if (m_usedTalentCount == 0) + { + SetFreeTalentPoints(talentPointsForLevel); + return false; + } + + uint32 cost = 0; + + if(!no_cost) + { + cost = resetTalentsCost(); + + if (player->GetMoney() < cost) + { + player->SendBuyError( BUY_ERR_NOT_ENOUGHT_MONEY, 0, 0, 0); + return false; + } + } + + for (unsigned int i = 0; i < sTalentStore.GetNumRows(); i++) + { + TalentEntry const *talentInfo = sTalentStore.LookupEntry(i); + + if (!talentInfo) continue; + + TalentTabEntry const *talentTabInfo = sTalentTabStore.LookupEntry( talentInfo->TalentTab ); + + if(!talentTabInfo) + continue; + + // unlearn only talents for pets family talent type + if(!((1 << pet_family->petTalentType) & talentTabInfo->petTalentMask)) + continue; + + for (int j = 0; j < 5; j++) + { + for(PetSpellMap::iterator itr = m_spells.begin(); itr != m_spells.end();) + { + if(itr->second->state == PETSPELL_REMOVED) + { + ++itr; + continue; + } + // remove learned spells (all ranks) + uint32 itrFirstId = spellmgr.GetFirstSpellInChain(itr->first); + + // unlearn if first rank is talent or learned by talent + if (itrFirstId == talentInfo->RankID[j] || spellmgr.IsSpellLearnToSpell(talentInfo->RankID[j],itrFirstId)) + { + removeSpell(itr->first); + itr = m_spells.begin(); + continue; + } + else + ++itr; + } + } + } + + SetFreeTalentPoints(talentPointsForLevel); + + if(!no_cost) + { + player->ModifyMoney(-(int32)cost); + + m_resetTalentsCost = cost; + m_resetTalentsTime = time(NULL); + } + player->PetSpellInitialize(); + return true; +} + +void Pet::InitTalentForLevel() +{ + uint32 level = getLevel(); + uint32 talentPointsForLevel = GetMaxTalentPointsForLevel(level); + // Reset talents in case low level (on level down) or wrong points for level (hunter can unlearn TP increase talent) + if(talentPointsForLevel == 0 || m_usedTalentCount > talentPointsForLevel) + { + // Remove all talent points + resetTalents(true); + } + SetFreeTalentPoints(talentPointsForLevel - m_usedTalentCount); +} + uint32 Pet::resetTalentsCost() const { uint32 days = (sWorld.GetGameTime() - m_resetTalentsTime)/DAY; @@ -1496,6 +1641,15 @@ uint32 Pet::resetTalentsCost() const return (m_resetTalentsCost + 1*GOLD > 10*GOLD ? 10*GOLD : m_resetTalentsCost + 1*GOLD); } +uint8 Pet::GetMaxTalentPointsForLevel(uint32 level) +{ + uint8 points = (level >= 20) ? ((level - 16) / 4) : 0; + // Mod points from owner SPELL_AURA_MOD_PET_TALENT_POINTS + if (Unit *owner = GetOwner()) + points+=owner->GetTotalAuraModifier(SPELL_AURA_MOD_PET_TALENT_POINTS); + return points; +} + void Pet::ToggleAutocast(uint32 spellid, bool apply) { if(IsPassiveSpell(spellid)) @@ -1555,7 +1709,8 @@ bool Pet::Create(uint32 guidlow, Map *map, uint32 Entry, uint32 pet_number) bool Pet::HasSpell(uint32 spell) const { - return (m_spells.find(spell) != m_spells.end()); + PetSpellMap::const_iterator itr = m_spells.find((uint16)spell); + return (itr != m_spells.end() && itr->second->state != PETSPELL_REMOVED ); } // Get all passive spells in our skill line diff --git a/src/game/Pet.h b/src/game/Pet.h index 383089280..88e12c4a9 100644 --- a/src/game/Pet.h +++ b/src/game/Pet.h @@ -201,13 +201,18 @@ class Pet : public Creature void InitPetCreateSpells(); void CheckLearning(uint32 spellid); + + bool resetTalents(bool no_cost = false); uint32 resetTalentsCost() const; - uint8 GetMaxTalentPointsForLevel(uint32 level) { return (level >= 20) ? ((level - 16) / 4) : 0; } + void InitTalentForLevel(); + + uint8 GetMaxTalentPointsForLevel(uint32 level); uint8 GetFreeTalentPoints() { return GetByteValue(UNIT_FIELD_BYTES_1, 1); } void SetFreeTalentPoints(uint8 points) { SetByteValue(UNIT_FIELD_BYTES_1, 1, points); } uint32 m_resetTalentsCost; time_t m_resetTalentsTime; + uint32 m_usedTalentCount; uint64 GetAuraUpdateMask() { return m_auraUpdateMask; } void SetAuraUpdateMask(uint8 slot) { m_auraUpdateMask |= (uint64(1) << slot); } diff --git a/src/game/PetHandler.cpp b/src/game/PetHandler.cpp index a79b6f1d5..19a6eac92 100644 --- a/src/game/PetHandler.cpp +++ b/src/game/PetHandler.cpp @@ -487,11 +487,11 @@ void WorldSession::HandlePetUnlearnOpcode(WorldPacket& recvPacket) sLog.outDetail("CMSG_PET_UNLEARN"); uint64 guid; - recvPacket >> guid; + recvPacket >> guid; // Pet guid Pet* pet = _player->GetPet(); - if(!pet || pet->getPetType() != HUNTER_PET || pet->m_spells.size() <= 1) + if(!pet || pet->getPetType() != HUNTER_PET || pet->m_usedTalentCount == 0) return; if(guid != pet->GetGUID()) @@ -506,37 +506,7 @@ void WorldSession::HandlePetUnlearnOpcode(WorldPacket& recvPacket) sLog.outError("WorldSession::HandlePetUnlearnOpcode: object "I64FMTD" is considered pet-like but doesn't have a charminfo!", pet->GetGUID()); return; } - - uint32 cost = pet->resetTalentsCost(); - - if (GetPlayer()->GetMoney() < cost) - { - GetPlayer()->SendBuyError( BUY_ERR_NOT_ENOUGHT_MONEY, 0, 0, 0); - return; - } - - for(PetSpellMap::iterator itr = pet->m_spells.begin(); itr != pet->m_spells.end();) - { - uint32 spell_id = itr->first; // Pet::removeSpell can invalidate iterator at erase NEW spell - ++itr; - //pet->removeSpell(spell_id); - pet->unlearnSpell(spell_id); - } - - for(uint8 i = 0; i < 10; i++) - { - if(charmInfo->GetActionBarEntry(i)->SpellOrAction && charmInfo->GetActionBarEntry(i)->Type == ACT_ENABLED || charmInfo->GetActionBarEntry(i)->Type == ACT_DISABLED) - charmInfo->GetActionBarEntry(i)->SpellOrAction = 0; - } - - // relearn pet passives - pet->LearnPetPassives(); - - pet->m_resetTalentsTime = time(NULL); - pet->m_resetTalentsCost = cost; - GetPlayer()->ModifyMoney(-(int32)cost); - - GetPlayer()->PetSpellInitialize(); + pet->resetTalents(); } void WorldSession::HandlePetSpellAutocastOpcode( WorldPacket& recvPacket ) @@ -809,7 +779,4 @@ void WorldSession::HandlePetLearnTalent( WorldPacket & recv_data ) // learn! (other talent ranks will unlearned at learning) pet->learnSpell(spellid); sLog.outDetail("TalentID: %u Rank: %u Spell: %u\n", talent_id, requested_rank, spellid); - - // update free talent points - pet->SetFreeTalentPoints(CurTalentPoints - 1); } diff --git a/src/game/SpellAuraDefines.h b/src/game/SpellAuraDefines.h index 5d401b2f2..d9f0892fb 100644 --- a/src/game/SpellAuraDefines.h +++ b/src/game/SpellAuraDefines.h @@ -187,8 +187,8 @@ enum AuraType SPELL_AURA_MOD_BASE_RESISTANCE_PCT = 142, SPELL_AURA_MOD_RESISTANCE_EXCLUSIVE = 143, SPELL_AURA_SAFE_FALL = 144, - SPELL_AURA_CHARISMA = 145, - SPELL_AURA_PERSUADED = 146, + SPELL_AURA_MOD_PET_TALENT_POINTS = 145, + SPELL_AURA_ALLOW_TAME_PET_TYPE = 146, SPELL_AURA_ADD_CREATURE_IMMUNITY = 147, SPELL_AURA_RETAIN_COMBO_POINTS = 148, SPELL_AURA_RESIST_PUSHBACK = 149, // Resist Pushback diff --git a/src/game/SpellAuras.cpp b/src/game/SpellAuras.cpp index 08fef1f30..c9b283fdc 100644 --- a/src/game/SpellAuras.cpp +++ b/src/game/SpellAuras.cpp @@ -195,8 +195,8 @@ pAuraHandler AuraHandler[TOTAL_AURAS]= &Aura::HandleAuraModBaseResistancePCT, //142 SPELL_AURA_MOD_BASE_RESISTANCE_PCT &Aura::HandleAuraModResistanceExclusive, //143 SPELL_AURA_MOD_RESISTANCE_EXCLUSIVE &Aura::HandleNoImmediateEffect, //144 SPELL_AURA_SAFE_FALL implemented in WorldSession::HandleMovementOpcodes - &Aura::HandleUnused, //145 SPELL_AURA_CHARISMA obsolete? - &Aura::HandleUnused, //146 SPELL_AURA_PERSUADED obsolete? + &Aura::HandleAuraModPetTalentsPoints, //145 SPELL_AURA_MOD_PET_TALENT_POINTS + &Aura::HandleNoImmediateEffect, //146 SPELL_AURA_ALLOW_TAME_PET_TYPE &Aura::HandleNULL, //147 SPELL_AURA_ADD_CREATURE_IMMUNITY &Aura::HandleAuraRetainComboPoints, //148 SPELL_AURA_RETAIN_COMBO_POINTS &Aura::HandleNoImmediateEffect, //149 SPELL_AURA_RESIST_PUSHBACK @@ -3122,6 +3122,16 @@ void Aura::HandleModPossessPet(bool apply, bool Real) } } +void Aura::HandleAuraModPetTalentsPoints(bool Apply, bool Real) +{ + if(!Real) + return; + + // Recalculate pet tlaent points + if (Pet *pet=m_target->GetPet()) + pet->InitTalentForLevel(); +} + void Aura::HandleModCharm(bool apply, bool Real) { if(!Real) diff --git a/src/game/SpellAuras.h b/src/game/SpellAuras.h index 66ca5bc77..b3718e3eb 100644 --- a/src/game/SpellAuras.h +++ b/src/game/SpellAuras.h @@ -112,6 +112,7 @@ class MANGOS_DLL_SPEC Aura void HandlePeriodicTriggerSpell(bool Apply, bool Real); void HandlePeriodicEnergize(bool Apply, bool Real); void HandleAuraModResistanceExclusive(bool Apply, bool Real); + void HandleAuraModPetTalentsPoints(bool Apply, bool Real); void HandleModStealth(bool Apply, bool Real); void HandleInvisibility(bool Apply, bool Real); void HandleInvisibilityDetect(bool Apply, bool Real); diff --git a/src/game/SpellEffects.cpp b/src/game/SpellEffects.cpp index 09697eb02..2fd902357 100644 --- a/src/game/SpellEffects.cpp +++ b/src/game/SpellEffects.cpp @@ -4152,6 +4152,7 @@ void Spell::EffectSummonPet(uint32 i) NewSummon->InitStatsForLevel(petlevel); NewSummon->InitPetCreateSpells(); + NewSummon->InitTalentForLevel(); if(NewSummon->getPetType()==SUMMON_PET) { diff --git a/src/game/Unit.cpp b/src/game/Unit.cpp index 37e9c8541..92575b6c2 100644 --- a/src/game/Unit.cpp +++ b/src/game/Unit.cpp @@ -11103,7 +11103,6 @@ Pet* Unit::CreateTamedPetFrom(Creature* creatureTarget,uint32 spell_id) pet->SetUInt32Value(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE); uint32 level = (creatureTarget->getLevel() < (getLevel() - 5)) ? (getLevel() - 5) : creatureTarget->getLevel(); - pet->SetFreeTalentPoints(pet->GetMaxTalentPointsForLevel(level)); if(!pet->InitStatsForLevel(level)) { @@ -11116,6 +11115,7 @@ Pet* Unit::CreateTamedPetFrom(Creature* creatureTarget,uint32 spell_id) // this enables pet details window (Shift+P) pet->AIM_Initialize(); pet->InitPetCreateSpells(); + pet->InitTalentForLevel(); pet->SetHealth(pet->GetMaxHealth()); return pet; diff --git a/src/shared/revision_nr.h b/src/shared/revision_nr.h index 2a3210a62..c9cdb24bc 100644 --- a/src/shared/revision_nr.h +++ b/src/shared/revision_nr.h @@ -1,4 +1,4 @@ #ifndef __REVISION_NR_H__ #define __REVISION_NR_H__ - #define REVISION_NR "7124" + #define REVISION_NR "7125" #endif // __REVISION_NR_H__