[Patch] WIP: Allow Druid, Paladin and Warrior to tame pets

Updates:

Note: I won’t be updating this frequently so if anyone wants to share fixes or updates, feel free to post them in replies to this thread

[ul]
[li]2014-08-19 [/li]
Reorganized the bugs section
[li]Added more info re: Warriors not being able to use spells and a workaround for taming.[/li][/ul]

[li]2014-08-05 Added bug re: stablemaster[/li]
[SIZE=14px]Past Updates:[/SIZE]

[ul]
[li]2014-08-04 Fixed a condition check, thanks +trickerer for pointing it out[/li][li]2014-07-15 Updated info about feed pet and happiness system[/li][li]2014-07-15 Fixed crash when Hunters use call pet[/li][li]2014-07-14 - Initial release[/li][/ul]

General Info:

This patch allows players to tame permanent pets on their Druid, Paladin and Warrior. These pets are considered “Hunter Pets” by the core.

Details:

[ul]
[li]This patch is for 3.3.5a[/li][li]This patch is mainly for fun / soloing[/li]
Hunters on your server might be angry /emoticons/default_wink.png

[li]The classes have not been rebalanced to account for the new damage source so they may be OP in PvP[/li][/ul]

[li]Open the spoiler for more[/li]

[ul]
The pets can be renamed once by the player

After taming, right-click on the pet’s portrait and choose “rename”
[/ul]

[li]The pets get a talent sheet (Ferocity, Tenacity, Cunning)[/li][ul]
Talents are available at pet level 20
[li]Pets get one point every 4 levels[/li][li]Access this sheet from a tab on your character talent sheet[/li][/ul]

[li]These classes will most likely NOT be able to tame exotic pets (not tested)[/li][ul]
Exotic pets are a Beastmastery talent for Hunters
[/ul]

[li]These classes will NOT get the extra 4 pet talent points[/li][ul]
The extra talent points are a Beastmastery talent for Hunters
[/ul]

[li][SIZE=14px]These pets do not use the happiness system[/SIZE][/li][ul]
When the client receives happiness updates (the event UNIT_HAPPINESS), it checks the player class before updating the UI. If the class is not a Hunter then it actually hides the happiness icon and reports no happiness.

[li]This will most likely effect their damage output (needs testing)[/li]
“Happy” pets would do 125% of the damage listed on their sheet
[li]“Content” pets would do 100% of the damage listed on their sheet[/li][li]“Unhappy” pets would do 75% of the damage listed on their sheet[/li][/ul]

[li]This may cause pets to run away over time (needs testing)[/li][ul]
On a Hunter, if you let your pet stay “unhappy” too long, it will literally run away and you’ll need to go tame a new one. These pets do not use happiness so it is unclear if this will effect them
[/ul]

[li]Currently GMs must teach the player the necessary spells (more info in commit comment)[/li][ul]
To teach Tame Beast, Call Pet and Dismiss Pet, target the player and use GM command: .cast 1579
[li]To teach Revive Pet, target the player and use GM command: .learn 982[/li][li]If you want to teach Feed Pet[/li]
Target the player and use GM command: .learn 6991
[li]You can still feed the pet even though there is no happiness benefit[/li][li]Each pet type eats a certain food but this information is not available on the pet sheet without the happiness system. For reference, here is the list of approved foods:[/li]

Hunter Pet Diets @ wowwiki (scroll down a bit to see the table)
[/ul]

Known Issues (3):

[ul]
[li]Warriors[/li]
Cannot use Tame Beast due to it requiring mana

Workaround: Have a GM tame a pet by targeting a creature and using the GM command .npc tame. Then log out and manually edit the table character_pet (in the characters database) and change the field owner from the guid of the GM to the guid of the Warrior (player) from the characters table. The next time that Warrior logs in he/she will have a pet. They can rename it by right-clicking the portrait as long as the GM hasn’t done that already.
[/ul]

[li]Cannot use Revive Pet or Mend Pet due to them requiring mana[/li][ul]
Workaround: None. You will have to edit the core to remove mana cost from these spells if the class is Warrior
[/ul]

