mirror of
https://github.com/EQEmu/Server.git
synced 2025-12-11 12:41:30 +00:00
[Bots] Correct ^pull logic and add checks for Enchanter pets (#4827)
* [Bots] Fix bots getting stuck on ^pull - Removed initial LoS check from bots and rely only on owner when told to ^pull. This prevents bots from getting stuck in a loop if they don't have LoS when trying to initiate the pull. * [Bots] Add valid state checks to ^clickitem - Bots previously weren't checking if they were in a valid state or not before trying to click an item when told (in a death state, held, feared, silenced, in group/raid, etc.) * [Bots] Add valid state checks to ^clickitem - Bots previously weren't checking if they were in a valid state or not before trying to click an item when told (in a death state, held, feared, silenced, in group/raid, etc.) Miscommit * undo * Cleanup and Fixes - Cleanup - Fixed Animation Empathy checks and consolidates - Enchanter pets will honor Animation Empathy rules all around - Pets will properly guard if allowed when pulling
This commit is contained in:
parent
3d607d352c
commit
a2b28b2e16
@ -3068,11 +3068,11 @@ void Mob::AddToHateList(Mob* other, int64 hate /*= 0*/, int64 damage /*= 0*/, bo
|
||||
else
|
||||
SetAssistAggro(true);
|
||||
|
||||
bool wasengaged = IsEngaged();
|
||||
bool was_engaged = IsEngaged();
|
||||
Mob* owner = other->GetOwner();
|
||||
Mob* mypet = GetPet();
|
||||
Mob* myowner = GetOwner();
|
||||
Mob* targetmob = GetTarget();
|
||||
Mob* my_pet = GetPet();
|
||||
Mob* my_owner = GetOwner();
|
||||
Mob* target_mob = GetTarget();
|
||||
bool on_hatelist = CheckAggro(other);
|
||||
|
||||
AddRampage(other);
|
||||
@ -3101,7 +3101,7 @@ void Mob::AddToHateList(Mob* other, int64 hate /*= 0*/, int64 damage /*= 0*/, bo
|
||||
if (IsPet()) {
|
||||
if ((IsGHeld() || (IsHeld() && IsFocused())) && !on_hatelist) // we want them to be able to climb the hate list
|
||||
return;
|
||||
if ((IsHeld() || IsPetStop() || IsPetRegroup()) && !wasengaged) // not 100% sure on stop/regroup kind of hard to test, but regroup is like "classic hold"
|
||||
if ((IsHeld() || IsPetStop() || IsPetRegroup()) && !was_engaged) // not 100% sure on stop/regroup kind of hard to test, but regroup is like "classic hold"
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -3134,7 +3134,7 @@ void Mob::AddToHateList(Mob* other, int64 hate /*= 0*/, int64 damage /*= 0*/, bo
|
||||
return;
|
||||
}
|
||||
|
||||
if (other == myowner) {
|
||||
if (other == my_owner) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -3236,26 +3236,39 @@ void Mob::AddToHateList(Mob* other, int64 hate /*= 0*/, int64 damage /*= 0*/, bo
|
||||
}
|
||||
}
|
||||
|
||||
if (mypet && !mypet->IsHeld() && !mypet->IsPetStop()) { // I have a pet, add other to it
|
||||
if (
|
||||
!mypet->IsFamiliar() &&
|
||||
!mypet->GetSpecialAbility(SpecialAbility::AggroImmunity) &&
|
||||
!(IsBot() && mypet->GetSpecialAbility(SpecialAbility::BotAggroImmunity)) &&
|
||||
!(IsClient() && mypet->GetSpecialAbility(SpecialAbility::ClientAggroImmunity)) &&
|
||||
!(IsNPC() && mypet->GetSpecialAbility(SpecialAbility::NPCAggroImmunity))
|
||||
) {
|
||||
mypet->hate_list.AddEntToHateList(other, 0, 0, bFrenzy);
|
||||
if (my_pet) {
|
||||
bool aggro_immunity = my_pet->GetSpecialAbility(SpecialAbility::AggroImmunity);
|
||||
bool bot_aggro_immunity = IsBot() && my_pet->GetSpecialAbility(SpecialAbility::BotAggroImmunity);
|
||||
bool client_aggro_immunity = IsClient() && my_pet->GetSpecialAbility(SpecialAbility::ClientAggroImmunity);
|
||||
bool npc_aggro_immunity = IsNPC() && my_pet->GetSpecialAbility(SpecialAbility::NPCAggroImmunity);
|
||||
bool can_add_to_hatelist = !my_pet->IsFamiliar() &&
|
||||
!aggro_immunity &&
|
||||
!bot_aggro_immunity &&
|
||||
!client_aggro_immunity &&
|
||||
!npc_aggro_immunity;
|
||||
|
||||
if (can_add_to_hatelist) {
|
||||
bool bot_with_controllable_pet = IsBot() && CastToBot()->HasControllablePet(BotAnimEmpathy::Attack);
|
||||
|
||||
if (!IsBot() || bot_with_controllable_pet) {
|
||||
my_pet->hate_list.AddEntToHateList(other, 0, 0, bFrenzy);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (myowner) { // I am a pet, add other to owner if it's NPC/LD
|
||||
if (
|
||||
myowner->IsAIControlled() &&
|
||||
!myowner->GetSpecialAbility(SpecialAbility::AggroImmunity) &&
|
||||
!(myowner->IsBot() && GetSpecialAbility(SpecialAbility::BotAggroImmunity)) &&
|
||||
!(myowner->IsClient() && GetSpecialAbility(SpecialAbility::ClientAggroImmunity)) &&
|
||||
!(myowner->IsNPC() && GetSpecialAbility(SpecialAbility::NPCAggroImmunity))
|
||||
) {
|
||||
myowner->hate_list.AddEntToHateList(other, 0, 0, bFrenzy);
|
||||
else if (my_owner) { // I am a pet, add other to owner if it's NPC/LD
|
||||
if (my_owner->IsAIControlled()) {
|
||||
bool aggro_immunity = my_owner->GetSpecialAbility(SpecialAbility::AggroImmunity);
|
||||
bool bot_aggro_immunity = my_owner->IsBot() && GetSpecialAbility(SpecialAbility::BotAggroImmunity);
|
||||
bool client_aggro_immunity = my_owner->IsClient() && GetSpecialAbility(SpecialAbility::ClientAggroImmunity);
|
||||
bool npc_aggro_immunity = my_owner->IsNPC() && GetSpecialAbility(SpecialAbility::NPCAggroImmunity);
|
||||
bool can_add_to_hatelist = !aggro_immunity &&
|
||||
!bot_aggro_immunity &&
|
||||
!client_aggro_immunity &&
|
||||
!npc_aggro_immunity;
|
||||
|
||||
if (can_add_to_hatelist) {
|
||||
my_owner->hate_list.AddEntToHateList(other, 0, 0, bFrenzy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -3264,7 +3277,7 @@ void Mob::AddToHateList(Mob* other, int64 hate /*= 0*/, int64 damage /*= 0*/, bo
|
||||
entity_list.AddTempPetsToHateList(this, other, bFrenzy);
|
||||
}
|
||||
|
||||
if (!wasengaged) {
|
||||
if (!was_engaged) {
|
||||
if (IsNPC() && other->IsClient() && other->CastToClient()) {
|
||||
if (parse->HasQuestSub(GetNPCTypeID(), EVENT_AGGRO)) {
|
||||
parse->EventNPC(EVENT_AGGRO, CastToNPC(), other, "", 0);
|
||||
|
||||
194
zone/bot.cpp
194
zone/bot.cpp
@ -2102,10 +2102,6 @@ void Bot::SetGuardMode() {
|
||||
StopMoving();
|
||||
m_GuardPoint = GetPosition();
|
||||
SetGuardFlag();
|
||||
|
||||
if (HasPet() && (GetClass() != Class::Enchanter || GetPet()->GetPetType() != petAnimation || GetAA(aaAnimationEmpathy) >= 2)) {
|
||||
GetPet()->StopMoving();
|
||||
}
|
||||
}
|
||||
|
||||
void Bot::SetHoldMode() {
|
||||
@ -2270,7 +2266,7 @@ void Bot::AI_Process()
|
||||
}
|
||||
|
||||
// This causes conflicts with default pet handler (bounces between targets)
|
||||
if (NOT_PULLING_BOT && NOT_RETURNING_BOT && HasPet() && (GetClass() != Class::Enchanter || GetPet()->GetPetType() != petAnimation || GetAA(aaAnimationEmpathy) >= 2)) {
|
||||
if (NOT_PULLING_BOT && NOT_RETURNING_BOT && HasControllablePet(BotAnimEmpathy::Attack)) {
|
||||
// We don't add to hate list here because it's assumed to already be on the list
|
||||
GetPet()->SetTarget(tar);
|
||||
}
|
||||
@ -2284,11 +2280,11 @@ void Bot::AI_Process()
|
||||
}
|
||||
|
||||
// COMBAT RANGE CALCS
|
||||
bool front_mob = InFrontMob(tar, GetX(), GetY());
|
||||
bool behind_mob = BehindMob(tar, GetX(), GetY());
|
||||
bool stop_melee_level = GetLevel() >= GetStopMeleeLevel();
|
||||
bool front_mob = InFrontMob(tar, GetX(), GetY());
|
||||
bool behind_mob = BehindMob(tar, GetX(), GetY());
|
||||
bool stop_melee_level = GetLevel() >= GetStopMeleeLevel();
|
||||
|
||||
tar_distance = sqrt(tar_distance); // sqrt this for future calculations
|
||||
// Item variables
|
||||
const EQ::ItemInstance* p_item = GetBotItem(EQ::invslot::slotPrimary);
|
||||
const EQ::ItemInstance* s_item = GetBotItem(EQ::invslot::slotSecondary);
|
||||
|
||||
@ -2310,20 +2306,36 @@ void Bot::AI_Process()
|
||||
|
||||
// PULLING FLAG (ACTIONABLE RANGE)
|
||||
|
||||
if (PULLING_BOT || RETURNING_BOT) {
|
||||
if (!TargetValidation(tar)) { return; }
|
||||
if (PULLING_BOT) {
|
||||
if (!TargetValidation(tar)) {
|
||||
SetPullFlag(false);
|
||||
SetPullingFlag(false);
|
||||
bot_owner->SetBotPulling(false);
|
||||
|
||||
if (GetPet()) {
|
||||
GetPet()->SetPetOrder(SPO_Follow);
|
||||
GetPet()->CastToNPC()->SaveGuardSpot(true);
|
||||
}
|
||||
|
||||
if (RuleB(Bots, BotsRequireLoS) && !HasLoS()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (at_combat_range) {
|
||||
if (
|
||||
!tar->GetSpecialAbility(SpecialAbility::RangedAttackImmunity) &&
|
||||
if (!at_combat_range && RuleB(Bots, UseSpellPulling)) {
|
||||
uint16 pull_spell_id = RuleI(Bots, PullSpellID);
|
||||
|
||||
if (IsValidSpell(pull_spell_id) && tar_distance <= spells[pull_spell_id].range) {
|
||||
at_combat_range = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (at_combat_range && DoLosChecks(tar)) {
|
||||
bool ai_cast_successful = false;
|
||||
bool can_range_attack = !tar->GetSpecialAbility(SpecialAbility::RangedAttackImmunity) &&
|
||||
RuleB(Bots, AllowRangedPulling) &&
|
||||
IsBotRanged() &&
|
||||
ranged_timer.Check(false)
|
||||
) {
|
||||
ranged_timer.Check(false);
|
||||
|
||||
if (can_range_attack) {
|
||||
StopMoving(CalculateHeadingToTarget(tar->GetX(), tar->GetY()));
|
||||
|
||||
if (BotRangedAttack(tar) && CheckDoubleRangedAttack()) {
|
||||
@ -2335,30 +2347,33 @@ void Bot::AI_Process()
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
RuleB(Bots, AllowAISpellPulling) &&
|
||||
bool can_ai_spell_pull = RuleB(Bots, AllowAISpellPulling) &&
|
||||
!IsBotNonSpellFighter() &&
|
||||
AI_HasSpells()
|
||||
) {
|
||||
AI_HasSpells();
|
||||
|
||||
if (can_ai_spell_pull) {
|
||||
StopMoving(CalculateHeadingToTarget(tar->GetX(), tar->GetY()));
|
||||
SetPullingSpell(true);
|
||||
AI_EngagedCastCheck();
|
||||
ai_cast_successful = AI_EngagedCastCheck();
|
||||
SetPullingSpell(false);
|
||||
|
||||
return;
|
||||
if (ai_cast_successful) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (RuleB(Bots, UseSpellPulling)) {
|
||||
uint16 spell_id = RuleI(Bots, PullSpellID);
|
||||
if (RuleB(Bots, UseSpellPulling)) {
|
||||
uint16 pull_spell_id = RuleI(Bots, PullSpellID);
|
||||
|
||||
if (tar_distance <= spells[spell_id].range) {
|
||||
StopMoving();
|
||||
SetPullingSpell(true);
|
||||
CastSpell(spell_id, tar->GetID());
|
||||
SetPullingSpell(false);
|
||||
|
||||
return;
|
||||
if (IsValidSpell(pull_spell_id) && tar_distance <= spells[pull_spell_id].range) {
|
||||
StopMoving(CalculateHeadingToTarget(tar->GetX(), tar->GetY()));
|
||||
SetPullingSpell(true);
|
||||
CastSpell(pull_spell_id, tar->GetID());
|
||||
SetPullingSpell(false);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
TryPursueTarget(leash_distance);
|
||||
@ -2551,6 +2566,12 @@ void Bot::DoOutOfCombatChecks(Client* bot_owner, Mob* follow_mob, float leash_di
|
||||
if (PULLING_BOT || RETURNING_BOT || !bot_owner->GetBotPulling()) {
|
||||
SetPullingFlag(false);
|
||||
SetReturningFlag(false);
|
||||
bot_owner->SetBotPulling(false);
|
||||
|
||||
if (GetPet()) {
|
||||
GetPet()->SetPetOrder(SPO_Follow);
|
||||
GetPet()->CastToNPC()->SaveGuardSpot(true);
|
||||
}
|
||||
}
|
||||
|
||||
if (TryAutoDefend(bot_owner, leash_distance) ) {
|
||||
@ -2559,14 +2580,7 @@ void Bot::DoOutOfCombatChecks(Client* bot_owner, Mob* follow_mob, float leash_di
|
||||
|
||||
SetTarget(nullptr);
|
||||
|
||||
if (
|
||||
HasPet() &&
|
||||
(
|
||||
GetClass() != Class::Enchanter ||
|
||||
GetPet()->GetPetType() != petAnimation ||
|
||||
GetAA(aaAnimationEmpathy) >= 1
|
||||
)
|
||||
) {
|
||||
if (HasControllablePet(BotAnimEmpathy::BackOff)) {
|
||||
GetPet()->WipeHateList();
|
||||
GetPet()->SetTarget(nullptr);
|
||||
}
|
||||
@ -2729,7 +2743,7 @@ bool Bot::TryAutoDefend(Client* bot_owner, float leash_distance) {
|
||||
SetTarget(hater);
|
||||
SetAttackingFlag();
|
||||
|
||||
if (HasPet() && (GetClass() != Class::Enchanter || GetPet()->GetPetType() != petAnimation || GetAA(aaAnimationEmpathy) >= 2)) {
|
||||
if (HasControllablePet(BotAnimEmpathy::Attack)) {
|
||||
GetPet()->AddToHateList(hater, 1);
|
||||
GetPet()->SetTarget(hater);
|
||||
}
|
||||
@ -2799,14 +2813,7 @@ bool Bot::TryPursueTarget(float leash_distance) {
|
||||
WipeHateList();
|
||||
SetTarget(nullptr);
|
||||
|
||||
if (
|
||||
HasPet() &&
|
||||
(
|
||||
GetClass() != Class::Enchanter ||
|
||||
GetPet()->GetPetType() != petAnimation ||
|
||||
GetAA(aaAnimationEmpathy) >= 2
|
||||
)
|
||||
) {
|
||||
if (HasControllablePet(BotAnimEmpathy::BackOff)) {
|
||||
GetPet()->WipeHateList();
|
||||
GetPet()->SetTarget(nullptr);
|
||||
}
|
||||
@ -3191,7 +3198,8 @@ bool Bot::IsValidTarget(
|
||||
bot_owner->SetBotPulling(false);
|
||||
|
||||
if (GetPet()) {
|
||||
GetPet()->SetPetOrder(m_previous_pet_order);
|
||||
GetPet()->SetPetOrder(SPO_Follow);
|
||||
GetPet()->CastToNPC()->SaveGuardSpot(true);
|
||||
}
|
||||
}
|
||||
|
||||
@ -3225,7 +3233,8 @@ Mob* Bot::GetBotTarget(Client* bot_owner)
|
||||
bot_owner->SetBotPulling(false);
|
||||
|
||||
if (GetPet()) {
|
||||
GetPet()->SetPetOrder(m_previous_pet_order);
|
||||
GetPet()->SetPetOrder(SPO_Follow);
|
||||
GetPet()->CastToNPC()->SaveGuardSpot(true);
|
||||
}
|
||||
}
|
||||
|
||||
@ -3248,15 +3257,11 @@ bool Bot::TargetValidation(Mob* other) {
|
||||
}
|
||||
|
||||
bool Bot::ReturningFlagChecks(Client* bot_owner, Mob* leash_owner, float fm_distance) {
|
||||
auto engage_range = (GetBotDistanceRanged() < 30 ? 30 : GetBotDistanceRanged());
|
||||
bool target_check = !GetTarget() || Distance(GetPosition(), GetTarget()->GetPosition()) <= 75.0f;
|
||||
bool returned_check = (NOT_GUARDING && fm_distance <= GetFollowDistance()) ||
|
||||
(GUARDING && DistanceSquared(GetPosition(), GetGuardPoint()) <= GetFollowDistance());
|
||||
|
||||
if (
|
||||
(GetTarget() && Distance(GetPosition(), GetTarget()->GetPosition()) <= engage_range) &&
|
||||
(
|
||||
(NOT_GUARDING && fm_distance <= GetFollowDistance()) ||
|
||||
(GUARDING && DistanceSquared(GetPosition(), GetGuardPoint()) <= GetFollowDistance())
|
||||
)
|
||||
) { // Once we're back, clear blocking flags so everyone else can join in
|
||||
if (target_check && returned_check) { // Once we're back, clear blocking flags so everyone else can join in
|
||||
WipeHateList();
|
||||
SetTarget(nullptr);
|
||||
SetPullingFlag(false);
|
||||
@ -3264,9 +3269,10 @@ bool Bot::ReturningFlagChecks(Client* bot_owner, Mob* leash_owner, float fm_dist
|
||||
bot_owner->SetBotPulling(false);
|
||||
|
||||
if (GetPet()) {
|
||||
GetPet()->SetPetOrder(m_previous_pet_order);
|
||||
GetPet()->SetPetOrder(SPO_Follow);
|
||||
GetPet()->CastToNPC()->SaveGuardSpot(true);
|
||||
|
||||
if (GetClass() != Class::Enchanter || GetPet()->GetPetType() != petAnimation || GetAA(aaAnimationEmpathy) >= 1) {
|
||||
if (HasControllablePet(BotAnimEmpathy::BackOff)) {
|
||||
GetPet()->WipeHateList();
|
||||
GetPet()->SetTarget(nullptr);
|
||||
}
|
||||
@ -3307,7 +3313,8 @@ bool Bot::PullingFlagChecks(Client* bot_owner) {
|
||||
bot_owner->SetBotPulling(false);
|
||||
|
||||
if (GetPet()) {
|
||||
GetPet()->SetPetOrder(m_previous_pet_order);
|
||||
GetPet()->SetPetOrder(SPO_Follow);
|
||||
GetPet()->CastToNPC()->SaveGuardSpot(true);
|
||||
}
|
||||
|
||||
return false;
|
||||
@ -3316,11 +3323,16 @@ bool Bot::PullingFlagChecks(Client* bot_owner) {
|
||||
SetPullingFlag(false);
|
||||
SetReturningFlag();
|
||||
|
||||
if (HasPet() &&
|
||||
(GetClass() != Class::Enchanter || GetPet()->GetPetType() != petAnimation || GetAA(aaAnimationEmpathy) >= 1)) {
|
||||
Mob* my_pet = GetPet();
|
||||
|
||||
GetPet()->WipeHateList();
|
||||
GetPet()->SetTarget(nullptr);
|
||||
if (my_pet) {
|
||||
if (HasControllablePet(BotAnimEmpathy::BackOff)) {
|
||||
my_pet->WipeHateList();
|
||||
my_pet->SetTarget(nullptr);
|
||||
} else {
|
||||
my_pet->AddToHateList(GetTarget(), 1);
|
||||
my_pet->SetTarget(GetTarget());
|
||||
}
|
||||
}
|
||||
|
||||
if (GetPlayerState() & static_cast<uint32>(PlayerState::Aggressive)) {
|
||||
@ -3482,7 +3494,7 @@ Client* Bot::SetLeashOwner(Client* bot_owner, Group* bot_group, Raid* raid, uint
|
||||
|
||||
void Bot::SetOwnerTarget(Client* bot_owner) {
|
||||
if (GetPet() && (PULLING_BOT || RETURNING_BOT)) {
|
||||
GetPet()->SetPetOrder(m_previous_pet_order);
|
||||
GetPet()->SetPetOrder(SPO_Follow);
|
||||
}
|
||||
|
||||
SetAttackFlag(false);
|
||||
@ -3502,7 +3514,7 @@ void Bot::SetOwnerTarget(Client* bot_owner) {
|
||||
SetTarget(attack_target);
|
||||
SetAttackingFlag();
|
||||
|
||||
if (GetPet() && (GetClass() != Class::Enchanter || GetPet()->GetPetType() != petAnimation || GetAA(aaAnimationEmpathy) >= 2)) {
|
||||
if (HasControllablePet(BotAnimEmpathy::Attack)) {
|
||||
GetPet()->WipeHateList();
|
||||
GetPet()->AddToHateList(attack_target, 1);
|
||||
GetPet()->SetTarget(attack_target);
|
||||
@ -3520,20 +3532,21 @@ void Bot::BotPullerProcess(Client* bot_owner, Raid* raid) {
|
||||
SetReturningFlag(false);
|
||||
bot_owner->SetBotPulling(false);
|
||||
|
||||
if (GetPet()) {
|
||||
GetPet()->SetPetOrder(SPO_Follow);
|
||||
GetPet()->CastToNPC()->SaveGuardSpot(true);
|
||||
}
|
||||
|
||||
if (NOT_HOLDING && NOT_PASSIVE) {
|
||||
auto pull_target = bot_owner->GetTarget();
|
||||
|
||||
if (pull_target) {
|
||||
if (raid) {
|
||||
const auto msg = fmt::format("Pulling {}.", pull_target->GetCleanName());
|
||||
raid->RaidSay(msg.c_str(), GetCleanName(), 0, 100);
|
||||
} else {
|
||||
RaidGroupSay(
|
||||
fmt::format(
|
||||
"Pulling {}.",
|
||||
pull_target->GetCleanName()
|
||||
).c_str()
|
||||
);
|
||||
}
|
||||
RaidGroupSay(
|
||||
fmt::format(
|
||||
"Pulling {}.",
|
||||
pull_target->GetCleanName()
|
||||
).c_str()
|
||||
);
|
||||
|
||||
InterruptSpell();
|
||||
WipeHateList();
|
||||
@ -3542,12 +3555,15 @@ void Bot::BotPullerProcess(Client* bot_owner, Raid* raid) {
|
||||
SetPullingFlag();
|
||||
bot_owner->SetBotPulling();
|
||||
|
||||
if (HasPet() && (GetClass() != Class::Enchanter || GetPet()->GetPetType() != petAnimation || GetAA(aaAnimationEmpathy) >= 1)) {
|
||||
if (GetPet()) {
|
||||
GetPet()->WipeHateList();
|
||||
GetPet()->SetTarget(nullptr);
|
||||
m_previous_pet_order = GetPet()->GetPetOrder();
|
||||
GetPet()->CastToNPC()->SaveGuardSpot(GetPosition());
|
||||
GetPet()->SetPetOrder(SPO_Guard);
|
||||
|
||||
if (HasControllablePet(BotAnimEmpathy::Guard)) {
|
||||
m_previous_pet_order = GetPet()->GetPetOrder();
|
||||
GetPet()->CastToNPC()->SaveGuardSpot(GetPosition());
|
||||
GetPet()->SetPetOrder(SPO_Guard);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -13438,3 +13454,13 @@ bool Bot::IsValidBotStance(uint8 stance) {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Bot::HasControllablePet(uint8 ranks_required) {
|
||||
if (!GetPet()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return GetClass() != Class::Enchanter ||
|
||||
GetPet()->GetPetType() != petAnimation ||
|
||||
GetAA(aaAnimationEmpathy) >= ranks_required;
|
||||
}
|
||||
@ -229,6 +229,12 @@ static std::map<uint16, std::string> botSubType_names = {
|
||||
{ CommandedSubTypes::Selo, "Selo" }
|
||||
};
|
||||
|
||||
namespace BotAnimEmpathy {
|
||||
constexpr uint8 Guard = 1;
|
||||
constexpr uint8 Attack = 2;
|
||||
constexpr uint8 BackOff = 3;
|
||||
};
|
||||
|
||||
class Bot : public NPC {
|
||||
friend class Mob;
|
||||
public:
|
||||
@ -786,6 +792,7 @@ public:
|
||||
EQ::ItemInstance* GetBotItem(uint16 slot_id);
|
||||
bool GetSpawnStatus() { return _spawnStatus; }
|
||||
uint8 GetPetChooserID() { return _petChooserID; }
|
||||
bool HasControllablePet(uint8 ranks_required = 0);
|
||||
bool IsBotRanged() { return _botRangedSetting; }
|
||||
bool IsBotCharmer() { return _botCharmer; }
|
||||
bool IsBot() const override { return true; }
|
||||
|
||||
@ -1465,8 +1465,11 @@ void bot_command_summon(Client *c, const Seperator *sep)
|
||||
continue;
|
||||
}
|
||||
|
||||
bot_iter->GetPet()->WipeHateList();
|
||||
bot_iter->GetPet()->SetTarget(nullptr);
|
||||
if (bot_iter->HasControllablePet(BotAnimEmpathy::BackOff)) {
|
||||
bot_iter->GetPet()->WipeHateList();
|
||||
bot_iter->GetPet()->SetTarget(nullptr);
|
||||
}
|
||||
|
||||
bot_iter->GetPet()->Teleport(c->GetPosition());
|
||||
}
|
||||
|
||||
|
||||
@ -5,8 +5,8 @@ void bot_command_pull(Client *c, const Seperator *sep)
|
||||
if (helper_command_alias_fail(c, "bot_command_pull", sep->arg[0], "pull")) {
|
||||
return;
|
||||
}
|
||||
if (helper_is_help_or_usage(sep->arg[1])) {
|
||||
|
||||
if (helper_is_help_or_usage(sep->arg[1])) {
|
||||
c->Message(Chat::White, "usage: <enemy_target> %s ([actionable: target | byname | ownergroup | ownerraid | targetgroup | namesgroup | healrotationtargets | mmr | byclass | byrace | spawned] ([actionable_name]))", sep->arg[0]);
|
||||
return;
|
||||
}
|
||||
@ -40,7 +40,7 @@ void bot_command_pull(Client *c, const Seperator *sep)
|
||||
|
||||
if (
|
||||
!target_mob ||
|
||||
target_mob == c ||
|
||||
target_mob->IsOfClientBotMerc() ||
|
||||
!c->IsAttackAllowed(target_mob)
|
||||
) {
|
||||
c->Message(Chat::White, "Your current target is not attackable!");
|
||||
@ -55,12 +55,16 @@ void bot_command_pull(Client *c, const Seperator *sep)
|
||||
}
|
||||
|
||||
if (target_mob->IsNPC() && target_mob->GetHateList().size()) {
|
||||
|
||||
c->Message(Chat::White, "Your current target is already engaged!");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Bot* bot_puller = nullptr;
|
||||
Bot* backup_bot_puller = nullptr;
|
||||
Bot* alternate_bot_puller = nullptr;
|
||||
bool backup_puller_found = false;
|
||||
bool alternate_puller_found = false;
|
||||
|
||||
for (auto bot_iter : sbl) {
|
||||
if (!bot_iter->ValidStateCheck(c)) {
|
||||
@ -72,72 +76,37 @@ void bot_command_pull(Client *c, const Seperator *sep)
|
||||
case Class::Monk:
|
||||
case Class::Bard:
|
||||
case Class::Ranger:
|
||||
bot_puller = bot_iter;
|
||||
break;
|
||||
case Class::Warrior:
|
||||
case Class::ShadowKnight:
|
||||
case Class::Paladin:
|
||||
case Class::Berserker:
|
||||
case Class::Beastlord:
|
||||
if (!bot_puller) {
|
||||
bot_iter->SetPullFlag();
|
||||
|
||||
bot_puller = bot_iter;
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (bot_puller->GetClass()) {
|
||||
case Class::Druid:
|
||||
case Class::Shaman:
|
||||
case Class::Cleric:
|
||||
case Class::Wizard:
|
||||
case Class::Necromancer:
|
||||
case Class::Magician:
|
||||
case Class::Enchanter:
|
||||
bot_puller = bot_iter;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
|
||||
continue;
|
||||
case Class::Druid:
|
||||
case Class::Shaman:
|
||||
case Class::Cleric:
|
||||
if (!bot_puller) {
|
||||
|
||||
bot_puller = bot_iter;
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (bot_puller->GetClass()) {
|
||||
case Class::Wizard:
|
||||
case Class::Necromancer:
|
||||
case Class::Magician:
|
||||
case Class::Enchanter:
|
||||
bot_puller = bot_iter;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
|
||||
continue;
|
||||
case Class::Wizard:
|
||||
case Class::Necromancer:
|
||||
case Class::Magician:
|
||||
case Class::Enchanter:
|
||||
if (!bot_puller) {
|
||||
bot_puller = bot_iter;
|
||||
}
|
||||
|
||||
continue;
|
||||
return;
|
||||
default:
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!backup_puller_found) {
|
||||
switch (bot_iter->GetClass()) {
|
||||
case Class::Warrior:
|
||||
case Class::ShadowKnight:
|
||||
case Class::Paladin:
|
||||
case Class::Berserker:
|
||||
case Class::Beastlord:
|
||||
backup_bot_puller = bot_iter;
|
||||
backup_puller_found = true;
|
||||
|
||||
bot_puller = bot_iter;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
if (!backup_puller_found && !alternate_puller_found) {
|
||||
alternate_bot_puller = bot_iter;
|
||||
alternate_puller_found = true;
|
||||
}
|
||||
}
|
||||
|
||||
bot_puller = backup_bot_puller ? backup_bot_puller : alternate_bot_puller;
|
||||
|
||||
if (bot_puller) {
|
||||
bot_puller->SetPullFlag();
|
||||
}
|
||||
|
||||
@ -17,6 +17,12 @@ void bot_command_release(Client *c, const Seperator *sep)
|
||||
sbl.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end());
|
||||
for (auto bot_iter : sbl) {
|
||||
bot_iter->WipeHateList();
|
||||
|
||||
if (bot_iter->GetPet() && bot_iter->HasControllablePet(BotAnimEmpathy::BackOff)) {
|
||||
bot_iter->GetPet()->WipeHateList();
|
||||
bot_iter->GetPet()->SetTarget(nullptr);
|
||||
}
|
||||
|
||||
bot_iter->SetPauseAI(false);
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user