diff options
Diffstat (limited to 'src/game/Pet.cpp')
-rw-r--r-- | src/game/Pet.cpp | 1776 |
1 files changed, 902 insertions, 874 deletions
diff --git a/src/game/Pet.cpp b/src/game/Pet.cpp index 029142ea92f..3eb12b190bd 100644 --- a/src/game/Pet.cpp +++ b/src/game/Pet.cpp @@ -1,7 +1,7 @@ /* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * - * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> + * Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/> * * 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 @@ -21,12 +21,10 @@ #include "Common.h" #include "Database/DatabaseEnv.h" #include "Log.h" -#include "WorldSession.h" #include "WorldPacket.h" #include "ObjectMgr.h" #include "SpellMgr.h" #include "Pet.h" -#include "MapManager.h" #include "Formulas.h" #include "SpellAuras.h" #include "CreatureAI.h" @@ -41,73 +39,22 @@ char const* petTypeSuffix[MAX_PET_TYPE] = "'s Companion" // MINI_PET }; -//numbers represent minutes * 100 while happy (you get 100 loyalty points per min while happy) -uint32 const LevelUpLoyalty[6] = -{ - 5500, - 11500, - 17000, - 23500, - 31000, - 39500, -}; - -uint32 const LevelStartLoyalty[6] = -{ - 2000, - 4500, - 7000, - 10000, - 13500, - 17500, -}; +#define PET_XP_FACTOR 0.1f -Pet::Pet(PetType type) : Creature() +Pet::Pet(Player *owner, PetType type) : Guardian(NULL, owner), +m_petType(type), m_removed(false), m_happinessTimer(7500), m_duration(0), +m_resetTalentsCost(0), m_resetTalentsTime(0), m_usedTalentCount(0), m_auraRaidUpdateMask(0), m_loading(false), +m_declinedname(NULL), m_owner(owner) { - m_isPet = true; + m_summonMask |= SUMMON_MASK_PET; + if(type == HUNTER_PET) + m_summonMask |= SUMMON_MASK_HUNTER_PET; m_name = "Pet"; - m_petType = type; - - m_removed = false; m_regenTimer = 4000; - m_happinessTimer = 7500; - m_loyaltyTimer = 12000; - m_duration = 0; - m_bonusdamage = 0; - - m_loyaltyPoints = 0; - m_TrainingPoints = 0; - m_resetTalentsCost = 0; - m_resetTalentsTime = 0; - - m_auraUpdateMask = 0; - - // pets always have a charminfo, even if they are not actually charmed - CharmInfo* charmInfo = InitCharmInfo(); - - if(type == MINI_PET || type == POSSESSED_PET) // always passive - SetReactState(REACT_PASSIVE); - else if(type == GUARDIAN_PET) // always aggressive - SetReactState(REACT_AGGRESSIVE); - - m_spells.clear(); - m_Auras.clear(); - m_CreatureSpellCooldowns.clear(); - m_CreatureCategoryCooldowns.clear(); - m_autospells.clear(); - m_declinedname = NULL; - //m_isActive = true; } Pet::~Pet() { - if(m_uint32Values) // only for fully created Object - { - for (PetSpellMap::iterator i = m_spells.begin(); i != m_spells.end(); ++i) - delete i->second; - ObjectAccessor::Instance().RemoveObject(this); - } - delete m_declinedname; } @@ -115,9 +62,11 @@ void Pet::AddToWorld() { ///- Register the pet for guid lookup if(!IsInWorld()) - { + { + ///- Register the pet for guid lookup ObjectAccessor::Instance().AddObject(this); Unit::AddToWorld(); + AIM_Initialize(); } } @@ -126,32 +75,42 @@ void Pet::RemoveFromWorld() ///- Remove the pet from the accessor if(IsInWorld()) { - ObjectAccessor::Instance().RemoveObject(this); ///- Don't call the function for Creature, normal mobs + totems go in a different storage Unit::RemoveFromWorld(); + ObjectAccessor::Instance().RemoveObject(this); } } -bool Pet::LoadPetFromDB( Unit* owner, uint32 petentry, uint32 petnumber, bool current ) +bool Pet::LoadPetFromDB( Player* owner, uint32 petentry, uint32 petnumber, bool current ) { + m_loading = true; + uint32 ownerid = owner->GetGUIDLow(); QueryResult *result; - if(petnumber) - // known petnumber entry 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 - result = CharacterDatabase.PQuery("SELECT id, entry, owner, modelid, level, exp, Reactstate, loyaltypoints, loyalty, trainpoint, slot, name, renamed, curhealth, curmana, curhappiness, abdata, TeachSpelldata, savetime, resettalents_cost, resettalents_time, CreatedBySpell, PetType FROM character_pet WHERE owner = '%u' AND id = '%u'",ownerid, petnumber); - else if(current) - // current pet (slot 0) 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 - result = CharacterDatabase.PQuery("SELECT id, entry, owner, modelid, level, exp, Reactstate, loyaltypoints, loyalty, trainpoint, slot, name, renamed, curhealth, curmana, curhappiness, abdata, TeachSpelldata, savetime, resettalents_cost, resettalents_time, CreatedBySpell, PetType FROM character_pet WHERE owner = '%u' AND slot = '0'",ownerid ); - else if(petentry) + if (petnumber) + // known petnumber entry 0 1 2(?) 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 + result = CharacterDatabase.PQuery("SELECT id, entry, owner, modelid, level, exp, Reactstate, slot, name, renamed, curhealth, curmana, curhappiness, abdata, savetime, resettalents_cost, resettalents_time, CreatedBySpell, PetType " + "FROM character_pet WHERE owner = '%u' AND id = '%u'", + ownerid, petnumber); + else if (current) + // current pet (slot 0) 0 1 2(?) 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 + result = CharacterDatabase.PQuery("SELECT id, entry, owner, modelid, level, exp, Reactstate, slot, name, renamed, curhealth, curmana, curhappiness, abdata, savetime, resettalents_cost, resettalents_time, CreatedBySpell, PetType " + "FROM character_pet WHERE owner = '%u' AND slot = '%u'", + ownerid, PET_SAVE_AS_CURRENT ); + else if (petentry) // known petentry entry (unique for summoned pet, but non unique for hunter pet (only from current or not stabled pets) - // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 - result = CharacterDatabase.PQuery("SELECT id, entry, owner, modelid, level, exp, Reactstate, loyaltypoints, loyalty, trainpoint, slot, name, renamed, curhealth, curmana, curhappiness, abdata, TeachSpelldata, savetime, resettalents_cost, resettalents_time, CreatedBySpell, PetType FROM character_pet WHERE owner = '%u' AND entry = '%u' AND (slot = '0' OR slot = '3') ",ownerid, petentry ); + // 0 1 2(?) 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 + result = CharacterDatabase.PQuery("SELECT id, entry, owner, modelid, level, exp, Reactstate, slot, name, renamed, curhealth, curmana, curhappiness, abdata, savetime, resettalents_cost, resettalents_time, CreatedBySpell, PetType " + "FROM character_pet WHERE owner = '%u' AND entry = '%u' AND (slot = '%u' OR slot > '%u') ", + ownerid, petentry,PET_SAVE_AS_CURRENT,PET_SAVE_LAST_STABLE_SLOT); else // any current or other non-stabled pet (for hunter "call pet") - // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 - result = CharacterDatabase.PQuery("SELECT id, entry, owner, modelid, level, exp, Reactstate, loyaltypoints, loyalty, trainpoint, slot, name, renamed, curhealth, curmana, curhappiness, abdata, TeachSpelldata, savetime, resettalents_cost, resettalents_time, CreatedBySpell, PetType FROM character_pet WHERE owner = '%u' AND (slot = '0' OR slot = '3') ",ownerid); + // 0 1 2(?) 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 + result = CharacterDatabase.PQuery("SELECT id, entry, owner, modelid, level, exp, Reactstate, slot, name, renamed, curhealth, curmana, curhappiness, abdata, savetime, resettalents_cost, resettalents_time, CreatedBySpell, PetType " + "FROM character_pet WHERE owner = '%u' AND (slot = '%u' OR slot > '%u') ", + ownerid,PET_SAVE_AS_CURRENT,PET_SAVE_LAST_STABLE_SLOT); if(!result) return false; @@ -160,28 +119,47 @@ bool Pet::LoadPetFromDB( Unit* owner, uint32 petentry, uint32 petnumber, bool cu // update for case of current pet "slot = 0" petentry = fields[1].GetUInt32(); - if(!petentry) + if (!petentry) { delete result; return false; } - uint32 summon_spell_id = fields[21].GetUInt32(); + uint32 summon_spell_id = fields[17].GetUInt32(); SpellEntry const* spellInfo = sSpellStore.LookupEntry(summon_spell_id); bool is_temporary_summoned = spellInfo && GetSpellDuration(spellInfo) > 0; // check temporary summoned pets like mage water elemental - if(current && is_temporary_summoned) + if (current && is_temporary_summoned) + { + delete result; + return false; + } + + PetType pet_type = PetType(fields[18].GetUInt8()); + if(pet_type==HUNTER_PET) + { + CreatureInfo const* creatureInfo = objmgr.GetCreatureTemplate(petentry); + if(!creatureInfo || !creatureInfo->isTameable(owner->CanTameExoticPets())) + { + delete result; + return false; + } + } + + uint32 pet_number = fields[0].GetUInt32(); + + if (current && owner->IsPetNeedBeTemporaryUnsummoned()) { + owner->SetTemporaryUnsummonedPetNumber(pet_number); delete result; return false; } Map *map = owner->GetMap(); uint32 guid = objmgr.GenerateLowGuid(HIGHGUID_PET); - uint32 pet_number = fields[0].GetUInt32(); - if(!Create(guid, map, petentry, pet_number)) + if (!Create(guid, map, owner->GetPhaseMask(), petentry, pet_number)) { delete result; return false; @@ -189,181 +167,146 @@ bool Pet::LoadPetFromDB( Unit* owner, uint32 petentry, uint32 petnumber, bool cu float px, py, pz; owner->GetClosePoint(px, py, pz, GetObjectSize(), PET_FOLLOW_DIST, PET_FOLLOW_ANGLE); - Relocate(px, py, pz, owner->GetOrientation()); - if(!IsPositionValid()) + if (!IsPositionValid()) { - sLog.outError("ERROR: Pet (guidlow %d, entry %d) not loaded. Suggested coordinates isn't valid (X: %f Y: %f)", + sLog.outError("Pet (guidlow %d, entry %d) not loaded. Suggested coordinates isn't valid (X: %f Y: %f)", GetGUIDLow(), GetEntry(), GetPositionX(), GetPositionY()); delete result; return false; } - setPetType(PetType(fields[22].GetUInt8())); - SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE,owner->getFaction()); + setPetType(pet_type); + setFaction(owner->getFaction()); SetUInt32Value(UNIT_CREATED_BY_SPELL, summon_spell_id); CreatureInfo const *cinfo = GetCreatureInfo(); - if(cinfo->type == CREATURE_TYPE_CRITTER) + if (cinfo->type == CREATURE_TYPE_CRITTER) { - AIM_Initialize(); map->Add((Creature*)this); delete result; return true; } - if(getPetType()==HUNTER_PET || (getPetType()==SUMMON_PET && cinfo->type == CREATURE_TYPE_DEMON && owner->getClass() == CLASS_WARLOCK)) - m_charmInfo->SetPetNumber(pet_number, true); - else - m_charmInfo->SetPetNumber(pet_number, false); - SetUInt64Value(UNIT_FIELD_SUMMONEDBY, owner->GetGUID()); + + m_charmInfo->SetPetNumber(pet_number, IsPermanentPetFor(owner)); + SetDisplayId(fields[3].GetUInt32()); SetNativeDisplayId(fields[3].GetUInt32()); - uint32 petlevel=fields[4].GetUInt32(); - SetUInt32Value(UNIT_NPC_FLAGS , 0); - SetName(fields[11].GetString()); + uint32 petlevel = fields[4].GetUInt32(); + SetUInt32Value(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_NONE); + SetName(fields[8].GetString()); - switch(getPetType()) + switch (getPetType()) { - case SUMMON_PET: petlevel=owner->getLevel(); - SetUInt32Value(UNIT_FIELD_BYTES_0,2048); + SetUInt32Value(UNIT_FIELD_BYTES_0, 0x800); //class=mage SetUInt32Value(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE); // this enables popup window (pet dismiss, cancel) break; case HUNTER_PET: - SetUInt32Value(UNIT_FIELD_BYTES_0, 0x02020100); - SetByteValue(UNIT_FIELD_BYTES_1, 1, fields[8].GetUInt32()); - SetByteValue(UNIT_FIELD_BYTES_2, 0, SHEATH_STATE_MELEE ); - SetByteValue(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_SANCTUARY | UNIT_BYTE2_FLAG_AURAS | UNIT_BYTE2_FLAG_UNK5 ); - - if(fields[12].GetBool()) - SetByteValue(UNIT_FIELD_BYTES_2, 2, UNIT_RENAME_NOT_ALLOWED); - else - SetByteValue(UNIT_FIELD_BYTES_2, 2, UNIT_RENAME_ALLOWED); + SetUInt32Value(UNIT_FIELD_BYTES_0, 0x02020100); //class=warrior,gender=none,power=focus + SetSheath(SHEATH_STATE_MELEE); + SetByteValue(UNIT_FIELD_BYTES_2, 2, fields[9].GetBool() ? UNIT_RENAME_NOT_ALLOWED : UNIT_RENAME_ALLOWED); SetUInt32Value(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE); // this enables popup window (pet abandon, cancel) - SetTP(fields[9].GetInt32()); - SetMaxPower(POWER_HAPPINESS,GetCreatePowers(POWER_HAPPINESS)); - SetPower( POWER_HAPPINESS,fields[15].GetUInt32()); + SetMaxPower(POWER_HAPPINESS, GetCreatePowers(POWER_HAPPINESS)); + SetPower(POWER_HAPPINESS, fields[12].GetUInt32()); setPowerType(POWER_FOCUS); break; default: - sLog.outError("Pet have incorrect type (%u) for pet loading.",getPetType()); + sLog.outError("Pet have incorrect type (%u) for pet loading.", getPetType()); } - InitStatsForLevel( petlevel); + SetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP, time(NULL)); SetUInt32Value(UNIT_FIELD_PETEXPERIENCE, fields[5].GetUInt32()); - SetUInt64Value(UNIT_FIELD_CREATEDBY, owner->GetGUID()); + SetCreatorGUID(owner->GetGUID()); SetReactState( ReactStates( fields[6].GetUInt8() )); - m_loyaltyPoints = fields[7].GetInt32(); - uint32 savedhealth = fields[13].GetUInt32(); - uint32 savedmana = fields[14].GetUInt32(); + SetCanModifyStats(true); + InitStatsForLevel(petlevel); + + if(getPetType() == SUMMON_PET && !current) //all (?) summon pets come with full health when called, but not when they are current + { + SetHealth(GetMaxHealth()); + SetPower(POWER_MANA, GetMaxPower(POWER_MANA)); + } + else + { + uint32 savedhealth = fields[10].GetUInt32(); + uint32 savedmana = fields[11].GetUInt32(); + SetHealth(savedhealth > GetMaxHealth() ? GetMaxHealth() : savedhealth); + SetPower(POWER_MANA, savedmana > GetMaxPower(POWER_MANA) ? GetMaxPower(POWER_MANA) : savedmana); + } // set current pet as current - if(fields[10].GetUInt32() != 0) + // 0=current + // 1..MAX_PET_STABLES in stable slot + // PET_SAVE_NOT_IN_SLOT(100) = not stable slot (summoning)) + if(fields[7].GetUInt32() != 0) { CharacterDatabase.BeginTransaction(); - CharacterDatabase.PExecute("UPDATE character_pet SET slot = '3' WHERE owner = '%u' AND slot = '0' AND id <> '%u'",ownerid, m_charmInfo->GetPetNumber()); - CharacterDatabase.PExecute("UPDATE character_pet SET slot = '0' WHERE owner = '%u' AND id = '%u'",ownerid, m_charmInfo->GetPetNumber()); + CharacterDatabase.PExecute("UPDATE character_pet SET slot = '%u' WHERE owner = '%u' AND slot = '%u' AND id <> '%u'", + PET_SAVE_NOT_IN_SLOT, ownerid, PET_SAVE_AS_CURRENT, m_charmInfo->GetPetNumber()); + CharacterDatabase.PExecute("UPDATE character_pet SET slot = '%u' WHERE owner = '%u' AND id = '%u'", + PET_SAVE_AS_CURRENT, ownerid, m_charmInfo->GetPetNumber()); CharacterDatabase.CommitTransaction(); } - if(!is_temporary_summoned) + // Send fake summon spell cast - this is needed for correct cooldown application for spells + // Example: 46584 - without this cooldown (which should be set always when pet is loaded) isn't set clientside + // TODO: pets should be summoned from real cast instead of just faking it? + if (GetUInt32Value(UNIT_CREATED_BY_SPELL)) { - // permanent controlled pets store state in DB - Tokens tokens = StrSplit(fields[16].GetString(), " "); - - if(tokens.size() != 20) - { - delete result; - return false; - } - - int index; - Tokens::iterator iter; - for(iter = tokens.begin(), index = 0; index < 10; ++iter, ++index ) - { - m_charmInfo->GetActionBarEntry(index)->Type = atol((*iter).c_str()); - ++iter; - m_charmInfo->GetActionBarEntry(index)->SpellOrAction = atol((*iter).c_str()); - - // patch for old data where some spells have ACT_DECIDE but should have ACT_CAST - // so overwrite old state - SpellEntry const *spellInfo = sSpellStore.LookupEntry(m_charmInfo->GetActionBarEntry(index)->SpellOrAction); - if (spellInfo && spellInfo->AttributesEx & SPELL_ATTR_EX_UNAUTOCASTABLE_BY_PET) m_charmInfo->GetActionBarEntry(index)->Type = ACT_CAST; - } - - //init teach spells - tokens = StrSplit(fields[17].GetString(), " "); - for (iter = tokens.begin(), index = 0; index < 4; ++iter, ++index) - { - uint32 tmp = atol((*iter).c_str()); - - ++iter; - - if(tmp) - AddTeachSpell(tmp, atol((*iter).c_str())); - else - break; - } + WorldPacket data(SMSG_SPELL_GO, (8+8+4+4+2)); + data.append(owner->GetPackGUID()); + data.append(owner->GetPackGUID()); + data << uint8(0); + data << uint32(GetUInt32Value(UNIT_CREATED_BY_SPELL)); + data << uint32(256); // CAST_FLAG_UNKNOWN3 + data << uint32(0); + SendMessageToSet(&data, true); } - // since last save (in seconds) - uint32 timediff = (time(NULL) - fields[18].GetUInt32()); + owner->SetMinion(this, true); + map->Add((Creature*)this); - delete result; + m_resetTalentsCost = fields[15].GetUInt32(); + m_resetTalentsTime = fields[16].GetUInt64(); + InitTalentForLevel(); // set original talents points before spell loading - //load spells/cooldowns/auras - SetCanModifyStats(true); + uint32 timediff = (time(NULL) - fields[14].GetUInt32()); _LoadAuras(timediff); - //init AB - if(is_temporary_summoned) - { - // Temporary summoned pets always have initial spell list at load - InitPetCreateSpells(); - } - else + // load action bar, if data broken will fill later by default spells. + if (!is_temporary_summoned) { + m_charmInfo->LoadPetActionBar(fields[13].GetCppString()); + + _LoadSpells(); + _LoadSpellCooldowns(); LearnPetPassives(); + InitLevelupSpellsForLevel(); CastPetAuras(current); } - if(getPetType() == SUMMON_PET && !current) //all (?) summon pets come with full health when called, but not when they are current - { - SetHealth(GetMaxHealth()); - SetPower(POWER_MANA, GetMaxPower(POWER_MANA)); - } - else - { - SetHealth(savedhealth > GetMaxHealth() ? GetMaxHealth() : savedhealth); - SetPower(POWER_MANA, savedmana > GetMaxPower(POWER_MANA) ? GetMaxPower(POWER_MANA) : savedmana); - } + CleanupActionBar(); // remove unknown spells from action bar after load - AIM_Initialize(); - map->Add((Creature*)this); + delete result; + sLog.outDebug("New Pet has guid %u", GetGUIDLow()); - // Spells should be loaded after pet is added to map, because in CanCast is check on it - _LoadSpells(); - _LoadSpellCooldowns(); + owner->PetSpellInitialize(); - owner->SetPet(this); // in DB stored only full controlled creature - sLog.outDebug("New Pet has guid %u", GetGUIDLow()); + if(owner->GetGroup()) + owner->SetGroupUpdateFlag(GROUP_UPDATE_PET); - if(owner->GetTypeId() == TYPEID_PLAYER) - { - ((Player*)owner)->PetSpellInitialize(); - if(((Player*)owner)->GetGroup()) - ((Player*)owner)->SetGroupUpdateFlag(GROUP_UPDATE_PET); - } + owner->SendTalentsInfoData(true); - if(owner->GetTypeId() == TYPEID_PLAYER && getPetType() == HUNTER_PET) + if(getPetType() == HUNTER_PET) { result = CharacterDatabase.PQuery("SELECT genitive, dative, accusative, instrumental, prepositional FROM character_pet_declinedname WHERE owner = '%u' AND id = '%u'", owner->GetGUIDLow(), GetCharmInfo()->GetPetNumber()); @@ -373,128 +316,121 @@ bool Pet::LoadPetFromDB( Unit* owner, uint32 petentry, uint32 petnumber, bool cu delete m_declinedname; m_declinedname = new DeclinedName; - Field *fields = result->Fetch(); - for(int i = 0; i < MAX_DECLINED_NAME_CASES; ++i) + Field *fields2 = result->Fetch(); + for(uint8 i = 0; i < MAX_DECLINED_NAME_CASES; ++i) { - m_declinedname->name[i] = fields[i].GetCppString(); + m_declinedname->name[i] = fields2[i].GetCppString(); } } } + m_loading = false; + + SynchronizeLevelWithOwner(); return true; } void Pet::SavePetToDB(PetSaveMode mode) { - if(!GetEntry()) + if (!GetEntry()) return; // save only fully controlled creature - if(!isControlled()) + if (!isControlled()) + return; + + // not save not player pets + if(!IS_PLAYER_GUID(GetOwnerGUID())) + return; + + Player* pOwner = (Player*)GetOwner(); + if (!pOwner) return; + // not save pet as current if another pet temporary unsummoned + if (mode == PET_SAVE_AS_CURRENT && pOwner->GetTemporaryUnsummonedPetNumber() && + pOwner->GetTemporaryUnsummonedPetNumber() != m_charmInfo->GetPetNumber()) + { + // pet will lost anyway at restore temporary unsummoned + if(getPetType()==HUNTER_PET) + return; + + // for warlock case + mode = PET_SAVE_NOT_IN_SLOT; + } + uint32 curhealth = GetHealth(); uint32 curmana = GetPower(POWER_MANA); - switch(mode) + // stable and not in slot saves + if(mode > PET_SAVE_AS_CURRENT) { - case PET_SAVE_IN_STABLE_SLOT_1: - case PET_SAVE_IN_STABLE_SLOT_2: - case PET_SAVE_NOT_IN_SLOT: - { - RemoveAllAuras(); - - //only alive hunter pets get auras saved, the others don't - if(!(getPetType() == HUNTER_PET && isAlive())) - m_Auras.clear(); - } - default: - break; + RemoveAllAuras(); } _SaveSpells(); _SaveSpellCooldowns(); _SaveAuras(); - switch(mode) + // current/stable/not_in_slot + if(mode >= PET_SAVE_AS_CURRENT) { - case PET_SAVE_AS_CURRENT: - case PET_SAVE_IN_STABLE_SLOT_1: - case PET_SAVE_IN_STABLE_SLOT_2: - case PET_SAVE_NOT_IN_SLOT: - { - uint32 loyalty =1; - if(getPetType()!=HUNTER_PET) - loyalty = GetLoyaltyLevel(); - - uint32 owner = GUID_LOPART(GetOwnerGUID()); - std::string name = m_name; - CharacterDatabase.escape_string(name); - CharacterDatabase.BeginTransaction(); - // remove current data - CharacterDatabase.PExecute("DELETE FROM character_pet WHERE owner = '%u' AND id = '%u'", owner,m_charmInfo->GetPetNumber() ); - - // prevent duplicate using slot (except PET_SAVE_NOT_IN_SLOT) - if(mode!=PET_SAVE_NOT_IN_SLOT) - CharacterDatabase.PExecute("UPDATE character_pet SET slot = 3 WHERE owner = '%u' AND slot = '%u'", owner, uint32(mode) ); - - // prevent existence another hunter pet in PET_SAVE_AS_CURRENT and PET_SAVE_NOT_IN_SLOT - if(getPetType()==HUNTER_PET && (mode==PET_SAVE_AS_CURRENT||mode==PET_SAVE_NOT_IN_SLOT)) - CharacterDatabase.PExecute("DELETE FROM character_pet WHERE owner = '%u' AND (slot = '0' OR slot = '3')", owner ); - // save pet - std::ostringstream ss; - ss << "INSERT INTO character_pet ( id, entry, owner, modelid, level, exp, Reactstate, loyaltypoints, loyalty, trainpoint, slot, name, renamed, curhealth, curmana, curhappiness, abdata,TeachSpelldata,savetime,resettalents_cost,resettalents_time,CreatedBySpell,PetType) " - << "VALUES (" - << m_charmInfo->GetPetNumber() << ", " - << GetEntry() << ", " - << owner << ", " - << GetNativeDisplayId() << ", " - << getLevel() << ", " - << GetUInt32Value(UNIT_FIELD_PETEXPERIENCE) << ", " - << uint32(GetReactState()) << ", " - << m_loyaltyPoints << ", " - << GetLoyaltyLevel() << ", " - << m_TrainingPoints << ", " - << uint32(mode) << ", '" - << name.c_str() << "', " - << uint32((GetByteValue(UNIT_FIELD_BYTES_2, 2) == UNIT_RENAME_ALLOWED)?0:1) << ", " - << (curhealth<1?1:curhealth) << ", " - << curmana << ", " - << GetPower(POWER_HAPPINESS) << ", '"; - - for(uint32 i = 0; i < 10; i++) - ss << uint32(m_charmInfo->GetActionBarEntry(i)->Type) << " " << uint32(m_charmInfo->GetActionBarEntry(i)->SpellOrAction) << " "; - ss << "', '"; - - //save spells the pet can teach to it's Master - { - int i = 0; - for(TeachSpellMap::iterator itr = m_teachspells.begin(); i < 4 && itr != m_teachspells.end(); ++i, ++itr) - ss << itr->first << " " << itr->second << " "; - for(; i < 4; ++i) - ss << uint32(0) << " " << uint32(0) << " "; - } - - ss << "', " - << time(NULL) << ", " - << uint32(m_resetTalentsCost) << ", " - << uint64(m_resetTalentsTime) << ", " - << GetUInt32Value(UNIT_CREATED_BY_SPELL) << ", " - << uint32(getPetType()) << ")"; - - CharacterDatabase.Execute( ss.str().c_str() ); - - CharacterDatabase.CommitTransaction(); - break; - } - case PET_SAVE_AS_DELETED: + uint32 owner = GUID_LOPART(GetOwnerGUID()); + std::string name = m_name; + CharacterDatabase.escape_string(name); + CharacterDatabase.BeginTransaction(); + // remove current data + CharacterDatabase.PExecute("DELETE FROM character_pet WHERE owner = '%u' AND id = '%u'", owner,m_charmInfo->GetPetNumber() ); + + // prevent duplicate using slot (except PET_SAVE_NOT_IN_SLOT) + if(mode <= PET_SAVE_LAST_STABLE_SLOT) + CharacterDatabase.PExecute("UPDATE character_pet SET slot = '%u' WHERE owner = '%u' AND slot = '%u'", + PET_SAVE_NOT_IN_SLOT, owner, uint32(mode) ); + + // prevent existence another hunter pet in PET_SAVE_AS_CURRENT and PET_SAVE_NOT_IN_SLOT + if(getPetType()==HUNTER_PET && (mode==PET_SAVE_AS_CURRENT||mode > PET_SAVE_LAST_STABLE_SLOT)) + CharacterDatabase.PExecute("DELETE FROM character_pet WHERE owner = '%u' AND (slot = '%u' OR slot > '%u')", + owner,PET_SAVE_AS_CURRENT,PET_SAVE_LAST_STABLE_SLOT); + // save pet + std::ostringstream ss; + ss << "INSERT INTO character_pet ( id, entry, owner, modelid, level, exp, Reactstate, slot, name, renamed, curhealth, curmana, curhappiness, abdata, savetime, resettalents_cost, resettalents_time, CreatedBySpell, PetType) " + << "VALUES (" + << m_charmInfo->GetPetNumber() << ", " + << GetEntry() << ", " + << owner << ", " + << GetNativeDisplayId() << ", " + << getLevel() << ", " + << GetUInt32Value(UNIT_FIELD_PETEXPERIENCE) << ", " + << uint32(GetReactState()) << ", " + << uint32(mode) << ", '" + << name.c_str() << "', " + << uint32((GetByteValue(UNIT_FIELD_BYTES_2, 2) == UNIT_RENAME_ALLOWED)?0:1) << ", " + << (curhealth<1?1:curhealth) << ", " + << curmana << ", " + << GetPower(POWER_HAPPINESS) << ", '"; + + // save only spell slots from action bar + for(uint32 i = ACTION_BAR_INDEX_PET_SPELL_START; i < ACTION_BAR_INDEX_PET_SPELL_END; ++i) { - RemoveAllAuras(); - DeleteFromDB(m_charmInfo->GetPetNumber()); - break; - } - default: - sLog.outError("Unknown pet save/remove mode: %d",mode); + ss << uint32(m_charmInfo->GetActionBarEntry(i)->Type) << " " + << uint32(m_charmInfo->GetActionBarEntry(i)->SpellOrAction) << " "; + }; + + ss << "', " + << time(NULL) << ", " + << uint32(m_resetTalentsCost) << ", " + << uint64(m_resetTalentsTime) << ", " + << GetUInt32Value(UNIT_CREATED_BY_SPELL) << ", " + << uint32(getPetType()) << ")"; + + CharacterDatabase.Execute( ss.str().c_str() ); + CharacterDatabase.CommitTransaction(); + } + // delete + else + { + RemoveAllAuras(); + DeleteFromDB(m_charmInfo->GetPetNumber()); } } @@ -512,11 +448,7 @@ void Pet::setDeathState(DeathState s) // overwrite virtual Creature::setDeathState(s); if(getDeathState()==CORPSE) { - //remove summoned pet (no corpse) - if(getPetType()==SUMMON_PET) - Remove(PET_SAVE_NOT_IN_SLOT); - // other will despawn at corpse desppawning (Pet::Update code) - else + if(getPetType() == HUNTER_PET) { // pet corpse non lootable and non skinnable SetUInt32Value( UNIT_DYNAMIC_FLAGS, 0x00 ); @@ -527,12 +459,12 @@ void Pet::setDeathState(DeathState s) // overwrite virtual if(!mapEntry || (mapEntry->map_type != MAP_ARENA && mapEntry->map_type != MAP_BATTLEGROUND)) ModifyPower(POWER_HAPPINESS, -HAPPINESS_LEVEL_SIZE); - SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_ROTATE); + //SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_STUNNED); } } else if(getDeathState()==ALIVE) { - RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_ROTATE); + //RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_STUNNED); CastPetAuras(true); } } @@ -546,9 +478,8 @@ void Pet::Update(uint32 diff) { case CORPSE: { - if( m_deathTimer <= diff ) + if(getPetType() != HUNTER_PET || m_deathTimer <= diff ) { - assert(getPetType()!=SUMMON_PET && "Must be already removed."); Remove(PET_SAVE_NOT_IN_SLOT); //hunters' pets never get removed because of death, NEVER! return; } @@ -557,8 +488,9 @@ void Pet::Update(uint32 diff) case ALIVE: { // unsummon pet that lost owner - Unit* owner = GetOwner(); + Player* owner = GetOwner(); if(!owner || (!IsWithinDistInMap(owner, OWNER_MAX_DISTANCE) && !isPossessed()) || isControlled() && !owner->GetPetGUID()) + //if(!owner || (!IsWithinDistInMap(owner, OWNER_MAX_DISTANCE) && (owner->GetCharmGUID() && (owner->GetCharmGUID() != GetGUID()))) || (isControlled() && !owner->GetPetGUID())) { Remove(PET_SAVE_NOT_IN_SLOT, true); return; @@ -568,6 +500,7 @@ void Pet::Update(uint32 diff) { if( owner->GetPetGUID() != GetGUID() ) { + sLog.outError("Pet %u is not pet of owner %u, removed", GetEntry(), m_owner->GetName()); Remove(getPetType()==HUNTER_PET?PET_SAVE_AS_DELETED:PET_SAVE_NOT_IN_SLOT); return; } @@ -584,18 +517,26 @@ void Pet::Update(uint32 diff) } } - if(getPetType() != HUNTER_PET) - break; - - //regenerate Focus + //regenerate focus for hunter pets or energy for deathknight's ghoul if(m_regenTimer <= diff) { - RegenerateFocus(); + switch (getPowerType()) + { + case POWER_FOCUS: + case POWER_ENERGY: + Regenerate(getPowerType()); + break; + default: + break; + } m_regenTimer = 4000; } else m_regenTimer -= diff; + if(getPetType() != HUNTER_PET) + break; + if(m_happinessTimer <= diff) { LooseHappiness(); @@ -604,14 +545,6 @@ void Pet::Update(uint32 diff) else m_happinessTimer -= diff; - if(m_loyaltyTimer <= diff) - { - TickLoyaltyChange(); - m_loyaltyTimer = 12000; - } - else - m_loyaltyTimer -= diff; - break; } default: @@ -620,104 +553,52 @@ void Pet::Update(uint32 diff) Creature::Update(diff); } -void Pet::RegenerateFocus() +void Pet::Regenerate(Powers power) { - uint32 curValue = GetPower(POWER_FOCUS); - uint32 maxValue = GetMaxPower(POWER_FOCUS); + uint32 curValue = GetPower(power); + uint32 maxValue = GetMaxPower(power); if (curValue >= maxValue) return; - float addvalue = 24 * sWorld.getRate(RATE_POWER_FOCUS); - - AuraList const& ModPowerRegenPCTAuras = GetAurasByType(SPELL_AURA_MOD_POWER_REGEN_PERCENT); - for(AuraList::const_iterator i = ModPowerRegenPCTAuras.begin(); i != ModPowerRegenPCTAuras.end(); ++i) - if ((*i)->GetModifier()->m_miscvalue == POWER_FOCUS) - addvalue *= ((*i)->GetModifierValue() + 100) / 100.0f; - - ModifyPower(POWER_FOCUS, (int32)addvalue); -} + float addvalue = 0.0f; -void Pet::LooseHappiness() -{ - uint32 curValue = GetPower(POWER_HAPPINESS); - if (curValue <= 0) - return; - int32 addvalue = (140 >> GetLoyaltyLevel()) * 125; //value is 70/35/17/8/4 (per min) * 1000 / 8 (timer 7.5 secs) - if(isInCombat()) //we know in combat happiness fades faster, multiplier guess - addvalue = int32(addvalue * 1.5); - ModifyPower(POWER_HAPPINESS, -addvalue); -} - -void Pet::ModifyLoyalty(int32 addvalue) -{ - uint32 loyaltylevel = GetLoyaltyLevel(); - - if(addvalue > 0) //only gain influenced, not loss - addvalue = int32((float)addvalue * sWorld.getRate(RATE_LOYALTY)); - - if(loyaltylevel >= BEST_FRIEND && (addvalue + m_loyaltyPoints) > int32(GetMaxLoyaltyPoints(loyaltylevel))) - return; - - m_loyaltyPoints += addvalue; - - if(m_loyaltyPoints < 0) + switch (power) { - if(loyaltylevel > REBELLIOUS) + case POWER_FOCUS: { - //level down - --loyaltylevel; - SetLoyaltyLevel(LoyaltyLevel(loyaltylevel)); - m_loyaltyPoints = GetStartLoyaltyPoints(loyaltylevel); - SetTP(m_TrainingPoints - int32(getLevel())); + // For hunter pets. + addvalue = 24 * sWorld.getRate(RATE_POWER_FOCUS); + break; } - else + case POWER_ENERGY: { - m_loyaltyPoints = 0; - Unit* owner = GetOwner(); - if(owner && owner->GetTypeId() == TYPEID_PLAYER) - { - WorldPacket data(SMSG_PET_BROKEN, 0); - ((Player*)owner)->GetSession()->SendPacket(&data); - - //run away - ((Player*)owner)->RemovePet(this,PET_SAVE_AS_DELETED); - } + // For deathknight's ghoul. + addvalue = 20; + break; } - } - //level up - else if(m_loyaltyPoints > int32(GetMaxLoyaltyPoints(loyaltylevel))) - { - ++loyaltylevel; - SetLoyaltyLevel(LoyaltyLevel(loyaltylevel)); - m_loyaltyPoints = GetStartLoyaltyPoints(loyaltylevel); - SetTP(m_TrainingPoints + getLevel()); - } -} - -void Pet::TickLoyaltyChange() -{ - int32 addvalue; - - switch(GetHappinessState()) - { - case HAPPY: addvalue = 20; break; - case CONTENT: addvalue = 10; break; - case UNHAPPY: addvalue = -20; break; default: return; } - ModifyLoyalty(addvalue); + + // Apply modifiers (if any). + AuraEffectList const& ModPowerRegenPCTAuras = GetAurasByType(SPELL_AURA_MOD_POWER_REGEN_PERCENT); + for(AuraEffectList::const_iterator i = ModPowerRegenPCTAuras.begin(); i != ModPowerRegenPCTAuras.end(); ++i) + if ((*i)->GetMiscValue() == power) + addvalue *= ((*i)->GetAmount() + 100) / 100.0f; + + ModifyPower(power, (int32)addvalue); } -void Pet::KillLoyaltyBonus(uint32 level) +void Pet::LooseHappiness() { - if(level > 100) + uint32 curValue = GetPower(POWER_HAPPINESS); + if (curValue <= 0) return; - - //at lower levels gain is faster | the lower loyalty the more loyalty is gained - uint32 bonus = uint32(((100 - level) / 10) + (6 - GetLoyaltyLevel())); - ModifyLoyalty(bonus); + int32 addvalue = 670; //value is 70/35/17/8/4 (per min) * 1000 / 8 (timer 7.5 secs) + if(isInCombat()) //we know in combat happiness fades faster, multiplier guess + addvalue = int32(addvalue * 1.5); + ModifyPower(POWER_HAPPINESS, -addvalue); } HappinessState Pet::GetHappinessState() @@ -730,11 +611,6 @@ HappinessState Pet::GetHappinessState() return CONTENT; } -void Pet::SetLoyaltyLevel(LoyaltyLevel level) -{ - SetByteValue(UNIT_FIELD_BYTES_1, 1, level); -} - bool Pet::CanTakeMoreActiveSpells(uint32 spellid) { uint8 activecount = 1; @@ -745,8 +621,11 @@ bool Pet::CanTakeMoreActiveSpells(uint32 spellid) chainstartstore[0] = spellmgr.GetFirstSpellInChain(spellid); - for (PetSpellMap::iterator itr = m_spells.begin(); itr != m_spells.end(); ++itr) + for (PetSpellMap::const_iterator itr = m_spells.begin(); itr != m_spells.end(); ++itr) { + if(itr->second.state == PETSPELL_REMOVED) + continue; + if(IsPassiveSpell(itr->first)) continue; @@ -771,102 +650,9 @@ bool Pet::CanTakeMoreActiveSpells(uint32 spellid) return true; } -bool Pet::HasTPForSpell(uint32 spellid) -{ - int32 neededtrainp = GetTPForSpell(spellid); - if((m_TrainingPoints - neededtrainp < 0 || neededtrainp < 0) && neededtrainp != 0) - return false; - return true; -} - -int32 Pet::GetTPForSpell(uint32 spellid) -{ - uint32 basetrainp = 0; - - SkillLineAbilityMap::const_iterator lower = spellmgr.GetBeginSkillLineAbilityMap(spellid); - SkillLineAbilityMap::const_iterator upper = spellmgr.GetEndSkillLineAbilityMap(spellid); - for(SkillLineAbilityMap::const_iterator _spell_idx = lower; _spell_idx != upper; ++_spell_idx) - { - if(!_spell_idx->second->reqtrainpoints) - return 0; - - basetrainp = _spell_idx->second->reqtrainpoints; - break; - } - - uint32 spenttrainp = 0; - uint32 chainstart = spellmgr.GetFirstSpellInChain(spellid); - - for (PetSpellMap::iterator itr = m_spells.begin(); itr != m_spells.end(); ++itr) - { - if(itr->second->state == PETSPELL_REMOVED) - continue; - - if(spellmgr.GetFirstSpellInChain(itr->first) == chainstart) - { - SkillLineAbilityMap::const_iterator _lower = spellmgr.GetBeginSkillLineAbilityMap(itr->first); - SkillLineAbilityMap::const_iterator _upper = spellmgr.GetEndSkillLineAbilityMap(itr->first); - - for(SkillLineAbilityMap::const_iterator _spell_idx2 = _lower; _spell_idx2 != _upper; ++_spell_idx2) - { - if(_spell_idx2->second->reqtrainpoints > spenttrainp) - { - spenttrainp = _spell_idx2->second->reqtrainpoints; - break; - } - } - } - } - - return int32(basetrainp) - int32(spenttrainp); -} - -uint32 Pet::GetMaxLoyaltyPoints(uint32 level) -{ - return LevelUpLoyalty[level - 1]; -} - -uint32 Pet::GetStartLoyaltyPoints(uint32 level) -{ - return LevelStartLoyalty[level - 1]; -} - -void Pet::SetTP(int32 TP) -{ - m_TrainingPoints = TP; - SetUInt32Value(UNIT_TRAINING_POINTS, (uint32)GetDispTP()); -} - -int32 Pet::GetDispTP() -{ - if(getPetType()!= HUNTER_PET) - return(0); - if(m_TrainingPoints < 0) - return -m_TrainingPoints; - else - return -(m_TrainingPoints + 1); -} - void Pet::Remove(PetSaveMode mode, bool returnreagent) { - Unit* owner = GetOwner(); - - if(owner) - { - if(owner->GetTypeId()==TYPEID_PLAYER) - { - ((Player*)owner)->RemovePet(this,mode,returnreagent); - return; - } - - // only if current pet in slot - if(owner->GetPetGUID()==GetGUID()) - owner->SetPet(0); - } - - CleanupsBeforeDelete(); - AddObjectToRemoveList(); - m_removed = true; + m_owner->RemovePet(this,mode,returnreagent); } void Pet::GivePetXP(uint32 xp) @@ -900,18 +686,14 @@ void Pet::GivePetXP(uint32 xp) { newXP -= nextLvlXP; - SetLevel( level + 1 ); - SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP, uint32((Trinity::XP::xp_to_level(level+1))/4)); + GivePetLevel(level+1); + SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP, objmgr.GetXPForLevel(level+1)*PET_XP_FACTOR); level = getLevel(); nextLvlXP = GetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP); - GivePetLevel(level); } SetUInt32Value(UNIT_FIELD_PETEXPERIENCE, newXP); - - if(getPetType() == HUNTER_PET) - KillLoyaltyBonus(level); } void Pet::GivePetLevel(uint32 level) @@ -919,16 +701,16 @@ void Pet::GivePetLevel(uint32 level) if(!level) return; - InitStatsForLevel( level); - - SetTP(m_TrainingPoints + (GetLoyaltyLevel() - 1)); + InitStatsForLevel(level); + InitLevelupSpellsForLevel(); + InitTalentForLevel(); } bool Pet::CreateBaseAtCreature(Creature* creature) { if(!creature) { - sLog.outError("CRITICAL ERROR: NULL pointer parsed into CreateBaseAtCreature()"); + sLog.outError("CRITICAL: NULL pointer parsed into CreateBaseAtCreature()"); return false; } uint32 guid=objmgr.GenerateLowGuid(HIGHGUID_PET); @@ -938,14 +720,14 @@ bool Pet::CreateBaseAtCreature(Creature* creature) sLog.outDebug("Create pet"); uint32 pet_number = objmgr.GeneratePetNumber(); - if(!Create(guid, creature->GetMap(), creature->GetEntry(), pet_number)) + if(!Create(guid, creature->GetMap(), creature->GetPhaseMask(), creature->GetEntry(), pet_number)) return false; Relocate(creature->GetPositionX(), creature->GetPositionY(), creature->GetPositionZ(), creature->GetOrientation()); if(!IsPositionValid()) { - sLog.outError("ERROR: Pet (guidlow %d, entry %d) not created base at creature. Suggested coordinates isn't valid (X: %f Y: %f)", + sLog.outError("Pet (guidlow %d, entry %d) not created base at creature. Suggested coordinates isn't valid (X: %f Y: %f)", GetGUIDLow(), GetEntry(), GetPositionX(), GetPositionY()); return false; } @@ -953,15 +735,10 @@ bool Pet::CreateBaseAtCreature(Creature* creature) CreatureInfo const *cinfo = GetCreatureInfo(); if(!cinfo) { - sLog.outError("ERROR: CreateBaseAtCreature() failed, creatureInfo is missing!"); + sLog.outError("CreateBaseAtCreature() failed, creatureInfo is missing!"); return false; } - if(cinfo->type == CREATURE_TYPE_CRITTER) - { - setPetType(MINI_PET); - return true; - } SetDisplayId(creature->GetDisplayId()); SetNativeDisplayId(creature->GetNativeDisplayId()); SetMaxPower(POWER_HAPPINESS, GetCreatePowers(POWER_HAPPINESS)); @@ -969,45 +746,48 @@ bool Pet::CreateBaseAtCreature(Creature* creature) setPowerType(POWER_FOCUS); SetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP, 0); SetUInt32Value(UNIT_FIELD_PETEXPERIENCE, 0); - SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP, uint32((Trinity::XP::xp_to_level(creature->getLevel()))/4)); - SetUInt32Value(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE); - SetUInt32Value(UNIT_NPC_FLAGS, 0); + SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP, objmgr.GetXPForLevel(creature->getLevel())*PET_XP_FACTOR); + SetUInt32Value(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_NONE); - CreatureFamilyEntry const* cFamily = sCreatureFamilyStore.LookupEntry(creature->GetCreatureInfo()->family); - if( char* familyname = cFamily->Name[sWorld.GetDefaultDbcLocale()] ) - SetName(familyname); + if(CreatureFamilyEntry const* cFamily = sCreatureFamilyStore.LookupEntry(cinfo->family)) + SetName(cFamily->Name[sWorld.GetDefaultDbcLocale()]); else - SetName(creature->GetName()); + SetName(creature->GetNameForLocaleIdx(objmgr.GetDBCLocaleIndex())); - m_loyaltyPoints = 1000; if(cinfo->type == CREATURE_TYPE_BEAST) { SetUInt32Value(UNIT_FIELD_BYTES_0, 0x02020100); - SetByteValue(UNIT_FIELD_BYTES_2, 0, SHEATH_STATE_MELEE ); - SetByteValue(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_SANCTUARY | UNIT_BYTE2_FLAG_AURAS | UNIT_BYTE2_FLAG_UNK5 ); + SetSheath(SHEATH_STATE_MELEE); SetByteValue(UNIT_FIELD_BYTES_2, 2, UNIT_RENAME_ALLOWED); - - SetUInt32Value(UNIT_MOD_CAST_SPEED, creature->GetUInt32Value(UNIT_MOD_CAST_SPEED) ); - SetLoyaltyLevel(REBELLIOUS); + SetUInt32Value(UNIT_MOD_CAST_SPEED, creature->GetUInt32Value(UNIT_MOD_CAST_SPEED)); } return true; } -bool Pet::InitStatsForLevel(uint32 petlevel) +// TODO: Move stat mods code to pet passive auras +bool Guardian::InitStatsForLevel(uint32 petlevel) { CreatureInfo const *cinfo = GetCreatureInfo(); assert(cinfo); - Unit* owner = GetOwner(); - if(!owner) + SetLevel(petlevel); + + //Determine pet type + PetType petType = MAX_PET_TYPE; + if(HasSummonMask(SUMMON_MASK_PET) && m_owner->GetTypeId() == TYPEID_PLAYER) { - sLog.outError("ERROR: attempt to summon pet (Entry %u) without owner! Attempt terminated.", cinfo->Entry); - return false; + if(m_owner->getClass() == CLASS_WARLOCK) + petType = SUMMON_PET; + else if(m_owner->getClass() == CLASS_HUNTER) + { + petType = HUNTER_PET; + m_summonMask |= SUMMON_MASK_HUNTER_PET; + } + else + sLog.outError("Unknown type pet %u is summoned by player class %u", GetEntry(), m_owner->getClass()); } - uint32 creature_ID = (getPetType() == HUNTER_PET) ? 1 : cinfo->Entry; - - SetLevel(petlevel); + uint32 creature_ID = (petType == HUNTER_PET) ? 1 : cinfo->Entry; SetMeleeDamageSchool(SpellSchools(cinfo->dmgschool)); @@ -1019,8 +799,9 @@ bool Pet::InitStatsForLevel(uint32 petlevel) SetFloatValue(UNIT_MOD_CAST_SPEED, 1.0); + //scale CreatureFamilyEntry const* cFamily = sCreatureFamilyStore.LookupEntry(cinfo->family); - if(cFamily && cFamily->minScale > 0.0f && getPetType()==HUNTER_PET) + if(cFamily && cFamily->minScale > 0.0f && petType==HUNTER_PET) { float scale; if (getLevel() >= cFamily->maxScaleLevel) @@ -1028,15 +809,14 @@ bool Pet::InitStatsForLevel(uint32 petlevel) else if (getLevel() <= cFamily->minScaleLevel) scale = cFamily->minScale; else - scale = cFamily->minScale + (float)(getLevel() - cFamily->minScaleLevel) / (float)cFamily->maxScaleLevel * (cFamily->maxScale - cFamily->minScale); + scale = cFamily->minScale + float(getLevel() - cFamily->minScaleLevel) / cFamily->maxScaleLevel * (cFamily->maxScale - cFamily->minScale); SetFloatValue(OBJECT_FIELD_SCALE_X, scale); } - m_bonusdamage = 0; + //resistance int32 createResistance[MAX_SPELL_SCHOOL] = {0,0,0,0,0,0,0}; - - if(cinfo && getPetType() != HUNTER_PET) + if(cinfo && petType != HUNTER_PET) { createResistance[SPELL_SCHOOL_HOLY] = cinfo->resistance1; createResistance[SPELL_SCHOOL_FIRE] = cinfo->resistance2; @@ -1045,163 +825,129 @@ bool Pet::InitStatsForLevel(uint32 petlevel) createResistance[SPELL_SCHOOL_SHADOW] = cinfo->resistance5; createResistance[SPELL_SCHOOL_ARCANE] = cinfo->resistance6; } + for (uint8 i = SPELL_SCHOOL_HOLY; i < MAX_SPELL_SCHOOL; ++i) + SetModifierValue(UnitMods(UNIT_MOD_RESISTANCE_START + i), BASE_VALUE, float(createResistance[i])); - switch(getPetType()) + //health, mana, armor and resistance + PetLevelInfo const* pInfo = objmgr.GetPetLevelInfo(creature_ID, petlevel); + if(pInfo) // exist in DB { - case SUMMON_PET: - { - if(owner->GetTypeId() == TYPEID_PLAYER) - { - switch(owner->getClass()) - { - case CLASS_WARLOCK: - { + SetCreateHealth(pInfo->health); + if(petType != HUNTER_PET) //hunter pet use focus + SetCreateMana(pInfo->mana); - //the damage bonus used for pets is either fire or shadow damage, whatever is higher - uint32 fire = owner->GetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS + SPELL_SCHOOL_FIRE); - uint32 shadow = owner->GetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS + SPELL_SCHOOL_SHADOW); - uint32 val = (fire > shadow) ? fire : shadow; + if(pInfo->armor > 0) + SetModifierValue(UNIT_MOD_ARMOR, BASE_VALUE, float(pInfo->armor)); - SetBonusDamage(int32 (val * 0.15f)); - //bonusAP += val * 0.57; - break; - } - case CLASS_MAGE: - { - //40% damage bonus of mage's frost damage - float val = owner->GetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS + SPELL_SCHOOL_FROST) * 0.4; - if(val < 0) - val = 0; - SetBonusDamage( int32(val)); - break; - } - default: - break; - } - } + for(uint8 stat = 0; stat < MAX_STATS; ++stat) + SetCreateStat(Stats(stat), float(pInfo->stats[stat])); + } + else // not exist in DB, use some default fake data + { + // remove elite bonuses included in DB values + //SetCreateHealth(uint32(((float(cinfo->maxhealth) / cinfo->maxlevel) / (1 + 2 * cinfo->rank)) * petlevel) ); + //SetCreateMana( uint32(((float(cinfo->maxmana) / cinfo->maxlevel) / (1 + 2 * cinfo->rank)) * petlevel) ); + + SetCreateStat(STAT_STRENGTH, 22); + SetCreateStat(STAT_AGILITY, 22); + SetCreateStat(STAT_STAMINA, 25); + SetCreateStat(STAT_INTELLECT, 28); + SetCreateStat(STAT_SPIRIT, 27); + } + + m_bonusdamage = 0; + switch(petType) + { + case SUMMON_PET: + { + //the damage bonus used for pets is either fire or shadow damage, whatever is higher + uint32 fire = m_owner->GetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS + SPELL_SCHOOL_FIRE); + uint32 shadow = m_owner->GetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS + SPELL_SCHOOL_SHADOW); + uint32 val = (fire > shadow) ? fire : shadow; + SetBonusDamage(int32 (val * 0.15f)); + //bonusAP += val * 0.57; SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, float(petlevel - (petlevel / 4)) ); SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, float(petlevel + (petlevel / 4)) ); //SetModifierValue(UNIT_MOD_ATTACK_POWER, BASE_VALUE, float(cinfo->attackpower)); - - PetLevelInfo const* pInfo = objmgr.GetPetLevelInfo(creature_ID, petlevel); - if(pInfo) // exist in DB - { - SetCreateHealth(pInfo->health); - SetCreateMana(pInfo->mana); - - if(pInfo->armor > 0) - SetModifierValue(UNIT_MOD_ARMOR, BASE_VALUE, float(pInfo->armor)); - - for(int stat = 0; stat < MAX_STATS; ++stat) - { - SetCreateStat(Stats(stat), float(pInfo->stats[stat])); - } - } - else // not exist in DB, use some default fake data - { - sLog.outErrorDb("Summoned pet (Entry: %u) not have pet stats data in DB",cinfo->Entry); - - // remove elite bonuses included in DB values - SetCreateHealth(uint32(((float(cinfo->maxhealth) / cinfo->maxlevel) / (1 + 2 * cinfo->rank)) * petlevel) ); - SetCreateMana( uint32(((float(cinfo->maxmana) / cinfo->maxlevel) / (1 + 2 * cinfo->rank)) * petlevel) ); - - SetCreateStat(STAT_STRENGTH, 22); - SetCreateStat(STAT_AGILITY, 22); - SetCreateStat(STAT_STAMINA, 25); - SetCreateStat(STAT_INTELLECT, 28); - SetCreateStat(STAT_SPIRIT, 27); - } break; } case HUNTER_PET: { - SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP, uint32((Trinity::XP::xp_to_level(petlevel))/4)); - + SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP, objmgr.GetXPForLevel(petlevel)*PET_XP_FACTOR); //these formula may not be correct; however, it is designed to be close to what it should be //this makes dps 0.5 of pets level SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, float(petlevel - (petlevel / 4)) ); //damage range is then petlevel / 2 SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, float(petlevel + (petlevel / 4)) ); //damage is increased afterwards as strength and pet scaling modify attack power - - //stored standard pet stats are entry 1 in pet_levelinfo - PetLevelInfo const* pInfo = objmgr.GetPetLevelInfo(creature_ID, petlevel); - if(pInfo) // exist in DB - { - SetCreateHealth(pInfo->health); - SetModifierValue(UNIT_MOD_ARMOR, BASE_VALUE, float(pInfo->armor)); - //SetModifierValue(UNIT_MOD_ATTACK_POWER, BASE_VALUE, float(cinfo->attackpower)); - - for( int i = STAT_STRENGTH; i < MAX_STATS; i++) - { - SetCreateStat(Stats(i), float(pInfo->stats[i])); - } - } - else // not exist in DB, use some default fake data - { - sLog.outErrorDb("Hunter pet levelstats missing in DB"); - - // remove elite bonuses included in DB values - SetCreateHealth( uint32(((float(cinfo->maxhealth) / cinfo->maxlevel) / (1 + 2 * cinfo->rank)) * petlevel) ); - - SetCreateStat(STAT_STRENGTH, 22); - SetCreateStat(STAT_AGILITY, 22); - SetCreateStat(STAT_STAMINA, 25); - SetCreateStat(STAT_INTELLECT, 28); - SetCreateStat(STAT_SPIRIT, 27); - } break; } - case GUARDIAN_PET: - SetUInt32Value(UNIT_FIELD_PETEXPERIENCE, 0); - SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP, 1000); - + default: + { switch(GetEntry()) { + case 510: // mage Water Elemental + { + //40% damage bonus of mage's frost damage + float val = m_owner->GetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS + SPELL_SCHOOL_FROST) * 0.4; + if(val < 0) + val = 0; + SetBonusDamage( int32(val)); + break; + } case 1964: //force of nature - SetCreateHealth(30 + 30*petlevel); + { + if(!pInfo) + SetCreateHealth(30 + 30*petlevel); SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, float(petlevel * 2.5f - (petlevel / 2))); SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, float(petlevel * 2.5f + (petlevel / 2))); break; + } case 15352: //earth elemental 36213 - SetCreateHealth(100 + 120*petlevel); + { + if(!pInfo) + SetCreateHealth(100 + 120*petlevel); SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, float(petlevel - (petlevel / 4))); SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, float(petlevel + (petlevel / 4))); break; + } case 15438: //fire elemental - SetCreateHealth(40*petlevel); - SetCreateMana(28 + 10*petlevel); + { + if(!pInfo) + { + SetCreateHealth(40*petlevel); + SetCreateMana(28 + 10*petlevel); + } SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, float(petlevel * 4 - petlevel)); SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, float(petlevel * 4 + petlevel)); break; + } default: - SetCreateMana(28 + 10*petlevel); - SetCreateHealth(28 + 30*petlevel); - - // FIXME: this is wrong formula, possible each guardian pet have own damage formula - //these formula may not be correct; however, it is designed to be close to what it should be - //this makes dps 0.5 of pets level - SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, float(petlevel - (petlevel / 4))); - //damage range is then petlevel / 2 - SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, float(petlevel + (petlevel / 4))); + { + if(!pInfo) + { + SetCreateMana(28 + 10*petlevel); + SetCreateHealth(28 + 30*petlevel); + } + // FIXME: this is wrong formula, possible each guardian pet have own damage formula + //these formula may not be correct; however, it is designed to be close to what it should be + //this makes dps 0.5 of pets level + SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, float(petlevel - (petlevel / 4))); + //damage range is then petlevel / 2 + SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, float(petlevel + (petlevel / 4))); break; + } } break; - default: - sLog.outError("Pet have incorrect type (%u) for levelup.", getPetType()); - break; + } } - for (int i = SPELL_SCHOOL_HOLY; i < MAX_SPELL_SCHOOL; ++i) - SetModifierValue(UnitMods(UNIT_MOD_RESISTANCE_START + i), BASE_VALUE, float(createResistance[i])); - UpdateAllStats(); SetHealth(GetMaxHealth()); SetPower(POWER_MANA, GetMaxPower(POWER_MANA)); - return true; } @@ -1272,7 +1018,7 @@ void Pet::_LoadSpellCooldowns() continue; data << uint32(spell_id); - data << uint32(uint32(db_time-curTime)*1000); // in m.secs + data << uint32(uint32(db_time-curTime)*IN_MILISECONDS); _AddCreatureSpellCooldown(spell_id,db_time); @@ -1302,7 +1048,7 @@ void Pet::_SaveSpellCooldowns() m_CreatureSpellCooldowns.erase(itr++); else { - CharacterDatabase.PExecute("INSERT INTO pet_spell_cooldown (guid,spell,time) VALUES ('%u', '%u', '" I64FMTD "')", m_charmInfo->GetPetNumber(), itr->first, uint64(itr->second)); + CharacterDatabase.PExecute("INSERT INTO pet_spell_cooldown (guid,spell,time) VALUES ('%u', '%u', '" UI64FMTD "')", m_charmInfo->GetPetNumber(), itr->first, uint64(itr->second)); ++itr; } } @@ -1310,7 +1056,7 @@ void Pet::_SaveSpellCooldowns() void Pet::_LoadSpells() { - QueryResult *result = CharacterDatabase.PQuery("SELECT spell,slot,active FROM pet_spell WHERE guid = '%u'",m_charmInfo->GetPetNumber()); + QueryResult *result = CharacterDatabase.PQuery("SELECT spell,active FROM pet_spell WHERE guid = '%u'",m_charmInfo->GetPetNumber()); if(result) { @@ -1318,7 +1064,7 @@ void Pet::_LoadSpells() { Field *fields = result->Fetch(); - addSpell(fields[0].GetUInt16(), fields[2].GetUInt16(), PETSPELL_UNCHANGED, fields[1].GetUInt16()); + addSpell(fields[0].GetUInt32(), ActiveStates(fields[1].GetUInt16()), PETSPELL_UNCHANGED); } while( result->NextRow() ); @@ -1328,63 +1074,67 @@ void Pet::_LoadSpells() void Pet::_SaveSpells() { - for (PetSpellMap::const_iterator itr = m_spells.begin(), next = m_spells.begin(); itr != m_spells.end(); itr = next) + for (PetSpellMap::iterator itr = m_spells.begin(), next = m_spells.begin(); itr != m_spells.end(); itr = next) { ++next; - if (itr->second->type == PETSPELL_FAMILY) continue; // prevent saving family passives to DB - if (itr->second->state == PETSPELL_REMOVED || itr->second->state == PETSPELL_CHANGED) - CharacterDatabase.PExecute("DELETE FROM pet_spell WHERE guid = '%u' and spell = '%u'", m_charmInfo->GetPetNumber(), itr->first); - if (itr->second->state == PETSPELL_NEW || itr->second->state == PETSPELL_CHANGED) - CharacterDatabase.PExecute("INSERT INTO pet_spell (guid,spell,slot,active) VALUES ('%u', '%u', '%u','%u')", m_charmInfo->GetPetNumber(), itr->first, itr->second->slotId,itr->second->active); - - if (itr->second->state == PETSPELL_REMOVED) - _removeSpell(itr->first); - else - itr->second->state = PETSPELL_UNCHANGED; + + // prevent saving family passives to DB + if (itr->second.type == PETSPELL_FAMILY) + continue; + + switch(itr->second.state) + { + case PETSPELL_REMOVED: + CharacterDatabase.PExecute("DELETE FROM pet_spell WHERE guid = '%u' and spell = '%u'", m_charmInfo->GetPetNumber(), itr->first); + m_spells.erase(itr); + continue; + case PETSPELL_CHANGED: + CharacterDatabase.PExecute("DELETE FROM pet_spell WHERE guid = '%u' and spell = '%u'", m_charmInfo->GetPetNumber(), itr->first); + CharacterDatabase.PExecute("INSERT INTO pet_spell (guid,spell,active) VALUES ('%u', '%u', '%u')", m_charmInfo->GetPetNumber(), itr->first, itr->second.active); + break; + case PETSPELL_NEW: + CharacterDatabase.PExecute("INSERT INTO pet_spell (guid,spell,active) VALUES ('%u', '%u', '%u')", m_charmInfo->GetPetNumber(), itr->first, itr->second.active); + break; + case PETSPELL_UNCHANGED: + continue; + } + + itr->second.state = PETSPELL_UNCHANGED; } } void Pet::_LoadAuras(uint32 timediff) { - m_Auras.clear(); - for (int i = 0; i < TOTAL_AURAS; i++) - m_modAuras[i].clear(); - - // all aura related fields - for(int i = UNIT_FIELD_AURA; i <= UNIT_FIELD_AURASTATE; ++i) - SetUInt32Value(i, 0); + sLog.outDebug("Loading auras for pet %u",GetGUIDLow()); - QueryResult *result = CharacterDatabase.PQuery("SELECT caster_guid,spell,effect_index,stackcount,amount,maxduration,remaintime,remaincharges FROM pet_aura WHERE guid = '%u'",m_charmInfo->GetPetNumber()); + QueryResult *result = CharacterDatabase.PQuery("SELECT caster_guid,spell,effect_mask,stackcount,amount0, amount1, amount2 ,maxduration,remaintime,remaincharges FROM pet_aura WHERE guid = '%u'",m_charmInfo->GetPetNumber()); if(result) { do { + int32 damage[3]; Field *fields = result->Fetch(); uint64 caster_guid = fields[0].GetUInt64(); uint32 spellid = fields[1].GetUInt32(); - uint32 effindex = fields[2].GetUInt32(); - uint32 stackcount= fields[3].GetUInt32(); - int32 damage = (int32)fields[4].GetUInt32(); - int32 maxduration = (int32)fields[5].GetUInt32(); - int32 remaintime = (int32)fields[6].GetUInt32(); - int32 remaincharges = (int32)fields[7].GetUInt32(); + uint32 effmask = fields[2].GetUInt32(); + uint32 stackcount = fields[3].GetUInt32(); + damage[0] = int32(fields[4].GetUInt32()); + damage[1] = int32(fields[5].GetUInt32()); + damage[2] = int32(fields[6].GetUInt32()); + int32 maxduration = (int32)fields[7].GetUInt32(); + int32 remaintime = (int32)fields[8].GetUInt32(); + int32 remaincharges = (int32)fields[9].GetUInt32(); SpellEntry const* spellproto = sSpellStore.LookupEntry(spellid); if(!spellproto) { - sLog.outError("Unknown aura (spellid %u, effindex %u), ignore.",spellid,effindex); - continue; - } - - if(effindex >= 3) - { - sLog.outError("Invalid effect index (spellid %u, effindex %u), ignore.",spellid,effindex); + sLog.outError("Unknown aura (spellid %u), ignore.",spellid); continue; } // negative effects should continue counting down after logout - if (remaintime != -1 && !IsPositiveEffect(spellid, effindex)) + if (remaintime != -1 && !IsPositiveSpell(spellid)) { if(remaintime <= int32(timediff)) continue; @@ -1399,21 +1149,12 @@ void Pet::_LoadAuras(uint32 timediff) remaincharges = spellproto->procCharges; } else - remaincharges = -1; - - /// do not load single target auras (unless they were cast by the player) - if (caster_guid != GetGUID() && IsSingleTargetSpell(spellproto)) - continue; + remaincharges = 0; - for(uint32 i=0; i<stackcount; i++) - { - Aura* aura = CreateAura(spellproto, effindex, NULL, this, NULL); - - if(!damage) - damage = aura->GetModifier()->m_amount; - aura->SetLoadedState(caster_guid,damage,maxduration,remaintime,remaincharges); - AddAura(aura); - } + Aura* aura = new Aura(spellproto, effmask, NULL, this, NULL, NULL); + aura->SetLoadedState(caster_guid,maxduration,remaintime,remaincharges, stackcount, &damage[0]); + AddAura(aura); + sLog.outDetail("Added aura spellid %u, effectmask %u", spellproto->Id, effmask); } while( result->NextRow() ); @@ -1426,56 +1167,36 @@ void Pet::_SaveAuras() CharacterDatabase.PExecute("DELETE FROM pet_aura WHERE guid = '%u'", m_charmInfo->GetPetNumber()); AuraMap const& auras = GetAuras(); - if (auras.empty()) - return; - - spellEffectPair lastEffectPair = auras.begin()->first; - uint32 stackCounter = 1; - - for(AuraMap::const_iterator itr = auras.begin(); ; ++itr) + for(AuraMap::const_iterator itr = auras.begin(); itr !=auras.end() ; ++itr) { - if(itr == auras.end() || lastEffectPair != itr->first) - { - AuraMap::const_iterator itr2 = itr; - // save previous spellEffectPair to db - itr2--; - SpellEntry const *spellInfo = itr2->second->GetSpellProto(); - /// do not save single target auras (unless they were cast by the player) - if (!(itr2->second->GetCasterGUID() != GetGUID() && IsSingleTargetSpell(spellInfo))) - { - if(!itr2->second->IsPassive()) - { - // skip all auras from spell that apply at cast SPELL_AURA_MOD_SHAPESHIFT or pet area auras. - uint8 i; - for (i = 0; i < 3; i++) - if (spellInfo->EffectApplyAuraName[i] == SPELL_AURA_MOD_STEALTH || - spellInfo->Effect[i] == SPELL_EFFECT_APPLY_AREA_AURA_OWNER || - spellInfo->Effect[i] == SPELL_EFFECT_APPLY_AREA_AURA_PET ) - break; - - if (i == 3) - { - CharacterDatabase.PExecute("INSERT INTO pet_aura (guid,caster_guid,spell,effect_index,stackcount,amount,maxduration,remaintime,remaincharges) " - "VALUES ('%u', '" I64FMTD "', '%u', '%u', '%u', '%d', '%d', '%d', '%d')", - m_charmInfo->GetPetNumber(), itr2->second->GetCasterGUID(),(uint32)itr2->second->GetId(), (uint32)itr2->second->GetEffIndex(), (uint32)itr2->second->GetStackAmount(), itr2->second->GetModifier()->m_amount,int(itr2->second->GetAuraMaxDuration()),int(itr2->second->GetAuraDuration()),int(itr2->second->m_procCharges)); - } - } - } - if(itr == auras.end()) - break; - } + // skip all auras from spell that apply at cast SPELL_AURA_MOD_SHAPESHIFT or pet area auras. + // do not save single target auras (unless they were cast by the player) + if (itr->second->IsPassive() || itr->second->IsAuraType(SPELL_AURA_MOD_STEALTH)) + continue; + bool isCaster = itr->second->GetCasterGUID() == GetGUID(); + if (!isCaster) + if (itr->second->IsSingleTarget() + || itr->second->IsAreaAura()) + continue; - if (lastEffectPair == itr->first) - stackCounter++; - else + int32 amounts[MAX_SPELL_EFFECTS]; + for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) { - lastEffectPair = itr->first; - stackCounter = 1; + if (AuraEffect *partAura = itr->second->GetPartAura(i)) + amounts[i] = partAura->GetAmount(); + else + amounts[i] = 0; } + + CharacterDatabase.PExecute("INSERT INTO pet_aura (guid,caster_guid,spell,effect_mask,stackcount,amount0, amount1, amount2,maxduration,remaintime,remaincharges) " + "VALUES ('%u', '" UI64FMTD "', '%u', '%u', '%d', '%d', '%d', '%d', '%d', '%d', '%u')", + m_charmInfo->GetPetNumber(), itr->second->GetCasterGUID(), itr->second->GetId(), (uint32)itr->second->GetEffectMask(), + (int32)itr->second->GetStackAmount(), amounts[0], amounts[1], amounts[2] + ,itr->second->GetAuraMaxDuration(), itr->second->GetAuraDuration(), (uint32)itr->second->GetAuraCharges()); } } -bool Pet::addSpell(uint16 spell_id, uint16 active, PetSpellState state, uint16 slot_id, PetSpellType type) +bool Pet::addSpell(uint32 spell_id,ActiveStates active /*= ACT_DECIDE*/, PetSpellState state /*= PETSPELL_NEW*/, PetSpellType type /*= PETSPELL_NORMAL*/) { SpellEntry const *spellInfo = sSpellStore.LookupEntry(spell_id); if (!spellInfo) @@ -1492,23 +1213,24 @@ bool Pet::addSpell(uint16 spell_id, uint16 active, PetSpellState state, uint16 s return false; } - // same spells don't have autocast option - if (spellInfo->AttributesEx & SPELL_ATTR_EX_UNAUTOCASTABLE_BY_PET) - active = ACT_CAST; - PetSpellMap::iterator itr = m_spells.find(spell_id); if (itr != m_spells.end()) { - if (itr->second->state == PETSPELL_REMOVED) + if (itr->second.state == PETSPELL_REMOVED) { - delete itr->second; m_spells.erase(itr); state = PETSPELL_CHANGED; } - else if (state == PETSPELL_UNCHANGED && itr->second->state != PETSPELL_UNCHANGED) + 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; + itr->second.state = PETSPELL_UNCHANGED; + + if(active == ACT_ENABLED) + ToggleAutocast(spell_id, true); + else if(active == ACT_DISABLED) + ToggleAutocast(spell_id, false); + return false; } else @@ -1517,184 +1239,419 @@ bool Pet::addSpell(uint16 spell_id, uint16 active, PetSpellState state, uint16 s uint32 oldspell_id = 0; - PetSpell *newspell = new PetSpell; - newspell->state = state; - newspell->type = type; + 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(IsPassiveSpell(spell_id)) - newspell->active = ACT_PASSIVE; + if(IsAutocastableSpell(spell_id)) + newspell.active = ACT_DISABLED; else - newspell->active = ACT_DISABLED; + newspell.active = ACT_PASSIVE; } else - newspell->active = active; - - uint32 chainstart = spellmgr.GetFirstSpellInChain(spell_id); + newspell.active = active; - 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 )) { - slot_id = itr->second->slotId; - newspell->active = itr->second->active; - - if(newspell->active == ACT_ENABLED) - ToggleAutocast(itr->first, false); + for(uint8 i=0; i < MAX_TALENT_RANK; ++i) + { + // skip learning spell and no rank spell case + uint32 rankSpellId = talentInfo->RankID[i]; + if(!rankSpellId || rankSpellId==spell_id) + continue; - oldspell_id = itr->first; - removeSpell(itr->first); + // skip unknown ranks + if(!HasSpell(rankSpellId)) + continue; + removeSpell(rankSpellId,false,false); + } } } - - uint16 tmpslot=slot_id; - - if (tmpslot == 0xffff) + else if(spellmgr.GetSpellRank(spell_id)!=0) { - uint16 maxid = 0; - PetSpellMap::iterator itr; - for (itr = m_spells.begin(); itr != m_spells.end(); ++itr) + for (PetSpellMap::const_iterator itr2 = m_spells.begin(); itr2 != m_spells.end(); ++itr2) { - if(itr->second->state == PETSPELL_REMOVED) continue; - if (itr->second->slotId > maxid) maxid = itr->second->slotId; + if(itr2->second.state == PETSPELL_REMOVED) continue; + + if( spellmgr.IsRankSpellDueToSpell(spellInfo,itr2->first) ) + { + // replace by new high rank + if(spellmgr.IsHighRankOfSpell(spell_id,itr2->first)) + { + newspell.active = itr2->second.active; + + if(newspell.active == ACT_ENABLED) + ToggleAutocast(itr2->first, false); + + oldspell_id = itr2->first; + unlearnSpell(itr2->first,false,false); + break; + } + // ignore new lesser rank + else if(spellmgr.IsHighRankOfSpell(itr2->first,spell_id)) + return false; + } } - tmpslot = maxid + 1; } - newspell->slotId = tmpslot; m_spells[spell_id] = newspell; if (IsPassiveSpell(spell_id)) CastSpell(this, spell_id, true); - else if(state == PETSPELL_NEW) - m_charmInfo->AddSpellToAB(oldspell_id, spell_id, (ActiveStates)active); + else + m_charmInfo->AddSpellToActionBar(spell_id); - if(newspell->active == ACT_ENABLED) + 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; } -bool Pet::learnSpell(uint16 spell_id) +bool Pet::learnSpell(uint32 spell_id) { // prevent duplicated entires in spell book if (!addSpell(spell_id)) return false; - Unit* owner = GetOwner(); - if(owner->GetTypeId()==TYPEID_PLAYER) - ((Player*)owner)->PetSpellInitialize(); + if(!m_loading) + { + WorldPacket data(SMSG_PET_LEARNED_SPELL, 2); + data << uint16(spell_id); + m_owner->GetSession()->SendPacket(&data); + m_owner->PetSpellInitialize(); + } return true; } -void Pet::removeSpell(uint16 spell_id) +void Pet::InitLevelupSpellsForLevel() { - PetSpellMap::iterator itr = m_spells.find(spell_id); - if (itr == m_spells.end()) - return; - - if(itr->second->state == PETSPELL_REMOVED) - return; + uint32 level = getLevel(); - if(itr->second->state == PETSPELL_NEW) + if(PetLevelupSpellSet const *levelupSpells = GetCreatureInfo()->family ? spellmgr.GetPetLevelupSpellList(GetCreatureInfo()->family) : NULL) { - delete itr->second; - m_spells.erase(itr); + // PetLevelupSpellSet ordered by levels, process in reversed order + for(PetLevelupSpellSet::const_reverse_iterator itr = levelupSpells->rbegin(); itr != levelupSpells->rend(); ++itr) + { + // will called first if level down + if(itr->first > level) + unlearnSpell(itr->second,true); // will learn prev rank if any + // will called if level up + else + learnSpell(itr->second); // will unlearn prev rank if any + } } - else - itr->second->state = PETSPELL_REMOVED; - RemoveAurasDueToSpell(spell_id); + int32 petSpellsId = GetCreatureInfo()->PetSpellDataId ? -(int32)GetCreatureInfo()->PetSpellDataId : GetEntry(); + + // default spells (can be not learned if pet level (as owner level decrease result for example) less first possible in normal game) + if(PetDefaultSpellsEntry const *defSpells = spellmgr.GetPetDefaultSpellsEntry(petSpellsId)) + { + for(uint8 i = 0; i < MAX_CREATURE_SPELL_DATA_SLOT; ++i) + { + SpellEntry const* spellEntry = sSpellStore.LookupEntry(defSpells->spellid[i]); + if(!spellEntry) + continue; + + // will called first if level down + if(spellEntry->spellLevel > level) + unlearnSpell(spellEntry->Id,true); + // will called if level up + else + learnSpell(spellEntry->Id); + } + } } -bool Pet::_removeSpell(uint16 spell_id) +bool Pet::unlearnSpell(uint32 spell_id, bool learn_prev, bool clear_ab) { - PetSpellMap::iterator itr = m_spells.find(spell_id); - if (itr != m_spells.end()) + if(removeSpell(spell_id,learn_prev,clear_ab)) { - delete itr->second; - m_spells.erase(itr); + if(!m_loading) + { + WorldPacket data(SMSG_PET_REMOVED_SPELL, 2); + data << uint16(spell_id); + m_owner->GetSession()->SendPacket(&data); + } return true; } return false; } -void Pet::InitPetCreateSpells() +bool Pet::removeSpell(uint32 spell_id, bool learn_prev, bool clear_ab) { - m_charmInfo->InitPetActionBar(); + PetSpellMap::iterator itr = m_spells.find(spell_id); + if (itr == m_spells.end()) + return false; - m_spells.clear(); - int32 usedtrainpoints = 0, petspellid; - PetCreateSpellEntry const* CreateSpells = objmgr.GetPetCreateSpellEntry(GetEntry()); - if(CreateSpells) + if(itr->second.state == PETSPELL_REMOVED) + return false; + + if(itr->second.state == PETSPELL_NEW) + m_spells.erase(itr); + else + itr->second.state = PETSPELL_REMOVED; + + 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); + } + + if (learn_prev) + { + if (uint32 prev_id = spellmgr.GetPrevSpellInChain (spell_id)) + learnSpell(prev_id); + else + learn_prev = false; + } + + // if remove last rank or non-ranked then update action bar at server and client if need + if (clear_ab && !learn_prev && m_charmInfo->RemoveSpellFromActionBar(spell_id)) { - for(uint8 i = 0; i < 4; i++) + if(!m_loading) { - if(!CreateSpells->spellid[i]) - break; + // need update action bar for last removed rank + if (Unit* owner = GetOwner()) + if (owner->GetTypeId() == TYPEID_PLAYER) + ((Player*)owner)->PetSpellInitialize(); + } + } + + return true; +} - SpellEntry const *learn_spellproto = sSpellStore.LookupEntry(CreateSpells->spellid[i]); - if(!learn_spellproto) - continue; - if(learn_spellproto->Effect[0] == SPELL_EFFECT_LEARN_SPELL || learn_spellproto->Effect[0] == SPELL_EFFECT_LEARN_PET_SPELL) +void Pet::CleanupActionBar() +{ + for(uint8 i = 0; i < MAX_UNIT_ACTION_BAR_INDEX; ++i) + if(UnitActionBarEntry const* ab = m_charmInfo->GetActionBarEntry(i)) + if(ab->SpellOrAction && ab->IsActionBarForSpell()) { - petspellid = learn_spellproto->EffectTriggerSpell[0]; - Unit* owner = GetOwner(); - if(owner->GetTypeId() == TYPEID_PLAYER && !((Player*)owner)->HasSpell(learn_spellproto->Id)) - { - if(IsPassiveSpell(petspellid)) //learn passive skills when tamed, not sure if thats right - ((Player*)owner)->learnSpell(learn_spellproto->Id); - else - AddTeachSpell(learn_spellproto->EffectTriggerSpell[0], learn_spellproto->Id); - } + if(!HasSpell(ab->SpellOrAction)) + m_charmInfo->SetActionBar(i, 0, ACT_PASSIVE); + else if(ab->Type == ACT_ENABLED) + ToggleAutocast(ab->SpellOrAction, true); } - else - petspellid = learn_spellproto->Id; +} + +void Pet::InitPetCreateSpells() +{ + m_charmInfo->InitPetActionBar(); + m_spells.clear(); + + LearnPetPassives(); + InitLevelupSpellsForLevel(); - addSpell(petspellid); + CastPetAuras(false); +} - SkillLineAbilityMap::const_iterator lower = spellmgr.GetBeginSkillLineAbilityMap(learn_spellproto->EffectTriggerSpell[0]); - SkillLineAbilityMap::const_iterator upper = spellmgr.GetEndSkillLineAbilityMap(learn_spellproto->EffectTriggerSpell[0]); +bool Pet::resetTalents(bool no_cost) +{ + Unit *owner = GetOwner(); + if (!owner || owner->GetTypeId()!=TYPEID_PLAYER) + return false; + + // not need after this call + if(((Player*)owner)->HasAtLoginFlag(AT_LOGIN_RESET_PET_TALENTS)) + ((Player*)owner)->RemoveAtLoginFlag(AT_LOGIN_RESET_PET_TALENTS,true); + + 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; - for(SkillLineAbilityMap::const_iterator _spell_idx = lower; _spell_idx != upper; ++_spell_idx) + 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 (uint32 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 < MAX_TALENT_RANK; j++) + { + for(PetSpellMap::const_iterator itr = m_spells.begin(); itr != m_spells.end();) { - usedtrainpoints += _spell_idx->second->reqtrainpoints; - break; + 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,false); + itr = m_spells.begin(); + continue; + } + else + ++itr; } } } - LearnPetPassives(); + SetFreeTalentPoints(talentPointsForLevel); - CastPetAuras(false); + if(!no_cost) + { + player->ModifyMoney(-(int32)cost); - SetTP(-usedtrainpoints); + m_resetTalentsCost = cost; + m_resetTalentsTime = time(NULL); + } + if(!m_loading) + player->PetSpellInitialize(); + return true; } -void Pet::CheckLearning(uint32 spellid) +void Pet::resetTalentsForAllPetsOf(Player* owner, Pet* online_pet /*= NULL*/) { - //charmed case -> prevent crash - if(GetTypeId() == TYPEID_PLAYER || getPetType() != HUNTER_PET) + // not need after this call + if(((Player*)owner)->HasAtLoginFlag(AT_LOGIN_RESET_PET_TALENTS)) + ((Player*)owner)->RemoveAtLoginFlag(AT_LOGIN_RESET_PET_TALENTS,true); + + // reset for online + if(online_pet) + online_pet->resetTalents(true); + + // now need only reset for offline pets (all pets except online case) + uint32 except_petnumber = online_pet ? online_pet->GetCharmInfo()->GetPetNumber() : 0; + + QueryResult *resultPets = CharacterDatabase.PQuery( + "SELECT id FROM character_pet WHERE owner = '%u' AND id <> '%u'", + owner->GetGUIDLow(),except_petnumber); + + // no offline pets + if(!resultPets) return; - Unit* owner = GetOwner(); + QueryResult *result = CharacterDatabase.PQuery( + "SELECT DISTINCT pet_spell.spell FROM pet_spell, character_pet " + "WHERE character_pet.owner = '%u' AND character_pet.id = pet_spell.guid AND character_pet.id <> %u", + owner->GetGUIDLow(),except_petnumber); - if(m_teachspells.empty() || !owner || owner->GetTypeId() != TYPEID_PLAYER) + if(!result) + { + delete resultPets; return; + } - TeachSpellMap::iterator itr = m_teachspells.find(spellid); - if(itr == m_teachspells.end()) + bool need_comma = false; + std::ostringstream ss; + ss << "DELETE FROM pet_spell WHERE guid IN ("; + + do + { + Field *fields = resultPets->Fetch(); + uint32 id = fields[0].GetUInt32(); + if(need_comma) + ss << ","; + ss << id; + need_comma = true; + } + while( resultPets->NextRow() ); + delete resultPets; + + ss << ") AND spell IN ("; + + bool need_execute = false; + do + { + Field *fields = result->Fetch(); + uint32 spell = fields[0].GetUInt32(); + if(!GetTalentSpellCost(spell)) + continue; + if(need_execute) + ss << ","; + ss << spell; + need_execute = true; + } + while( result->NextRow() ); + delete result; + if(!need_execute) return; + ss << ")"; - if(urand(0, 100) < 10) + CharacterDatabase.Execute(ss.str().c_str()); +} + +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) { - ((Player*)owner)->learnSpell(itr->second); - m_teachspells.erase(itr); + // Remove all talent points + resetTalents(true); } + SetFreeTalentPoints(talentPointsForLevel - m_usedTalentCount); + + Unit *owner = GetOwner(); + if (!owner || owner->GetTypeId() != TYPEID_PLAYER) + return; + + if(!m_loading) + ((Player*)owner)->SendTalentsInfoData(true); } uint32 Pet::resetTalentsCost() const @@ -1715,47 +1672,88 @@ 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)) + if(!IsAutocastableSpell(spellid)) return; - //if(const SpellEntry *tempSpell = GetSpellStore()->LookupEntry(spellid)) - // if(tempSpell->EffectImplicitTargetA[0] != TARGET_SRC_CASTER - // && tempSpell->EffectImplicitTargetA[0] != TARGET_UNIT_TARGET_ENEMY) - // return; - - PetSpellMap::const_iterator itr = m_spells.find((uint16)spellid); + PetSpellMap::iterator itr = m_spells.find(spellid); + if(itr == m_spells.end()) + return; - int i; + uint32 i; if(apply) { - for (i = 0; i < m_autospells.size() && m_autospells[i] != spellid; i++); + for (i = 0; i < m_autospells.size() && m_autospells[i] != spellid; ++i) + ; // just search + if (i == m_autospells.size()) { m_autospells.push_back(spellid); - itr->second->active = ACT_ENABLED; - itr->second->state = PETSPELL_CHANGED; + + if(itr->second.active != ACT_ENABLED) + { + itr->second.active = ACT_ENABLED; + if(itr->second.state != PETSPELL_NEW) + itr->second.state = PETSPELL_CHANGED; + } } } else { AutoSpellList::iterator itr2 = m_autospells.begin(); - for (i = 0; i < m_autospells.size() && m_autospells[i] != spellid; i++, itr2++); + for (i = 0; i < m_autospells.size() && m_autospells[i] != spellid; ++i, itr2++) + ; // just search + if (i < m_autospells.size()) { m_autospells.erase(itr2); - itr->second->active = ACT_DISABLED; - itr->second->state = PETSPELL_CHANGED; + if(itr->second.active != ACT_DISABLED) + { + itr->second.active = ACT_DISABLED; + if(itr->second.state != PETSPELL_NEW) + itr->second.state = PETSPELL_CHANGED; + } } } } -bool Pet::Create(uint32 guidlow, Map *map, uint32 Entry, uint32 pet_number) +bool Pet::IsPermanentPetFor(Player* owner) +{ + switch(getPetType()) + { + case SUMMON_PET: + switch(owner->getClass()) + { + case CLASS_WARLOCK: + return GetCreatureInfo()->type == CREATURE_TYPE_DEMON; + case CLASS_DEATH_KNIGHT: + return GetCreatureInfo()->type == CREATURE_TYPE_UNDEAD; + default: + return false; + } + case HUNTER_PET: + return true; + default: + return false; + } +} + +bool Pet::Create(uint32 guidlow, Map *map, uint32 phaseMask, uint32 Entry, uint32 pet_number) { SetMapId(map->GetId()); SetInstanceId(map->GetInstanceId()); + SetPhaseMask(phaseMask,false); Object::_Create(guidlow, pet_number, HIGHGUID_PET); @@ -1765,18 +1763,15 @@ bool Pet::Create(uint32 guidlow, Map *map, uint32 Entry, uint32 pet_number) if(!InitEntry(Entry)) return false; - SetByteValue(UNIT_FIELD_BYTES_2, 0, SHEATH_STATE_MELEE ); - SetByteValue(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_SANCTUARY | UNIT_BYTE2_FLAG_AURAS | UNIT_BYTE2_FLAG_UNK5 ); - - if(getPetType() == MINI_PET) // always non-attackable - SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + SetSheath(SHEATH_STATE_MELEE); return true; } bool Pet::HasSpell(uint32 spell) const { - return (m_spells.find(spell) != m_spells.end()); + PetSpellMap::const_iterator itr = m_spells.find(spell); + return (itr != m_spells.end() && itr->second.state != PETSPELL_REMOVED ); } // Get all passive spells in our skill line @@ -1797,20 +1792,20 @@ void Pet::LearnPetPassives() // Passive 01~10, Passive 00 (20782, not used), Ferocious Inspiration (34457) // Scale 01~03 (34902~34904, bonus from owner, not used) for(PetFamilySpellsSet::const_iterator petSet = petStore->second.begin(); petSet != petStore->second.end(); ++petSet) - addSpell(*petSet, ACT_DECIDE, PETSPELL_NEW, 0xffff, PETSPELL_FAMILY); + addSpell(*petSet, ACT_DECIDE, PETSPELL_NEW, PETSPELL_FAMILY); } } void Pet::CastPetAuras(bool current) { Unit* owner = GetOwner(); - if(!owner) + if(!owner || owner->GetTypeId()!=TYPEID_PLAYER) return; - if(getPetType() != HUNTER_PET && (getPetType() != SUMMON_PET || owner->getClass() != CLASS_WARLOCK)) + if(!IsPermanentPetFor((Player*)owner)) return; - for(PetAuraSet::iterator itr = owner->m_petAuras.begin(); itr != owner->m_petAuras.end();) + for(PetAuraSet::const_iterator itr = owner->m_petAuras.begin(); itr != owner->m_petAuras.end();) { PetAura const* pa = *itr; ++itr; @@ -1837,3 +1832,36 @@ void Pet::CastPetAura(PetAura const* aura) CastSpell(this, auraId, true); } +void Pet::learnSpellHighRank(uint32 spellid) +{ + learnSpell(spellid); + + if(uint32 next = spellmgr.GetNextSpellInChain(spellid)) + learnSpellHighRank(next); +} + +void Pet::SynchronizeLevelWithOwner() +{ + Unit* owner = GetOwner(); + if (!owner || owner->GetTypeId() != TYPEID_PLAYER) + return; + + switch(getPetType()) + { + // always same level + case SUMMON_PET: + GivePetLevel(owner->getLevel()); + break; + // can't be greater owner level + case HUNTER_PET: + if(getLevel() > owner->getLevel()) + { + GivePetLevel(owner->getLevel()); + SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP, objmgr.GetXPForLevel(owner->getLevel())/4); + SetUInt32Value(UNIT_FIELD_PETEXPERIENCE, GetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP)-1); + } + break; + default: + break; + } +} |