[li]General[/li][ul]
Some people have reported the stablemaster does not work for these classes, you can talk but not store a pet
[/ul]

Patch:

The patch is in the spoiler below, enjoy!

From 072e539a43e065fb2c0ef6ccb69a3bd7e476c497 Mon Sep 17 00:00:00 2001
From: MrSmite <[email protected]>
Date: Mon, 14 Jul 2014 15:40:19 -0400
Subject: [PATCH] WIP: Allow Druid, Paladin and Warrior to have pets

Currently GM must teach player hunter spells

 1. To teach Tame Beast, Call Pet and Dismiss Pet, target the player and use GM command: .cast 1579
 2. To teach Revive Pet, target the player and use GM command: .learn 982

Note: Feed pet is not needed, client ignores happiness for non-hunter class
Note: The spells will appear in the player's spell book on the General tab
Note: Hunters learn these abilities at level 10. Currently it is GM discretion for these classes.
---
 src/server/game/Entities/Pet/Pet.cpp        |  3 ++-
 src/server/game/Entities/Player/Player.cpp  |  6 ++++--
 src/server/game/Entities/Unit/Unit.cpp      | 14 +++++++++++++-
 src/server/game/Spells/SpellEffects.cpp     |  6 ++++--
 src/server/scripts/Commands/cs_misc.cpp     |  3 ++-
 src/server/scripts/Spells/spell_generic.cpp |  3 ++-
 src/server/scripts/World/npcs_special.cpp   |  3 ++-
 7 files changed, 29 insertions(+), 9 deletions(-)

diff --git a/src/server/game/Entities/Pet/Pet.cpp b/src/server/game/Entities/Pet/Pet.cpp
index 408a703..fc69317 100644
--- a/src/server/game/Entities/Pet/Pet.cpp
+++ b/src/server/game/Entities/Pet/Pet.cpp
@@ -849,8 +849,9 @@ bool Guardian::InitStatsForLevel(uint8 petlevel)
         {
             petType = SUMMON_PET;
         }
-        else if (GetOwner()->getClass() == CLASS_HUNTER)
+        else if (GetOwner()->getClass() == CLASS_HUNTER || GetOwner()->getClass() == CLASS_DRUID || GetOwner()->getClass() == CLASS_PALADIN || GetOwner()->getClass() == CLASS_WARRIOR)
         {
+            // Custom - Allow Druid, Paladin and Warrior to have / tame pets like Hunters
             petType = HUNTER_PET;
             m_unitTypeMask |= UNIT_MASK_HUNTER_PET;
         }
diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp
index c6ff104..ddb3857 100644
--- a/src/server/game/Entities/Player/Player.cpp
+++ b/src/server/game/Entities/Player/Player.cpp
@@ -2010,7 +2010,8 @@ bool Player::BuildEnumData(PreparedQueryResult result, WorldPacket* data)
     uint32 petFamily = 0;
 
     // show pet at selection character in character list only for non-ghost character
