fix(Core/Hunter): Stable Master, exotics pets management, tame validations. (#2100)
This commit is contained in:
@@ -520,7 +520,7 @@ void CharacterDatabaseConnection::DoPrepareStatements()
|
|||||||
// Pet
|
// 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, "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 owner, id, entry, 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_ENTRY, "SELECT entry, slot 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, 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_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_PET, "SELECT id FROM character_pet WHERE owner = ? AND id <> ?", CONNECTION_SYNCH);
|
||||||
|
|||||||
@@ -149,6 +149,11 @@ struct CreatureTemplate
|
|||||||
return SKILL_SKINNING; // normal case
|
return SKILL_SKINNING; // normal case
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IsExotic() const
|
||||||
|
{
|
||||||
|
return (type_flags & CREATURE_TYPE_FLAG_EXOTIC_PET) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
bool IsTameable(bool exotic) const
|
bool IsTameable(bool exotic) const
|
||||||
{
|
{
|
||||||
if (type != CREATURE_TYPE_BEAST || family == 0 || (type_flags & CREATURE_TYPE_FLAG_TAMEABLE_PET) == 0)
|
if (type != CREATURE_TYPE_BEAST || family == 0 || (type_flags & CREATURE_TYPE_FLAG_TAMEABLE_PET) == 0)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-GPL2
|
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-GPL2
|
||||||
* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
|
* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
|
||||||
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
|
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
|
||||||
@@ -97,6 +97,55 @@ void Pet::RemoveFromWorld()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SpellCastResult Pet::TryLoadFromDB(Player* owner, bool current /*= false*/, PetType mandatoryPetType /*= MAX_PET_TYPE*/)
|
||||||
|
{
|
||||||
|
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_PET_BY_ENTRY_AND_SLOT);
|
||||||
|
stmt->setUInt32(0, owner->GetGUIDLow());
|
||||||
|
stmt->setUInt8(1, uint8(current ? PET_SAVE_AS_CURRENT : PET_SAVE_NOT_IN_SLOT));
|
||||||
|
|
||||||
|
PreparedQueryResult result = CharacterDatabase.AsyncQuery(stmt);
|
||||||
|
|
||||||
|
if (!result)
|
||||||
|
return SPELL_FAILED_NO_PET;
|
||||||
|
|
||||||
|
Field* fields = result->Fetch();
|
||||||
|
|
||||||
|
uint32 petentry = fields[1].GetUInt32();
|
||||||
|
uint32 savedHealth = fields[10].GetUInt32();
|
||||||
|
uint32 summon_spell_id = fields[15].GetUInt32();
|
||||||
|
auto petType = PetType(fields[16].GetUInt8());
|
||||||
|
|
||||||
|
// update for case of current pet "slot = 0"
|
||||||
|
if (!petentry)
|
||||||
|
return SPELL_FAILED_NO_PET;
|
||||||
|
|
||||||
|
CreatureTemplate const* creatureInfo = sObjectMgr->GetCreatureTemplate(petentry);
|
||||||
|
if (!creatureInfo)
|
||||||
|
{
|
||||||
|
sLog->outError("Pet entry %u does not exist but used at pet load (owner: %s).", petentry, owner->GetName().c_str());
|
||||||
|
return SPELL_FAILED_NO_PET;
|
||||||
|
}
|
||||||
|
|
||||||
|
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(summon_spell_id);
|
||||||
|
|
||||||
|
bool isTemporarySummoned = spellInfo && spellInfo->GetDuration() > 0;
|
||||||
|
|
||||||
|
// check temporary summoned pets like mage water elemental
|
||||||
|
if (current && isTemporarySummoned)
|
||||||
|
return SPELL_FAILED_NO_PET;
|
||||||
|
|
||||||
|
if (!savedHealth)
|
||||||
|
{
|
||||||
|
owner->ToPlayer()->SendTameFailure(PET_TAME_DEAD);
|
||||||
|
return SPELL_FAILED_TARGETS_DEAD;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mandatoryPetType != MAX_PET_TYPE && petType != mandatoryPetType)
|
||||||
|
return SPELL_FAILED_BAD_TARGETS;
|
||||||
|
|
||||||
|
return SPELL_CAST_OK;
|
||||||
|
}
|
||||||
|
|
||||||
bool Pet::LoadPetFromDB(Player* owner, uint8 asynchLoadType, uint32 petentry, uint32 petnumber, bool current, AsynchPetSummon* info)
|
bool Pet::LoadPetFromDB(Player* owner, uint8 asynchLoadType, uint32 petentry, uint32 petnumber, bool current, AsynchPetSummon* info)
|
||||||
{
|
{
|
||||||
// we are loading pet at that moment
|
// we are loading pet at that moment
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ class Pet : public Guardian
|
|||||||
bool CreateBaseAtCreature(Creature* creature);
|
bool CreateBaseAtCreature(Creature* creature);
|
||||||
bool CreateBaseAtCreatureInfo(CreatureTemplate const* cinfo, Unit* owner);
|
bool CreateBaseAtCreatureInfo(CreatureTemplate const* cinfo, Unit* owner);
|
||||||
bool CreateBaseAtTamed(CreatureTemplate const* cinfo, Map* map, uint32 phaseMask);
|
bool CreateBaseAtTamed(CreatureTemplate const* cinfo, Map* map, uint32 phaseMask);
|
||||||
|
static SpellCastResult TryLoadFromDB(Player* owner, bool current = false, PetType mandatoryPetType = MAX_PET_TYPE);
|
||||||
static bool LoadPetFromDB(Player* owner, uint8 asynchLoadType, uint32 petentry = 0, uint32 petnumber = 0, bool current = false, AsynchPetSummon* info = NULL);
|
static bool LoadPetFromDB(Player* owner, uint8 asynchLoadType, uint32 petentry = 0, uint32 petnumber = 0, bool current = false, AsynchPetSummon* info = NULL);
|
||||||
bool isBeingLoaded() const { return m_loading;}
|
bool isBeingLoaded() const { return m_loading;}
|
||||||
void SavePetToDB(PetSaveMode mode, bool logout);
|
void SavePetToDB(PetSaveMode mode, bool logout);
|
||||||
|
|||||||
@@ -20815,7 +20815,10 @@ void Player::RemovePet(Pet* pet, PetSaveMode mode, bool returnreagent)
|
|||||||
{
|
{
|
||||||
// xinef: dont save dead pet as current, save him not in slot
|
// xinef: dont save dead pet as current, save him not in slot
|
||||||
if (!pet->IsAlive() && mode == PET_SAVE_AS_CURRENT && pet->getPetType() == HUNTER_PET)
|
if (!pet->IsAlive() && mode == PET_SAVE_AS_CURRENT && pet->getPetType() == HUNTER_PET)
|
||||||
|
{
|
||||||
mode = PET_SAVE_NOT_IN_SLOT;
|
mode = PET_SAVE_NOT_IN_SLOT;
|
||||||
|
m_temporaryUnsummonedPetNumber = 0;
|
||||||
|
}
|
||||||
|
|
||||||
#if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS)
|
#if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS)
|
||||||
sLog->outDebug(LOG_FILTER_PETS, "RemovePet %u, %u, %u", pet->GetEntry(), mode, returnreagent);
|
sLog->outDebug(LOG_FILTER_PETS, "RemovePet %u, %u, %u", pet->GetEntry(), mode, returnreagent);
|
||||||
@@ -27579,3 +27582,19 @@ void Player::SummonPet(uint32 entry, float x, float y, float z, float ang, PetTy
|
|||||||
AsynchPetSummon* asynchPetInfo = new AsynchPetSummon(entry, pos, petType, duration, createdBySpell, casterGUID);
|
AsynchPetSummon* asynchPetInfo = new AsynchPetSummon(entry, pos, petType, duration, createdBySpell, casterGUID);
|
||||||
Pet::LoadPetFromDB(this, asynchLoadType, entry, 0, false, asynchPetInfo);
|
Pet::LoadPetFromDB(this, asynchLoadType, entry, 0, false, asynchPetInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Player::IsPetDismissed()
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Check PET_SAVE_NOT_IN_SLOT means the pet is dismissed. If someone ever
|
||||||
|
* Changes the slot flag, they will break this validation.
|
||||||
|
*/
|
||||||
|
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_PET_BY_ENTRY_AND_SLOT);
|
||||||
|
stmt->setUInt32(0, GetGUIDLow());
|
||||||
|
stmt->setUInt8(1, uint8(PET_SAVE_NOT_IN_SLOT));
|
||||||
|
|
||||||
|
if (PreparedQueryResult result = CharacterDatabase.AsyncQuery(stmt))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1214,6 +1214,7 @@ class Player : public Unit, public GridObject<Player>
|
|||||||
uint32 GetInnTriggerId() const { return _innTriggerId; }
|
uint32 GetInnTriggerId() const { return _innTriggerId; }
|
||||||
|
|
||||||
Pet* GetPet() const;
|
Pet* GetPet() const;
|
||||||
|
bool IsPetDismissed();
|
||||||
void SummonPet(uint32 entry, float x, float y, float z, float ang, PetType petType, uint32 despwtime, uint32 createdBySpell, uint64 casterGUID, uint8 asynchLoadType);
|
void SummonPet(uint32 entry, float x, float y, float z, float ang, PetType petType, uint32 despwtime, uint32 createdBySpell, uint64 casterGUID, uint8 asynchLoadType);
|
||||||
void RemovePet(Pet* pet, PetSaveMode mode, bool returnreagent = false);
|
void RemovePet(Pet* pet, PetSaveMode mode, bool returnreagent = false);
|
||||||
uint32 GetPhaseMaskForSpawn() const; // used for proper set phase for DB at GM-mode creature/GO spawn
|
uint32 GetPhaseMaskForSpawn() const; // used for proper set phase for DB at GM-mode creature/GO spawn
|
||||||
|
|||||||
@@ -18484,6 +18484,13 @@ void Unit::NearTeleportTo(float x, float y, float z, float orientation, bool cas
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Unit::SendTameFailure(uint8 result)
|
||||||
|
{
|
||||||
|
WorldPacket data(SMSG_PET_TAME_FAILURE, 1);
|
||||||
|
data << uint8(result);
|
||||||
|
ToPlayer()->SendDirectMessage(&data);
|
||||||
|
}
|
||||||
|
|
||||||
void Unit::SendTeleportPacket(Position& pos)
|
void Unit::SendTeleportPacket(Position& pos)
|
||||||
{
|
{
|
||||||
Position oldPos = { GetPositionX(), GetPositionY(), GetPositionZ(), GetOrientation() };
|
Position oldPos = { GetPositionX(), GetPositionY(), GetPositionZ(), GetOrientation() };
|
||||||
|
|||||||
@@ -1761,6 +1761,7 @@ class Unit : public WorldObject
|
|||||||
void SendSpellDamageImmune(Unit* target, uint32 spellId);
|
void SendSpellDamageImmune(Unit* target, uint32 spellId);
|
||||||
|
|
||||||
void NearTeleportTo(float x, float y, float z, float orientation, bool casting = false, bool vehicleTeleport = false, bool withPet = false, bool removeTransport = false);
|
void NearTeleportTo(float x, float y, float z, float orientation, bool casting = false, bool vehicleTeleport = false, bool withPet = false, bool removeTransport = false);
|
||||||
|
void SendTameFailure(uint8 result);
|
||||||
void SendTeleportPacket(Position& pos);
|
void SendTeleportPacket(Position& pos);
|
||||||
virtual bool UpdatePosition(float x, float y, float z, float ang, bool teleport = false);
|
virtual bool UpdatePosition(float x, float y, float z, float ang, bool teleport = false);
|
||||||
// returns true if unit's position really changed
|
// returns true if unit's position really changed
|
||||||
|
|||||||
@@ -581,6 +581,24 @@ void WorldSession::SendStablePetCallback(PreparedQueryResult result, uint64 guid
|
|||||||
data << uint8(1); // 1 = current, 2/3 = in stable (any from 4, 5, ... create problems with proper show)
|
data << uint8(1); // 1 = current, 2/3 = in stable (any from 4, 5, ... create problems with proper show)
|
||||||
++num;
|
++num;
|
||||||
}
|
}
|
||||||
|
else if (_player->IsPetDismissed() || _player->GetTemporaryUnsummonedPetNumber())
|
||||||
|
{
|
||||||
|
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_PET_BY_ENTRY_AND_SLOT);
|
||||||
|
stmt->setUInt32(0, _player->GetGUIDLow());
|
||||||
|
stmt->setUInt8(1, uint8(_player->GetTemporaryUnsummonedPetNumber() ? PET_SAVE_AS_CURRENT : PET_SAVE_NOT_IN_SLOT));
|
||||||
|
|
||||||
|
if (PreparedQueryResult _result = CharacterDatabase.AsyncQuery(stmt))
|
||||||
|
{
|
||||||
|
Field* fields = _result->Fetch();
|
||||||
|
|
||||||
|
data << uint32(fields[0].GetUInt32()); // id
|
||||||
|
data << uint32(fields[1].GetUInt32()); // entry
|
||||||
|
data << uint32(fields[4].GetUInt16()); // level
|
||||||
|
data << fields[8].GetString(); // petname
|
||||||
|
data << uint8(1);
|
||||||
|
++num;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (result)
|
if (result)
|
||||||
{
|
{
|
||||||
@@ -639,10 +657,22 @@ void WorldSession::HandleStablePet(WorldPacket & recvData)
|
|||||||
Pet* pet = _player->GetPet();
|
Pet* pet = _player->GetPet();
|
||||||
|
|
||||||
// can't place in stable dead pet
|
// can't place in stable dead pet
|
||||||
if (!pet || !pet->IsAlive() || pet->getPetType() != HUNTER_PET)
|
if (pet)
|
||||||
{
|
{
|
||||||
SendStableResult(STABLE_ERR_STABLE);
|
if (!pet->IsAlive() || pet->getPetType() != HUNTER_PET)
|
||||||
return;
|
{
|
||||||
|
SendStableResult(STABLE_ERR_STABLE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SpellCastResult loadResult = Pet::TryLoadFromDB(_player, _player->GetTemporaryUnsummonedPetNumber() != 0, HUNTER_PET);
|
||||||
|
if (loadResult != SPELL_CAST_OK)
|
||||||
|
{
|
||||||
|
SendStableResult(STABLE_ERR_STABLE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PET_SLOTS);
|
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PET_SLOTS);
|
||||||
@@ -681,8 +711,27 @@ void WorldSession::HandleStablePetCallback(PreparedQueryResult result)
|
|||||||
WorldPacket data(SMSG_STABLE_RESULT, 1);
|
WorldPacket data(SMSG_STABLE_RESULT, 1);
|
||||||
if (freeSlot > 0 && freeSlot <= GetPlayer()->m_stableSlots)
|
if (freeSlot > 0 && freeSlot <= GetPlayer()->m_stableSlots)
|
||||||
{
|
{
|
||||||
_player->RemovePet(_player->GetPet(), PetSaveMode(freeSlot));
|
if (_player->GetPetGUID())
|
||||||
|
{
|
||||||
|
_player->RemovePet(_player->GetPet(), PetSaveMode(freeSlot));
|
||||||
|
SendStableResult(STABLE_SUCCESS_STABLE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// change pet slot directly in database
|
||||||
|
SQLTransaction trans = CharacterDatabase.BeginTransaction();
|
||||||
|
|
||||||
|
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UDP_CHAR_PET_SLOT_BY_SLOT);
|
||||||
|
stmt->setUInt8(0, freeSlot);
|
||||||
|
stmt->setUInt32(1, _player->GetGUIDLow());
|
||||||
|
stmt->setUInt8(2, uint8(_player->GetTemporaryUnsummonedPetNumber() ? PET_SAVE_AS_CURRENT : PET_SAVE_NOT_IN_SLOT));
|
||||||
|
trans->Append(stmt);
|
||||||
|
|
||||||
|
CharacterDatabase.CommitTransaction(trans);
|
||||||
|
|
||||||
|
_player->SetTemporaryUnsummonedPetNumber(0);
|
||||||
SendStableResult(STABLE_SUCCESS_STABLE);
|
SendStableResult(STABLE_SUCCESS_STABLE);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
SendStableResult(STABLE_ERR_STABLE);
|
SendStableResult(STABLE_ERR_STABLE);
|
||||||
@@ -725,10 +774,12 @@ void WorldSession::HandleUnstablePetCallback(PreparedQueryResult result, uint32
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
uint32 petEntry = 0;
|
uint32 petEntry = 0;
|
||||||
|
uint32 slot = 0;
|
||||||
if (result)
|
if (result)
|
||||||
{
|
{
|
||||||
Field* fields = result->Fetch();
|
Field* fields = result->Fetch();
|
||||||
petEntry = fields[0].GetUInt32();
|
petEntry = fields[0].GetUInt32();
|
||||||
|
slot = fields[1].GetUInt32();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!petEntry)
|
if (!petEntry)
|
||||||
@@ -757,7 +808,30 @@ void WorldSession::HandleUnstablePetCallback(PreparedQueryResult result, uint32
|
|||||||
|
|
||||||
// delete dead pet
|
// delete dead pet
|
||||||
if (pet)
|
if (pet)
|
||||||
|
{
|
||||||
_player->RemovePet(pet, PET_SAVE_AS_DELETED);
|
_player->RemovePet(pet, PET_SAVE_AS_DELETED);
|
||||||
|
}
|
||||||
|
else if (_player->IsPetDismissed() || _player->GetTemporaryUnsummonedPetNumber())
|
||||||
|
{
|
||||||
|
// try to find if pet is actually temporary unsummoned and alive
|
||||||
|
SpellCastResult loadResult = Pet::TryLoadFromDB(_player, _player->GetTemporaryUnsummonedPetNumber() != 0, HUNTER_PET);
|
||||||
|
if (loadResult != SPELL_CAST_OK)
|
||||||
|
{
|
||||||
|
SendStableResult(STABLE_ERR_STABLE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// change pet slot directly in database
|
||||||
|
SQLTransaction trans = CharacterDatabase.BeginTransaction();
|
||||||
|
|
||||||
|
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UDP_CHAR_PET_SLOT_BY_SLOT);
|
||||||
|
stmt->setUInt8(0, slot);
|
||||||
|
stmt->setUInt32(1, _player->GetGUIDLow());
|
||||||
|
stmt->setUInt8(2, uint8(_player->GetTemporaryUnsummonedPetNumber() ? PET_SAVE_AS_CURRENT : PET_SAVE_NOT_IN_SLOT));
|
||||||
|
trans->Append(stmt);
|
||||||
|
|
||||||
|
CharacterDatabase.CommitTransaction(trans);
|
||||||
|
_player->SetTemporaryUnsummonedPetNumber(0);
|
||||||
|
}
|
||||||
|
|
||||||
if (!Pet::LoadPetFromDB(_player, PET_LOAD_HANDLE_UNSTABLE_CALLBACK, petEntry, petId))
|
if (!Pet::LoadPetFromDB(_player, PET_LOAD_HANDLE_UNSTABLE_CALLBACK, petEntry, petId))
|
||||||
{
|
{
|
||||||
@@ -830,14 +904,26 @@ void WorldSession::HandleStableSwapPet(WorldPacket & recvData)
|
|||||||
|
|
||||||
Pet* pet = _player->GetPet();
|
Pet* pet = _player->GetPet();
|
||||||
|
|
||||||
if (!pet || pet->getPetType() != HUNTER_PET)
|
if (pet)
|
||||||
{
|
{
|
||||||
SendStableResult(STABLE_ERR_STABLE);
|
if (pet->getPetType() != HUNTER_PET)
|
||||||
return;
|
{
|
||||||
|
SendStableResult(STABLE_ERR_STABLE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (_player->IsPetDismissed() || _player->GetTemporaryUnsummonedPetNumber())
|
||||||
|
{
|
||||||
|
// try to find if pet is actually temporary unsummoned and alive
|
||||||
|
SpellCastResult loadResult = Pet::TryLoadFromDB(_player, _player->GetTemporaryUnsummonedPetNumber() != 0, HUNTER_PET);
|
||||||
|
if (loadResult != SPELL_CAST_OK)
|
||||||
|
{
|
||||||
|
SendStableResult(STABLE_ERR_STABLE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find swapped pet slot in stable
|
// Find swapped pet slot in stable
|
||||||
|
|
||||||
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PET_SLOT_BY_ID);
|
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PET_SLOT_BY_ID);
|
||||||
|
|
||||||
stmt->setUInt32(0, _player->GetGUIDLow());
|
stmt->setUInt32(0, _player->GetGUIDLow());
|
||||||
@@ -881,15 +967,24 @@ void WorldSession::HandleStableSwapPetCallback(PreparedQueryResult result, uint3
|
|||||||
}
|
}
|
||||||
|
|
||||||
Pet* pet = _player->GetPet();
|
Pet* pet = _player->GetPet();
|
||||||
// The player's pet could have been removed during the delay of the DB callback
|
|
||||||
if (!pet)
|
|
||||||
{
|
|
||||||
SendStableResult(STABLE_ERR_STABLE);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// move alive pet to slot or delete dead pet
|
// move alive pet to slot or delete dead pet
|
||||||
_player->RemovePet(pet, pet->IsAlive() ? PetSaveMode(slot) : PET_SAVE_AS_DELETED);
|
if (pet)
|
||||||
|
_player->RemovePet(pet, pet->IsAlive() ? PetSaveMode(slot) : PET_SAVE_AS_DELETED);
|
||||||
|
else if (_player->IsPetDismissed() || _player->GetTemporaryUnsummonedPetNumber())
|
||||||
|
{
|
||||||
|
// change pet slot directly in database
|
||||||
|
SQLTransaction trans = CharacterDatabase.BeginTransaction();
|
||||||
|
|
||||||
|
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UDP_CHAR_PET_SLOT_BY_SLOT);
|
||||||
|
stmt->setUInt8(0, slot);
|
||||||
|
stmt->setUInt32(1, _player->GetGUIDLow());
|
||||||
|
stmt->setUInt8(2, uint8(_player->GetTemporaryUnsummonedPetNumber() ? PET_SAVE_AS_CURRENT : PET_SAVE_NOT_IN_SLOT));
|
||||||
|
trans->Append(stmt);
|
||||||
|
|
||||||
|
CharacterDatabase.CommitTransaction(trans);
|
||||||
|
_player->SetTemporaryUnsummonedPetNumber(0);
|
||||||
|
}
|
||||||
|
|
||||||
// summon unstabled pet
|
// summon unstabled pet
|
||||||
if (!Pet::LoadPetFromDB(_player, PET_LOAD_HANDLE_UNSTABLE_CALLBACK, petEntry, petId))
|
if (!Pet::LoadPetFromDB(_player, PET_LOAD_HANDLE_UNSTABLE_CALLBACK, petEntry, petId))
|
||||||
|
|||||||
@@ -3475,6 +3475,23 @@ enum PetNameInvalidReason
|
|||||||
PET_NAME_DECLENSION_DOESNT_MATCH_BASE_NAME = 16
|
PET_NAME_DECLENSION_DOESNT_MATCH_BASE_NAME = 16
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum PetTameFailure
|
||||||
|
{
|
||||||
|
PET_TAME_INVALID_CREATURE = 1,
|
||||||
|
PET_TAME_TOO_MANY = 2,
|
||||||
|
PET_TAME_CREATURE_ALREADY_OWNED = 3,
|
||||||
|
PET_TAME_NOT_TAMEABLE = 4,
|
||||||
|
PET_TAME_ANOTHER_SUMMON_ACTIVE = 5,
|
||||||
|
PET_TAME_UNITS_CANT_TAME = 6,
|
||||||
|
PET_TAME_NOPET_AVAILABLE = 7,
|
||||||
|
PET_TAME_INTERNAL_ERROR = 8,
|
||||||
|
PET_TAME_TOO_HIGHLEVEL = 9,
|
||||||
|
PET_TAME_DEAD = 10,
|
||||||
|
PET_TAME_NOTDEAD = 11,
|
||||||
|
PET_TAME_CANT_CONTROL_EXOTIC = 12,
|
||||||
|
PET_TAME_UNKNOWNERROR = 13
|
||||||
|
};
|
||||||
|
|
||||||
enum DungeonStatusFlag
|
enum DungeonStatusFlag
|
||||||
{
|
{
|
||||||
DUNGEON_STATUSFLAG_NORMAL = 0x01,
|
DUNGEON_STATUSFLAG_NORMAL = 0x01,
|
||||||
|
|||||||
@@ -1118,30 +1118,50 @@ class spell_hun_tame_beast : public SpellScriptLoader
|
|||||||
|
|
||||||
SpellCastResult CheckCast()
|
SpellCastResult CheckCast()
|
||||||
{
|
{
|
||||||
Unit* caster = GetCaster();
|
Unit* caster = GetCaster();
|
||||||
if (caster->GetTypeId() != TYPEID_PLAYER)
|
if (caster->GetTypeId() != TYPEID_PLAYER)
|
||||||
return SPELL_FAILED_DONT_REPORT;
|
return SPELL_FAILED_DONT_REPORT;
|
||||||
|
|
||||||
|
Player* player = GetCaster()->ToPlayer();
|
||||||
|
|
||||||
if (!GetExplTargetUnit())
|
if (!GetExplTargetUnit())
|
||||||
return SPELL_FAILED_BAD_IMPLICIT_TARGETS;
|
{
|
||||||
|
player->SendTameFailure(PET_TAME_INVALID_CREATURE);
|
||||||
|
return SPELL_FAILED_DONT_REPORT;
|
||||||
|
}
|
||||||
|
|
||||||
if (Creature* target = GetExplTargetUnit()->ToCreature())
|
if (Creature* target = GetExplTargetUnit()->ToCreature())
|
||||||
{
|
{
|
||||||
if (target->getLevel() > caster->getLevel())
|
if (target->getLevel() > player->getLevel())
|
||||||
return SPELL_FAILED_HIGHLEVEL;
|
{
|
||||||
|
player->SendTameFailure(PET_TAME_TOO_HIGHLEVEL);
|
||||||
|
return SPELL_FAILED_DONT_REPORT;
|
||||||
|
}
|
||||||
|
|
||||||
// use SMSG_PET_TAME_FAILURE?
|
if (target->GetCreatureTemplate()->IsExotic() && !player->CanTameExoticPets())
|
||||||
if (!target->GetCreatureTemplate()->IsTameable(caster->ToPlayer()->CanTameExoticPets()))
|
{
|
||||||
return SPELL_FAILED_BAD_TARGETS;
|
player->SendTameFailure(PET_TAME_CANT_CONTROL_EXOTIC);
|
||||||
|
return SPELL_FAILED_DONT_REPORT;
|
||||||
|
}
|
||||||
|
|
||||||
if (caster->GetPetGUID())
|
if (!target->GetCreatureTemplate()->IsTameable(player->CanTameExoticPets()))
|
||||||
return SPELL_FAILED_ALREADY_HAVE_SUMMON;
|
{
|
||||||
|
player->SendTameFailure(PET_TAME_NOT_TAMEABLE);
|
||||||
|
return SPELL_FAILED_DONT_REPORT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (caster->GetPetGUID() || player->GetTemporaryUnsummonedPetNumber() || player->IsPetDismissed() || player->GetCharmGUID())
|
||||||
|
{
|
||||||
|
player->SendTameFailure(PET_TAME_ANOTHER_SUMMON_ACTIVE);
|
||||||
|
return SPELL_FAILED_DONT_REPORT;
|
||||||
|
}
|
||||||
|
|
||||||
if (caster->GetCharmGUID())
|
|
||||||
return SPELL_FAILED_ALREADY_HAVE_CHARM;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
return SPELL_FAILED_BAD_IMPLICIT_TARGETS;
|
{
|
||||||
|
player->SendTameFailure(PET_TAME_INVALID_CREATURE);
|
||||||
|
return SPELL_FAILED_DONT_REPORT;
|
||||||
|
}
|
||||||
|
|
||||||
return SPELL_CAST_OK;
|
return SPELL_CAST_OK;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user