Core/Pets: merged funjoker's new Pet Slot system

* fixed packets and handling for stable master
* fixed Control Pet / Demon handling
* corrected wrong trainer spell for the Control spells
* Added base support for Assist behaivior
* corrected action bar layout
* Hunter Pets will now have the same level as their tamer (patch 4.1 change)
* corrected health stats for pets
This commit is contained in:
Ovalord committed 2018-01-07 02:00:31 +01:00
1 parent b2945a5c58
commit b7c15093a6
32 files changed
+803 -685

No files matched your search

@@ -0,0 +1 @@
ALTER TABLE `character_pet` ADD COLUMN `active` TINYINT(3) UNSIGNED NOT NULL DEFAULT '0' AFTER `renamed`;
@@ -0,0 +1,4 @@
DELETE FROM `npc_trainer` WHERE `SpellID` IN (93321, 93375, 79682, 80388);
INSERT INTO `npc_trainer` (`ID`, `SpellID`, `MoneyCost`, `ReqLevel`) VALUES
(200003, 93321, 110, 10),
(200009, 93375, 680, 10);
@@ -43,11 +43,11 @@ void CharacterDatabaseConnection::DoPrepareStatements()
PrepareStatement(CHAR_SEL_MAIL_LIST_ITEMS, "SELECT itemEntry,count FROM item_instance WHERE guid = ?", CONNECTION_SYNCH);
PrepareStatement(CHAR_SEL_ENUM, "SELECT c.guid, c.name, c.race, c.class, c.gender, c.skin, c.face, c.hairStyle, c.hairColor, c.facialStyle, c.level, c.zone, c.map, c.position_x, c.position_y, c.position_z, "
"gm.guildid, c.playerFlags, c.at_login, cp.entry, cp.modelid, cp.level, c.equipmentCache, cb.guid, c.slot "
"FROM characters AS c LEFT JOIN character_pet AS cp ON c.guid = cp.owner AND cp.slot = ? LEFT JOIN guild_member AS gm ON c.guid = gm.guid "
"FROM characters AS c LEFT JOIN character_pet AS cp ON c.guid = cp.owner AND cp.active = 1 LEFT JOIN guild_member AS gm ON c.guid = gm.guid "
"LEFT JOIN character_banned AS cb ON c.guid = cb.guid AND cb.active = 1 WHERE c.account = ? AND c.deleteInfos_Name IS NULL", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_ENUM_DECLINED_NAME, "SELECT c.guid, c.name, c.race, c.class, c.gender, c.skin, c.face, c.hairStyle, c.hairColor, c.facialStyle, c.level, c.zone, c.map, "
"c.position_x, c.position_y, c.position_z, gm.guildid, c.playerFlags, c.at_login, cp.entry, cp.modelid, cp.level, c.equipmentCache, "
"cb.guid, c.slot, cd.genitive FROM characters AS c LEFT JOIN character_pet AS cp ON c.guid = cp.owner AND cp.slot = ? "
"cb.guid, c.slot, cd.genitive FROM characters AS c LEFT JOIN character_pet AS cp ON c.guid = cp.owner AND cp.active = 1 "
"LEFT JOIN character_declinedname AS cd ON c.guid = cd.guid LEFT JOIN guild_member AS gm ON c.guid = gm.guid "
"LEFT JOIN character_banned AS cb ON c.guid = cb.guid AND cb.active = 1 WHERE c.account = ? AND c.deleteInfos_Name IS NULL", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_FREE_NAME, "SELECT guid, name, at_login FROM characters WHERE guid = ? AND account = ? AND NOT EXISTS (SELECT NULL FROM characters WHERE name = ?)", CONNECTION_ASYNC);
@@ -600,11 +600,10 @@ void CharacterDatabaseConnection::DoPrepareStatements()
// Pet
PrepareStatement(CHAR_SEL_PET_SLOTS, "SELECT owner, slot FROM character_pet WHERE owner = ? AND slot >= ? AND slot <= ? ORDER BY slot", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_PET_SLOTS_DETAIL, "SELECT owner, id, entry, level, name FROM character_pet WHERE owner = ? AND slot >= ? AND slot <= ? ORDER BY slot", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_PET_SLOTS_DETAIL, "SELECT slot, id, entry, modelid, level, name FROM character_pet WHERE owner = ? AND slot >= ? AND slot <= ? ORDER BY slot", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_PET_ENTRY, "SELECT entry FROM character_pet WHERE owner = ? AND id = ? AND slot >= ? AND slot <= ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_PET_SLOT_BY_ID, "SELECT slot, entry FROM character_pet WHERE owner = ? AND id = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_PET_SLOT_BY_ID, "SELECT slot, id, entry FROM character_pet WHERE owner = ? AND id = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_PET_SPELL_LIST, "SELECT DISTINCT pet_spell.spell FROM pet_spell, character_pet WHERE character_pet.owner = ? AND character_pet.id = pet_spell.guid AND character_pet.id <> ?", CONNECTION_SYNCH);
PrepareStatement(CHAR_SEL_CHAR_PET, "SELECT id FROM character_pet WHERE owner = ? AND id <> ?", CONNECTION_SYNCH);
PrepareStatement(CHAR_SEL_CHAR_PETS, "SELECT id FROM character_pet WHERE owner = ?", CONNECTION_SYNCH);
PrepareStatement(CHAR_DEL_CHAR_PET_DECLINEDNAME_BY_OWNER, "DELETE FROM character_pet_declinedname WHERE owner = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_DEL_CHAR_PET_DECLINEDNAME, "DELETE FROM character_pet_declinedname WHERE id = ?", CONNECTION_ASYNC);
@@ -621,17 +620,16 @@ void CharacterDatabaseConnection::DoPrepareStatements()
PrepareStatement(CHAR_INS_PET_SPELL, "INSERT INTO pet_spell (guid, spell, active) VALUES (?, ?, ?)", CONNECTION_BOTH);
PrepareStatement(CHAR_INS_PET_AURA, "INSERT INTO pet_aura (guid, casterGuid, spell, effectMask, recalculateMask, stackCount, amount0, amount1, amount2, "
"base_amount0, base_amount1, base_amount2, maxDuration, remainTime, remainCharges) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_BOTH);
PrepareStatement(CHAR_SEL_CHAR_PET_BY_ENTRY, "SELECT id, entry, owner, modelid, level, exp, Reactstate, slot, name, renamed, curhealth, curmana, abdata, savetime, CreatedBySpell, PetType FROM character_pet WHERE owner = ? AND id = ?", CONNECTION_SYNCH);
PrepareStatement(CHAR_SEL_CHAR_PET_BY_ENTRY_AND_SLOT_2, "SELECT id, entry, owner, modelid, level, exp, Reactstate, slot, name, renamed, curhealth, curmana, abdata, savetime, CreatedBySpell, PetType FROM character_pet WHERE owner = ? AND entry = ? AND (slot = ? OR slot > ?)", CONNECTION_SYNCH);
PrepareStatement(CHAR_SEL_CHAR_PET_BY_SLOT, "SELECT id, entry, owner, modelid, level, exp, Reactstate, slot, name, renamed, curhealth, curmana, abdata, savetime, CreatedBySpell, PetType FROM character_pet WHERE owner = ? AND (slot = ? OR slot > ?) ", CONNECTION_SYNCH);
PrepareStatement(CHAR_SEL_CHAR_PET_BY_ENTRY_AND_SLOT, "SELECT id, entry, owner, modelid, level, exp, Reactstate, slot, name, renamed, curhealth, curmana, abdata, savetime, CreatedBySpell, PetType FROM character_pet WHERE owner = ? AND slot = ?", CONNECTION_SYNCH);
PrepareStatement(CHAR_DEL_CHAR_PET_BY_OWNER, "DELETE FROM character_pet WHERE owner = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_UPD_CHAR_PET_NAME, "UPDATE character_pet SET name = ?, renamed = 1 WHERE owner = ? AND id = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_UPD_CHAR_PET_SLOT_BY_SLOT_EXCLUDE_ID, "UPDATE character_pet SET slot = ? WHERE owner = ? AND slot = ? AND id <> ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_UPD_CHAR_PET_SLOT_BY_SLOT, "UPDATE character_pet SET slot = ? WHERE owner = ? AND slot = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_UPD_CHAR_PET_SLOT_BY_ID, "UPDATE character_pet SET slot = ? WHERE owner = ? AND id = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_DEL_CHAR_PET_BY_ID, "DELETE FROM character_pet WHERE id = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_DEL_CHAR_PET_BY_SLOT, "DELETE FROM character_pet WHERE owner = ? AND (slot = ? OR slot > ?)", CONNECTION_ASYNC);
PrepareStatement(CHAR_INS_PET, "INSERT INTO character_pet (id, entry, owner, modelid, level, exp, Reactstate, slot, name, renamed, active, curhealth, curmana, abdata, savetime, CreatedBySpell, PetType) "
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_PET_ID_BY_SLOT, "SELECT id, owner, slot FROM character_pet WHERE owner = ? AND slot = ?", CONNECTION_SYNCH);
PrepareStatement(CHAR_SEL_UNUSED_ACTIVE_PET_SLOT, "SELECT cp.slot+1 FROM character_pet cp LEFT JOIN character_pet cpa ON cp.slot+1 = cpa.slot WHERE cpa.slot IS NULL AND cp.slot + 1 <= 4 ORDER BY cp.slot LIMIT 1", CONNECTION_SYNCH);
PrepareStatement(CHAR_SEL_CHAR_ALL_PETS_DETAIL, "SELECT id, entry, owner, modelid, level, exp, Reactstate, slot, name, renamed, active, curhealth, curmana, abdata, savetime, CreatedBySpell, PetType FROM character_pet WHERE owner = ? ORDER BY slot", CONNECTION_ASYNC);
// PvPstats
PrepareStatement(CHAR_SEL_PVPSTATS_MAXID, "SELECT MAX(id) FROM pvpstats_battlegrounds", CONNECTION_SYNCH);
@@ -522,25 +522,22 @@ enum CharacterDatabaseStatements
CHAR_DEL_PET_SPELLS,
CHAR_DEL_CHAR_PET_BY_OWNER,
CHAR_DEL_CHAR_PET_DECLINEDNAME_BY_OWNER,
CHAR_SEL_CHAR_PET_BY_ENTRY_AND_SLOT,
CHAR_SEL_PET_SLOTS,
CHAR_SEL_PET_ID_BY_SLOT,
CHAR_SEL_UNUSED_ACTIVE_PET_SLOT,
CHAR_SEL_PET_SLOTS_DETAIL,
CHAR_SEL_PET_ENTRY,
CHAR_SEL_PET_SLOT_BY_ID,
CHAR_SEL_PET_SPELL_LIST,
CHAR_SEL_CHAR_PET,
CHAR_SEL_CHAR_PETS,
CHAR_SEL_CHAR_PET_BY_ENTRY,
CHAR_SEL_CHAR_PET_BY_ENTRY_AND_SLOT_2,
CHAR_SEL_CHAR_PET_BY_SLOT,
CHAR_DEL_CHAR_PET_DECLINEDNAME,
CHAR_INS_CHAR_PET_DECLINEDNAME,
CHAR_UPD_CHAR_PET_NAME,
CHAR_UPD_CHAR_PET_SLOT_BY_SLOT_EXCLUDE_ID,
CHAR_UPD_CHAR_PET_SLOT_BY_SLOT,
CHAR_UPD_CHAR_PET_SLOT_BY_ID,
CHAR_DEL_CHAR_PET_BY_ID,
CHAR_DEL_CHAR_PET_BY_SLOT,
CHAR_INS_PET,
CHAR_SEL_CHAR_ALL_PETS_DETAIL,
CHAR_SEL_ITEMCONTAINER_ITEMS,
CHAR_DEL_ITEMCONTAINER_ITEMS,
+12 -12
View File
@@ -347,8 +347,8 @@ void PetAI::OwnerAttackedBy(Unit* attacker)
if (!attacker || !me->IsAlive())
return;
// Passive pets don't do anything
if (me->HasReactState(REACT_PASSIVE))
// Passive and assist pets don't do anything
if (me->HasReactState(REACT_PASSIVE) || me->HasReactState(REACT_ASSIST))
return;
// Prevent pet from disengaging from current target
@@ -368,8 +368,8 @@ void PetAI::OwnerAttacked(Unit* target)
if (!target || !me->IsAlive())
return;
// Passive pets don't do anything
if (me->HasReactState(REACT_PASSIVE))
// Passive and Defesnive pets don't do anything
if (me->HasReactState(REACT_PASSIVE) || me->HasReactState(REACT_DEFENSIVE))
return;
// Prevent pet from disengaging from current target
@@ -388,8 +388,8 @@ Unit* PetAI::SelectNextTarget(bool allowAutoSelect) const
// The parameter: allowAutoSelect lets us disable aggressive pet auto targeting for certain situations
// Passive pets don't do next target selection
if (me->HasReactState(REACT_PASSIVE))
return NULL;
if (me->HasReactState(REACT_PASSIVE) || me->HasReactState(REACT_ASSIST))
return nullptr;
// Check pet attackers first so we don't drag a bunch of targets to the owner
if (Unit* myAttacker = me->getAttackerForHelper())
@@ -398,7 +398,7 @@ Unit* PetAI::SelectNextTarget(bool allowAutoSelect) const
// Not sure why we wouldn't have an owner but just in case...
if (!me->GetCharmerOrOwner())
return NULL;
return nullptr;
// Check owner attackers
if (Unit* ownerAttacker = me->GetCharmerOrOwner()->getAttackerForHelper())
@@ -421,7 +421,7 @@ Unit* PetAI::SelectNextTarget(bool allowAutoSelect) const
}
// Default - no valid targets
return NULL;
return nullptr;
}
void PetAI::HandleReturnMovement()
@@ -433,7 +433,7 @@ void PetAI::HandleReturnMovement()
if (me->IsCharmed())
return;
if (me->GetCharmInfo()->HasCommandState(COMMAND_STAY))
if (me->GetCharmInfo()->HasCommandState(COMMAND_STAY) || me->GetCharmInfo()->HasCommandState(COMMAND_MOVE_TO))
{
if (!me->GetCharmInfo()->IsAtStay() && !me->GetCharmInfo()->IsReturning())
{
@@ -447,7 +447,7 @@ void PetAI::HandleReturnMovement()
me->GetMotionMaster()->MovePoint(me->GetGUID().GetCounter(), x, y, z);
}
}
else if (!me->GetCharmInfo()->HasCommandState(COMMAND_MOVE_TO))
if (me->GetCharmInfo()->HasCommandState(COMMAND_FOLLOW))
{
if (!me->GetCharmInfo()->IsFollowing() && !me->GetCharmInfo()->IsReturning())
{
@@ -562,8 +562,8 @@ bool PetAI::CanAttack(Unit* target)
if (me->GetCharmInfo()->IsReturning())
return !me->GetCharmInfo()->IsCommandFollow();
// Stay - can attack if target is within range or commanded to
if (me->GetCharmInfo()->HasCommandState(COMMAND_STAY))
// Stay/Move To - can attack if target is within range or commanded to
if (me->GetCharmInfo()->HasCommandState(COMMAND_STAY) || me->GetCharmInfo()->HasCommandState(COMMAND_MOVE_TO))
return (me->IsWithinMeleeRange(target) || me->GetCharmInfo()->IsCommandAttack());
// Pets attacking something (or chasing) should only switch targets if owner tells them to
@@ -975,7 +975,7 @@ void Battleground::RemovePlayerAtLeave(ObjectGuid guid, bool Transport, bool Sen
bgTypeId = BATTLEGROUND_AA; // set the bg type to all arenas (it will be used for queue refreshing)
// unsummon current and summon old pet if there was one and there isn't a current pet
player->RemovePet(NULL, PET_SAVE_NOT_IN_SLOT);
player->RemovePet(nullptr, PET_SAVE_DISMISS);
player->ResummonPetTemporaryUnSummonedIfAny();
}
@@ -251,7 +251,7 @@ void TempSummon::UnSummon(uint32 msTime)
//ASSERT(!IsPet());
if (IsPet())
{
((Pet*)this)->Remove(PET_SAVE_NOT_IN_SLOT);
((Pet*)this)->Remove(PET_SAVE_DISMISS);
ASSERT(!IsInWorld());
return;
}
@@ -325,6 +325,21 @@ bool Minion::IsGuardianPet() const
return IsPet() || (m_Properties && m_Properties->Category == SUMMON_CATEGORY_PET);
}
bool Minion::IsWarlockMinion() const
{
switch (GetEntry())
{
case ENTRY_IMP:
case ENTRY_VOIDWALKER:
case ENTRY_SUCCUBUS:
case ENTRY_FELHUNTER:
case ENTRY_FELGUARD:
return true;
default:
return false;
}
}
Guardian::Guardian(SummonPropertiesEntry const* properties, Unit* owner, bool isWorldObject) : Minion(properties, owner, isWorldObject)
, m_bonusSpellDamage(0)
{
@@ -37,6 +37,33 @@ struct TempSummonData
uint32 time; ///< Despawn time, usable only with certain temp summon types
};
enum PetEntries
{
// Warlock Pet/Minions
ENTRY_IMP = 416,
ENTRY_VOIDWALKER = 1860,
ENTRY_SUCCUBUS = 1863,
ENTRY_FELHUNTER = 417,
ENTRY_FELGUARD = 17252,
// Mage Pet
ENTRY_WATER_ELEMENTAL = 510,
// Druid Minion
ENTRY_TREANT = 1964,
// Shaman Minions
ENTRY_FIRE_ELEMENTAL = 15438,
ENTRY_SPIRIT_WOLF = 29264,
// Death Knight Pet/Minions
ENTRY_GHOUL = 26125,
ENTRY_BLOODWORM = 28017,
ENTRY_RUNIC_WEAPON = 27893,
ENTRY_GARGOYLE = 27829,
ENTRY_SHADOWFIEND = 19668
};
class TC_GAME_API TempSummon : public Creature
{
public:
@@ -76,6 +103,7 @@ class TC_GAME_API Minion : public TempSummon
bool IsPetGhoul() const { return GetEntry() == 26125; } // Ghoul may be guardian or pet
bool IsSpiritWolf() const { return GetEntry() == 29264; } // Spirit wolf from feral spirits
bool IsGuardianPet() const;
bool IsWarlockMinion() const;
protected:
Unit* const m_owner;
float m_followAngle;
+139 -146
View File
@@ -95,78 +95,58 @@ void Pet::RemoveFromWorld()
}
}
bool Pet::LoadPetFromDB(Player* owner, uint32 petEntry, uint32 petnumber, bool current)
bool Pet::LoadPetData(Player* owner, uint32 petEntry, uint32 petnumber, bool current)
{
m_loading = true;
ObjectGuid::LowType ownerid = owner->GetGUID().GetCounter();
PreparedStatement* stmt;
PreparedQueryResult result;
PlayerPetData* playerPetData;
if (petnumber)
{
// Known petnumber entry
stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_PET_BY_ENTRY);
stmt->setUInt32(0, ownerid);
stmt->setUInt32(1, petnumber);
playerPetData = owner->GetPlayerPetDataById(petnumber);
}
else if (current)
else if ((getPetType() == SUMMON_PET) && petEntry)
{
// Current pet (slot 0)
stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_PET_BY_ENTRY_AND_SLOT);
stmt->setUInt32(0, ownerid);
stmt->setUInt8(1, uint8(PET_SAVE_AS_CURRENT));
playerPetData = owner->GetPlayerPetDataByCreatureId(petEntry);
}
else if (petEntry)
else if (getPetType() == HUNTER_PET)
{
// known petEntry entry (unique for summoned pet, but non unique for hunter pet (only from current or not stabled pets)
stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_PET_BY_ENTRY_AND_SLOT_2);
stmt->setUInt32(0, ownerid);
stmt->setUInt32(1, petEntry);
stmt->setUInt8(2, uint8(PET_SAVE_AS_CURRENT));
stmt->setUInt8(3, uint8(PET_SAVE_LAST_STABLE_SLOT));
playerPetData = owner->GetPlayerPetDataBySlot(petEntry);
}
else
{
// Any current or other non-stabled pet (for hunter "call pet")
stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_PET_BY_SLOT);
stmt->setUInt32(0, ownerid);
stmt->setUInt8(1, uint8(PET_SAVE_AS_CURRENT));
stmt->setUInt8(2, uint8(PET_SAVE_LAST_STABLE_SLOT));
playerPetData = owner->GetPlayerPetDataCurrent();
}
result = CharacterDatabase.Query(stmt);
if (!result)
if (!playerPetData)
{
m_loading = false;
return false;
}
Field* fields = result->Fetch();
// update for case of current pet "slot = 0"
petEntry = fields[1].GetUInt32();
petEntry = playerPetData->CreatureId;
if (!petEntry)
return false;
uint32 summonSpellId = fields[14].GetUInt32();
uint32 summonSpellId = playerPetData->SummonSpellId;
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(summonSpellId);
bool isTemporarySummon = spellInfo && spellInfo->GetDuration() > 0;
if (current && isTemporarySummon)
return false;
PetType petType = PetType(fields[15].GetUInt8());
PetType petType = playerPetData->Type;
if (petType == HUNTER_PET)
{
CreatureTemplate const* creatureInfo = sObjectMgr->GetCreatureTemplate(petEntry);
if (!creatureInfo || !creatureInfo->IsTameable(owner->CanTameExoticPets()))
{
owner->GetSession()->SendStableResult(STABLE_ERR_EXOTIC);
return false;
}
}
uint32 petId = fields[0].GetUInt32();
uint32 petId = playerPetData->PetId;
if (current && owner->IsPetNeedBeTemporaryUnsummoned())
{
@@ -204,11 +184,11 @@ bool Pet::LoadPetFromDB(Player* owner, uint32 petEntry, uint32 petnumber, bool c
m_charmInfo->SetPetNumber(petId, IsPermanentPetFor(owner));
SetDisplayId(fields[3].GetUInt32());
SetNativeDisplayId(fields[3].GetUInt32());
uint32 petlevel = fields[4].GetUInt16();
SetDisplayId(playerPetData->DisplayId);
SetNativeDisplayId(playerPetData->DisplayId);
uint32 petlevel = playerPetData->Petlevel;
SetUInt32Value(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_NONE);
SetName(fields[8].GetString());
SetName(playerPetData->Name);
switch (getPetType())
{
@@ -224,7 +204,7 @@ bool Pet::LoadPetFromDB(Player* owner, uint32 petEntry, uint32 petnumber, bool c
SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_GENDER, GENDER_NONE);
SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_POWER_TYPE, POWER_FOCUS);
SetSheath(SHEATH_STATE_MELEE);
SetByteFlag(UNIT_FIELD_BYTES_2, UNIT_BYTES_2_OFFSET_PET_FLAGS, fields[9].GetBool() ? UNIT_CAN_BE_ABANDONED : UNIT_CAN_BE_RENAMED | UNIT_CAN_BE_ABANDONED);
SetByteFlag(UNIT_FIELD_BYTES_2, UNIT_BYTES_2_OFFSET_PET_FLAGS, playerPetData->Renamed ? UNIT_CAN_BE_ABANDONED : UNIT_CAN_BE_RENAMED | UNIT_CAN_BE_ABANDONED);
SetUInt32Value(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE);
// this enables popup window (pet abandon, cancel)
@@ -235,11 +215,11 @@ bool Pet::LoadPetFromDB(Player* owner, uint32 petEntry, uint32 petnumber, bool c
break;
}
SetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP, uint32(time(NULL))); // cast can't be helped here
SetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP, uint32(time(nullptr))); // cast can't be helped here
SetCreatorGUID(owner->GetGUID());
InitStatsForLevel(petlevel);
SetUInt32Value(UNIT_FIELD_PETEXPERIENCE, fields[5].GetUInt32());
SetUInt32Value(UNIT_FIELD_PETEXPERIENCE, playerPetData->PetExp);
SynchronizeLevelWithOwner();
@@ -254,15 +234,15 @@ bool Pet::LoadPetFromDB(Player* owner, uint32 petEntry, uint32 petnumber, bool c
return false;
}
SetReactState(ReactStates(fields[6].GetUInt8()));
SetReactState(ReactStates(playerPetData->Reactstate));
SetCanModifyStats(true);
if (getPetType() == SUMMON_PET && !current) //all (?) summon pets come with full health when called, but not when they are current
SetPower(POWER_MANA, GetMaxPower(POWER_MANA));
else
{
uint32 savedhealth = fields[10].GetUInt32();
uint32 savedmana = fields[11].GetUInt32();
uint32 savedhealth = playerPetData->SavedHealth;
uint32 savedmana = playerPetData->SavedMana;
if (!savedhealth && getPetType() == HUNTER_PET)
setDeathState(JUST_DIED);
else
@@ -272,29 +252,7 @@ bool Pet::LoadPetFromDB(Player* owner, uint32 petEntry, uint32 petnumber, bool c
}
}
// set current pet as current
// 0=current
// 1..MAX_PET_STABLES in stable slot
// PET_SAVE_NOT_IN_SLOT(100) = not stable slot (summoning))
if (fields[7].GetUInt8())
{
SQLTransaction trans = CharacterDatabase.BeginTransaction();
stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHAR_PET_SLOT_BY_SLOT_EXCLUDE_ID);
stmt->setUInt8(0, uint8(PET_SAVE_NOT_IN_SLOT));
stmt->setUInt32(1, ownerid);
stmt->setUInt8(2, uint8(PET_SAVE_AS_CURRENT));
stmt->setUInt32(3, m_charmInfo->GetPetNumber());
trans->Append(stmt);
stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHAR_PET_SLOT_BY_ID);
stmt->setUInt8(0, uint8(PET_SAVE_AS_CURRENT));
stmt->setUInt32(1, ownerid);
stmt->setUInt32(2, m_charmInfo->GetPetNumber());
trans->Append(stmt);
CharacterDatabase.CommitTransaction(trans);
}
SetSlot(playerPetData->Slot);
// 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
@@ -317,13 +275,13 @@ bool Pet::LoadPetFromDB(Player* owner, uint32 petEntry, uint32 petnumber, bool c
InitTalentForLevel(); // set original talents points before spell loading
uint32 timediff = uint32(time(NULL) - fields[13].GetUInt32());
uint32 timediff = uint32(time(nullptr) - playerPetData->Timediff);
_LoadAuras(timediff);
// load action bar, if data broken will fill later by default spells.
if (!isTemporarySummon)
{
m_charmInfo->LoadPetActionBar(fields[12].GetString());
m_charmInfo->LoadPetActionBar(playerPetData->Actionbar);
_LoadSpells();
InitTalentForLevel(); // re-init to check talent count
@@ -349,10 +307,10 @@ bool Pet::LoadPetFromDB(Player* owner, uint32 petEntry, uint32 petnumber, bool c
if (getPetType() == HUNTER_PET)
{
stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PET_DECLINED_NAME);
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PET_DECLINED_NAME);
stmt->setUInt32(0, owner->GetGUID().GetCounter());
stmt->setUInt32(1, GetCharmInfo()->GetPetNumber());
result = CharacterDatabase.Query(stmt);
PreparedQueryResult result = CharacterDatabase.Query(stmt);
if (result)
{
@@ -374,6 +332,8 @@ bool Pet::LoadPetFromDB(Player* owner, uint32 petEntry, uint32 petnumber, bool c
LoadTemplateImmunities();
m_loading = false;
SetActive(true);
return true;
}
@@ -390,20 +350,6 @@ void Pet::SavePetToDB(PetSaveMode mode)
if (!GetOwnerGUID().IsPlayer())
return;
Player* owner = GetOwner();
// not save pet as current if another pet temporary unsummoned
if (mode == PET_SAVE_AS_CURRENT && owner->GetTemporaryUnsummonedPetNumber() &&
owner->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);
@@ -411,16 +357,33 @@ void Pet::SavePetToDB(PetSaveMode mode)
// save auras before possibly removing them
_SaveAuras(trans);
// stable and not in slot saves
if (mode > PET_SAVE_AS_CURRENT)
RemoveAllAuras();
_SaveSpells(trans);
GetSpellHistory()->SaveToDB<Pet>(trans);
CharacterDatabase.CommitTransaction(trans);
// current/stable/not_in_slot
if (mode >= PET_SAVE_AS_CURRENT)
if (mode == PET_SAVE_NEW_PET)
{
Optional<uint8> slot = IsHunterPet() ? GetOwner()->GetFirstUnusedActivePetSlot() : GetOwner()->GetFirstUnusedPetSlot();
if (slot)
SetSlot(*slot);
else
mode = PET_SAVE_AS_DELETED;
}
if (mode == PET_SAVE_DISMISS)
{
RemoveAllAuras();
SetActive(false);
}
if (mode == PET_SAVE_LOGOUT)
{
RemoveAllAuras();
}
// whole pet is saved to DB
if (mode >= PET_SAVE_CURRENT_STATE)
{
ObjectGuid::LowType ownerLowGUID = GetOwnerGUID().GetCounter();
std::string name = m_name;
@@ -432,62 +395,77 @@ void Pet::SavePetToDB(PetSaveMode mode)
stmt->setUInt32(0, m_charmInfo->GetPetNumber());
trans->Append(stmt);
// prevent duplicate using slot (except PET_SAVE_NOT_IN_SLOT)
if (mode <= PET_SAVE_LAST_STABLE_SLOT)
{
stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHAR_PET_SLOT_BY_SLOT);
stmt->setUInt8(0, uint8(PET_SAVE_NOT_IN_SLOT));
stmt->setUInt32(1, ownerLowGUID);
stmt->setUInt8(2, uint8(mode));
trans->Append(stmt);
}
// 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))
{
stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_PET_BY_SLOT);
stmt->setUInt32(0, ownerLowGUID);
stmt->setUInt8(1, uint8(PET_SAVE_AS_CURRENT));
stmt->setUInt8(2, uint8(PET_SAVE_LAST_STABLE_SLOT));
trans->Append(stmt);
}
// save pet
std::ostringstream ss;
ss << "INSERT INTO character_pet (id, entry, owner, modelid, level, exp, Reactstate, slot, name, renamed, curhealth, curmana, abdata, savetime, CreatedBySpell, PetType) "
<< "VALUES ("
<< m_charmInfo->GetPetNumber() << ','
<< GetEntry() << ','
<< ownerLowGUID << ','
<< GetNativeDisplayId() << ','
<< uint32(getLevel()) << ','
<< GetUInt32Value(UNIT_FIELD_PETEXPERIENCE) << ','
<< uint32(GetReactState()) << ','
<< uint32(mode) << ", '"
<< name.c_str() << "', "
<< uint32(HasByteFlag(UNIT_FIELD_BYTES_2, UNIT_BYTES_2_OFFSET_PET_FLAGS, UNIT_CAN_BE_RENAMED) ? 0 : 1) << ','
<< curhealth << ','
<< curmana << ", '";
stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_PET);
stmt->setUInt32(0, m_charmInfo->GetPetNumber());
stmt->setUInt32(1, GetEntry());
stmt->setUInt64(2, ownerLowGUID);
stmt->setUInt32(3, GetNativeDisplayId());
stmt->setUInt8(4, getLevel());
stmt->setUInt32(5, GetUInt32Value(UNIT_FIELD_PETEXPERIENCE));
stmt->setUInt8(6, GetReactState());
stmt->setInt16(7, m_petSlot);
stmt->setString(8, m_name);
stmt->setUInt8(9, HasByteFlag(UNIT_FIELD_BYTES_2, UNIT_BYTES_2_OFFSET_PET_FLAGS, UNIT_CAN_BE_RENAMED) ? 0 : 1);
stmt->setUInt8(10, IsActive() ? 1 : 0);
stmt->setUInt32(11, curhealth);
stmt->setUInt32(12, curmana);
for (uint32 i = ACTION_BAR_INDEX_START; i < ACTION_BAR_INDEX_END; ++i)
{
ss << uint32(m_charmInfo->GetActionBarEntry(i)->GetType()) << ' '
<< uint32(m_charmInfo->GetActionBarEntry(i)->GetAction()) << ' ';
};
stmt->setString(13, GenerateActionBarData());
ss << "', "
<< time(NULL) << ','
<< GetUInt32Value(UNIT_CREATED_BY_SPELL) << ','
<< uint32(getPetType()) << ')';
stmt->setUInt32(14, time(nullptr)); // unsure about this
stmt->setUInt32(15, GetUInt32Value(UNIT_CREATED_BY_SPELL));
stmt->setUInt8(16, getPetType());
trans->Append(stmt);
trans->Append(ss.str().c_str());
CharacterDatabase.CommitTransaction(trans);
PlayerPetData* playerPetData = (mode < PET_SAVE_NEW_PET) ? GetOwner()->GetPlayerPetDataById(m_charmInfo->GetPetNumber()) : new PlayerPetData();
// save as new if no data for Pet in PlayerPetDataStore
if (mode < PET_SAVE_NEW_PET && !playerPetData)
{
mode = PET_SAVE_NEW_PET;
playerPetData = new PlayerPetData();
}
uint32 petId = m_charmInfo->GetPetNumber();
if (m_petSlot > PET_SLOT_LAST)
{
TC_LOG_ERROR("sql.sql", "Player::LoadPetsFromDB: bad slot %u for pet %u!", m_petSlot, petId);
}
playerPetData->PetId = petId;
playerPetData->CreatureId = GetEntry();
playerPetData->Owner = ownerLowGUID;
playerPetData->DisplayId = GetNativeDisplayId();
playerPetData->Petlevel = getLevel();
playerPetData->PetExp = GetUInt32Value(UNIT_FIELD_PETEXPERIENCE);
playerPetData->Reactstate = GetReactState();
playerPetData->Slot = m_petSlot;
playerPetData->Name = m_name;
playerPetData->Renamed = HasByteFlag(UNIT_FIELD_BYTES_2, UNIT_BYTES_2_OFFSET_PET_FLAGS, UNIT_CAN_BE_RENAMED) ? 0 : 1;
playerPetData->Active = IsActive() ? 1 : 0;
playerPetData->SavedHealth = curhealth;
playerPetData->SavedMana = curmana;
playerPetData->Actionbar = GenerateActionBarData();
playerPetData->Timediff = time(nullptr);
playerPetData->SummonSpellId = GetUInt32Value(UNIT_CREATED_BY_SPELL);
playerPetData->Type = getPetType();
if (mode == PET_SAVE_NEW_PET)
GetOwner()->AddToPlayerPetDataStore(playerPetData);
}
// delete
else
{
RemoveAllAuras();
DeleteFromDB(m_charmInfo->GetPetNumber());
GetOwner()->DeleteFromPlayerPetDataStore(m_charmInfo->GetPetNumber());
GetOwner()->GetSession()->SendStablePet(ObjectGuid::Empty);
}
}
@@ -551,9 +529,9 @@ void Pet::Update(uint32 diff)
{
case CORPSE:
{
if (getPetType() != HUNTER_PET || m_corpseRemoveTime <= time(NULL))
if (!IsHunterPet() || m_corpseRemoveTime <= time(nullptr))
{
Remove(PET_SAVE_NOT_IN_SLOT); //hunters' pets never get removed because of death, NEVER!
Remove(PET_SAVE_DISMISS); //hunters' pets never get removed because of death, NEVER!
return;
}
break;
@@ -565,7 +543,7 @@ void Pet::Update(uint32 diff)
if ((!IsWithinDistInMap(owner, GetMap()->GetVisibilityRange()) && !isPossessed()) || (isControlled() && !owner->GetPetGUID()))
//if (!owner || (!IsWithinDistInMap(owner, GetMap()->GetVisibilityDistance()) && (owner->GetCharmGUID() && (owner->GetCharmGUID() != GetGUID()))) || (isControlled() && !owner->GetPetGUID()))
{
Remove(PET_SAVE_NOT_IN_SLOT, true);
Remove(PET_SAVE_DISMISS, true);
return;
}
@@ -574,7 +552,7 @@ void Pet::Update(uint32 diff)
if (owner->GetPetGUID() != GetGUID())
{
TC_LOG_ERROR("entities.pet", "Pet %u is not pet of owner %s, removed", GetEntry(), GetOwner()->GetName().c_str());
Remove(getPetType() == HUNTER_PET?PET_SAVE_AS_DELETED:PET_SAVE_NOT_IN_SLOT);
Remove(IsHunterPet() ? PET_SAVE_AS_DELETED : PET_SAVE_DISMISS);
return;
}
}
@@ -585,7 +563,7 @@ void Pet::Update(uint32 diff)
m_duration -= diff;
else
{
Remove(getPetType() != SUMMON_PET ? PET_SAVE_AS_DELETED:PET_SAVE_NOT_IN_SLOT);
Remove(getPetType() != SUMMON_PET ? PET_SAVE_AS_DELETED : PET_SAVE_DISMISS);
return;
}
}
@@ -676,7 +654,7 @@ void Pet::Remove(PetSaveMode mode, bool returnreagent)
void Pet::GivePetXP(uint32 xp)
{
if (getPetType() != HUNTER_PET)
if (!IsHunterPet())
return;
if (xp < 1)
@@ -716,7 +694,7 @@ void Pet::GivePetLevel(uint8 level)
if (!level || level == getLevel())
return;
if (getPetType() == HUNTER_PET)
if (!IsHunterPet())
{
SetUInt32Value(UNIT_FIELD_PETEXPERIENCE, 0);
SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP, uint32(sObjectMgr->GetXPForLevel(level)*PET_XP_FACTOR));
@@ -1341,7 +1319,7 @@ bool Pet::addSpell(uint32 spellId, ActiveStates active /*= ACT_DECIDE*/, PetSpel
if (active == ACT_DECIDE) // active was not used before, so we save it's autocast/passive state here
{
if (spellInfo->IsAutocastable())
newspell.active = ACT_DISABLED;
newspell.active = ACT_ENABLED;
else
newspell.active = ACT_PASSIVE;
}
@@ -1639,6 +1617,7 @@ bool Pet::resetTalents()
void Pet::resetTalentsForAllPetsOf(Player* owner, Pet* onlinePet /*= NULL*/)
{
/*
// not need after this call
if (owner->HasAtLoginFlag(AT_LOGIN_RESET_PET_TALENTS))
owner->RemoveAtLoginFlag(AT_LOGIN_RESET_PET_TALENTS, true);
@@ -1712,6 +1691,7 @@ void Pet::resetTalentsForAllPetsOf(Player* owner, Pet* onlinePet /*= NULL*/)
ss << ')';
CharacterDatabase.Execute(ss.str().c_str());
*/
}
void Pet::InitTalentForLevel()
@@ -1948,3 +1928,16 @@ void Pet::SetDisplayId(uint32 modelId)
if (GetOwner()->GetGroup())
GetOwner()->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_PET_MODEL_ID);
}
std::string Pet::GenerateActionBarData() const
{
std::ostringstream ss;
for (uint32 i = ACTION_BAR_INDEX_START; i < ACTION_BAR_INDEX_END; ++i)
{
ss << uint32(m_charmInfo->GetActionBarEntry(i)->GetType()) << ' '
<< uint32(m_charmInfo->GetActionBarEntry(i)->GetAction()) << ' ';
}
return ss.str();
}
+27 -2
View File
@@ -23,7 +23,23 @@
#include "TemporarySummon.h"
#define PET_FOCUS_REGEN_INTERVAL 4 * IN_MILLISECONDS
#define HAPPINESS_LEVEL_SIZE 333000
enum StableResultCode
{
STABLE_ERR_MONEY = 0x01, // "you don't have enough money"
STABLE_ERR_INVALID_SLOT = 0x03, // "That slot is locked"
STABLE_SUCCESS_STABLE = 0x08, // stable success
STABLE_SUCCESS_UNSTABLE = 0x09, // unstable/swap success
STABLE_SUCCESS_BUY_SLOT = 0x0A, // buy slot success
STABLE_ERR_EXOTIC = 0x0B, // "you are unable to control exotic creatures"
STABLE_ERR_STABLE = 0x0C // "Internal pet error"
};
enum PetStableInfo
{
PET_STABLE_ACTIVE = 1,
PET_STABLE_INACTIVE = 2
};
struct PetSpell
{
@@ -60,7 +76,7 @@ class TC_GAME_API Pet : public Guardian
bool CreateBaseAtCreature(Creature* creature);
bool CreateBaseAtCreatureInfo(CreatureTemplate const* cinfo, Unit* owner);
bool CreateBaseAtTamed(CreatureTemplate const* cinfo, Map* map);
bool LoadPetFromDB(Player* owner, uint32 petentry = 0, uint32 petnumber = 0, bool current = false);
bool LoadPetData(Player* owner, uint32 petentry = 0, uint32 petnumber = 0, bool current = false);
bool IsLoading() const override { return m_loading;}
void SavePetToDB(PetSaveMode mode);
void Remove(PetSaveMode mode, bool returnreagent = false);
@@ -119,6 +135,7 @@ class TC_GAME_API Pet : public Guardian
bool unlearnSpell(uint32 spell_id, bool learn_prev, bool clear_ab = true);
bool removeSpell(uint32 spell_id, bool learn_prev, bool clear_ab = true);
void CleanupActionBar();
std::string GenerateActionBarData() const;
PetSpellMap m_spells;
AutoSpellList m_autospells;
@@ -145,6 +162,12 @@ class TC_GAME_API Pet : public Guardian
Player* GetOwner() const;
uint32 GetSlot() { return m_petSlot; }
void SetSlot(uint32 newPetSlot) { m_petSlot = newPetSlot; } // use only together with DB update
bool IsActive() { return m_petActive; }
void SetActive(bool active) { m_petActive = active; }
protected:
PetType m_petType;
int32 m_duration; // time until unsummon (used mostly for summoned guardians and not used for controlled pets)
@@ -153,6 +176,8 @@ class TC_GAME_API Pet : public Guardian
uint32 m_focusRegenTimer;
DeclinedName *m_declinedname;
uint32 m_petSlot;
bool m_petActive = false;
private:
void SaveToDB(uint32, uint8, uint32) override // override of Creature::SaveToDB - must not be called
+14 -10
View File
@@ -26,23 +26,27 @@ enum PetType
MAX_PET_TYPE = 4
};
#define MAX_PET_STABLES 4
#define MAX_PET_STABLES 20
// stored in character_pet.slot
enum PetSaveMode
{
PET_SAVE_AS_DELETED = -1, // not saved in fact
PET_SAVE_AS_CURRENT = 0, // in current slot (with player)
PET_SAVE_FIRST_STABLE_SLOT = 1,
PET_SAVE_LAST_STABLE_SLOT = MAX_PET_STABLES, // last in DB stable slot index (including), all higher have same meaning as PET_SAVE_NOT_IN_SLOT
PET_SAVE_NOT_IN_SLOT = 100 // for avoid conflict with stable size grow will use 100
PET_SAVE_AS_DELETED = -1, // not saved in fact
PET_SAVE_UPADTE_SLOT = 0, // not used yet
PET_SAVE_CURRENT_STATE = 1, // Saves everything like it is atm
PET_SAVE_DISMISS = 2, // Saves everything like it is atm, but removes auras and sets unactive
PET_SAVE_LOGOUT = 3, // Saves everything like it is atm, but removes auras
PET_SAVE_NEW_PET = 4
};
enum HappinessState
enum PetStableSlot
{
UNHAPPY = 1,
CONTENT = 2,
HAPPY = 3
PET_SLOT_FIRST = 0,
PET_SLOT_LAST = 20,
PET_SLOT_FIRST_ACTIVE_SLOT = PET_SLOT_FIRST,
PET_SLOT_LAST_ACTIVE_SLOT = 4,
PET_SLOT_FIRST_STABLE_SLOT = 5,
PET_SLOT_LAST_STABLE_SLOT = PET_SLOT_LAST
};
enum PetSpellState
+225 -98
View File
@@ -583,6 +583,9 @@ Player::~Player()
for (uint8 i = 0; i < MAX_CUF_PROFILES; ++i)
delete _CUFProfiles[i];
for (uint8 i = 0; i < PlayerPetDataStore.size(); i++)
delete PlayerPetDataStore[i];
ClearResurrectRequestData();
sWorld->DecreasePlayerCount();
@@ -1498,7 +1501,7 @@ void Player::Update(uint32 p_time)
Pet* pet = GetPet();
if (pet && !pet->IsWithinDistInMap(this, GetMap()->GetVisibilityRange()) && !pet->isPossessed())
//if (pet && !pet->IsWithinDistInMap(this, GetMap()->GetVisibilityDistance()) && (GetCharmGUID() && (pet->GetGUID() != GetCharmGUID())))
RemovePet(pet, PET_SAVE_NOT_IN_SLOT, true);
RemovePet(pet, PET_SAVE_DISMISS, true);
if (IsAlive())
{
@@ -1541,7 +1544,7 @@ void Player::setDeathState(DeathState s)
ClearResurrectRequestData();
//FIXME: is pet dismissed at dying or releasing spirit? if second, add setDeathState(DEAD) to HandleRepopRequestOpcode and define pet unsummon here with (s == DEAD)
RemovePet(nullptr, PET_SAVE_NOT_IN_SLOT, true);
RemovePet(nullptr, PET_SAVE_DISMISS, true);
// save value before aura remove in Unit::setDeathState
ressSpellId = GetUInt32Value(PLAYER_SELF_RES_SPELL);
@@ -3727,6 +3730,9 @@ void Player::LearnSpell(uint32 spell_id, bool dependent, uint32 fromSkill /*= 0*
LearnSpell(itr2->second, false, fromSkill);
}
}
if (CanControlPet(spell_id) && GetPet())
PetSpellInitialize();
}
void Player::RemoveSpell(uint32 spell_id, bool disabled, bool learn_low_rank)
@@ -3995,7 +4001,7 @@ bool Player::ResetTalents(bool no_cost)
}
}
RemovePet(nullptr, PET_SAVE_NOT_IN_SLOT, true);
RemovePet(nullptr, PET_SAVE_DISMISS, true);
for (uint32 talentId = 0; talentId < sTalentStore.GetNumRows(); ++talentId)
{
@@ -4072,7 +4078,7 @@ bool Player::ResetTalents(bool no_cost)
if (Pet* pet = GetPet())
{
if (pet->getPetType() == HUNTER_PET && !pet->GetCreatureTemplate()->IsTameable(CanTameExoticPets()))
RemovePet(NULL, PET_SAVE_NOT_IN_SLOT, true);
RemovePet(nullptr, PET_SAVE_NOT_IN_SLOT, true);
}
*/
@@ -18613,11 +18619,138 @@ void Player::LoadPet()
if (IsInWorld())
{
Pet* pet = new Pet(this);
if (!pet->LoadPetFromDB(this, 0, 0, true))
if (!pet->LoadPetData(this, 0, 0, true))
delete pet;
}
}
void Player::LoadPetsFromDB(PreparedQueryResult result)
{
if (!result)
return;
do
{
Field* fields = result->Fetch();
PlayerPetData* playerPetData = new PlayerPetData();
uint8 slot = fields[7].GetUInt8();
uint32 petId = fields[0].GetUInt32();
if (slot < PET_SLOT_FIRST || slot > PET_SLOT_LAST)
{
TC_LOG_ERROR("sql.sql", "Player::LoadPetsFromDB: bad slot %u for pet %u!", slot, petId);
continue;
}
playerPetData->PetId = petId;
playerPetData->CreatureId = fields[1].GetUInt32();
playerPetData->Owner = fields[2].GetUInt64();
playerPetData->DisplayId = fields[3].GetUInt32();
playerPetData->Petlevel = fields[4].GetUInt16();
playerPetData->PetExp = fields[5].GetUInt32();
playerPetData->Reactstate = ReactStates(fields[6].GetUInt8());
playerPetData->Slot = slot;
playerPetData->Name = fields[8].GetString();
playerPetData->Renamed = fields[9].GetBool();
playerPetData->Active = fields[10].GetBool();
playerPetData->SavedHealth = fields[11].GetUInt32();
playerPetData->SavedMana = fields[12].GetUInt32();
playerPetData->Actionbar = fields[13].GetString();
playerPetData->Timediff = fields[14].GetUInt32();
playerPetData->SummonSpellId = fields[15].GetUInt32();
playerPetData->Type = PetType(fields[16].GetUInt8());
PlayerPetDataStore.push_back(playerPetData);
} while (result->NextRow());
}
PlayerPetData* Player::GetPlayerPetDataById(uint32 petId)
{
for (PlayerPetData* p : PlayerPetDataStore)
if (p->PetId == petId)
return p;
return nullptr;
}
PlayerPetData* Player::GetPlayerPetDataBySlot(uint8 slot)
{
for (PlayerPetData* p : PlayerPetDataStore)
if (p->Slot == slot)
return p;
return nullptr;
}
PlayerPetData* Player::GetPlayerPetDataByCreatureId(uint32 creatureId)
{
for (PlayerPetData* p : PlayerPetDataStore)
if (p->CreatureId == creatureId)
return p;
return nullptr;
}
PlayerPetData* Player::GetPlayerPetDataCurrent()
{
for (PlayerPetData* p : PlayerPetDataStore)
if (p->Active == true)
return p;
return nullptr;
}
Optional<uint8> Player::GetFirstUnusedActivePetSlot()
{
std::set<uint8> unusedActiveSlot = { 0, 1, 2, 3, 4 }; //unfiltered
for (PlayerPetData* p : PlayerPetDataStore)
if (unusedActiveSlot.find(p->Slot) != unusedActiveSlot.end())
unusedActiveSlot.erase(p->Slot);
if (!unusedActiveSlot.empty())
return *unusedActiveSlot.begin();
return Optional<uint8>{};
}
Optional<uint8> Player::GetFirstUnusedPetSlot()
{
std::set<uint8> unusedSlot;
for (uint8 i = 0; i < PET_SLOT_LAST; i++) // 4 MAX StableSlots (256 theoretically)
unusedSlot.insert(i);
for (PlayerPetData* p : PlayerPetDataStore)
if (unusedSlot.find(p->Slot) != unusedSlot.end())
unusedSlot.erase(p->Slot);
if (!unusedSlot.empty())
return *unusedSlot.begin();
return Optional<uint8>{};
}
void Player::DeleteFromPlayerPetDataStore(uint32 petNumber)
{
for (uint8 i = 0; i < PlayerPetDataStore.size(); ++i)
{
if (PlayerPetDataStore[i]->PetId == petNumber)
{
delete PlayerPetDataStore[i];
PlayerPetDataStore.erase(PlayerPetDataStore.begin() + (i--));
}
}
}
void Player::AddToPlayerPetDataStore(PlayerPetData* playerPetData)
{
PlayerPetDataStore.push_back(playerPetData);
}
void Player::_LoadQuestStatus(PreparedQueryResult result)
{
uint16 slot = 0;
@@ -19783,7 +19916,7 @@ void Player::SaveToDB(bool create /*=false*/)
// save pet (hunter pet level and experience and all type pets health/mana).
if (Pet* pet = GetPet())
pet->SavePetToDB(PET_SAVE_AS_CURRENT);
pet->SavePetToDB(PET_SAVE_CURRENT_STATE);
}
// fast save function for item/money cheating preventing - save only inventory and money state
@@ -20883,12 +21016,11 @@ void Player::RemovePet(Pet* pet, PetSaveMode mode, bool returnreagent)
case 1863:
case 417:
case 17252:
mode = PET_SAVE_NOT_IN_SLOT;
mode = PET_SAVE_DISMISS;
break;
}
}
// only if current pet in slot
pet->SavePetToDB(mode);
SetMinion(pet, false);
@@ -21086,8 +21218,31 @@ void Player::SendOnCancelExpectedVehicleRideAura() const
GetSession()->SendPacket(&data);
}
bool Player::CanControlPet(uint32 spellId) const
{
// Hunters and Warlocks cannot control their pets until they learned their spell
if (spellId)
{
switch (spellId)
{
case 93321: // Control Pet
return getClass() == CLASS_HUNTER;
case 93375: // Control Demon
return getClass() == CLASS_WARLOCK;
}
}
else
return (HasAura(93321) && getClass() == CLASS_HUNTER || HasAura(93375) && getClass() == CLASS_WARLOCK);
return false;
}
void Player::PetSpellInitialize()
{
// Do not send the pet action bar when Hunters or Warlocks cannot control their pets
if (!CanControlPet())
return;
Pet* pet = GetPet();
if (!pet)
@@ -21095,7 +21250,7 @@ void Player::PetSpellInitialize()
CharmInfo* charmInfo = pet->GetCharmInfo();
WorldPacket data(SMSG_PET_SPELLS, 8+2+4+4+4*MAX_UNIT_ACTION_BAR_INDEX+1+1);
WorldPacket data(SMSG_PET_SPELLS, 8 + 2 + 4 + 4 + 4 * MAX_UNIT_ACTION_BAR_INDEX + 1 + 1);
data << uint64(pet->GetGUID());
data << uint16(pet->GetCreatureTemplate()->family); // creature family (required for pet talents)
data << uint32(pet->GetDuration());
@@ -22871,7 +23026,7 @@ template<>
inline void BeforeVisibilityDestroy<Creature>(Creature* t, Player* p)
{
if (p->GetPetGUID() == t->GetGUID() && t->IsPet())
t->ToPet()->Remove(PET_SAVE_NOT_IN_SLOT, true);
t->ToPet()->Remove(PET_SAVE_DISMISS, true);
}
void Player::UpdateVisibilityOf(WorldObject* target)
@@ -25979,7 +26134,7 @@ void Player::UnsummonPetTemporaryIfAny()
m_oldpetspell = pet->GetUInt32Value(UNIT_CREATED_BY_SPELL);
}
RemovePet(pet, PET_SAVE_AS_CURRENT);
RemovePet(pet, PET_SAVE_DISMISS);
}
void Player::ResummonPetTemporaryUnSummonedIfAny()
@@ -25995,7 +26150,7 @@ void Player::ResummonPetTemporaryUnSummonedIfAny()
return;
Pet* NewPet = new Pet(this);
if (!NewPet->LoadPetFromDB(this, 0, m_temporaryUnsummonedPetNumber, true))
if (!NewPet->LoadPetData(this, 0, m_temporaryUnsummonedPetNumber, false))
delete NewPet;
m_temporaryUnsummonedPetNumber = 0;
@@ -26572,7 +26727,7 @@ void Player::ActivateSpec(uint8 spec)
// TO-DO: We need more research to know what happens with warlock's reagent
if (Pet* pet = GetPet())
RemovePet(pet, PET_SAVE_NOT_IN_SLOT);
RemovePet(pet, PET_SAVE_DISMISS);
ClearAllReactives();
UnsummonAllTotems();
@@ -27373,34 +27528,6 @@ Pet* Player::SummonPet(uint32 entry, float x, float y, float z, float ang, PetTy
{
Pet* pet = new Pet(this, petType);
if (petType == SUMMON_PET && pet->LoadPetFromDB(this, entry))
{
// Remove Demonic Sacrifice auras (known pet)
Unit::AuraEffectList const& auraClassScripts = GetAuraEffectsByType(SPELL_AURA_OVERRIDE_CLASS_SCRIPTS);
for (Unit::AuraEffectList::const_iterator itr = auraClassScripts.begin(); itr != auraClassScripts.end();)
{
if ((*itr)->GetMiscValue() == 2228)
{
RemoveAurasDueToSpell((*itr)->GetId());
itr = auraClassScripts.begin();
}
else
++itr;
}
if (duration > 0)
pet->SetDuration(duration);
return nullptr;
}
// petentry == 0 for hunter "call pet" (current pet summoned if any)
if (!entry)
{
delete pet;
return nullptr;
}
pet->Relocate(x, y, z, ang);
if (!pet->IsPositionValid())
{
@@ -27409,70 +27536,70 @@ Pet* Player::SummonPet(uint32 entry, float x, float y, float z, float ang, PetTy
return nullptr;
}
Map* map = GetMap();
uint32 pet_number = sObjectMgr->GeneratePetNumber();
if (!pet->Create(map->GenerateLowGuid<HighGuid::Pet>(), map, entry, pet_number))
bool hasPetData = pet->LoadPetData(this, entry);
if (!hasPetData && petType == HUNTER_PET)
{
TC_LOG_ERROR("misc", "Player::SummonPet: No such creature entry %u", entry);
delete pet;
return nullptr;
}
pet->CopyPhaseFrom(this);
pet->SetCreatorGUID(GetGUID());
pet->SetFaction(GetFaction());
pet->setPowerType(POWER_MANA);
pet->SetUInt32Value(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_NONE);
pet->SetUInt32Value(UNIT_FIELD_BYTES_1, 0);
pet->InitStatsForLevel(getLevel());
SetMinion(pet, true);
switch (petType)
if (!hasPetData)
{
case SUMMON_PET:
// this enables pet details window (Shift+P)
pet->GetCharmInfo()->SetPetNumber(pet_number, true);
pet->SetUInt32Value(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_CLASS);
pet->SetUInt32Value(UNIT_FIELD_PETEXPERIENCE, 0);
pet->SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP, 1000);
pet->SetFullHealth();
pet->SetPower(POWER_MANA, pet->GetMaxPower(POWER_MANA));
pet->SetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP, uint32(time(nullptr))); // cast can't be helped in this case
break;
default:
break;
}
map->AddToMap(pet->ToCreature());
switch (petType)
{
case SUMMON_PET:
pet->InitPetCreateSpells();
pet->InitTalentForLevel();
pet->SavePetToDB(PET_SAVE_AS_CURRENT);
PetSpellInitialize();
break;
default:
break;
}
if (petType == SUMMON_PET)
{
// Remove Demonic Sacrifice auras (known pet)
Unit::AuraEffectList const& auraClassScripts = GetAuraEffectsByType(SPELL_AURA_OVERRIDE_CLASS_SCRIPTS);
for (Unit::AuraEffectList::const_iterator itr = auraClassScripts.begin(); itr != auraClassScripts.end();)
Map* map = GetMap();
uint32 pet_number = sObjectMgr->GeneratePetNumber();
if (!pet->Create(map->GenerateLowGuid<HighGuid::Pet>(), map, entry, pet_number))
{
if ((*itr)->GetMiscValue() == 2228)
{
RemoveAurasDueToSpell((*itr)->GetId());
itr = auraClassScripts.begin();
}
else
++itr;
TC_LOG_ERROR("misc", "Player::SummonPet: No such creature entry %u", entry);
delete pet;
return nullptr;
}
// generate new name for summon pet
std::string new_name = sObjectMgr->GeneratePetName(entry);
if (!new_name.empty())
pet->SetName(new_name);
pet->CopyPhaseFrom(this);
pet->SetCreatorGUID(GetGUID());
pet->SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE, GetFaction());
pet->setPowerType(POWER_MANA);
pet->SetUInt64Value(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_NONE);
pet->SetUInt32Value(UNIT_FIELD_BYTES_1, 0);
pet->InitStatsForLevel(getLevel());
pet->SetReactState(REACT_ASSIST);
SetMinion(pet, true);
switch (petType)
{
case SUMMON_PET:
pet->GetCharmInfo()->SetPetNumber(pet_number, true);
pet->SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_CLASS, CLASS_MAGE);
pet->SetUInt32Value(UNIT_FIELD_PETEXPERIENCE, 0);
pet->SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP, 1000);
pet->SetFullHealth();
pet->SetPower(POWER_MANA, pet->GetMaxPower(POWER_MANA));
pet->SetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP, uint32(time(nullptr))); // cast can't be helped in this case
break;
default:
break;
}
map->AddToMap(pet->ToCreature());
switch (petType)
{
case SUMMON_PET:
pet->InitPetCreateSpells();
pet->SavePetToDB(PET_SAVE_NEW_PET);
PetSpellInitialize();
GetSession()->SendPetAdded(pet->GetSlot(), pet->GetCharmInfo()->GetPetNumber(), pet->GetEntry(), pet->GetDisplayId(), pet->getLevel(), pet->GetName());
break;
default:
break;
}
}
+37
View File
@@ -943,6 +943,7 @@ enum PlayerLoginQueryIndex
PLAYER_LOGIN_QUERY_LOAD_CURRENCY = 34,
PLAYER_LOGIN_QUERY_LOAD_CUF_PROFILES = 35,
PLAYER_LOGIN_QUERY_LOAD_CORPSE_LOCATION = 36,
PLAYER_LOGIN_QUERY_LOAD_ALL_PETS = 37,
MAX_PLAYER_LOGIN_QUERY
};
@@ -1094,6 +1095,27 @@ class TC_GAME_API PlayerTaxi
std::ostringstream& operator << (std::ostringstream& ss, PlayerTaxi const& taxi);
struct PlayerPetData
{
uint32 PetId;
uint32 CreatureId;
uint64 Owner;
uint32 DisplayId;
uint32 Petlevel;
uint32 PetExp;
ReactStates Reactstate;
uint8 Slot;
std::string Name;
bool Renamed;
bool Active;
uint32 SavedHealth;
uint32 SavedMana;
std::string Actionbar;
uint32 Timediff;
uint32 SummonSpellId;
PetType Type;
};
class Player;
/// Holder for Battleground data
@@ -1519,6 +1541,7 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>
void SendItemDurations();
void LoadCorpse(PreparedQueryResult result);
void LoadPet();
void LoadPetsFromDB(PreparedQueryResult result);
bool AddItem(uint32 itemId, uint32 count);
@@ -1768,6 +1791,7 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>
bool RemoveMItem(uint32 id);
void SendOnCancelExpectedVehicleRideAura() const;
bool CanControlPet(uint32 spellId = 0) const;
void PetSpellInitialize();
void CharmSpellInitialize();
void PossessSpellInitialize();
@@ -2551,9 +2575,20 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>
VoidStorageItem* GetVoidStorageItem(uint8 slot) const;
VoidStorageItem* GetVoidStorageItem(uint64 id, uint8& slot) const;
// LFG Call to Arms
void SetTempCallToArmsRoles(uint8 roles) { _tmpLfgRolesCheck = roles; }
uint8 GetCallToArmsTempRoles() { return _tmpLfgRolesCheck; }
// Pets
PlayerPetData* GetPlayerPetDataById(uint32 petId);
PlayerPetData* GetPlayerPetDataBySlot(uint8 slot);
PlayerPetData* GetPlayerPetDataByCreatureId(uint32 creatureId);
PlayerPetData* GetPlayerPetDataCurrent();
Optional<uint8> GetFirstUnusedActivePetSlot();
Optional<uint8> GetFirstUnusedPetSlot();
void DeleteFromPlayerPetDataStore(uint32 petNumber);
void AddToPlayerPetDataStore(PlayerPetData* playerPetData);
protected:
// Gamemaster whisper whitelist
GuidList WhisperList;
@@ -2915,6 +2950,8 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>
Archaeology _archaeology;
uint8 _tmpLfgRolesCheck;
std::vector<PlayerPetData*> PlayerPetDataStore;
};
TC_GAME_API void AddItemsSetItem(Player* player, Item* item);
+37 -18
View File
@@ -960,17 +960,6 @@ void Creature::CalculateMinMaxDamage(WeaponAttackType attType, bool normalized,
######## ########
#######################################*/
#define ENTRY_IMP 416
#define ENTRY_VOIDWALKER 1860
#define ENTRY_SUCCUBUS 1863
#define ENTRY_FELHUNTER 417
#define ENTRY_FELGUARD 17252
#define ENTRY_WATER_ELEMENTAL 510
#define ENTRY_TREANT 1964
#define ENTRY_FIRE_ELEMENTAL 15438
#define ENTRY_GHOUL 26125
#define ENTRY_BLOODWORM 28017
bool Guardian::UpdateStats(Stats stat)
{
if (stat >= MAX_STATS)
@@ -1102,13 +1091,12 @@ void Guardian::UpdateMaxHealth()
float multiplicator;
switch (GetEntry())
{
case ENTRY_IMP: multiplicator = 8.4f; break;
case ENTRY_VOIDWALKER: multiplicator = 11.0f; break;
case ENTRY_SUCCUBUS: multiplicator = 9.1f; break;
case ENTRY_FELHUNTER: multiplicator = 9.5f; break;
case ENTRY_FELGUARD: multiplicator = 11.0f; break;
case ENTRY_BLOODWORM: multiplicator = 1.0f; break;
default: multiplicator = 10.0f; break;
case ENTRY_BLOODWORM:
multiplicator = 1.0f;
break;
default:
multiplicator = 14.0f;
break;
}
float value = GetModifierValue(unitMod, BASE_VALUE) + GetCreateHealth();
@@ -1116,6 +1104,37 @@ void Guardian::UpdateMaxHealth()
value += GetModifierValue(unitMod, TOTAL_VALUE) + stamina * multiplicator;
value *= GetModifierValue(unitMod, TOTAL_PCT);
if (Unit* owner = GetOwner())
{
switch (GetEntry())
{
case ENTRY_IMP:
value = (7.7 * getLevel() / 80) * GetOwner()->GetStat(STAT_STAMINA) + GetModifierValue(unitMod, BASE_VALUE) + GetCreateHealth();
break;
case ENTRY_VOIDWALKER:
value = (15.0 * getLevel() / 80) * GetOwner()->GetStat(STAT_STAMINA) + GetModifierValue(unitMod, BASE_VALUE) + GetCreateHealth();
break;
case ENTRY_FELHUNTER:
case ENTRY_FELGUARD:
case ENTRY_SUCCUBUS:
value = (9.1 * getLevel() / 80) * GetOwner()->GetStat(STAT_STAMINA) + GetModifierValue(unitMod, BASE_VALUE) + GetCreateHealth();
break;
case ENTRY_WATER_ELEMENTAL:
value = (7.0 * getLevel() / 80) * GetOwner()->GetStat(STAT_STAMINA) + GetModifierValue(unitMod, BASE_VALUE) + GetCreateHealth();
break;
case ENTRY_SPIRIT_WOLF:
case ENTRY_GARGOYLE:
case ENTRY_SHADOWFIEND:
value = (4.2 * getLevel() / 80) * GetOwner()->GetStat(STAT_STAMINA) + GetModifierValue(unitMod, BASE_VALUE) + GetCreateHealth();
break;
case ENTRY_TREANT:
value = (1.9 * getLevel() / 80) * GetOwner()->GetStat(STAT_STAMINA) + GetModifierValue(unitMod, BASE_VALUE) + GetCreateHealth();
break;
default:
break;
}
}
SetMaxHealth((uint32)value);
}
+17 -14
View File
@@ -5943,7 +5943,7 @@ void Unit::SetMinion(Minion *minion, bool apply)
{
// remove existing minion pet
if (oldPet->IsPet())
((Pet*)oldPet)->Remove(PET_SAVE_AS_CURRENT);
((Pet*)oldPet)->Remove(PET_SAVE_DISMISS);
else
oldPet->UnSummon();
SetPetGUID(minion->GetGUID());
@@ -10041,7 +10041,7 @@ uint32 Unit::GetPowerIndex(uint32 powerType) const
uint32 classId = getClass();
if (Pet const* pet = ToPet())
{
if (pet->getPetType() == HUNTER_PET)
if (pet->IsHunterPet())
classId = CLASS_HUNTER;
else if (pet->IsPetGhoul())
classId = CLASS_ROGUE;
@@ -10290,17 +10290,19 @@ void CharmInfo::RestoreState()
void CharmInfo::InitPetActionBar()
{
// the first 3 SpellOrActions are attack, follow and stay
for (uint32 i = 0; i < ACTION_BAR_INDEX_PET_SPELL_START - ACTION_BAR_INDEX_START; ++i)
SetActionBar(ACTION_BAR_INDEX_START + i, COMMAND_ATTACK - i, ACT_COMMAND);
// the first 3 SpellOrActions are attack, follow and moveTo
SetActionBar(ACTION_BAR_INDEX_ATTACK, COMMAND_ATTACK, ACT_COMMAND);
SetActionBar(ACTION_BAR_INDEX_FOLLOW, COMMAND_FOLLOW, ACT_COMMAND);
SetActionBar(ACTION_BAR_INDEX_MOVE_TO, COMMAND_MOVE_TO, ACT_COMMAND);
// middle 4 SpellOrActions are spells/special attacks/abilities
for (uint32 i = 0; i < ACTION_BAR_INDEX_PET_SPELL_END-ACTION_BAR_INDEX_PET_SPELL_START; ++i)
SetActionBar(ACTION_BAR_INDEX_PET_SPELL_START + i, 0, ACT_PASSIVE);
// last 3 SpellOrActions are reactions
for (uint32 i = 0; i < ACTION_BAR_INDEX_END - ACTION_BAR_INDEX_PET_SPELL_END; ++i)
SetActionBar(ACTION_BAR_INDEX_PET_SPELL_END + i, COMMAND_ATTACK - i, ACT_REACTION);
SetActionBar(ACTION_BAR_INDEX_ASSIST, REACT_ASSIST, ACT_REACTION);
SetActionBar(ACTION_BAR_INDEX_DEFENSIVE, REACT_DEFENSIVE, ACT_REACTION);
SetActionBar(ACTION_BAR_INDEX_PASSIVE, REACT_PASSIVE, ACT_REACTION);
}
void CharmInfo::InitEmptyActionBar(bool withAttack)
@@ -10423,7 +10425,7 @@ bool CharmInfo::AddSpellToActionBar(SpellInfo const* spellInfo, ActiveStates new
uint8 j = (preferredSlot + i) % MAX_UNIT_ACTION_BAR_INDEX;
if (!PetActionBar[j].GetAction() && PetActionBar[j].IsActionBarForSpell())
{
SetActionBar(j, spell_id, newstate == ACT_DECIDE ? spellInfo->IsAutocastable() ? ACT_DISABLED : ACT_PASSIVE : newstate);
SetActionBar(j, spell_id, newstate == ACT_DECIDE ? spellInfo->IsAutocastable() ? ACT_ENABLED : ACT_PASSIVE : newstate);
return true;
}
}
@@ -11359,17 +11361,17 @@ void Unit::SetContestedPvP(Player* attackedPlayer)
Pet* Unit::CreateTamedPetFrom(Creature* creatureTarget, uint32 spell_id)
{
if (GetTypeId() != TYPEID_PLAYER)
return NULL;
return nullptr;
Pet* pet = new Pet(ToPlayer(), HUNTER_PET);
if (!pet->CreateBaseAtCreature(creatureTarget))
{
delete pet;
return NULL;
return nullptr;
}
uint8 level = creatureTarget->getLevel() + 5 < getLevel() ? (getLevel() - 5) : creatureTarget->getLevel();
uint8 level = getLevel();
InitTamedPet(pet, level, spell_id);
@@ -11379,18 +11381,18 @@ Pet* Unit::CreateTamedPetFrom(Creature* creatureTarget, uint32 spell_id)
Pet* Unit::CreateTamedPetFrom(uint32 creatureEntry, uint32 spell_id)
{
if (GetTypeId() != TYPEID_PLAYER)
return NULL;
return nullptr;
CreatureTemplate const* creatureInfo = sObjectMgr->GetCreatureTemplate(creatureEntry);
if (!creatureInfo)
return NULL;
return nullptr;
Pet* pet = new Pet(ToPlayer(), HUNTER_PET);
if (!pet->CreateBaseAtCreatureInfo(creatureInfo, this) || !InitTamedPet(pet, getLevel(), spell_id))
{
delete pet;
return NULL;
return nullptr;
}
return pet;
@@ -11418,6 +11420,7 @@ bool Unit::InitTamedPet(Pet* pet, uint8 level, uint32 spell_id)
pet->InitPetCreateSpells();
//pet->InitLevelupSpellsForLevel();
pet->SetFullHealth();
pet->SetReactState(REACT_ASSIST);
return true;
}
+8 -1
View File
@@ -1109,7 +1109,8 @@ enum ReactStates
{
REACT_PASSIVE = 0,
REACT_DEFENSIVE = 1,
REACT_AGGRESSIVE = 2
REACT_AGGRESSIVE = 2,
REACT_ASSIST = 3
};
enum CommandStates : uint8
@@ -1171,8 +1172,14 @@ typedef UnitActionBarEntry CharmSpellInfo;
enum ActionBarIndex
{
ACTION_BAR_INDEX_START = 0,
ACTION_BAR_INDEX_ATTACK = ACTION_BAR_INDEX_START,
ACTION_BAR_INDEX_FOLLOW = 1,
ACTION_BAR_INDEX_MOVE_TO = 2,
ACTION_BAR_INDEX_PET_SPELL_START = 3,
ACTION_BAR_INDEX_PET_SPELL_END = 7,
ACTION_BAR_INDEX_ASSIST = ACTION_BAR_INDEX_PET_SPELL_END,
ACTION_BAR_INDEX_DEFENSIVE = 8,
ACTION_BAR_INDEX_PASSIVE = 9,
ACTION_BAR_INDEX_END = 10
};
@@ -225,6 +225,10 @@ bool LoginQueryHolder::Initialize()
stmt->setUInt32(0, lowGuid);
res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_CORPSE_LOCATION, stmt);
stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_ALL_PETS_DETAIL);
stmt->setUInt64(0, lowGuid);
res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_ALL_PETS, stmt);
return res;
}
@@ -289,8 +293,7 @@ void WorldSession::HandleCharEnumOpcode(WorldPacket& /*recvData*/)
else
stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_ENUM);
stmt->setUInt8(0, PET_SAVE_AS_CURRENT);
stmt->setUInt32(1, GetAccountId());
stmt->setUInt32(0, GetAccountId());
_queryProcessor.AddQuery(CharacterDatabase.AsyncQuery(stmt).WithPreparedCallback(std::bind(&WorldSession::HandleCharEnum, this, std::placeholders::_1)));
}
@@ -993,6 +996,10 @@ void WorldSession::HandlePlayerLogin(LoginQueryHolder* holder)
if (pCurrChar->HasAtLoginFlag(AT_LOGIN_RESET_PET_TALENTS))
Pet::resetTalentsForAllPetsOf(pCurrChar);
pCurrChar->LoadPetsFromDB(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_ALL_PETS));
if (pCurrChar->getClass() == CLASS_HUNTER)
pCurrChar->GetSession()->SendStablePet(ObjectGuid::Empty);
// Load pet if any (if player not alive and in taxi flight or another then pet will remember as temporary unsummoned)
pCurrChar->LoadPet();
+1 -1
View File
@@ -74,7 +74,7 @@ void WorldSession::HandleRepopRequestOpcode(WorldPacket& recvData)
}
//this is spirit release confirm?
GetPlayer()->RemovePet(NULL, PET_SAVE_NOT_IN_SLOT, true);
GetPlayer()->RemovePet(nullptr, PET_SAVE_DISMISS, true);
GetPlayer()->BuildPlayerRepop();
GetPlayer()->RepopAtGraveyard();
}
+69 -312
View File
@@ -37,17 +37,6 @@
#include "CreatureAI.h"
#include "SpellInfo.h"
enum StableResultCode
{
STABLE_ERR_MONEY = 0x01, // "you don't have enough money"
STABLE_ERR_INVALID_SLOT = 0x03, // "That slot is locked"
STABLE_SUCCESS_STABLE = 0x08, // stable success
STABLE_SUCCESS_UNSTABLE = 0x09, // unstable/swap success
STABLE_SUCCESS_BUY_SLOT = 0x0A, // buy slot success
STABLE_ERR_EXOTIC = 0x0B, // "you are unable to control exotic creatures"
STABLE_ERR_STABLE = 0x0C, // "Internal pet error"
};
void WorldSession::HandleTabardVendorActivateOpcode(WorldPacket& recvData)
{
ObjectGuid guid;
@@ -486,68 +475,29 @@ void WorldSession::HandleListStabledPetsOpcode(WorldPacket& recvData)
void WorldSession::SendStablePet(ObjectGuid guid)
{
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PET_SLOTS_DETAIL);
WorldPacket data(MSG_LIST_STABLED_PETS, 200);
stmt->setUInt32(0, _player->GetGUID().GetCounter());
stmt->setUInt8(1, PET_SAVE_FIRST_STABLE_SLOT);
stmt->setUInt8(2, PET_SAVE_LAST_STABLE_SLOT);
data << uint64(guid); // Stablemaster
data << uint8(_player->PlayerPetDataStore.size());
data << uint8(PET_SLOT_LAST_STABLE_SLOT); // Stable Slots
_queryProcessor.AddQuery(CharacterDatabase.AsyncQuery(stmt).WithPreparedCallback(std::bind(&WorldSession::SendStablePetCallback, this, guid, std::placeholders::_1)));
}
void WorldSession::SendStablePetCallback(ObjectGuid guid, PreparedQueryResult result)
{
if (!GetPlayer())
return;
TC_LOG_DEBUG("network", "WORLD: Recv MSG_LIST_STABLED_PETS Send.");
WorldPacket data(MSG_LIST_STABLED_PETS, 200); // guess size
data << uint64(guid);
Pet* pet = _player->GetPet();
size_t wpos = data.wpos();
data << uint8(0); // place holder for slot show number
data << uint8(GetPlayer()->m_stableSlots);
uint8 num = 0; // counter for place holder
// not let move dead pet in slot
if (pet && pet->IsAlive() && pet->getPetType() == HUNTER_PET)
for (PlayerPetData* p : _player->PlayerPetDataStore)
{
data << uint32(num); // 4.x unknown, some kind of order?
data << uint32(pet->GetCharmInfo()->GetPetNumber());
data << uint32(pet->GetEntry());
data << uint32(pet->getLevel());
data << pet->GetName(); // petname
data << uint8(1); // 1 = current, 2/3 = in stable (any from 4, 5, ... create problems with proper show)
++num;
uint32 petSlot = p->Slot;
uint8 flags = PET_STABLE_ACTIVE;
if (petSlot > PET_SLOT_LAST_ACTIVE_SLOT)
flags |= PET_STABLE_INACTIVE;
data << int32(petSlot);
data << uint32(p->PetId);
data << uint32(p->CreatureId);
data << uint32(p->Petlevel);
data << p->Name;
data << uint8(flags);
}
if (result)
{
do
{
Field* fields = result->Fetch();
data << uint32(num);
data << uint32(fields[1].GetUInt32()); // petnumber
data << uint32(fields[2].GetUInt32()); // creature entry
data << uint32(fields[3].GetUInt16()); // level
data << fields[4].GetString(); // name
data << uint8(2); // 1 = current, 2/3 = in stable (any from 4, 5, ... create problems with proper show)
++num;
}
while (result->NextRow());
}
data.put<uint8>(wpos, num); // set real data to placeholder
SendPacket(&data);
}
void WorldSession::SendStableResult(uint8 res)
@@ -557,12 +507,32 @@ void WorldSession::SendStableResult(uint8 res)
SendPacket(&data);
}
void WorldSession::HandleStablePet(WorldPacket& recvData)
void WorldSession::HandleSetPetSlot(WorldPacket& recvData)
{
TC_LOG_DEBUG("network", "WORLD: Recv CMSG_STABLE_PET");
ObjectGuid npcGUID;
ObjectGuid guid;
uint32 petId;
uint8 new_slot;
recvData >> npcGUID;
recvData >> petId >> new_slot;
guid[3] = recvData.ReadBit();
guid[2] = recvData.ReadBit();
guid[0] = recvData.ReadBit();
guid[7] = recvData.ReadBit();
guid[5] = recvData.ReadBit();
guid[6] = recvData.ReadBit();
guid[1] = recvData.ReadBit();
guid[4] = recvData.ReadBit();
recvData.ReadByteSeq(guid[5]);
recvData.ReadByteSeq(guid[3]);
recvData.ReadByteSeq(guid[1]);
recvData.ReadByteSeq(guid[7]);
recvData.ReadByteSeq(guid[4]);
recvData.ReadByteSeq(guid[0]);
recvData.ReadByteSeq(guid[6]);
recvData.ReadByteSeq(guid[2]);
if (!GetPlayer()->IsAlive())
{
@@ -570,276 +540,63 @@ void WorldSession::HandleStablePet(WorldPacket& recvData)
return;
}
if (!CheckStableMaster(npcGUID))
if (!CheckStableMaster(guid))
{
SendStableResult(STABLE_ERR_STABLE);
return;
}
// remove fake death
if (GetPlayer()->HasUnitState(UNIT_STATE_DIED))
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
Pet* pet = _player->GetPet();
// can't place in stable dead pet
if (!pet || !pet->IsAlive() || pet->getPetType() != HUNTER_PET)
if (new_slot > PET_SLOT_LAST_STABLE_SLOT || new_slot < PET_SLOT_FIRST_ACTIVE_SLOT)
{
SendStableResult(STABLE_ERR_STABLE);
return;
}
PlayerPetData* playerPetData = _player->GetPlayerPetDataById(petId);
CreatureTemplate const* creatureInfo = nullptr;
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PET_SLOTS);
if (playerPetData)
creatureInfo = sObjectMgr->GetCreatureTemplate(playerPetData->CreatureId);
stmt->setUInt32(0, _player->GetGUID().GetCounter());
stmt->setUInt8(1, PET_SAVE_FIRST_STABLE_SLOT);
stmt->setUInt8(2, PET_SAVE_LAST_STABLE_SLOT);
_queryProcessor.AddQuery(CharacterDatabase.AsyncQuery(stmt).WithPreparedCallback(std::bind(&WorldSession::HandleStablePetCallback, this, std::placeholders::_1)));
}
void WorldSession::HandleStablePetCallback(PreparedQueryResult result)
{
if (!GetPlayer())
return;
uint8 freeSlot = 1;
if (result)
{
do
{
Field* fields = result->Fetch();
uint8 slot = fields[1].GetUInt8();
// slots ordered in query, and if not equal then free
if (slot != freeSlot)
break;
// this slot not free, skip
++freeSlot;
}
while (result->NextRow());
}
if (freeSlot > 0 && freeSlot <= GetPlayer()->m_stableSlots)
{
_player->RemovePet(_player->GetPet(), PetSaveMode(freeSlot));
SendStableResult(STABLE_SUCCESS_STABLE);
}
else
SendStableResult(STABLE_ERR_INVALID_SLOT);
}
void WorldSession::HandleUnstablePet(WorldPacket& recvData)
{
TC_LOG_DEBUG("network", "WORLD: Recv CMSG_UNSTABLE_PET.");
ObjectGuid npcGUID;
uint32 petnumber;
recvData >> npcGUID >> petnumber;
if (!CheckStableMaster(npcGUID))
{
SendStableResult(STABLE_ERR_STABLE);
return;
}
// remove fake death
if (GetPlayer()->HasUnitState(UNIT_STATE_DIED))
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PET_ENTRY);
stmt->setUInt32(0, _player->GetGUID().GetCounter());
stmt->setUInt32(1, petnumber);
stmt->setUInt8(2, PET_SAVE_FIRST_STABLE_SLOT);
stmt->setUInt8(3, PET_SAVE_LAST_STABLE_SLOT);
_queryProcessor.AddQuery(CharacterDatabase.AsyncQuery(stmt).WithPreparedCallback(std::bind(&WorldSession::HandleUnstablePetCallback, this, petnumber, std::placeholders::_1)));
}
void WorldSession::HandleUnstablePetCallback(uint32 petId, PreparedQueryResult result)
{
if (!GetPlayer())
return;
uint32 petEntry = 0;
if (result)
{
Field* fields = result->Fetch();
petEntry = fields[0].GetUInt32();
}
if (!petEntry)
{
SendStableResult(STABLE_ERR_STABLE);
return;
}
CreatureTemplate const* creatureInfo = sObjectMgr->GetCreatureTemplate(petEntry);
if (!creatureInfo || !creatureInfo->IsTameable(_player->CanTameExoticPets()))
{
// if problem in exotic pet
if (creatureInfo && creatureInfo->IsTameable(true))
SendStableResult(STABLE_ERR_EXOTIC);
else
SendStableResult(STABLE_ERR_STABLE);
return;
}
Pet* pet = _player->GetPet();
if (pet && pet->IsAlive())
{
SendStableResult(STABLE_ERR_STABLE);
return;
}
// delete dead pet
if (pet)
_player->RemovePet(pet, PET_SAVE_AS_DELETED);
Pet* newPet = new Pet(_player, HUNTER_PET);
if (!newPet->LoadPetFromDB(_player, petEntry, petId))
{
delete newPet;
newPet = NULL;
SendStableResult(STABLE_ERR_STABLE);
return;
}
SendStableResult(STABLE_SUCCESS_UNSTABLE);
}
void WorldSession::HandleBuyStableSlot(WorldPacket& recvData)
{
TC_LOG_DEBUG("network", "WORLD: Recv CMSG_BUY_STABLE_SLOT.");
ObjectGuid npcGUID;
recvData >> npcGUID;
if (!CheckStableMaster(npcGUID))
{
SendStableResult(STABLE_ERR_STABLE);
return;
}
// remove fake death
if (GetPlayer()->HasUnitState(UNIT_STATE_DIED))
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
if (GetPlayer()->m_stableSlots < MAX_PET_STABLES)
{
/*StableSlotPricesEntry const* SlotPrice = sStableSlotPricesStore.LookupEntry(GetPlayer()->m_stableSlots+1);
if (_player->HasEnoughMoney(SlotPrice->Price))
{
++GetPlayer()->m_stableSlots;
_player->ModifyMoney(-int32(SlotPrice->Price));
SendStableResult(STABLE_SUCCESS_BUY_SLOT);
}
else
SendStableResult(STABLE_ERR_MONEY);*/
}
else
SendStableResult(STABLE_ERR_STABLE);
}
void WorldSession::HandleStableRevivePet(WorldPacket &/* recvData */)
{
TC_LOG_DEBUG("network", "HandleStableRevivePet: Not implemented");
}
void WorldSession::HandleStableSwapPet(WorldPacket& recvData)
{
TC_LOG_DEBUG("network", "WORLD: Recv CMSG_STABLE_SWAP_PET.");
ObjectGuid npcGUID;
uint32 petId;
recvData >> npcGUID >> petId;
if (!CheckStableMaster(npcGUID))
{
SendStableResult(STABLE_ERR_STABLE);
return;
}
// remove fake death
if (GetPlayer()->HasUnitState(UNIT_STATE_DIED))
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
Pet* pet = _player->GetPet();
if (!pet || pet->getPetType() != HUNTER_PET)
{
SendStableResult(STABLE_ERR_STABLE);
return;
}
// Find swapped pet slot in stable
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PET_SLOT_BY_ID);
stmt->setUInt32(0, _player->GetGUID().GetCounter());
stmt->setUInt32(1, petId);
_queryProcessor.AddQuery(CharacterDatabase.AsyncQuery(stmt).WithPreparedCallback(std::bind(&WorldSession::HandleStableSwapPetCallback, this, petId, std::placeholders::_1)));
}
void WorldSession::HandleStableSwapPetCallback(uint32 petId, PreparedQueryResult result)
{
if (!GetPlayer())
return;
if (!result)
{
SendStableResult(STABLE_ERR_STABLE);
return;
}
Field* fields = result->Fetch();
uint32 slot = fields[0].GetUInt8();
uint32 petEntry = fields[1].GetUInt32();
if (!petEntry)
{
SendStableResult(STABLE_ERR_STABLE);
return;
}
CreatureTemplate const* creatureInfo = sObjectMgr->GetCreatureTemplate(petEntry);
if (!creatureInfo || !creatureInfo->IsTameable(true))
{
SendStableResult(STABLE_ERR_STABLE);
return;
}
if (!creatureInfo->IsTameable(_player->CanTameExoticPets()))
if (!creatureInfo->IsTameable(_player->CanTameExoticPets()) && new_slot <= PET_SLOT_LAST_ACTIVE_SLOT)
{
SendStableResult(STABLE_ERR_EXOTIC);
return;
}
// remove fake death 2
if (GetPlayer()->HasUnitState(UNIT_STATE_DIED))
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
Pet* pet = _player->GetPet();
// The player's pet could have been removed during the delay of the DB callback
if (!pet)
// can't place in stable dead pet
if (pet)
{
SendStableResult(STABLE_ERR_STABLE);
return;
if (pet->GetCharmInfo()->GetPetNumber() == petId)
{
if (!pet->IsAlive() || !pet->IsHunterPet())
{
SendStableResult(STABLE_ERR_STABLE);
return;
}
}
}
// move alive pet to slot or delete dead pet
_player->RemovePet(pet, pet->IsAlive() ? PetSaveMode(slot) : PET_SAVE_AS_DELETED);
// summon unstabled pet
Pet* newPet = new Pet(_player);
if (!newPet->LoadPetFromDB(_player, petEntry, petId))
if (playerPetData)
{
delete newPet;
SendStableResult(STABLE_ERR_STABLE);
UpdatePetSlot(petId, playerPetData->Slot, new_slot);
}
else
SendStableResult(STABLE_SUCCESS_UNSTABLE);
}
void WorldSession::HandleStableRevivePet(WorldPacket &/* recvData */)
{
TC_LOG_DEBUG("network", "HandleStableRevivePet: Not implemented");
}
void WorldSession::HandleRepairItemOpcode(WorldPacket& recvData)
+91 -2
View File
@@ -259,8 +259,10 @@ void WorldSession::HandlePetActionHelper(Unit* pet, ObjectGuid guid1, uint32 spe
ASSERT(pet->GetTypeId() == TYPEID_UNIT);
if (pet->IsPet())
{
if (((Pet*)pet)->getPetType() == HUNTER_PET)
if (((Pet*)pet)->IsHunterPet())
{
GetPlayer()->RemovePet((Pet*)pet, PET_SAVE_AS_DELETED);
}
else
//dismissing a summoned pet is like killing them (this prevents returning a soulshard...)
pet->setDeathState(CORPSE);
@@ -279,6 +281,7 @@ void WorldSession::HandlePetActionHelper(Unit* pet, ObjectGuid guid1, uint32 spe
charmInfo->SetIsCommandAttack(false);
charmInfo->SetIsAtStay(true);
charmInfo->SetIsCommandFollow(false);
charmInfo->SetIsFollowing(false);
charmInfo->SetIsReturning(false);
charmInfo->SaveStayPosition();
@@ -296,6 +299,7 @@ void WorldSession::HandlePetActionHelper(Unit* pet, ObjectGuid guid1, uint32 spe
// no break;
case REACT_DEFENSIVE: //recovery
case REACT_AGGRESSIVE: //activete
case REACT_ASSIST:
if (pet->GetTypeId() == TYPEID_UNIT)
pet->ToCreature()->SetReactState(ReactStates(spellid));
break;
@@ -621,7 +625,7 @@ void WorldSession::HandlePetRename(WorldPacket& recvData)
Pet* pet = ObjectAccessor::GetPet(*_player, petguid);
// check it!
if (!pet || !pet->IsPet() || ((Pet*)pet)->getPetType() != HUNTER_PET ||
if (!pet || !pet->IsPet() || !((Pet*)pet)->IsHunterPet() ||
!pet->HasByteFlag(UNIT_FIELD_BYTES_2, UNIT_BYTES_2_OFFSET_PET_FLAGS, UNIT_CAN_BE_RENAMED) ||
pet->GetOwnerGUID() != _player->GetGUID() || !pet->GetCharmInfo())
return;
@@ -887,3 +891,88 @@ void WorldSession::HandleLearnPreviewTalentsPet(WorldPacket& recvData)
recvData.rfinish();
}
void WorldSession::UpdatePetSlot(uint32 petNumberA, uint8 oldPetSlot, uint8 newPetSlot)
{
uint32 petNumberB = 0;
Pet* pet = _player->GetPet();
// first check check new PetSlot if another pet already exists there
PlayerPetData* playerPetDataA = _player->GetPlayerPetDataById(petNumberA);
PlayerPetData* playerPetDataB = _player->GetPlayerPetDataBySlot(newPetSlot);
SQLTransaction trans = CharacterDatabase.BeginTransaction();
// If Slot is already in use
if (playerPetDataB)
{
petNumberB = playerPetDataB->PetId;
// Check if current pet is the pet to swap
if (pet && pet->GetCharmInfo()->GetPetNumber() == petNumberB)
{
pet->SetSlot(oldPetSlot);
_player->RemovePet(pet, PET_SAVE_DISMISS);
}
else
{
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHAR_PET_SLOT_BY_SLOT);
stmt->setUInt32(0, oldPetSlot);
stmt->setUInt64(1, _player->GetGUID().GetCounter());
stmt->setUInt32(2, newPetSlot);
trans->Append(stmt);
}
playerPetDataB->Slot = oldPetSlot;
}
// Check if current pet is the pet to swap
if (pet && pet->GetCharmInfo()->GetPetNumber() == petNumberA)
{
pet->SetSlot(newPetSlot);
_player->RemovePet(pet, PET_SAVE_DISMISS);
}
else
{
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHAR_PET_SLOT_BY_ID);
stmt->setUInt32(0, newPetSlot);
stmt->setUInt64(1, _player->GetGUID().GetCounter());
stmt->setUInt32(2, petNumberA);
trans->Append(stmt);
}
playerPetDataA->Slot = newPetSlot;
CharacterDatabase.CommitTransaction(trans);
SendPetSlotUpdated(petNumberA, newPetSlot, petNumberB, oldPetSlot);
SendStableResult(STABLE_SUCCESS_STABLE);
}
void WorldSession::SendPetSlotUpdated(int32 petNumberA, int32 petSlotA, int32 petNumberB, int32 petSlotB)
{
WorldPacket data(SMSG_PET_SLOT_UPDATED, 4 + 4 + 4 + 4);
data << uint32(petNumberA);
data << uint32(petSlotA);
data << uint32(petNumberB);
data << uint32(petSlotB);
SendPacket(&data);
}
void WorldSession::SendPetAdded(int32 petSlot, int32 petNumber, int32 creatureID, int32 displayID, int32 level, std::string name)
{
WorldPacket data(SMSG_PET_ADDED, 4 + 4 + 1 + 4 + 4 + name.size());
data << uint32(level);
data << uint32(petSlot);
data << uint8(PET_STABLE_ACTIVE | (petSlot > PET_SLOT_LAST_ACTIVE_SLOT ? PET_STABLE_INACTIVE : 0));
data << uint32(creatureID);
data << uint32(petNumber);
data.WriteBits(uint32(name.length()), 8);
data << name;
SendPacket(&data);
}
+1 -1
View File
@@ -1201,7 +1201,7 @@ void Map::MoveAllCreaturesInMoveList()
/// @todo pets will disappear if this is outside CreatureRespawnRelocation
//need to check why pet is frequently relocated to an unloaded cell
if (c->IsPet())
((Pet*)c)->Remove(PET_SAVE_NOT_IN_SLOT, true);
((Pet*)c)->Remove(PET_SAVE_DISMISS, true);
else
AddObjectToRemoveList(c);
}
+3 -3
View File
@@ -490,7 +490,7 @@ void OpcodeTable::Initialize()
DEFINE_OPCODE_HANDLER(CMSG_SET_FACTION_ATWAR, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleSetFactionAtWar );
DEFINE_OPCODE_HANDLER(CMSG_SET_FACTION_INACTIVE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleSetFactionInactiveOpcode );
DEFINE_OPCODE_HANDLER(CMSG_SET_GUILD_BANK_TEXT, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleSetGuildBankTabText );
DEFINE_OPCODE_HANDLER(CMSG_SET_PET_SLOT, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_NULL );
DEFINE_OPCODE_HANDLER(CMSG_SET_PET_SLOT, STATUS_LOGGEDIN , PROCESS_THREADUNSAFE, &WorldSession::HandleSetPetSlot );
DEFINE_OPCODE_HANDLER(CMSG_SET_PLAYER_DECLINED_NAMES, STATUS_AUTHED, PROCESS_THREADUNSAFE, &WorldSession::HandleSetPlayerDeclinedNames );
DEFINE_OPCODE_HANDLER(CMSG_SET_PREFERED_CEMETERY, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_NULL );
DEFINE_OPCODE_HANDLER(CMSG_SET_PRIMARY_TALENT_TREE, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_NULL );
@@ -1056,7 +1056,7 @@ void OpcodeTable::Initialize()
DEFINE_OPCODE_HANDLER(SMSG_PETITION_SIGN_RESULTS, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide );
DEFINE_OPCODE_HANDLER(SMSG_PET_ACTION_FEEDBACK, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide );
DEFINE_OPCODE_HANDLER(SMSG_PET_ACTION_SOUND, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide );
DEFINE_OPCODE_HANDLER(SMSG_PET_ADDED, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_ServerSide );
DEFINE_OPCODE_HANDLER(SMSG_PET_ADDED, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide );
DEFINE_OPCODE_HANDLER(SMSG_PET_BROKEN, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_ServerSide );
DEFINE_OPCODE_HANDLER(SMSG_PET_CAST_FAILED, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide );
DEFINE_OPCODE_HANDLER(SMSG_PET_DISMISS_SOUND, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_ServerSide );
@@ -1067,7 +1067,7 @@ void OpcodeTable::Initialize()
DEFINE_OPCODE_HANDLER(SMSG_PET_NAME_QUERY_RESPONSE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide );
DEFINE_OPCODE_HANDLER(SMSG_PET_REMOVED_SPELL, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide );
DEFINE_OPCODE_HANDLER(SMSG_PET_RENAMEABLE, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_ServerSide );
DEFINE_OPCODE_HANDLER(SMSG_PET_SLOT_UPDATED, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_ServerSide );
DEFINE_OPCODE_HANDLER(SMSG_PET_SLOT_UPDATED, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide );
DEFINE_OPCODE_HANDLER(SMSG_PET_SPELLS, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide );
DEFINE_OPCODE_HANDLER(SMSG_PET_TAME_FAILURE, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_ServerSide );
DEFINE_OPCODE_HANDLER(SMSG_PET_UPDATE_COMBO_POINTS, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide );
+1 -1
View File
@@ -536,7 +536,7 @@ void WorldSession::LogoutPlayer(bool save)
guild->HandleMemberLogout(this);
///- Remove pet
_player->RemovePet(NULL, PET_SAVE_AS_CURRENT, true);
_player->RemovePet(nullptr, PET_SAVE_LOGOUT, true);
///- Clear whisper whitelist
_player->ClearWhisperWhiteList();
+4 -8
View File
@@ -383,9 +383,11 @@ class TC_GAME_API WorldSession
// Pet
void SendPetNameQuery(ObjectGuid guid, uint32 petnumber);
void SendStablePet(ObjectGuid guid);
void SendStablePetCallback(ObjectGuid guid, PreparedQueryResult result);
void SendStableResult(uint8 guid);
bool CheckStableMaster(ObjectGuid guid);
void UpdatePetSlot(uint32 petNumber, uint8 oldPetSlot, uint8 newPetSlot);
void SendPetSlotUpdated(int32 petNumberA, int32 petSlotA, int32 petNumberB, int32 petSlotB);
void SendPetAdded(int32 petSlot, int32 petNumber, int32 creatureID, int32 displayID, int32 level, std::string name);
// Account Data
AccountData* GetAccountData(AccountDataType type) { return &m_accountData[type]; }
@@ -719,14 +721,8 @@ class TC_GAME_API WorldSession
void HandleNpcTextQueryOpcode(WorldPacket& recvPacket);
void HandleBinderActivateOpcode(WorldPacket& recvPacket);
void HandleListStabledPetsOpcode(WorldPacket& recvPacket);
void HandleStablePet(WorldPacket& recvPacket);
void HandleStablePetCallback(PreparedQueryResult result);
void HandleUnstablePet(WorldPacket& recvPacket);
void HandleUnstablePetCallback(uint32 petId, PreparedQueryResult result);
void HandleBuyStableSlot(WorldPacket& recvPacket);
void HandleSetPetSlot(WorldPacket& recvPacket);
void HandleStableRevivePet(WorldPacket& recvPacket);
void HandleStableSwapPet(WorldPacket& recvPacket);
void HandleStableSwapPetCallback(uint32 petId, PreparedQueryResult result);
void SendTrainerBuyFailed(uint64 guid, uint32 spellId, uint32 reason);
void HandleDuelAcceptedOpcode(WorldPacket& recvPacket);
+29 -23
View File
@@ -2676,10 +2676,7 @@ void Spell::EffectTameCreature(SpellEffIndex /*effIndex*/)
if (!pet) // in very specific state like near world end/etc.
return;
// "kill" original creature
creatureTarget->DespawnOrUnsummon();
uint8 level = (creatureTarget->getLevel() < (m_caster->getLevel() - 5)) ? (m_caster->getLevel() - 5) : creatureTarget->getLevel();
uint8 level = m_caster->getLevel();
// prepare visual effect for levelup
pet->SetUInt32Value(UNIT_FIELD_LEVEL, level - 1);
@@ -2687,9 +2684,6 @@ void Spell::EffectTameCreature(SpellEffIndex /*effIndex*/)
// add to world
pet->GetMap()->AddToMap(pet->ToCreature());
// visual effect for levelup
pet->SetUInt32Value(UNIT_FIELD_LEVEL, level);
// caster have pet now
m_caster->SetMinion(pet, true);
@@ -2697,9 +2691,23 @@ void Spell::EffectTameCreature(SpellEffIndex /*effIndex*/)
if (m_caster->GetTypeId() == TYPEID_PLAYER)
{
pet->SavePetToDB(PET_SAVE_AS_CURRENT);
m_caster->ToPlayer()->PetSpellInitialize();
pet->SavePetToDB(PET_SAVE_NEW_PET);
if (uint8 slot = pet->GetSlot() <= PET_SLOT_LAST_ACTIVE_SLOT)
{
m_caster->ToPlayer()->GetSession()->SendPetAdded(pet->GetSlot(), pet->GetCharmInfo()->GetPetNumber(), creatureTarget->GetEntry(), creatureTarget->GetDisplayId(), creatureTarget->getLevel(), pet->GetName());
m_caster->ToPlayer()->PetSpellInitialize();
}
else
{
pet->Remove(PET_SAVE_AS_DELETED);
return;
}
}
// "kill" original creature if everything worked
creatureTarget->DespawnOrUnsummon();
// visual effect for levelup
pet->SetUInt32Value(UNIT_FIELD_LEVEL, level);
}
void Spell::EffectSummonPet(SpellEffIndex effIndex)
@@ -2707,15 +2715,18 @@ void Spell::EffectSummonPet(SpellEffIndex effIndex)
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT)
return;
Player* owner = NULL;
Player* owner = nullptr;
if (m_originalCaster)
{
owner = m_originalCaster->ToPlayer();
if (!owner && m_originalCaster->IsTotem())
owner = m_originalCaster->GetCharmerOrOwnerPlayerOrPlayerItself();
}
// SUMMON_PET SummonPet's entries are at MiscValue, HunterPetSlot at BasePoints
uint32 petentry = (m_spellInfo->Effects[effIndex].MiscValue == 0 && m_spellInfo->Effects[effIndex].BasePoints <= PET_SLOT_LAST_ACTIVE_SLOT) ? m_spellInfo->Effects[effIndex].BasePoints : m_spellInfo->Effects[effIndex].MiscValue;
PetType petType = (m_spellInfo->Effects[effIndex].MiscValue == 0 && m_spellInfo->Effects[effIndex].BasePoints <= PET_SLOT_LAST_ACTIVE_SLOT) ? HUNTER_PET : SUMMON_PET;
uint32 petentry = m_spellInfo->Effects[effIndex].MiscValue;
if (!owner)
{
@@ -2761,14 +2772,14 @@ void Spell::EffectSummonPet(SpellEffIndex effIndex)
}
if (owner->GetTypeId() == TYPEID_PLAYER)
owner->ToPlayer()->RemovePet(OldSummon, (OldSummon->getPetType() == HUNTER_PET ? PET_SAVE_AS_DELETED : PET_SAVE_NOT_IN_SLOT), false);
owner->ToPlayer()->RemovePet(OldSummon, (OldSummon->getPetType() == HUNTER_PET ? PET_SAVE_AS_DELETED : PET_SAVE_DISMISS), false);
else
return;
}
float x, y, z;
owner->GetClosePoint(x, y, z, owner->GetObjectSize());
Pet* pet = owner->SummonPet(petentry, x, y, z, owner->GetOrientation(), SUMMON_PET, 0);
Pet* pet = owner->SummonPet(petentry, x, y, z, owner->GetOrientation(), petType, 0);
if (!pet)
return;
@@ -2782,11 +2793,6 @@ void Spell::EffectSummonPet(SpellEffIndex effIndex)
pet->SetUInt32Value(UNIT_CREATED_BY_SPELL, m_spellInfo->Id);
// generate new name for summon pet
std::string new_name=sObjectMgr->GeneratePetName(petentry);
if (!new_name.empty())
pet->SetName(new_name);
ExecuteLogEffectSummonObject(effIndex, pet);
}
@@ -2812,7 +2818,7 @@ void Spell::EffectLearnPetSpell(SpellEffIndex effIndex)
return;
pet->learnSpell(learn_spellproto->Id);
pet->SavePetToDB(PET_SAVE_AS_CURRENT);
pet->SavePetToDB(PET_SAVE_CURRENT_STATE);
pet->GetOwner()->PetSpellInitialize();
}
@@ -4097,7 +4103,7 @@ void Spell::EffectDismissPet(SpellEffIndex effIndex)
Pet* pet = unitTarget->ToPet();
ExecuteLogEffectUnsummonObject(effIndex, pet);
pet->GetOwner()->RemovePet(pet, PET_SAVE_NOT_IN_SLOT);
pet->GetOwner()->RemovePet(pet, PET_SAVE_DISMISS);
}
void Spell::EffectSummonObject(SpellEffIndex effIndex)
@@ -4661,7 +4667,7 @@ void Spell::EffectResurrectPet(SpellEffIndex /*effIndex*/)
if (!player->GetPet())
{
// Position passed to SummonPet is irrelevant with current implementation,
// pet will be relocated without using these coords in Pet::LoadPetFromDB
// pet will be relocated without using these coords in Pet::LoadPetData
player->SummonPet(0, 0.0f, 0.0f, 0.0f, 0.0f, SUMMON_PET, 0);
hadPet = false;
}
@@ -4702,7 +4708,7 @@ void Spell::EffectResurrectPet(SpellEffIndex /*effIndex*/)
ci->SetIsReturning(false);
}
pet->SavePetToDB(PET_SAVE_AS_CURRENT);
pet->SavePetToDB(PET_SAVE_CURRENT_STATE);
}
void Spell::EffectDestroyAllTotems(SpellEffIndex /*effIndex*/)
@@ -5284,7 +5290,7 @@ void Spell::EffectCreateTamedPet(SpellEffIndex effIndex)
if (unitTarget->GetTypeId() == TYPEID_PLAYER)
{
pet->SavePetToDB(PET_SAVE_AS_CURRENT);
pet->SavePetToDB(PET_SAVE_NEW_PET);
unitTarget->ToPlayer()->PetSpellInitialize();
}
}
+1 -1
View File
@@ -1452,7 +1452,7 @@ public:
// caster have pet now
player->SetMinion(pet, true);
pet->SavePetToDB(PET_SAVE_AS_CURRENT);
pet->SavePetToDB(PET_SAVE_NEW_PET);
player->PetSpellInitialize();
return true;
+1 -1
View File
@@ -122,7 +122,7 @@ public:
pet->SetUInt32Value(UNIT_FIELD_LEVEL, creatureTarget->getLevel());
player->SetMinion(pet, true);
pet->SavePetToDB(PET_SAVE_AS_CURRENT);
pet->SavePetToDB(PET_SAVE_NEW_PET);
player->PetSpellInitialize();
return true;
@@ -555,6 +555,11 @@ class spell_siamat_wailing_winds : public SpellScriptLoader
{
PrepareAuraScript(spell_siamat_wailing_winds_AuraScript);
bool Validate(SpellInfo const* /*spellInfo*/) override
{
return ValidateSpellInfo({ SPELL_WAILING_WINDS_DAMAGE });
}
void OnPeriodic(AuraEffect const* /*aurEff*/)
{
if (Unit* caster = GetCaster())
+4 -4
View File
@@ -2580,7 +2580,7 @@ class spell_gen_pet_summoned : public SpellScriptLoader
{
PetType newPetType = (player->getClass() == CLASS_HUNTER) ? HUNTER_PET : SUMMON_PET;
Pet* newPet = new Pet(player, newPetType);
if (newPet->LoadPetFromDB(player, 0, player->GetLastPetNumber(), true))
if (newPet->LoadPetData(player, 0, player->GetLastPetNumber(), true))
{
// revive the pet if it is dead
if (newPet->getDeathState() == DEAD)
@@ -3105,7 +3105,7 @@ class spell_gen_summon_elemental : public SpellScriptLoader
if (GetCaster())
if (Unit* owner = GetCaster()->GetOwner())
if (owner->GetTypeId() == TYPEID_PLAYER) /// @todo this check is maybe wrong
owner->ToPlayer()->RemovePet(NULL, PET_SAVE_NOT_IN_SLOT, true);
owner->ToPlayer()->RemovePet(nullptr, PET_SAVE_DISMISS, true);
}
void Register() override
@@ -3693,10 +3693,10 @@ class spell_gen_gm_freeze : public SpellScriptLoader
{
if (Pet* pet = player->GetPet())
{
pet->SavePetToDB(PET_SAVE_AS_CURRENT);
pet->SavePetToDB(PET_SAVE_CURRENT_STATE);
// not let dismiss dead pet
if (pet->IsAlive())
player->RemovePet(pet, PET_SAVE_NOT_IN_SLOT);
player->RemovePet(pet, PET_SAVE_DISMISS);
}
}
}
@@ -1129,6 +1129,9 @@ class spell_hun_tame_beast : public SpellScriptLoader
if (!GetExplTargetUnit())
return SPELL_FAILED_BAD_IMPLICIT_TARGETS;
if (!caster->ToPlayer()->GetFirstUnusedActivePetSlot())
return SPELL_FAILED_DONT_REPORT;
if (Creature* target = GetExplTargetUnit()->ToCreature())
{
if (target->getLevel() > caster->getLevel())
-5
View File
@@ -51,11 +51,6 @@ enum WarlockPetCalculate
SPELL_WARLOCK_PET_SCALING_03 = 34957,
SPELL_WARLOCK_PET_SCALING_04 = 34958,
SPELL_WARLOCK_PET_SCALING_05 = 61013,
ENTRY_FELGUARD = 17252,
ENTRY_VOIDWALKER = 1860,
ENTRY_FELHUNTER = 417,
ENTRY_SUCCUBUS = 1863,
ENTRY_IMP = 416,
SPELL_WARLOCK_GLYPH_OF_VOIDWALKER = 56247,
};
@@ -2565,6 +2565,8 @@ class npc_stable_master : public CreatureScript
bool GossipSelect(Player* player, uint32 menuId, uint32 gossipListId) override
{
player->GetSession()->SendStablePet(me->GetGUID());
SmartAI::GossipSelect(player, menuId, gossipListId);
if (menuId != STABLE_MASTER_GOSSIP_SUB_MENU)
return true;