-    if (result && !(playerFlags & PLAYER_FLAGS_GHOST) && (plrClass == CLASS_WARLOCK || plrClass == CLASS_HUNTER || plrClass == CLASS_DEATH_KNIGHT))
+    // Custom - Allow Druid, Paladin and Warrior to have / tame pets like Hunters
+    if (result && !(playerFlags & PLAYER_FLAGS_GHOST) && (plrClass == CLASS_WARLOCK || plrClass == CLASS_HUNTER || plrClass == CLASS_DEATH_KNIGHT || plrClass == CLASS_DRUID || plrClass == CLASS_PALADIN || plrClass == CLASS_WARRIOR))
     {
         uint32 entry = fields[16].GetUInt32();
         CreatureTemplate const* creatureInfo = sObjectMgr->GetCreatureTemplate(entry);
@@ -14423,7 +14424,8 @@ void Player::PrepareGossipMenu(WorldObject* source, uint32 menuId /*= 0*/, bool
                         canTalk = false;
                     break;
                 case GOSSIP_OPTION_STABLEPET:
-                    if (getClass() != CLASS_HUNTER)
+                    // Custom - Allow Druid, Paladin and Warrior to have / tame pets like Hunters
+                    if (getClass() != CLASS_HUNTER && getClass() != CLASS_DRUID && getClass() != CLASS_PALADIN && getClass() != CLASS_WARRIOR)
                         canTalk = false;
                     break;
                 case GOSSIP_OPTION_QUESTGIVER:
diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp
index 5e2360b..50f1196 100644
--- a/src/server/game/Entities/Unit/Unit.cpp
+++ b/src/server/game/Entities/Unit/Unit.cpp
@@ -13503,13 +13503,25 @@ void Unit::SetMaxPower(Powers power, uint32 val)
 uint32 Unit::GetCreatePowers(Powers power) const
 {
     // Only hunter pets have POWER_FOCUS and POWER_HAPPINESS
+    // Custom - Allow Druid, Paladin and Warrior to have / tame pets like Hunters
+    //  NOTE: Hunter pets on non-hunters still don't get happiness. Client also checks class...
+
     switch (power)
     {
         case POWER_MANA:      return GetCreateMana();
         case POWER_RAGE:      return 1000;
         case POWER_FOCUS:     return (GetTypeId() == TYPEID_PLAYER || !((Creature const*)this)->IsPet() || ((Pet const*)this)->getPetType() != HUNTER_PET ? 0 : 100);
         case POWER_ENERGY:    return 100;
-        case POWER_HAPPINESS: return (GetTypeId() == TYPEID_PLAYER || !((Creature const*)this)->IsPet() || ((Pet const*)this)->getPetType() != HUNTER_PET ? 0 : 1050000);
+        case POWER_HAPPINESS:
+        {
+            uint32 val = (GetTypeId() == TYPEID_PLAYER || !((Creature const*)this)->IsPet() || ((Pet const*)this)->getPetType() != HUNTER_PET ? 0 : 1050000);
+            Unit *owner = ((Creature const*)this)->GetCharmerOrOwner();
+
+            if (owner && owner->getClass() != CLASS_HUNTER)
+                val = 0;
+
+            return val;
+        }
         case POWER_RUNIC_POWER: return 1000;
         case POWER_RUNE:      return 0;
         case POWER_HEALTH:    return 0;
diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp
index 606851b..7ff300e 100644
--- a/src/server/game/Spells/SpellEffects.cpp
+++ b/src/server/game/Spells/SpellEffects.cpp
@@ -2940,7 +2940,8 @@ void Spell::EffectTameCreature(SpellEffIndex /*effIndex*/)
     if (creatureTarget->IsPet())
         return;
 
-    if (m_caster->getClass() != CLASS_HUNTER)
+    // Custom - Allow Druid, Paladin and Warrior to have / tame pets like Hunters
+    if (m_caster->getClass() != CLASS_HUNTER  && m_caster->getClass() != CLASS_DRUID && m_caster->getClass() != CLASS_PALADIN && m_caster->getClass() != CLASS_WARRIOR)
         return;
 
     // cast finish successfully
@@ -5556,7 +5557,8 @@ void Spell::EffectCreateTamedPet(SpellEffIndex effIndex)
     if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
         return;
 
-    if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER || unitTarget->GetPetGUID() || unitTarget->getClass() != CLASS_HUNTER)
+    // Custom - Allow Druid, Paladin and Warrior to have / tame pets like Hunters
+    if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER || unitTarget->GetPetGUID() || (unitTarget->getClass() != CLASS_HUNTER && unitTarget->getClass() != CLASS_DRUID && unitTarget->getClass() != CLASS_PALADIN && unitTarget->getClass() != CLASS_WARRIOR))
         return;
 
     uint32 creatureEntry = m_spellInfo->Effects[effIndex].MiscValue;
diff --git a/src/server/scripts/Commands/cs_misc.cpp b/src/server/scripts/Commands/cs_misc.cpp
index 4585389..6f24679 100644
--- a/src/server/scripts/Commands/cs_misc.cpp
+++ b/src/server/scripts/Commands/cs_misc.cpp
@@ -2303,7 +2303,8 @@ public:
             player->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
 
             // if player class = hunter || warlock remove pet if alive
-            if ((player->getClass() == CLASS_HUNTER) || (player->getClass() == CLASS_WARLOCK))
+            // Custom - Allow Druid, Paladin and Warrior to have / tame pets like Hunters
+            if ((player->getClass() == CLASS_HUNTER) || (player->getClass() == CLASS_WARLOCK) || (player->getClass() == CLASS_DRUID) || (player->getClass() == CLASS_PALADIN) || (player->getClass() == CLASS_WARRIOR))
             {
                 if (Pet* pet = player->GetPet())
                 {
diff --git a/src/server/scripts/Spells/spell_generic.cpp b/src/server/scripts/Spells/spell_generic.cpp
index 4c72d79..4f64d7e 100644
--- a/src/server/scripts/Spells/spell_generic.cpp
+++ b/src/server/scripts/Spells/spell_generic.cpp
@@ -2842,7 +2842,8 @@ class spell_gen_pet_summoned : public SpellScriptLoader
                 Player* player = GetCaster()->ToPlayer();
                 if (player->GetLastPetNumber())
                 {
-                    PetType newPetType = (player->getClass() == CLASS_HUNTER) ? HUNTER_PET : SUMMON_PET;
+                    // Custom - Allow Druid, Paladin and Warrior to have / tame pets like Hunters
+                    PetType newPetType = (player->getClass() == CLASS_HUNTER || player->getClass() == CLASS_DRUID || player->getClass() == CLASS_PALADIN || player->getClass() == CLASS_WARRIOR) ? HUNTER_PET : SUMMON_PET;
                     Pet* newPet = new Pet(player, newPetType);
                     if (newPet->LoadPetFromDB(player, 0, player->GetLastPetNumber(), true))
                     {
diff --git a/src/server/scripts/World/npcs_special.cpp b/src/server/scripts/World/npcs_special.cpp
index e67823a..ad42fd7 100644
--- a/src/server/scripts/World/npcs_special.cpp
+++ b/src/server/scripts/World/npcs_special.cpp
@@ -1756,7 +1756,8 @@ public:
         if (creature->IsQuestGiver())
             player->PrepareQuestMenu(creature->GetGUID());
 
-        if (player->getClass() == CLASS_HUNTER)
+        // Custom - Allow Druid, Paladin and Warrior to have / tame pets like Hunters
+        if (player->getClass() == CLASS_HUNTER || player->getClass() == CLASS_DRUID || player->getClass() == CLASS_PALADIN || player->getClass() == CLASS_WARRIOR)
         {
             player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_PET1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1);
             if (player->GetPet() && player->GetPet()->getPetType() == HUNTER_PET)
-- 
1.7.11.msysgit.1

Question. Does this script take into account that pet damage scales based on the Hunter’s Ranged Attack Power? I was doing some tinkering with the SQL to make something like this work and the Attack Power was the biggest obstacle I ran into. At level 80 the pet’s dps is very weak because a Paladin doesn’t get ranged attack power. I went as far as creating a custom item that adds ranged attack power and the stats for the player affect it but it didn’t increase the dps of the pet. Maybe one of the flags in this script will cause the DPS to scale properly?

Also for a blizz-like way of getting the pet spells, I removed the race / class restrictions on the Dwarf taming quest chain which gave me the related spells after completing it.

This just lets the classes tame pets, I did no balancing at all. The pets will stiill get some of their master’s stat scaling though and since the classes themselves aren’t intended to have pets, the weaker damage you mention isn’t that bad.

It’s like you have a permanent DOT that you don’t need to feed /emoticons/default_wink.png

Just curious. I’ve been looking into getting it to scale properly but that’s as far as I got last night. Thanks.

I mainly created it because I typically play by myself and even though I love the Hunter class, it gets boring playing them all the time. I rolled a Paladin and Druid for survivability but that was boring without a companion /emoticons/default_smile.png

I whipped up some SQL tonight to make the Hunter quest chain available to Warriors, Paladins, Druids, and Hunters. Completing the chain gives you Tame Beast, Call Pet, Dismiss Pet, Feed Pet, and Revive Pet without having to use GM commands. The only spell missing is Mend Pet which comes from the Hunter Trainer. I’ve only tested this with a Human Paladin and a Tuaren Druid, but the code should be good for all of them.

Classic races have 7 quests associated with training and the BC races have 5. The first three quests are breadcrumb quests that send you to the questgiver. Then there are three training quests and finally the quest that gives you Feed Pet and Revive Pet. The BC races only have one Breadcrumb Quest.

For Humans, I have them going to the Dwarf area since there’s a breadcrumb in Stormwind.
For Undead, I have them going to Durotar because there’s a breadcrumb in ORG but not SMC.

All other races have the training quests in their areas.

Here’s the code:

/* Dwarf */
UPDATE world.quest_template SET requiredraces='69', requiredclasses='1031' WHERE id='6064'; 
UPDATE world.quest_template SET requiredraces='69', requiredclasses='1031' WHERE id='6074'; 
UPDATE world.quest_template SET requiredraces='69', requiredclasses='1031' WHERE id='6075'; 
UPDATE world.quest_template SET requiredraces='69', requiredclasses='1031' WHERE id='6076'; 
UPDATE world.quest_template SET requiredraces='69', requiredclasses='1031' WHERE id='6084'; 
UPDATE world.quest_template SET requiredraces='69', requiredclasses='1031' WHERE id='6085'; 
UPDATE world.quest_template SET requiredraces='69', requiredclasses='1031' WHERE id='6086';
/* Night Elf */
UPDATE world.quest_template SET requiredraces='8', requiredclasses='1031' WHERE id='6071';
UPDATE world.quest_template SET requiredraces='8', requiredclasses='1031' WHERE id='6072';
UPDATE world.quest_template SET requiredraces='8', requiredclasses='1031' WHERE id='6073';
UPDATE world.quest_template SET requiredraces='8', requiredclasses='1031' WHERE id='6063';
UPDATE world.quest_template SET requiredraces='8', requiredclasses='1031' WHERE id='6101';
UPDATE world.quest_template SET requiredraces='8', requiredclasses='1031' WHERE id='6102';
UPDATE world.quest_template SET requiredraces='8', requiredclasses='1031' WHERE id='6103';
/* Draenei */
UPDATE world.quest_template SET requiredraces='1024', requiredclasses='1031' WHERE id='10779';
UPDATE world.quest_template SET requiredraces='1024', requiredclasses='1031' WHERE id='9591';
UPDATE world.quest_template SET requiredraces='1024', requiredclasses='1031' WHERE id='9592';
UPDATE world.quest_template SET requiredraces='1024', requiredclasses='1031' WHERE id='9593';
UPDATE world.quest_template SET requiredraces='1024', requiredclasses='1031' WHERE id='9675';
/* Tauren */
UPDATE world.quest_template SET requiredraces='32', requiredclasses='1031' WHERE id='6065';
UPDATE world.quest_template SET requiredraces='32', requiredclasses='1031' WHERE id='6066';
UPDATE world.quest_template SET requiredraces='32', requiredclasses='1031' WHERE id='6067';
UPDATE world.quest_template SET requiredraces='32', requiredclasses='1031' WHERE id='6061';
UPDATE world.quest_template SET requiredraces='32', requiredclasses='1031' WHERE id='6087';
UPDATE world.quest_template SET requiredraces='32', requiredclasses='1031' WHERE id='6088';
UPDATE world.quest_template SET requiredraces='32', requiredclasses='1031' WHERE id='6089';
/* Orc/Troll */
UPDATE world.quest_template SET requiredraces='146', requiredclasses='1031' WHERE id='6068';
UPDATE world.quest_template SET requiredraces='146', requiredclasses='1031' WHERE id='6069';
UPDATE world.quest_template SET requiredraces='146', requiredclasses='1031' WHERE id='6070';
UPDATE world.quest_template SET requiredraces='146', requiredclasses='1031' WHERE id='6062';
UPDATE world.quest_template SET requiredraces='146', requiredclasses='1031' WHERE id='6082';
UPDATE world.quest_template SET requiredraces='146', requiredclasses='1031' WHERE id='6083';
UPDATE world.quest_template SET requiredraces='146', requiredclasses='1031' WHERE id='6081';
/* Blood Elf */
UPDATE world.quest_template SET requiredraces='512', requiredclasses='1031' WHERE id='10530';
UPDATE world.quest_template SET requiredraces='512', requiredclasses='1031' WHERE id='9484';
UPDATE world.quest_template SET requiredraces='512', requiredclasses='1031' WHERE id='9485';
UPDATE world.quest_template SET requiredraces='512', requiredclasses='1031' WHERE id='9486';
UPDATE world.quest_template SET requiredraces='512', requiredclasses='1031' WHERE id='9673';

Nice work. You can also just add those spells to the class trainers.

It doesnt seem like stables work for other than hunters, i think that is a client thing as well

Also, if you want to remove the mana cost simply add this inside spell.dbc:

//Don’t take power if the spell is cast while .cheat power is enabled.
if (m_caster->GetTypeId() == TYPEID_PLAYER)
{
if (m_caster->ToPlayer()->GetCommandStatus(CHEAT_POWER))
return;

if (m_spellInfo->Id == 1515 || m_spellInfo->Id == 982 || m_spellInfo->Id == 48990 || m_spellInfo->Id == 1579)
return;
}

In what way do they not work? It’s possible there is something I missed. I was able to purchase slots but I can’t remember if I actually put a pet there or not.

yeah you can purchase but you cant put in a pet

Ok, that means you need edits to HandleStablePetOpcode() or whatever it’s called…

Keep in mind the original intent of the patch was to allow other classes to tame a pet, not necessarily to turn them into Hunters. I think it’s fine only allowing one pet per non-Hunter.

+MrSmite nice project you have started here. But looking at the code I see at least two places with illogical conditions which may break something.

I mean code where you check “someplayer->getClass() != SOME_CLASS OR someplayer->getClass() != SOME_OTHER_CLASS” which pretty much means any class.

You need to look again.

The only place I used a check like that was in Spell::EffectCreateTamedPet() which is correct since the spell is supposed to fail if GetClass() is not equal to paladin, hunter, druid or warrior.

That’s what I mean:

@@ -14423,7 +14424,8 @@ void Player::PrepareGossipMenu(WorldObject* source, uint32 menuId /= 0/, bool
canTalk = false;
break;
case GOSSIP_OPTION_STABLEPET:

  •                if (getClass() != CLASS_HUNTER)
    
  •                // Custom - Allow Druid, Paladin and Warrior to have / tame pets like Hunters
    
  •                if (getClass() != CLASS_HUNTER || getClass() != CLASS_DRUID || getClass() != CLASS_PALADIN || getClass() != CLASS_WARRIOR)
                       canTalk = false;
                   break;
               case GOSSIP_OPTION_QUESTGIVER:
    

@@ -5556,7 +5557,8 @@ void Spell::EffectCreateTamedPet(SpellEffIndex effIndex)
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;

  • if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER || unitTarget->GetPetGUID() || unitTarget->getClass() != CLASS_HUNTER)
  • // Custom - Allow Druid, Paladin and Warrior to have / tame pets like Hunters

  • if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER || unitTarget->GetPetGUID() || unitTarget->getClass() != CLASS_HUNTER || unitTarget->getClass() != CLASS_DRUID || unitTarget->getClass() != CLASS_PALADIN || unitTarget->getClass() != CLASS_WARRIOR)
    return;

    uint32 creatureEntry = m_spellInfo->Effects[effIndex].MiscValue;

These conditions are always true.

Right, I see what you’re getting at. I posted the wrong patch, I fixed it to use && but copied the wrong one to the forums.

The OP has been updated.