diff --git a/common/ruletypes.h b/common/ruletypes.h index 0d66fd1cf..08cac9b0e 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -886,6 +886,7 @@ RULE_BOOL(Bots, AllowCommandedResurrect, true, "If enabled bots can be commanded RULE_BOOL(Bots, AllowCommandedSummonCorpse, true, "If enabled bots can be commanded to summon other's corpses.") RULE_INT(Bots, CampTimer, 25, "Number of seconds after /camp has begun before bots camp out.") RULE_BOOL(Bots, SendClassRaceOnHelp, true, "If enabled a reminder of how to check class/race IDs will be sent when using compatible commands.") +RULE_BOOL(Bots, AllowCrossGroupRaidAssist, true, "If enabled bots will autodefend group or raid members set as main assist.") RULE_CATEGORY_END() RULE_CATEGORY(Chat) diff --git a/zone/bot.cpp b/zone/bot.cpp index ceeefb898..8fdf57d50 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -2041,6 +2041,7 @@ void Bot::AI_Process() auto raid = entity_list.GetRaidByBotName(GetName()); uint32 r_group = RAID_GROUPLESS; + if (raid) { raid->VerifyRaid(); r_group = raid->GetGroup(GetName()); @@ -2137,7 +2138,6 @@ void Bot::AI_Process() // RETURNING FLAG if (GetReturningFlag()) { - LogTestDebugDetail("#{}: {} has ReturningFlag", __LINE__, GetCleanName()); //deleteme ReturningFlagChecks(bot_owner, leash_owner, fm_distance); return; @@ -2210,6 +2210,7 @@ void Bot::AI_Process() if (!DoLosChecks(this, tar)) { return; } + if (atCombatRange) { if (RuleB(Bots, AllowRangedPulling) && IsBotRanged() && ranged_timer.Check(false)) { StopMoving(CalculateHeadingToTarget(tar->GetX(), tar->GetY())); @@ -2246,8 +2247,8 @@ void Bot::AI_Process() } TryPursueTarget(leash_distance, Goal); + return; - //TODO bot rewrite - need pulling checks below to prevent assist } // ENGAGED AT COMBAT RANGE @@ -2459,42 +2460,150 @@ bool Bot::TryAutoDefend(Client* bot_owner, float leash_distance) { if ( m_auto_defend_timer.Check() && - bot_owner->GetAggroCount() && NOT_HOLDING && NOT_PASSIVE ) { - auto xhaters = bot_owner->GetXTargetAutoMgr(); + XTargetAutoHaters* tempHaters; + std::vector assisteeHaters; + std::vector assisteeMembers; + bool found = false; - if (xhaters && !xhaters->empty()) { - for (auto hater_iter : xhaters->get_list()) { - if (!hater_iter.spawn_id) { - continue; - } + if (bot_owner->GetAggroCount()) { + tempHaters = bot_owner->GetXTargetAutoMgr(); - if (bot_owner->GetBotPulling() && bot_owner->GetTarget() && hater_iter.spawn_id == bot_owner->GetTarget()->GetID()) { - continue; - } + if (tempHaters && !tempHaters->empty()) { + assisteeHaters.emplace_back(tempHaters); + assisteeMembers.emplace_back(bot_owner); + } + } - auto hater = entity_list.GetMob(hater_iter.spawn_id); - if (hater && hater->CastToNPC()->IsOnHatelist(bot_owner) && !hater->IsMezzed() && DistanceSquared(hater->GetPosition(), bot_owner->GetPosition()) <= leash_distance) { - // This is roughly equivilent to npc attacking a client pet owner - AddToHateList(hater, 1); - SetTarget(hater); - SetAttackingFlag(); + if ( + (!bot_owner->GetAssistee() || !entity_list.GetClientByCharID(bot_owner->GetAssistee())) && + RuleB(Bots, AllowCrossGroupRaidAssist) + ) { + XTargetAutoHaters* temp_xhaters = bot_owner->GetXTargetAutoMgr(); + bool assisteeFound = false; - if (HasPet() && (GetClass() != Class::Enchanter || GetPet()->GetPetType() != petAnimation || GetAA(aaAnimationEmpathy) >= 2)) { - GetPet()->AddToHateList(hater, 1); - GetPet()->SetTarget(hater); + if (IsRaidGrouped()) { + Raid* r = entity_list.GetRaidByBotName(GetName()); + if (r) { + for (const auto& m : r->members) { + if ( + m.member && + m.member->IsClient() && + m.member->GetAggroCount() && + r->IsAssister(m.member_name) + ) { + temp_xhaters = m.member->GetXTargetAutoMgr(); + + if (!temp_xhaters || temp_xhaters->empty()) { + continue; + } + + assisteeHaters.emplace_back(temp_xhaters); + assisteeMembers.emplace_back(m.member); + } } + } + } + else if (HasGroup()) { + Group* g = GetGroup(); + if (g) { + for (auto& m : g->members) { + if ( + m && + m->IsClient() && + m->CastToClient()->GetAggroCount() && + g->AmIMainAssist(m->GetName()) + ) { + temp_xhaters = m->CastToClient()->GetXTargetAutoMgr(); - m_auto_defend_timer.Disable(); + if (!temp_xhaters || temp_xhaters->empty()) { + continue; + } - return true; + assisteeHaters.emplace_back(temp_xhaters); + assisteeMembers.emplace_back(m->CastToClient()); + } + } + } + } + else { + return false; + } + } + else { + if (bot_owner->GetAssistee()) { + Client* c = entity_list.GetClientByCharID(bot_owner->GetAssistee()); + + if (bot_owner->IsInGroupOrRaid(c) && c->GetAggroCount()) { + tempHaters = bot_owner->GetXTargetAutoMgr(); + + if (tempHaters && !tempHaters->empty()) { + assisteeHaters.emplace_back(tempHaters); + assisteeMembers.emplace_back(c); + } } } } + + if (!assisteeHaters.empty()) { + for (XTargetAutoHaters* xHaters : assisteeHaters) { + if (!xHaters->empty()) { + for (auto hater_iter : xHaters->get_list()) { + if (!hater_iter.spawn_id) { + continue; + } + + Mob* hater = nullptr; + + for (Client* xMember : assisteeMembers) { + if ( + xMember && + xMember->GetBotPulling() && + xMember->GetTarget() && + (hater_iter.spawn_id == xMember->GetTarget()->GetID()) + ) { + continue; + } + + hater = entity_list.GetMob(hater_iter.spawn_id); + + if ( + hater && + !hater->IsMezzed() && + (DistanceSquared(hater->GetPosition(), bot_owner->GetPosition()) <= leash_distance) && + hater->CastToNPC()->IsOnHatelist(xMember) + ) { + break; + } + + hater = nullptr; + } + + if (hater) { + AddToHateList(hater, 1); + SetTarget(hater); + SetAttackingFlag(); + + if (HasPet() && (GetClass() != Class::Enchanter || GetPet()->GetPetType() != petAnimation || GetAA(aaAnimationEmpathy) >= 2)) { + GetPet()->AddToHateList(hater, 1); + GetPet()->SetTarget(hater); + } + + m_auto_defend_timer.Disable(); + + return true; + } + } + } + } + } + + return false; } } + return false; } @@ -3054,6 +3163,7 @@ bool Bot::ReturningFlagChecks(Client* bot_owner, Mob* leash_owner, float fm_dist Goal = follow_mob->GetPosition(); } + RunTo(Goal.x, Goal.y, Goal.z); } @@ -11272,8 +11382,6 @@ void Bot::DoCombatPositioning( bool behindMob, bool frontMob ) { - //LogTestDebug("{} says, 'DoCombatPositioning. {} #{}", GetCleanName(), __FILE__, __LINE__); //deleteme - if (HasTargetReflection()) { if (!IsTaunting() && !tar->IsFeared() && !tar->IsStunned()) { if (TryEvade(tar)) { diff --git a/zone/bot_command.cpp b/zone/bot_command.cpp index 1b00d701a..a2a25a71f 100644 --- a/zone/bot_command.cpp +++ b/zone/bot_command.cpp @@ -1336,6 +1336,7 @@ int bot_command_init(void) bot_command_add("precombat", "Sets flag used to determine pre-combat behavior", AccountStatus::Player, bot_command_precombat) || bot_command_add("pull", "Orders a designated bot to 'pull' an enemy", AccountStatus::Player, bot_command_pull) || bot_command_add("release", "Releases a suspended bot's AI processing (with hate list wipe)", AccountStatus::Player, bot_command_release) || + bot_command_add("setassistee", "Sets your target to be the person your bots assist. Bots will always assist you before others", AccountStatus::Player, bot_command_set_assistee) || bot_command_add("sithppercent", "HP threshold for a bot to start sitting in combat if allowed", AccountStatus::Player, bot_command_sit_hp_percent) || bot_command_add("sitincombat", "Toggles whether or a not a bot will attempt to med or sit to heal in combat", AccountStatus::Player, bot_command_sit_in_combat) || bot_command_add("sitmanapercent", "Mana threshold for a bot to start sitting in combat if allowed", AccountStatus::Player, bot_command_sit_mana_percent) || @@ -2243,6 +2244,7 @@ void SendSpellTypeWindow(Client* c, const Seperator* sep) { #include "bot_commands/precombat.cpp" #include "bot_commands/pull.cpp" #include "bot_commands/release.cpp" +#include "bot_commands/set_assistee.cpp" #include "bot_commands/sit_hp_percent.cpp" #include "bot_commands/sit_in_combat.cpp" #include "bot_commands/sit_mana_percent.cpp" diff --git a/zone/bot_command.h b/zone/bot_command.h index ecce9485d..d6644bbf6 100644 --- a/zone/bot_command.h +++ b/zone/bot_command.h @@ -1747,6 +1747,7 @@ void bot_command_heritage(Client *c, const Seperator *sep); void bot_command_inspect_message(Client *c, const Seperator *sep); void bot_command_list_bots(Client *c, const Seperator *sep); void bot_command_report(Client *c, const Seperator *sep); +void bot_command_set_assistee(Client* c, const Seperator* sep); void bot_command_spawn(Client *c, const Seperator *sep); void bot_command_stance(Client *c, const Seperator *sep); void bot_command_stop_melee_level(Client *c, const Seperator *sep); diff --git a/zone/bot_commands/set_assistee.cpp b/zone/bot_commands/set_assistee.cpp new file mode 100644 index 000000000..d2be953e2 --- /dev/null +++ b/zone/bot_commands/set_assistee.cpp @@ -0,0 +1,59 @@ +#include "../bot_command.h" + +void bot_command_set_assistee(Client* c, const Seperator* sep) +{ + if (helper_is_help_or_usage(sep->arg[1])) { + std::vector description = + { + "Sets your bots to assist your target in addition to yourself" + }; + + std::vector notes = + { + "- Your target must be another player in your group or raid.", + "- This needs to be set on every zone/camp you do." + }; + + std::vector example_format = { }; + std::vector examples_one = { }; + std::vector examples_two = { }; + std::vector examples_three = { }; + + std::vector actionables = { }; + + std::vector options = { }; + std::vector options_one = { }; + std::vector options_two = { }; + std::vector options_three = { }; + + std::string popup_text = c->SendCommandHelpWindow( + c, + description, + notes, + example_format, + examples_one, examples_two, examples_three, + actionables, + options, + options_one, options_two, options_three + ); + + popup_text = DialogueWindow::Table(popup_text); + + c->SendPopupToClient(sep->arg[0], popup_text.c_str()); + + return; + } + + Mob* assistee = c->GetTarget(); + + if (assistee && assistee->IsClient() && c->IsInGroupOrRaid(assistee)) { + c->SetAssistee(assistee->CastToClient()->CharacterID()); + c->Message(Chat::Green, "Your bots will now assist %s.", assistee->GetCleanName()); + + return; + } + + c->Message(Chat::Yellow, "You can only set your bots to assist clients that are in your group or raid."); + + return; +} diff --git a/zone/client.h b/zone/client.h index 2a090eeac..c4ffd5460 100644 --- a/zone/client.h +++ b/zone/client.h @@ -2221,6 +2221,8 @@ public: bool GetBotPulling() { return m_bot_pulling; } void SetBotPulling(bool flag = true) { m_bot_pulling = flag; } + uint32 GetAssistee() { return _assistee; } + void SetAssistee(uint32 id = 0) { _assistee = id; } bool GetBotPrecombat() { return m_bot_precombat; } void SetBotPrecombat(bool flag = true) { m_bot_precombat = flag; } @@ -2257,6 +2259,7 @@ private: uint8 cure_min_threshold; uint8 cure_threshold; bool illusion_block; + uint32 _assistee; bool CanTradeFVNoDropItem(); void SendMobPositions();