diff --git a/zone/bot_command.cpp b/zone/bot_command.cpp index 0f469a03c..971ce647d 100644 --- a/zone/bot_command.cpp +++ b/zone/bot_command.cpp @@ -1371,7 +1371,7 @@ int bot_command_init(void) bot_command_add("depart", "Orders a bot to open a magical doorway to a specified destination", 0, bot_command_depart) || bot_command_add("escape", "Orders a bot to send a target group to a safe location within the zone", 0, bot_command_escape) || bot_command_add("findaliases", "Find available aliases for a bot command", 0, bot_command_find_aliases) || - bot_command_add("follow", "Orders bots to follow a designated target", 0, bot_command_follow) || + bot_command_add("follow", "Orders bots to follow a designated target (option 'chain' auto-links eligible spawned bots)", 0, bot_command_follow) || bot_command_add("guard", "Orders bots to guard their current positions", 0, bot_command_guard) || bot_command_add("healrotation", "Lists the available bot heal rotation [subcommands]", 0, bot_command_heal_rotation) || bot_command_add("healrotationadaptivetargeting", "Enables or disables adaptive targeting within the heal rotation instance", 0, bot_subcommand_heal_rotation_adaptive_targeting) || @@ -3213,6 +3213,7 @@ void bot_command_follow(Client *c, const Seperator *sep) return; if (helper_is_help_or_usage(sep->arg[1])) { c->Message(m_usage, "usage: () %s ([option: reset]) [actionable: byname | ownergroup | botgroup | namesgroup | healrotation | spawned] ([actionable_name])", sep->arg[0]); + c->Message(m_usage, "usage: %s chain", sep->arg[0]); return; } const int ab_mask = ActionableBots::ABM_Type2; @@ -3222,8 +3223,15 @@ void bot_command_follow(Client *c, const Seperator *sep) int name_arg = 2; Mob* target_mob = nullptr; - std::string reset_arg = sep->arg[1]; - if (!reset_arg.compare("reset")) { + std::string optional_arg = sep->arg[1]; + if (!optional_arg.compare("chain")) { + + auto chain_count = helper_bot_follow_option_chain(c); + c->Message(m_action, "%i of your bot%s are now chain following", chain_count, (chain_count == 1 ? "" : "s")); + + return; + } + else if (!optional_arg.compare("reset")) { reset = true; ab_arg = 2; name_arg = 3; @@ -3250,16 +3258,21 @@ void bot_command_follow(Client *c, const Seperator *sep) bot_iter->SetFollowID(c->GetID()); else bot_iter->SetFollowID(my_group->GetLeader()->GetID()); + + bot_iter->SetManualFollow(false); } else { if (bot_iter == target_mob) bot_iter->SetFollowID(c->GetID()); else bot_iter->SetFollowID(target_mob->GetID()); + + bot_iter->SetManualFollow(true); } } else { bot_iter->SetFollowID(0); + bot_iter->SetManualFollow(false); } if (!bot_iter->GetPet()) continue; @@ -8622,6 +8635,75 @@ void helper_bot_out_of_combat(Client *bot_owner, Bot *my_bot) } } +int helper_bot_follow_option_chain(Client* bot_owner) +{ + if (!bot_owner) { + return 0; + } + + std::list sbl; + MyBots::PopulateSBL_BySpawnedBots(bot_owner, sbl); + if (sbl.empty()) { + return 0; + } + + int chain_follow_count = 0; + Mob* followee = bot_owner; + + // only add groups that do not belong to bot_owner + std::map bot_group_map; + for (auto bot_iter : sbl) { + + if (!bot_iter || bot_iter->GetManualFollow() || bot_iter->GetGroup() == bot_owner->GetGroup()) { + continue; + } + + Group* bot_group = bot_iter->GetGroup(); + if (!bot_iter->GetGroup()) { + continue; + } + + bot_group_map[bot_group->GetID()] = bot_group; + } + + std::list bot_member_list; + if (bot_owner->GetGroup()) { + + bot_owner->GetGroup()->GetBotList(bot_member_list); + for (auto bot_member_iter : bot_member_list) { + + if (!bot_member_iter || bot_member_iter->GetBotOwnerCharacterID() != bot_owner->CharacterID() || bot_member_iter == followee || bot_member_iter->GetManualFollow()) { + continue; + } + + bot_member_iter->SetFollowID(followee->GetID()); + followee = bot_member_iter; + ++chain_follow_count; + } + } + + for (auto bot_group_iter : bot_group_map) { + + if (!bot_group_iter.second) { + continue; + } + + bot_group_iter.second->GetBotList(bot_member_list); + for (auto bot_member_iter : bot_member_list) { + + if (!bot_member_iter || bot_member_iter->GetBotOwnerCharacterID() != bot_owner->CharacterID() || bot_member_iter == followee || bot_member_iter->GetManualFollow()) { + continue; + } + + bot_member_iter->SetFollowID(followee->GetID()); + followee = bot_member_iter; + ++chain_follow_count; + } + } + + return chain_follow_count; +} + bool helper_cast_standard_spell(Bot* casting_bot, Mob* target_mob, int spell_id, bool annouce_cast, uint32* dont_root_before) { if (!casting_bot || !target_mob) diff --git a/zone/bot_command.h b/zone/bot_command.h index b3f328a33..b7377d28b 100644 --- a/zone/bot_command.h +++ b/zone/bot_command.h @@ -672,6 +672,7 @@ void helper_bot_appearance_form_final(Client *bot_owner, Bot *my_bot); void helper_bot_appearance_form_update(Bot *my_bot); uint32 helper_bot_create(Client *bot_owner, std::string bot_name, uint8 bot_class, uint16 bot_race, uint8 bot_gender); void helper_bot_out_of_combat(Client *bot_owner, Bot *my_bot); +int helper_bot_follow_option_chain(Client *bot_owner); bool helper_cast_standard_spell(Bot* casting_bot, Mob* target_mob, int spell_id, bool annouce_cast = true, uint32* dont_root_before = nullptr); bool helper_command_disabled(Client *bot_owner, bool rule_value, const char *command); bool helper_command_alias_fail(Client *bot_owner, const char* command_handler, const char *alias, const char *command); diff --git a/zone/mob.cpp b/zone/mob.cpp index b12df39c1..44ce45d93 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -453,6 +453,10 @@ Mob::Mob( PrimaryAggro = false; AssistAggro = false; npc_assist_cap = 0; + +#ifdef BOTS + m_manual_follow = false; +#endif } Mob::~Mob() diff --git a/zone/mob.h b/zone/mob.h index 2578db0c8..ab3df4277 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -1188,8 +1188,8 @@ public: int32 GetManaRegen() const; - // Bots HealRotation methods #ifdef BOTS + // Bots HealRotation methods bool IsHealRotationTarget() { return (m_target_of_heal_rotation.use_count() && m_target_of_heal_rotation.get()); } bool JoinHealRotationTargetPool(std::shared_ptr* heal_rotation); bool LeaveHealRotationTargetPool(); @@ -1200,6 +1200,11 @@ public: float HealRotationExtendedHealFrequency(); const std::shared_ptr* TargetOfHealRotation() const { return &m_target_of_heal_rotation; } + + + // not Bots HealRotation methods + void SetManualFollow(bool flag) { m_manual_follow = flag; } + bool GetManualFollow() const { return m_manual_follow; } #endif protected: @@ -1581,6 +1586,8 @@ private: #ifdef BOTS std::shared_ptr m_target_of_heal_rotation; + + bool m_manual_follow; #endif };