EQEmulator Forums

EQEmulator Forums (https://www.eqemulator.org/forums/index.php)
-   Development::Server Code Submissions (https://www.eqemulator.org/forums/forumdisplay.php?f=669)
-   -   Pet Focus (https://www.eqemulator.org/forums/showthread.php?t=30607)

bad_captain 02-17-2010 10:20 AM

Pet Focus
 
Below are changes that can be used to make pet focii work about as close as possible to live without maintaining multiple versions of pets per focus, which is a maintenance headache.

First, in pets.cpp I add a parameter to makepet to allow new pets to be created at a specified pet power level (suspend minion, zoning, etc.) Then, I take out the current focus code, and call virtual GetPetFocusEffects, so it works for both clients and bots. Then, after the pet is created, I set the pet's focus power level so it can be recalled later if needed. I then create a method to call a single CalcPetFocusEffects() method that can be called from both clients and bots (to keep all the calcs in one place). The CalcPetFocusEffects() method contains all of the focus effect code. I update HP, AC, min and max damage, regen, size, and accuracy. HP, AC, min and max damage are as close to live as I could get. The actual values seen vary by class pet (mage pet increase was average- mostly hps, while necro increased AC significantly more, and beastlord warders increased melee damage the most. These equations keep the values within observed live values all the way up to pet power 90, which won't likely be seen here unless SoD is added. Lower level pet focus numbers (shovel of ponz, broom of trilon, etc. numbers couldn't be found, but the numbers end up in line with the rest.
Code:

--- pets.cpp        (revision 1126)
+++ pets.cpp        (working copy)
@@ -215,7 +215,7 @@
 */
 
 
-void Mob::MakePet(int16 spell_id, const char* pettype, const char *petname) {
+void Mob::MakePet(int16 spell_id, const char* pettype, sint16 petFocusPower, const char *petname) {
        //see if we are a special type of pet (for command filtering)
        PetType type = petOther;
        if(strncmp(pettype, "Familiar", 8) == 0) {
@@ -226,7 +226,6 @@
       
        if(HasPet())
                return;
-
       
        //lookup our pets table record for this type
        PetRecord record;
@@ -248,16 +247,7 @@
        NPCType *npc_type = new NPCType;
        memcpy(npc_type, base, sizeof(NPCType));
       
-        if (this->IsClient() && CastToClient()->GetFocusEffect(focusPetPower, spell_id) > 0)
-        {
-                npc_type->max_hp *= 1.20;
-                npc_type->cur_hp = npc_type->max_hp;
-                npc_type->AC *= 1.20;
-                npc_type->level += 1;
-                npc_type->min_dmg = (npc_type->min_dmg * 110 / 100);
-                npc_type->max_dmg = (npc_type->max_dmg * 110 / 100);
-                npc_type->size *= 1.15;
-        }
+        GetPetFocusEffects(npc_type, spell_id, petFocusPower);
 
        switch (GetAA(aaElementalDurability))
        {
@@ -354,9 +344,48 @@
        //this takes ownership of the npc_type data
        Pet *npc = new Pet(npc_type, this, type, spell_id);
 
+        npc->SetPetFocusPower(petFocusPower);
+
        entity_list.AddNPC(npc, true, true);
        SetPetID(npc->GetID());
 }
+
+void Mob::GetPetFocusEffects(NPCType *npc_type, int16 spell_id, sint16 &petFocusPower) {
+       
+        if(IsClient())
+        {
+                if(petFocusPower == 0)
+                {
+                        petFocusPower = CastToClient()->GetFocusEffect(focusPetPower, spell_id);
+                }
+
+                if(petFocusPower > 0)
+                {
+                        CalcPetFocusEffects(npc_type, petFocusPower);
+                }
+        }
+}
+
+void Mob::CalcPetFocusEffects(NPCType *npc_type, sint16 petFocusPower) {
+               
+        if(petFocusPower < 15){
+                    npc_type->AC = (int16)floor((double)npc_type->AC * (petFocusPower/2+100)/100);
+                    npc_type->min_dmg += (int32)floor((double)petFocusPower/5);
+                    npc_type->max_dmg += (int32)floor((double)petFocusPower/5);
+        }
+        else{
+                    npc_type->AC += (int16)floor((.75*petFocusPower*petFocusPower)/5);
+                    npc_type->level += (int8)floor((double)2*(petFocusPower / 40));
+                    npc_type->min_dmg += (int32)floor((double)petFocusPower/4 - 2);
+                    npc_type->max_dmg = (int32)floor((double)npc_type->max_dmg * ((0.4*(petFocusPower-10)+100)/100));
+        }
+
+        npc_type->max_hp = (sint32)floor((double)npc_type->max_hp * ((0.35*petFocusPower)+100)/100);
+        npc_type->cur_hp = npc_type->max_hp;
+        npc_type->hp_regen = (sint32)floor((double)npc_type->hp_regen * ((petFocusPower/2)+100)/100);
+    npc_type->size *= (((petFocusPower/2)+100)/100);
+        npc_type->accuracy_rating += (petFocusPower + 25);
+}


The declarations in mob.h:
Code:

--- mob.h        (revision 1205)
+++ mob.h        (working copy)
@@ -500,7 +500,9 @@
        int                CountDispellableBuffs();
        bool        HasBuffIcon(Mob* caster, Mob* target, int16 spell_id);
 
-        virtual void MakePet(int16 spell_id, const char* pettype, const char *petname = NULL);
+        virtual void MakePet(int16 spell_id, const char* pettype, sint16 petFocusPower, const char *petname = NULL);
+        virtual void GetPetFocusEffects(NPCType *npc_type, int16 spell_id, sint16 &focusPower);
+        void CalcPetFocusEffects(NPCType *npc_type, sint16 focusPower);
 //        inline void        MakePetType(int16 spell_id, const char* pettype, const char *petname = NULL) { MakePet(spell_id, pettype, petname); }        //for perl
 //        void        MakePet(int16 spell_id, int8 in_level, int8 in_class, int16 in_race, int8 in_texture = 0, int8 in_pettype = 0, float in_size = 0, int8 type = 0, int32 min_dmg = 0, int32 max_dmg = 0, const char *petname = NULL);


A few changes to account for added parameter to MakePet:
Code:

--- perl_mob.cpp        (revision 1205)
+++ perl_mob.cpp        (working copy)
@@ -1476,7 +1476,7 @@
                        name = (char *)SvPV_nolen(ST(3));
                }
 
-                THIS->MakePet(spell_id, pettype, name);
+                THIS->MakePet(spell_id, pettype, 0, name);
        }
        XSRETURN_EMPTY;
 }

Code:

--- command.cpp          (revision 1213)
+++ command.cpp          (working copy)
@@ -2731,7 +2731,7 @@
        if (sep->arg[1][0] == '\0')
                c->Message(0, "Usage: #makepet pet_type_name (will not survive across zones)");
        else
-                c->MakePet(0, sep->arg[1]);
+                c->MakePet(0, sep->arg[1], 0);
 }
 
 void command_level(Client *c, const Seperator *sep)

Code:

--- client_packet.cpp        (revision 1227)
+++ client_packet.cpp        (working copy)
@@ -8063,7 +8063,7 @@
        //Remake pet
        if (m_epp.pet_id > 1 && !GetPet() && m_epp.pet_id <= SPDAT_RECORDS)
        {
-                MakePet(m_epp.pet_id, spells[m_epp.pet_id].teleport_zone, m_epp.pet_name);
+                MakePet(m_epp.pet_id, spells[m_epp.pet_id].teleport_zone, 0, m_epp.pet_name);
                if (GetPet() && GetPet()->IsNPC()) {
                        NPC *pet = GetPet()->CastToNPC();
                        pet->SetPetState(m_epp.pet_buffs, m_epp.pet_items);

This one also makes a change to the SE_LimitMaxLevel in CalcFocusEffects() due to the fact that pet focii (as well as some other focus effects) have a 0 in the per level falloff over the max level, which designates that they should stop working altogether. They could be set to 100 in the database to achieve the same effect, but this one change keeps from having to maintain those changes, as any new focus effects pulled from live (lucy) would have the 0, and would need to be changed.
Code:

--- spell_effects.cpp        (revision 1126)
+++ spell_effects.cpp        (working copy)
@@ -1048,7 +1048,7 @@
                                }
                                else
                                {
-                                        MakePet(spell_id, spell.teleport_zone);
+                                        MakePet(spell_id, spell.teleport_zone, 0);
                                }
                                break;
                        }
@@ -3580,7 +3580,7 @@
 
                        if(lvldiff > 0){ //every level over cap reduces the effect by spell.base2[i] percent
                                lvlModifier -= spell.base2[i]*lvldiff;
-                                if(lvlModifier < 1)
+                                if((lvlModifier < 1) || (spell.base2[i] == 0))
                                        return 0;
                        }
                        break;


Next, is the code to save petFocusPower within the pet.
Code:

--- npc.h        (revision 1175)
+++ npc.h        (working copy)
@@ -201,6 +201,8 @@
        bool        IsAnimal() const { return(bodytype == BT_Animal); }
        int16  GetPetSpellID() const {return pet_spell_id;}
        void    SetPetSpellID(int16 amt) {pet_spell_id = amt;}
+        sint16  GetPetFocusPower() const {return pet_focus_power;}
+        void    SetPetFocusPower(sint16 amt) {pet_focus_power = amt;}
        int32        GetMaxDamage(int8 tlevel);
        void    SetTaunting(bool tog) {taunting = tog;}
        void        PickPocket(Client* thief);
@@ -336,6 +338,7 @@
       
        //pet crap:
        int16        pet_spell_id;
+        sint16        pet_focus_power;
        bool        taunting;
    Timer        taunt_timer;                //for pet taunting

Declaration for Bot's version of GetPetFocusEffects()
Code:

--- bot.h        (revision 1132)
+++ bot.h        (working copy)
@@ -113,6 +113,7 @@
        virtual sint32 CheckAggroAmount(int16 spellid);
        virtual void CalcBonuses();
        virtual void MakePet(int16 spell_id, const char* pettype, const char *petname = NULL);
+        virtual void GetPetFocusEffects(NPCType *npc_type, int16 spell_id, sint16 &focusPower);
        virtual FACTION_VALUE GetReverseFactionCon(Mob* iOther);
        inline virtual bool IsPet() { return false; }
        virtual bool IsNPC() const { return false; }

Bot code to get BotfocusPetPower. (It couldn't be accessed from within mob.cpp)
Code:

--- bot.cpp        (revision 1180)
+++ bot.cpp        (working copy)
@@ -6792,9 +6792,22 @@
 }
 
 void Bot::MakePet(int16 spell_id, const char* pettype, const char *petname) {
-        Mob::MakePet(spell_id, pettype, petname);
+        Mob::MakePet(spell_id, pettype, 0, petname);
 }
 
+void Bot::GetPetFocusEffects(NPCType *npc_type, int16 spell_id, sint16 &focusPower) {
+       
+        if(IsBot())
+        {
+                focusPower = GetBotFocusEffect(BotfocusPetPower, spell_id);
+
+                if(focusPower > 0)
+                {
+                        CalcPetFocusEffects(npc_type, focusPower);
+                }
+        }
+}
+
 void Bot::AI_Stop() {
        NPC::AI_Stop();
        Mob::AI_Stop();

Change to the player profile struct to store the petFocusPower to allow unsuspended pets to retain their power if the caster had removed the focus item after summoning, just like live.
Code:

--- eq_packet_structs.h        (revision 1208)
+++ eq_packet_structs.h        (working copy)
@@ -767,6 +767,7 @@
        uint16                        SpellID;
        uint32                        HP;
        uint32                        Mana;
+        sint16                        PetFocusPower;
        SpellBuff_Struct        Buffs[BUFF_COUNT];
        uint32                        Items[MAX_MATERIALS];
        char                        Name[64];
@@ -1004,8 +1005,8 @@
 /*12804*/        uint32                                aapoints;        //avaliable, unspent
 /*12808*/        uint8                                unknown12808[36];
 /*12844*/        Bandolier_Struct        bandoliers[MAX_PLAYER_BANDOLIER];
-/*14124*/        uint8                                unknown14124[4506];
-/*18630*/        SuspendedMinion_Struct                SuspendedMinion;
+/*14124*/        uint8                                unknown14124[4504];
+/*18628*/        SuspendedMinion_Struct                SuspendedMinion;
 /*19240*/        uint32                                timeentitledonaccount;
 /*19244*/        PotionBelt_Struct        potionbelt;        //there should be 3 more of these
 /*19532*/        uint8                                unknown19532[8];


Change to client.cpp to suspend and unsuspend pet, saving and restoring pet with same pet level.
Code:

--- client.cpp        (revision 1207)
+++ client.cpp        (working copy)
@@ -5608,7 +5608,7 @@
        {
                if(m_pp.SuspendedMinion.SpellID > 0)
                {
-                        MakePet(m_pp.SuspendedMinion.SpellID, spells[m_pp.SuspendedMinion.SpellID].teleport_zone);
+                        MakePet(m_pp.SuspendedMinion.SpellID, spells[m_pp.SuspendedMinion.SpellID].teleport_zone, m_pp.SuspendedMinion.PetFocusPower);
 
                        CurrentPet = GetPet()->CastToNPC();
 
@@ -5630,6 +5630,8 @@
 
                        CurrentPet->SetMana(m_pp.SuspendedMinion.Mana);
 
+                        CurrentPet->SetPetFocusPower(m_pp.SuspendedMinion.PetFocusPower);
+
                        Message_StringID(clientMessageTell, SUSPEND_MINION_UNSUSPEND, CurrentPet->GetCleanName());
                       
                        memset(&m_pp.SuspendedMinion, 0, sizeof(SuspendedMinion_Struct));
@@ -5664,10 +5666,12 @@
                        {
                                m_pp.SuspendedMinion.SpellID = SpellID;
 
-                                m_pp.SuspendedMinion.HP = CurrentPet->GetHP();;
+                                m_pp.SuspendedMinion.HP = CurrentPet->GetHP();
 
                                m_pp.SuspendedMinion.Mana = CurrentPet->GetMana();
 
+                                m_pp.SuspendedMinion.PetFocusPower = CurrentPet->GetPetFocusPower();
+
                                if(AALevel >= 2)
                                        CurrentPet->GetPetState(m_pp.SuspendedMinion.Buffs, m_pp.SuspendedMinion.Items, m_pp.SuspendedMinion.Name);


bad_captain 02-17-2010 10:35 AM

Note one: I don't update the database for the player profile to account for the changes to the suspend minion struct, so I assume unless someone can write one, there may be some weirdness if a player has a suspended pet while this patch is applied. I wasn't sure how to make that change to the player profile in the database, so I didn't want to mess something up.

Second Note: This does NOT work with zoning. A similar change would need to be made to extprofile to account for the petFocusPower to be saved along with the pet to be recalled after the zone, but that would be an easy code fix (just send the petFocusPower to the MakePet call) if the database change was made (again I didn't want to mess anythign up as I wasn't sure how to make the change, and this one would actually change the size of the blob in the database, instead of just shifting the structure around).

Third Note: I also have the code finished to allow for pets and npcs to equip a full inventory, instead of just the visible items. I ran into the same issue as the Second Note with the extprofile needing to be modified, so I wanted to go ahead and submit these changes, and add the equipment later. Pets will be summoned with equipment, with the quality increasing based on caster level and petPowerLEvel. The equipment include a high quality cloak with mod2s on it.

Note 4: Instead of just making the change to allow pet power to persist over zoning, I would go ahead and make the change to allow for the increased number inventory items to be saved with the pet.

Note 5: I will try and submit my code with equipment as soon as I can, and I know KLS wants to work on the npc inventory issue after spells, but this could be an intermediary step, or something for private servers to mess around with. IF I submit it, it will still suffer from the issues of saving the extra pet power and extra equipment (haste mask, cloak, etc) through zoning. It will be able to persist through suspend, though, so one workaround would be to suspend the pet before zoning (this is tested as working).

iRFNA 02-17-2010 12:57 PM

Nice, this will fix the 2nd most annoying thing about pets (needing to have your focus equipped everytime you zone). The 1st is, of course, the equipment thing!

Updating the extprofile really isn't that hard, it just requires tossing in a little buffer offset, and there isn't really much stored in there anyway. I'd been messing with it myself with the intent of doing what you've already done here.

The one thing you might want to include with the equipment stuff is not having that auto-equip with full equipment on summon part, but I'll just put that option in anyway if you don't want to.

Anyway, I'll try testing this code sometime today and reply if I find anything odd.

bad_captain 02-17-2010 02:49 PM

With the equipment fixes, focused pets will come pre-equipped with gear based on focus power and level. I.E. level < 71 will come equipped with phantom armor, Muzzle of Mardu and the 65ish level mage summoned focus items (Jolum's Bauble, Tavee's Mantle, etc.). >78 level, they come with Prime Armor, Nastrel's heirlooms, and upgraded haste mask. The cloak will be based on focus power, although at the current levels, most will only have the lowest tier.

If you (or anyone) has code to make the database changes for adding to the extprofile (and not messing up what's already there), please let me know. I couldn't find anything searching, and if I can find that, I can finish the pet equipment.

It was nice summoning a pet with full armor (minus weapons).

iRFNA 02-18-2010 12:34 AM

Well, assuming you start with the current code and want to insert more in this area:

Code:

struct ExtendedProfile_Struct {
        // Pet stuff
        int16                                pet_id;
        int16                                pet_hp;
        int16                                pet_mana;
        SpellBuff_Struct        pet_buffs[BUFF_COUNT];
        int32                                pet_items[MAX_MATERIALS];

        >>>here<<<

        char                                pet_name[64];
       
        uint32                                aa_effects;
        uint32                                perAA;                //% of exp going to AAs
};

...then you just need to use a little sql to put some padding in the char profiles in that area. Here's a quick query for that:

Code:

update character_ set extprofile = concat(left(extprofile, length(extprofile) - (64 + 4 + 4)), repeat(char(0), ???), right(extprofile, 64 + 4 + 4));
The (64 + 4 + 4) corresponds to the size of the data under the big bold here marker. Just pull apart the current exp profile, fill it with a buffer of 0x00 characters (I assume 0x00 is preferable?), and put it all back together. So, however much larger you make the pet equipment array, you'll just toss that in ???. Each added int32 is 4 bytes.

From what I can see in the code, it'd be safer to add everything under the current data so that if someone's extprofile isn't updated, it won't try to use faulty data for things like AAs or their pet name. However, it'd be pretty odd and annoying to split up the pet equipment array like that.

Another option would be to update the SetExtendedProfile function in extprofile.cpp to do basically this same thing except with memcpy/memmove and SecureZeroMemory. Maybe on a special case if the length is the same as the old one, reformat its data like that or however you need it, and then do the dump into the player's ExtendedProfile_Struct. Again, I'm assuming it's preferable to fill everything new with 0x00, which I think should be the case?

bad_captain 02-18-2010 09:30 AM

Thanks for that. I did most of the work last night, with just a little debugging, then I'll be able to try this out. I've had to rework some things I had finished earlier, since some of the basic pet focus code had changed.

bad_captain 02-22-2010 02:11 PM

Grr. Almost finished with this. I was losing items on zone, but I think I have it figured out. I'm going to try and test it out tonight, if I can get on my computer. My wife may have it to do school stuff.

iRFNA 02-22-2010 04:42 PM

That's odd, I had extra items zoning fine by extending the pet equipment array and altering the loops that use it in a couple places to use the new size. Try doing a search for that array and make sure everything looping through it isn't using MAX_MATERIALS as an upper bound.

bad_captain 02-24-2010 02:40 PM

I didn't allocate space for the array, and was truncating it.. I missed one line copying code in from when I worked on it before, and that's what caused my problem. New code posted in other thread.


All times are GMT -4. The time now is 04:48 PM.

Powered by vBulletin®, Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.