aboutsummaryrefslogtreecommitdiff
path: root/src/game/Pet.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/game/Pet.cpp')
-rw-r--r--src/game/Pet.cpp1776
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;
+ }
+}