diff --git a/utils/patches/patch_RoF.conf b/utils/patches/patch_RoF.conf index ad54b6341..a746804d9 100644 --- a/utils/patches/patch_RoF.conf +++ b/utils/patches/patch_RoF.conf @@ -196,6 +196,7 @@ OP_Consent=0x400e OP_ConsentDeny=0x34c1 OP_AutoFire=0x314e OP_PetCommands=0x0093 +OP_PetCommandState=0x74ed OP_PetHoTT=0x0df4 OP_DeleteSpell=0x305c OP_Surname=0x1a87 diff --git a/utils/patches/patch_RoF2.conf b/utils/patches/patch_RoF2.conf index 9f588d18f..ee9b0e646 100644 --- a/utils/patches/patch_RoF2.conf +++ b/utils/patches/patch_RoF2.conf @@ -195,6 +195,7 @@ OP_Consent=0x1fd1 OP_ConsentDeny=0x7a45 OP_AutoFire=0x241e OP_PetCommands=0x0159 +OP_PetCommandState=0x1dc8 OP_PetHoTT=0x794a OP_DeleteSpell=0x3358 OP_Surname=0x0423 diff --git a/utils/patches/patch_UF.conf b/utils/patches/patch_UF.conf index f41b62740..2002a85f9 100644 --- a/utils/patches/patch_UF.conf +++ b/utils/patches/patch_UF.conf @@ -198,6 +198,7 @@ OP_Consent=0x6bb9 # C OP_ConsentDeny=0x4cd1 # C OP_AutoFire=0x5db5 # C OP_PetCommands=0x7706 # C +OP_PetCommandState=0x1a79 OP_PetHoTT=0x2528 OP_DeleteSpell=0x0698 # C OP_Surname=0x44ae # C diff --git a/zone/attack.cpp b/zone/attack.cpp index 2b9d081bf..bbb17d2ec 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -2477,7 +2477,7 @@ bool NPC::Death(Mob* killer_mob, int32 damage, uint16 spell, EQEmu::skills::Skil return true; } -void Mob::AddToHateList(Mob* other, uint32 hate /*= 0*/, int32 damage /*= 0*/, bool iYellForHelp /*= true*/, bool bFrenzy /*= false*/, bool iBuffTic /*= false*/, uint16 spell_id) +void Mob::AddToHateList(Mob* other, uint32 hate /*= 0*/, int32 damage /*= 0*/, bool iYellForHelp /*= true*/, bool bFrenzy /*= false*/, bool iBuffTic /*= false*/, uint16 spell_id, bool pet_command) { if (!other) return; @@ -2516,13 +2516,18 @@ void Mob::AddToHateList(Mob* other, uint32 hate /*= 0*/, int32 damage /*= 0*/, b } } - if (IsPet() && GetOwner() && GetOwner()->GetAA(aaPetDiscipline) && IsHeld() && !IsFocused()) { //ignore aggro if hold and !focus - return; - } + // Pet that is /pet hold on will not add to their hate list if they're not engaged + // Pet that is /pet hold on and /pet focus on will not add others to their hate list + // Pet that is /pet ghold on will never add to their hate list unless /pet attack or /pet qattack - if (IsPet() && GetOwner() && GetOwner()->GetAA(aaPetDiscipline) && IsHeld() && GetOwner()->GetAA(aaAdvancedPetDiscipline) >= 1 && IsFocused()) { - if (!targetmob) - return; + // we skip these checks if it's forced through a pet command + if (!pet_command) { + if (IsPet() && GetOwner() && GetOwner()->GetAA(aaPetDiscipline)) { + if ((IsGHeld() || (IsHeld() && IsFocused())) && !on_hatelist) // we want them to be able to climb the hate list + return; + if (IsHeld() && !wasengaged) + return; + } } if (other->IsNPC() && (other->IsPet() || other->CastToNPC()->GetSwarmOwner() > 0)) @@ -3306,7 +3311,10 @@ void Mob::CommonDamage(Mob* attacker, int &damage, const uint16 spell_id, const } //end `if there is some damage being done and theres anattacker person involved` Mob *pet = GetPet(); - if (pet && !pet->IsFamiliar() && !pet->GetSpecialAbility(IMMUNE_AGGRO) && !pet->IsEngaged() && attacker && attacker != this && !attacker->IsCorpse()) + // pets that have GHold will never automatically add NPCs + // pets that have Hold and no Focus will add NPCs if they're engaged + // pets that have Hold and Focus will not add NPCs + if (pet && !pet->IsFamiliar() && !pet->GetSpecialAbility(IMMUNE_AGGRO) && !pet->IsEngaged() && attacker && attacker != this && !attacker->IsCorpse() && !pet->IsGHeld()) { if (!pet->IsHeld()) { Log(Logs::Detail, Logs::Aggro, "Sending pet %s into battle due to attack.", pet->GetName()); @@ -5295,4 +5303,4 @@ void Mob::DoOffHandAttackRounds(Mob *target, ExtraAttackOptions *opts) } } } -} \ No newline at end of file +} diff --git a/zone/bot.cpp b/zone/bot.cpp index c66fdeb56..d87bd472e 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -3852,8 +3852,8 @@ void Bot::Damage(Mob *from, int32 damage, uint16 spell_id, EQEmu::skills::SkillT } //void Bot::AddToHateList(Mob* other, uint32 hate = 0, int32 damage = 0, bool iYellForHelp = true, bool bFrenzy = false, bool iBuffTic = false) -void Bot::AddToHateList(Mob* other, uint32 hate, int32 damage, bool iYellForHelp, bool bFrenzy, bool iBuffTic) { - Mob::AddToHateList(other, hate, damage, iYellForHelp, bFrenzy, iBuffTic); +void Bot::AddToHateList(Mob* other, uint32 hate, int32 damage, bool iYellForHelp, bool bFrenzy, bool iBuffTic, bool pet_command) { + Mob::AddToHateList(other, hate, damage, iYellForHelp, bFrenzy, iBuffTic, pet_command); } bool Bot::Attack(Mob* other, int Hand, bool FromRiposte, bool IsStrikethrough, bool IsFromSpell, ExtraAttackOptions *opts) { diff --git a/zone/bot.h b/zone/bot.h index 6c1dfc53f..1a6bcf7cd 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -325,7 +325,7 @@ public: bool DoFinishedSpellGroupTarget(uint16 spell_id, Mob* spellTarget, EQEmu::CastingSlot slot, bool &stopLogic); void SendBotArcheryWearChange(uint8 material_slot, uint32 material, uint32 color); void Camp(bool databaseSave = true); - virtual void AddToHateList(Mob* other, uint32 hate = 0, int32 damage = 0, bool iYellForHelp = true, bool bFrenzy = false, bool iBuffTic = false); + virtual void AddToHateList(Mob* other, uint32 hate = 0, int32 damage = 0, bool iYellForHelp = true, bool bFrenzy = false, bool iBuffTic = false, bool pet_command = false); virtual void SetTarget(Mob* mob); virtual void Zone(); std::vector GetBotSpells() { return AIspells; } diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 995a38672..3ed19d37a 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -10001,26 +10001,17 @@ void Client::Handle_OP_PetCommands(const EQApplicationPacket *app) if ((mypet->GetPetType() == petAnimation && GetAA(aaAnimationEmpathy) >= 2) || mypet->GetPetType() != petAnimation) { if (target != this && DistanceSquaredNoZ(mypet->GetPosition(), target->GetPosition()) <= (RuleR(Pets, AttackCommandRange)*RuleR(Pets, AttackCommandRange))) { - if (mypet->IsHeld()) { - if (!mypet->IsFocused()) { - mypet->SetHeld(false); //break the hold and guard if we explicitly tell the pet to attack. - if (mypet->GetPetOrder() != SPO_Guard) - mypet->SetPetOrder(SPO_Follow); - } - else { - mypet->SetTarget(target); - } - } zone->AddAggroMob(); // classic acts like qattack int hate = 1; - if (IsEngaged()) { - auto top = hate_list.GetEntWithMostHateOnList(this); - if (top) - hate += hate_list.GetEntHateAmount(top); + if (mypet->IsEngaged()) { + auto top = mypet->GetHateMost(); + if (top && top != target) + hate += mypet->GetHateAmount(top) - mypet->GetHateAmount(target) + 100; // should be enough to cause target change } - mypet->AddToHateList(target, hate); + mypet->AddToHateList(target, hate, 0, true, false, false, SPELL_UNKNOWN, true); Message_StringID(MT_PetResponse, PET_ATTACKING, mypet->GetCleanName(), target->GetCleanName()); + SetTarget(target); } } break; @@ -10044,7 +10035,7 @@ void Client::Handle_OP_PetCommands(const EQApplicationPacket *app) if ((mypet->GetPetType() == petAnimation && GetAA(aaAnimationEmpathy) >= 2) || mypet->GetPetType() != petAnimation) { if (GetTarget() != this && DistanceSquaredNoZ(mypet->GetPosition(), GetTarget()->GetPosition()) <= (RuleR(Pets, AttackCommandRange)*RuleR(Pets, AttackCommandRange))) { zone->AddAggroMob(); - mypet->AddToHateList(GetTarget(), 1); + mypet->AddToHateList(GetTarget(), 1, 0, true, false, false, SPELL_UNKNOWN, true); Message_StringID(MT_PetResponse, PET_ATTACKING, mypet->GetCleanName(), GetTarget()->GetCleanName()); } } @@ -10098,7 +10089,6 @@ void Client::Handle_OP_PetCommands(const EQApplicationPacket *app) if ((mypet->GetPetType() == petAnimation && GetAA(aaAnimationEmpathy) >= 1) || mypet->GetPetType() != petAnimation) { if (mypet->IsNPC()) { - mypet->SetHeld(false); mypet->Say_StringID(MT_PetResponse, PET_GUARDINGLIFE); mypet->SetPetOrder(SPO_Guard); mypet->CastToNPC()->SaveGuardSpot(); @@ -10110,7 +10100,6 @@ void Client::Handle_OP_PetCommands(const EQApplicationPacket *app) if (mypet->IsFeared()) break; //could be exploited like PET_BACKOFF if ((mypet->GetPetType() == petAnimation && GetAA(aaAnimationEmpathy) >= 1) || mypet->GetPetType() != petAnimation) { - mypet->SetHeld(false); mypet->Say_StringID(MT_PetResponse, PET_FOLLOWING); mypet->SetPetOrder(SPO_Follow); mypet->SendAppearancePacket(AT_Anim, ANIM_STAND); @@ -10150,7 +10139,6 @@ void Client::Handle_OP_PetCommands(const EQApplicationPacket *app) if (mypet->IsFeared()) break; //could be exploited like PET_BACKOFF if ((mypet->GetPetType() == petAnimation && GetAA(aaAnimationEmpathy) >= 1) || mypet->GetPetType() != petAnimation) { - mypet->SetHeld(false); mypet->Say_StringID(MT_PetResponse, PET_GUARDME_STRING); mypet->SetPetOrder(SPO_Follow); mypet->SendAppearancePacket(AT_Anim, ANIM_STAND); @@ -10204,30 +10192,24 @@ void Client::Handle_OP_PetCommands(const EQApplicationPacket *app) } case PET_HOLD: { if (GetAA(aaPetDiscipline) && mypet->IsNPC()) { - if (mypet->IsFeared()) - break; //could be exploited like PET_BACKOFF - if (mypet->IsHeld()) { mypet->SetHeld(false); } else { - mypet->Say_StringID(MT_PetResponse, PET_ON_HOLD); - mypet->WipeHateList(); + mypet->Say_StringID(MT_PetResponse, PET_ON_HOLD); // they use new messages now, but only SoD+? mypet->SetHeld(true); } + mypet->SetGHeld(false); } break; } case PET_HOLD_ON: { if (GetAA(aaPetDiscipline) && mypet->IsNPC() && !mypet->IsHeld()) { - if (mypet->IsFeared()) - break; //could be exploited like PET_BACKOFF - mypet->Say_StringID(MT_PetResponse, PET_ON_HOLD); - mypet->WipeHateList(); mypet->SetHeld(true); + mypet->SetGHeld(false); } break; } @@ -10236,6 +10218,34 @@ void Client::Handle_OP_PetCommands(const EQApplicationPacket *app) mypet->SetHeld(false); break; } + case PET_GHOLD: { + if (GetAA(aaPetDiscipline) && mypet->IsNPC()) { + if (mypet->IsGHeld()) + { + mypet->SetGHeld(false); + } + else + { + mypet->Say_StringID(MT_PetResponse, PET_ON_HOLD); // message wrong + mypet->SetGHeld(true); + } + mypet->SetHeld(false); + } + break; + } + case PET_GHOLD_ON: { + if (GetAA(aaPetDiscipline) && mypet->IsNPC()) { + mypet->Say_StringID(MT_PetResponse, PET_ON_HOLD); + mypet->SetGHeld(true); + mypet->SetHeld(false); + } + break; + } + case PET_GHOLD_OFF: { + if (GetAA(aaPetDiscipline) && mypet->IsNPC() && mypet->IsGHeld()) + mypet->SetGHeld(false); + break; + } case PET_SPELLHOLD: { if (GetAA(aaAdvancedPetDiscipline) == 2 && mypet->IsNPC()) { if (mypet->IsFeared()) diff --git a/zone/mob.h b/zone/mob.h index 4d43a9572..9a1ff67a0 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -533,7 +533,7 @@ public: inline uint32 GetLevelCon(uint8 iOtherLevel) const { return this ? GetLevelCon(GetLevel(), iOtherLevel) : CON_GRAY; } virtual void AddToHateList(Mob* other, uint32 hate = 0, int32 damage = 0, bool iYellForHelp = true, - bool bFrenzy = false, bool iBuffTic = false, uint16 spell_id = SPELL_UNKNOWN); + bool bFrenzy = false, bool iBuffTic = false, uint16 spell_id = SPELL_UNKNOWN, bool pet_comand = false); bool RemoveFromHateList(Mob* mob); void SetHateAmountOnEnt(Mob* other, int32 hate = 0, int32 damage = 0) { hate_list.SetHateAmountOnEnt(other,hate,damage);} void HalveAggro(Mob *other) { uint32 in_hate = GetHateAmount(other); SetHateAmountOnEnt(other, (in_hate > 1 ? in_hate / 2 : 1)); } @@ -869,6 +869,8 @@ public: inline const eStandingPetOrder GetPetOrder() const { return pStandingPetOrder; } inline void SetHeld(bool nState) { held = nState; } inline const bool IsHeld() const { return held; } + inline void SetGHeld(bool nState) { gheld = nState; } + inline const bool IsGHeld() const { return gheld; } inline void SetNoCast(bool nState) { nocast = nState; } inline const bool IsNoCast() const { return nocast; } inline void SetFocused(bool nState) { focused = nState; } @@ -1179,6 +1181,7 @@ protected: uint32 pLastChange; bool held; + bool gheld; bool nocast; bool focused; bool spawned; diff --git a/zone/pets.h b/zone/pets.h index 2aa59cc59..3a7f5932c 100644 --- a/zone/pets.h +++ b/zone/pets.h @@ -37,6 +37,18 @@ #define PET_REGROUPON 32 // 0x20 - /pet regroup on, turns on regroup #define PET_REGROUPOFF 33 // 0x21 - /pet regroup off, turns off regroup +// can change the state of these buttons with a packet +#define PET_BUTTON_SIT 0 +#define PET_BUTTON_STOP 1 +#define PET_BUTTON_REGROUP 2 +#define PET_BUTTON_FOLLOW 3 +#define PET_BUTTON_GUARD 4 +#define PET_BUTTON_TAUNT 5 +#define PET_BUTTON_HOLD 6 +#define PET_BUTTON_GHOLD 7 +#define PET_BUTTON_FOCUS 8 +#define PET_BUTTON_SPELLHOLD 9 + class Mob; struct NPCType; diff --git a/zone/string_ids.h b/zone/string_ids.h index b1c2b1071..0326cff7e 100644 --- a/zone/string_ids.h +++ b/zone/string_ids.h @@ -322,6 +322,9 @@ #define SENTINEL_TRIG_YOU 6724 //You have triggered your sentinel. #define SENTINEL_TRIG_OTHER 6725 //%1 has triggered your sentinel. #define IDENTIFY_SPELL 6765 //Item Lore: %1. +#define PET_ON_GHOLD 6843 //Pet greater hold has been set to on. +#define PET_OFF_GHOLD 6846 //Pet greater hold has been set to off. +#define PET_GHOLD_ON_MSG 6847 //Now greater holding master. I will only attack something new if ordered. #define BUFF_NOT_BLOCKABLE 7608 //You cannot block this effect. #define LDON_DONT_KNOW_TRAPPED 7552 //You do not know if this object is trapped. #define LDON_HAVE_DISARMED 7553 //You have disarmed %1!