From 6a947449c35ab25fa9b6ab2087f9f1892ced7d72 Mon Sep 17 00:00:00 2001 From: H0zen Date: Fri, 20 May 2016 23:26:37 +0300 Subject: [PATCH] Hunter fixes (#126) * Hunter fixes -RevivePet will now properly revive the pet -CallPet will detect if the pet is dead; TODO: what if the pet is dismissed or stabled? -Player who TeleportTo will not carry the corpse of the dead pet with him * Correction: summoners' pets are always saved alive into db * Minor corrections -Now CallPet detects if the hunter doesn't have a current pet, spawned or not --- src/game/Object/Object.h | 2 +- src/game/Object/Pet.cpp | 67 ++++++++++++++++++++++--- src/game/Object/Pet.h | 9 ++++ src/game/Object/Player.cpp | 30 +++++++++-- src/game/WorldHandlers/Map.cpp | 1 + src/game/WorldHandlers/Spell.cpp | 39 ++++++++++---- src/game/WorldHandlers/SpellEffects.cpp | 41 ++++++++++++--- 7 files changed, 160 insertions(+), 29 deletions(-) diff --git a/src/game/Object/Object.h b/src/game/Object/Object.h index 565bf64c..0d1627a9 100644 --- a/src/game/Object/Object.h +++ b/src/game/Object/Object.h @@ -523,7 +523,7 @@ class WorldObject : public Object void GetClosePoint(float& x, float& y, float& z, float bounding_radius, float distance2d = 0.0f, float angle = 0.0f, const WorldObject* obj = NULL) const { // angle calculated from current orientation - GetNearPoint(obj, x, y, z, bounding_radius, distance2d + GetObjectBoundingRadius() + bounding_radius, GetOrientation() + angle); + GetNearPoint(obj, x, y, z, bounding_radius, distance2d, GetOrientation() + angle); } /** Gives a "free" spot for a searcher in contact-range of "this" (including bounding-radius calculation) * @param x, y, z - position for the found spot diff --git a/src/game/Object/Pet.cpp b/src/game/Object/Pet.cpp index 47e2e462..69d8a605 100644 --- a/src/game/Object/Pet.cpp +++ b/src/game/Object/Pet.cpp @@ -254,9 +254,9 @@ bool Pet::LoadPetFromDB(Player* owner, uint32 petentry, uint32 petnumber, bool c uint32 savedpower = fields[14].GetUInt32(); // set current pet as current - // 0=current - // 1..MAX_PET_STABLES in stable slot - // PET_SAVE_NOT_IN_SLOT(100) = not stable slot (summoning)) + // 0 = current + // 1..MAX_PET_STABLES = in stable slot + // PET_SAVE_NOT_IN_SLOT(100) = not stable slot (summoning) or hunter pet dead if (fields[10].GetUInt32() != 0) { CharacterDatabase.BeginTransaction(); @@ -327,6 +327,10 @@ bool Pet::LoadPetFromDB(Player* owner, uint32 petentry, uint32 petnumber, bool c { SetHealth(savedhealth > GetMaxHealth() ? GetMaxHealth() : savedhealth); SetPower(powerType, savedpower > GetMaxPower(powerType) ? GetMaxPower(powerType) : savedpower); + + if (getPetType() == HUNTER_PET && savedhealth == 0) + SetDeathState(JUST_DIED); + } AIM_Initialize(); @@ -390,6 +394,13 @@ void Pet::SavePetToDB(PetSaveMode mode) } uint32 curhealth = GetHealth(); + + if (getPetType() != HUNTER_PET) + { + if (curhealth < 1) + curhealth = 1; + } + uint32 curpower = GetPower(GetPowerType()); // stable and not in slot saves @@ -450,7 +461,7 @@ void Pet::SavePetToDB(PetSaveMode mode) savePet.addUInt32(uint32(mode)); savePet.addString(m_name); savePet.addUInt32(uint32(HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_RENAME) ? 0 : 1)); - savePet.addUInt32((curhealth < 1 ? 1 : curhealth)); + savePet.addUInt32((curhealth)); savePet.addUInt32(curpower); savePet.addUInt32(GetPower(POWER_HAPPINESS)); @@ -551,7 +562,11 @@ void Pet::Update(uint32 update_diff, uint32 diff) { case CORPSE: { - Unsummon(PET_SAVE_NOT_IN_SLOT); + if (getPetType() != HUNTER_PET || m_corpseRemoveTime <= time(NULL)) + { + Unsummon(PET_SAVE_NOT_IN_SLOT); + return; + } break; } case ALIVE: @@ -986,7 +1001,6 @@ bool Pet::CreateBaseAtCreature(Creature* creature) uint32 guid = creature->GetMap()->GenerateLocalLowGuid(HIGHGUID_PET); - BASIC_LOG("Create pet"); uint32 pet_number = sObjectMgr.GeneratePetNumber(); if (!Create(guid, pos, creature->GetCreatureInfo(), pet_number)) { return false; } @@ -2137,3 +2151,44 @@ void Pet::UpdateSpeed(UnitMoveType mtype, bool forced, float ratio) SetSpeedRate(mtype, speed * ratio, forced); } + +PetDatabaseStatus Pet::GetStatusFromDB(Player* owner) +{ + PetDatabaseStatus status = PET_DB_NO_PET; + + uint32 ownerid = owner->GetGUIDLow(); + + QueryResult* result; + // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 + result = CharacterDatabase.PQuery("SELECT id, entry, owner, modelid, level, exp, Reactstate, loyaltypoints, loyalty, trainpoint, slot, name, renamed, curhealth, curmana, curhappiness, abdata, TeachSpelldata, savetime, resettalents_cost, resettalents_time, CreatedBySpell, PetType " + "FROM character_pet WHERE owner = '%u' AND (slot = '%u') ", + ownerid, PET_SAVE_AS_CURRENT); + if (!result) + { return status; } + + Field* fields = result->Fetch(); + + uint32 petentry = fields[1].GetUInt32(); + + if (!petentry) + { + delete result; + return status; + } + + CreatureInfo const* creatureInfo = ObjectMgr::GetCreatureTemplate(petentry); + if (!creatureInfo) + { + delete result; + return status; + } + + uint32 savedHP = fields[13].GetUInt32(); + delete result; + if (savedHP > 0) + status = PET_DB_ALIVE; + else + status = PET_DB_DEAD; + + return status; +} diff --git a/src/game/Object/Pet.h b/src/game/Object/Pet.h index b5724049..4b735dad 100644 --- a/src/game/Object/Pet.h +++ b/src/game/Object/Pet.h @@ -52,6 +52,13 @@ enum PetSaveMode PET_SAVE_REAGENTS = 101 // PET_SAVE_NOT_IN_SLOT with reagents return }; +enum PetDatabaseStatus +{ + PET_DB_NO_PET = 0, + PET_DB_DEAD = 1, + PET_DB_ALIVE = 2, +}; + // There might be a lot more enum PetModeFlags { @@ -174,7 +181,9 @@ class Pet : public Creature bool LoadPetFromDB(Player* owner, uint32 petentry = 0, uint32 petnumber = 0, bool current = false); void SavePetToDB(PetSaveMode mode); void Unsummon(PetSaveMode mode, Unit* owner = NULL); + static void DeleteFromDB(uint32 guidlow, bool separate_transaction = true); + static PetDatabaseStatus GetStatusFromDB(Player*); void SetDeathState(DeathState s) override; // overwrite virtual Creature::SetDeathState and Unit::SetDeathState void Update(uint32 update_diff, uint32 diff) override; // overwrite virtual Creature::Update and Unit::Update diff --git a/src/game/Object/Player.cpp b/src/game/Object/Player.cpp index 63a56a83..a056e211 100644 --- a/src/game/Object/Player.cpp +++ b/src/game/Object/Player.cpp @@ -1609,8 +1609,21 @@ bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientati if (!(options & TELE_TO_NOT_UNSUMMON_PET)) { // same map, only remove pet if out of range for new position - if (pet && !pet->IsWithinDist3d(x, y, z, GetMap()->GetVisibilityDistance())) - { UnsummonPetTemporaryIfAny(); } + if (pet) + { + if (!pet->IsWithinDist3d(x, y, z, GetMap()->GetVisibilityDistance())) + { + if (pet->IsAlive()) + { + UnsummonPetTemporaryIfAny(); + } + else + { + pet->Unsummon(PET_SAVE_NOT_IN_SLOT); + pet = GetPet(); + } + } + } } if (!(options & TELE_TO_NOT_LEAVE_COMBAT)) @@ -1678,8 +1691,17 @@ bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientati // remove pet on map change if (pet) - { UnsummonPetTemporaryIfAny(); } - + { + if (pet->IsAlive()) + { + UnsummonPetTemporaryIfAny(); + } + else + { + pet->Unsummon(PET_SAVE_NOT_IN_SLOT); + pet = GetPet(); + } + } // remove all dyn objects RemoveAllDynObjects(); diff --git a/src/game/WorldHandlers/Map.cpp b/src/game/WorldHandlers/Map.cpp index d2a7ef2a..5aaa2897 100644 --- a/src/game/WorldHandlers/Map.cpp +++ b/src/game/WorldHandlers/Map.cpp @@ -2087,6 +2087,7 @@ bool Map::GetHeightInRange(float x, float y, float& z, float maxSearchDist /*= 4 { float height, vmapHeight, mapHeight; vmapHeight = VMAP_INVALID_HEIGHT_VALUE; + mapHeight = INVALID_HEIGHT_VALUE; VMAP::IVMapManager* vmgr = VMAP::VMapFactory::createOrGetVMapManager(); if (!vmgr->isLineOfSightCalcEnabled()) diff --git a/src/game/WorldHandlers/Spell.cpp b/src/game/WorldHandlers/Spell.cpp index 9d2ff969..39d5c1cc 100644 --- a/src/game/WorldHandlers/Spell.cpp +++ b/src/game/WorldHandlers/Spell.cpp @@ -5028,12 +5028,26 @@ SpellCastResult Spell::CheckCast(bool strict) case SPELL_EFFECT_SUMMON_DEAD_PET: { Creature* pet = m_caster->GetPet(); - if (!pet) - { return SPELL_FAILED_NO_PET; } - if (pet->IsAlive()) + if (pet && pet->IsAlive()) { return SPELL_FAILED_ALREADY_HAVE_SUMMON; } + if (!pet) + { + if (Player* player = m_caster->ToPlayer()) + { + PetDatabaseStatus status = Pet::GetStatusFromDB(player); + if (status == PET_DB_NO_PET) + return SPELL_FAILED_NO_PET; + else if (status == PET_DB_ALIVE) + return SPELL_FAILED_TARGET_NOT_DEAD; + } + else + { + return SPELL_FAILED_NO_PET; + } + } + break; } // Don't make this check for SPELL_EFFECT_SUMMON_CRITTER, SPELL_EFFECT_SUMMON_WILD or SPELL_EFFECT_SUMMON_GUARDIAN. @@ -5053,22 +5067,27 @@ SpellCastResult Spell::CheckCast(bool strict) } case SPELL_EFFECT_SUMMON_PET: { + Player* plr = m_caster->ToPlayer(); if (m_caster->GetPetGuid()) // let warlock do a replacement summon { - Pet* pet = ((Player*)m_caster)->GetPet(); + Pet* pet = m_caster->GetPet(); - if (m_caster->GetTypeId() == TYPEID_PLAYER && m_caster->getClass() == CLASS_WARLOCK) - { - if (strict) // Summoning Disorientation, trigger pet stun (cast by pet so it doesn't attack player) - { pet->CastSpell(pet, 32752, true, NULL, NULL, pet->GetObjectGuid()); } - } - else + if (plr && m_caster->getClass() != CLASS_WARLOCK) { return SPELL_FAILED_ALREADY_HAVE_SUMMON; } } if (m_caster->GetCharmGuid()) { return SPELL_FAILED_ALREADY_HAVE_CHARM; } + if (plr) + { + PetDatabaseStatus status = Pet::GetStatusFromDB(plr); + if (status == PET_DB_DEAD) + return SPELL_FAILED_TARGETS_DEAD; + else if ((plr->getClass() == CLASS_HUNTER) && (status == PET_DB_NO_PET)) + return SPELL_FAILED_NO_PET; + } + break; } case SPELL_EFFECT_SUMMON_PLAYER: diff --git a/src/game/WorldHandlers/SpellEffects.cpp b/src/game/WorldHandlers/SpellEffects.cpp index d993658e..36aa02d4 100644 --- a/src/game/WorldHandlers/SpellEffects.cpp +++ b/src/game/WorldHandlers/SpellEffects.cpp @@ -4884,16 +4884,41 @@ void Spell::EffectDispelMechanic(SpellEffectIndex eff_idx) void Spell::EffectSummonDeadPet(SpellEffectIndex /*eff_idx*/) { - if (m_caster->GetTypeId() != TYPEID_PLAYER) + Player* _player = m_caster->ToPlayer(); + + if (!_player || damage < 0) { return; } - Player* _player = (Player*)m_caster; + Pet* pet = _player->GetPet(); - if (!pet) - { return; } - if (pet->IsAlive()) - { return; } - if (damage < 0) - { return; } + + bool hadPet = true; + + if (pet) + { + if (pet->IsAlive()) + return; + } + else + { + Pet* newPet = new Pet; + if (!newPet->LoadPetFromDB(_player)) + { + delete newPet; + return; + } + hadPet = false; + } + + pet = _player->GetPet(); + if (!pet || pet->IsAlive()) + return; + + if (hadPet) + { + float px, py, pz; + _player->GetClosePoint(px, py, pz, pet->GetObjectBoundingRadius(), _player->GetObjectBoundingRadius()); + pet->NearTeleportTo(px, py, pz, PET_FOLLOW_ANGLE); + } pet->SetUInt32Value(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_NONE); pet->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE);