Merge pull request #910 from EQEmu/bot_ai_rework

Updated Bot AI to make use of neglected commands/features
This commit is contained in:
Uleat 2019-10-05 19:15:13 -04:00 committed by GitHub
commit 1f04938535
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 1377 additions and 534 deletions

View File

@ -2693,18 +2693,22 @@ void Mob::AddToHateList(Mob* other, uint32 hate /*= 0*/, int32 damage /*= 0*/, b
#ifdef BOTS #ifdef BOTS
// if other is a bot, add the bots client to the hate list // if other is a bot, add the bots client to the hate list
while (other->IsBot()) { while (other->IsBot()) {
auto other_ = other->CastToBot(); auto other_ = other->CastToBot();
if (!other_ || !other_->GetBotOwner()) if (!other_ || !other_->GetBotOwner()) {
break; break;
}
auto owner_ = other_->GetBotOwner()->CastToClient(); auto owner_ = other_->GetBotOwner()->CastToClient();
if (!owner_ || owner_->IsDead() || !owner_->InZone()) // added isdead and inzone checks to avoid issues in AddAutoXTarget(...) below if (!owner_ || owner_->IsDead() || !owner_->InZone()) { // added isdead and inzone checks to avoid issues in AddAutoXTarget(...) below
break; break;
}
if (owner_->GetFeigned()) { if (owner_->GetFeigned()) {
AddFeignMemory(owner_); AddFeignMemory(owner_);
} }
else if (!hate_list.IsEntOnHateList(owner_)) { else if (!hate_list.IsEntOnHateList(owner_)) {
hate_list.AddEntToHateList(owner_, 0, 0, false, true); hate_list.AddEntToHateList(owner_, 0, 0, false, true);
owner_->AddAutoXTarget(this); // this was being called on dead/out-of-zone clients owner_->AddAutoXTarget(this); // this was being called on dead/out-of-zone clients
} }

File diff suppressed because it is too large Load Diff

View File

@ -37,21 +37,24 @@
#include <sstream> #include <sstream>
#define BOT_FOLLOW_DISTANCE_DEFAULT 184 // as DSq value (~13.565 units) constexpr float BOT_FOLLOW_DISTANCE_DEFAULT = 184.0f; // as DSq value (~13.565 units)
#define BOT_FOLLOW_DISTANCE_DEFAULT_MAX 2500 // as DSq value (50 units) constexpr float BOT_FOLLOW_DISTANCE_DEFAULT_MAX = 2500.0f; // as DSq value (50 units)
#define BOT_FOLLOW_DISTANCE_WALK 1000 // as DSq value (~31.623 units) constexpr float BOT_FOLLOW_DISTANCE_WALK = 1000.0f; // as DSq value (~31.623 units)
#define BOT_LEASH_DISTANCE 250000 // as DSq value (500 units) constexpr float BOT_LEASH_DISTANCE = 250000.0f; // as DSq value (500 units)
#define BOT_KEEP_ALIVE_INTERVAL 5000 // 5 seconds constexpr uint32 BOT_KEEP_ALIVE_INTERVAL = 5000; // 5 seconds
//constexpr uint32 BOT_COMBAT_JITTER_INTERVAL_MIN = 5000; // 5 seconds
//constexpr uint32 BOT_COMBAT_JITTER_INTERVAL_MAX = 20000; // 20 seconds
extern WorldServer worldserver; extern WorldServer worldserver;
const int BotAISpellRange = 100; // TODO: Write a method that calcs what the bot's spell range is based on spell, equipment, AA, whatever and replace this constexpr int BotAISpellRange = 100; // TODO: Write a method that calcs what the bot's spell range is based on spell, equipment, AA, whatever and replace this
const int MaxSpellTimer = 15; constexpr int MaxSpellTimer = 15;
const int MaxDisciplineTimer = 10; constexpr int MaxDisciplineTimer = 10;
const int DisciplineReuseStart = MaxSpellTimer + 1; constexpr int DisciplineReuseStart = MaxSpellTimer + 1;
const int MaxTimer = MaxSpellTimer + MaxDisciplineTimer; constexpr int MaxTimer = MaxSpellTimer + MaxDisciplineTimer;
@ -260,10 +263,24 @@ public:
void Stand(); void Stand();
bool IsSitting(); bool IsSitting();
bool IsStanding(); bool IsStanding();
virtual int GetWalkspeed() const { return (int)((float)_GetWalkSpeed() * 1.785714f); } // 1.25 / 0.7 = 1.7857142857142857142857142857143 virtual int GetWalkspeed() const { return (int)((float)_GetWalkSpeed() * 1.785714285f); } // 1.25 / 0.7 = 1.7857142857142857142857142857143
virtual int GetRunspeed() const { return (int)((float)_GetRunSpeed() * 1.785714f); } virtual int GetRunspeed() const { return (int)((float)_GetRunSpeed() * 1.785714285f); }
virtual void WalkTo(float x, float y, float z); virtual void WalkTo(float x, float y, float z);
virtual void RunTo(float x, float y, float z); virtual void RunTo(float x, float y, float z);
virtual void StopMoving();
virtual void StopMoving(float new_heading);
//bool GetCombatJitterFlag() { return m_combat_jitter_flag; }
bool GetGuardFlag() { return m_guard_flag; }
void SetGuardFlag(bool flag = true) { m_guard_flag = flag; }
bool GetHoldFlag() { return m_hold_flag; }
void SetHoldFlag(bool flag = true) { m_hold_flag = flag; }
bool GetAttackFlag() { return m_attack_flag; }
void SetAttackFlag(bool flag = true) { m_attack_flag = flag; }
bool GetAttackingFlag() { return m_attacking_flag; }
bool GetPullFlag() { return m_pull_flag; }
void SetPullFlag(bool flag = true) { m_pull_flag = flag; }
bool GetPullingFlag() { return m_pulling_flag; }
bool GetReturningFlag() { return m_returning_flag; }
bool UseDiscipline(uint32 spell_id, uint32 target); bool UseDiscipline(uint32 spell_id, uint32 target);
uint8 GetNumberNeedingHealedInGroup(uint8 hpr, bool includePets); uint8 GetNumberNeedingHealedInGroup(uint8 hpr, bool includePets);
bool GetNeedsCured(Mob *tar); bool GetNeedsCured(Mob *tar);
@ -338,6 +355,7 @@ public:
uint8 GetStopMeleeLevel() { return _stopMeleeLevel; } uint8 GetStopMeleeLevel() { return _stopMeleeLevel; }
void SetStopMeleeLevel(uint8 level); void SetStopMeleeLevel(uint8 level);
void SetGuardMode(); void SetGuardMode();
void SetHoldMode();
// Mob AI Virtual Override Methods // Mob AI Virtual Override Methods
virtual void AI_Process(); virtual void AI_Process();
@ -674,7 +692,18 @@ private:
int32 end_regen; int32 end_regen;
uint32 timers[MaxTimer]; uint32 timers[MaxTimer];
Timer evade_timer; // can be moved to pTimers at some point Timer m_evade_timer; // can be moved to pTimers at some point
Timer m_alt_combat_hate_timer;
//Timer m_combat_jitter_timer;
//bool m_combat_jitter_flag;
bool m_guard_flag;
bool m_hold_flag;
bool m_attack_flag;
bool m_attacking_flag;
bool m_pull_flag;
bool m_pulling_flag;
bool m_returning_flag;
eStandingPetOrder m_previous_pet_order;
BotCastingRoles m_CastingRoles; BotCastingRoles m_CastingRoles;
@ -716,6 +745,10 @@ private:
int32 GenerateBaseManaPoints(); int32 GenerateBaseManaPoints();
void GenerateSpecialAttacks(); void GenerateSpecialAttacks();
void SetBotID(uint32 botID); void SetBotID(uint32 botID);
//void SetCombatJitterFlag(bool flag = true) { m_combat_jitter_flag = flag; }
void SetAttackingFlag(bool flag = true) { m_attacking_flag = flag; }
void SetPullingFlag(bool flag = true) { m_pulling_flag = flag; }
void SetReturningFlag(bool flag = true) { m_returning_flag = flag; }
// Private "Inventory" Methods // Private "Inventory" Methods
void GetBotItems(EQEmu::InventoryProfile &inv, std::string* errorMessage); void GetBotItems(EQEmu::InventoryProfile &inv, std::string* errorMessage);

View File

@ -71,6 +71,8 @@
#include "water_map.h" #include "water_map.h"
#include "worldserver.h" #include "worldserver.h"
#include <fmt/format.h>
extern QueryServ* QServ; extern QueryServ* QServ;
extern WorldServer worldserver; extern WorldServer worldserver;
extern TaskManager *taskmanager; extern TaskManager *taskmanager;
@ -1391,7 +1393,7 @@ int bot_command_init(void)
bot_command_add("healrotationstart", "Starts a heal rotation", 0, bot_subcommand_heal_rotation_start) || bot_command_add("healrotationstart", "Starts a heal rotation", 0, bot_subcommand_heal_rotation_start) ||
bot_command_add("healrotationstop", "Stops a heal rotation", 0, bot_subcommand_heal_rotation_stop) || bot_command_add("healrotationstop", "Stops a heal rotation", 0, bot_subcommand_heal_rotation_stop) ||
bot_command_add("help", "List available commands and their description - specify partial command as argument to search", 0, bot_command_help) || bot_command_add("help", "List available commands and their description - specify partial command as argument to search", 0, bot_command_help) ||
bot_command_add("hold", "Suspends a bot's AI processing until released", 0, bot_command_hold) || bot_command_add("hold", "Prevents a bot from attacking until released", 0, bot_command_hold) ||
bot_command_add("identify", "Orders a bot to cast an item identification spell", 0, bot_command_identify) || bot_command_add("identify", "Orders a bot to cast an item identification spell", 0, bot_command_identify) ||
bot_command_add("inventory", "Lists the available bot inventory [subcommands]", 0, bot_command_inventory) || bot_command_add("inventory", "Lists the available bot inventory [subcommands]", 0, bot_command_inventory) ||
bot_command_add("inventorygive", "Gives the item on your cursor to a bot", 0, bot_subcommand_inventory_give) || bot_command_add("inventorygive", "Gives the item on your cursor to a bot", 0, bot_subcommand_inventory_give) ||
@ -1418,6 +1420,7 @@ int bot_command_init(void)
bot_command_add("sendhome", "Orders a bot to open a magical doorway home", 0, bot_command_send_home) || bot_command_add("sendhome", "Orders a bot to open a magical doorway home", 0, bot_command_send_home) ||
bot_command_add("size", "Orders a bot to change a player's size", 0, bot_command_size) || bot_command_add("size", "Orders a bot to change a player's size", 0, bot_command_size) ||
bot_command_add("summoncorpse", "Orders a bot to summon a corpse to its feet", 0, bot_command_summon_corpse) || bot_command_add("summoncorpse", "Orders a bot to summon a corpse to its feet", 0, bot_command_summon_corpse) ||
bot_command_add("suspend", "Suspends a bot's AI processing until released", 0, bot_command_suspend) ||
bot_command_add("taunt", "Toggles taunt use by a bot", 0, bot_command_taunt) || bot_command_add("taunt", "Toggles taunt use by a bot", 0, bot_command_taunt) ||
bot_command_add("track", "Orders a capable bot to track enemies", 0, bot_command_track) || bot_command_add("track", "Orders a capable bot to track enemies", 0, bot_command_track) ||
bot_command_add("waterbreathing", "Orders a bot to cast a water breathing spell", 0, bot_command_water_breathing) bot_command_add("waterbreathing", "Orders a bot to cast a water breathing spell", 0, bot_command_water_breathing)
@ -2577,38 +2580,55 @@ void bot_command_aggressive(Client *c, const Seperator *sep)
void bot_command_attack(Client *c, const Seperator *sep) void bot_command_attack(Client *c, const Seperator *sep)
{ {
if (helper_command_alias_fail(c, "bot_command_attack", sep->arg[0], "attack")) if (helper_command_alias_fail(c, "bot_command_attack", sep->arg[0], "attack")) {
return; return;
}
if (helper_is_help_or_usage(sep->arg[1])) { if (helper_is_help_or_usage(sep->arg[1])) {
c->Message(m_usage, "usage: <enemy_target> %s [actionable: byname | ownergroup | botgroup | namesgroup | healrotation | spawned] ([actionable_name])", sep->arg[0]);
c->Message(m_usage, "usage: <enemy_target> %s [actionable: byname | ownergroup | botgroup | namesgroup | healrotation | default: spawned] ([actionable_name])", sep->arg[0]);
return; return;
} }
const int ab_mask = ActionableBots::ABM_Type2; const int ab_mask = ActionableBots::ABM_Type2;
Mob* target_mob = ActionableTarget::AsSingle_ByAttackable(c); Mob* target_mob = ActionableTarget::AsSingle_ByAttackable(c);
if (!target_mob) { if (!target_mob) {
c->Message(m_fail, "You must <target> an enemy to use this command"); c->Message(m_fail, "You must <target> an enemy to use this command");
return; return;
} }
std::list<Bot*> sbl; std::string ab_arg(sep->arg[1]);
if (ActionableBots::PopulateSBL(c, sep->arg[1], sbl, ab_mask, sep->arg[2]) == ActionableBots::ABT_None) if (ab_arg.empty()) {
return; ab_arg = "spawned";
}
std::list<Bot*> sbl;
if (ActionableBots::PopulateSBL(c, ab_arg.c_str(), sbl, ab_mask, sep->arg[2]) == ActionableBots::ABT_None) {
return;
}
size_t attacker_count = 0;
Bot *first_attacker = nullptr;
sbl.remove(nullptr); sbl.remove(nullptr);
for (auto bot_iter : sbl) { for (auto bot_iter : sbl) {
bot_iter->WipeHateList();
bot_iter->AddToHateList(target_mob, 1);
if (!bot_iter->GetPet())
continue;
bot_iter->GetPet()->WipeHateList(); if (bot_iter->GetAppearance() != eaDead && bot_iter->GetBotStance() != EQEmu::constants::stancePassive) {
bot_iter->GetPet()->AddToHateList(target_mob, 1);
if (!first_attacker) {
first_attacker = bot_iter;
}
++attacker_count;
bot_iter->SetAttackFlag();
}
}
if (attacker_count == 1 && first_attacker) {
Bot::BotGroupSay(first_attacker, "Attacking %s!", target_mob->GetCleanName());
}
else {
c->Message(m_action, "%i of your bots are attacking %s!", sbl.size(), target_mob->GetCleanName());
} }
if (sbl.size() == 1)
Bot::BotGroupSay(sbl.front(), "Attacking %s", target_mob->GetCleanName());
else
c->Message(m_action, "%i of your bots are attacking %s", sbl.size(), target_mob->GetCleanName());
} }
void bot_command_bind_affinity(Client *c, const Seperator *sep) void bot_command_bind_affinity(Client *c, const Seperator *sep)
@ -3098,26 +3118,50 @@ void bot_command_follow(Client *c, const Seperator *sep)
void bot_command_guard(Client *c, const Seperator *sep) void bot_command_guard(Client *c, const Seperator *sep)
{ {
if (helper_command_alias_fail(c, "bot_command_guard", sep->arg[0], "guard")) if (helper_command_alias_fail(c, "bot_command_guard", sep->arg[0], "guard")) {
return; return;
}
if (helper_is_help_or_usage(sep->arg[1])) { if (helper_is_help_or_usage(sep->arg[1])) {
c->Message(m_usage, "usage: %s [actionable: target | byname | ownergroup | botgroup | namesgroup | healrotation | spawned] ([actionable_name])", sep->arg[0]);
c->Message(m_usage, "usage: %s ([option: clear]) [actionable: target | byname | ownergroup | botgroup | namesgroup | healrotation | spawned] ([actionable_name])", sep->arg[0]);
return; return;
} }
const int ab_mask = (ActionableBots::ABM_Target | ActionableBots::ABM_Type2); const int ab_mask = (ActionableBots::ABM_Target | ActionableBots::ABM_Type2);
bool clear = false;
int ab_arg = 1;
int name_arg = 2;
std::string clear_arg = sep->arg[1];
if (!clear_arg.compare("clear")) {
clear = true;
ab_arg = 2;
name_arg = 3;
}
std::list<Bot*> sbl; std::list<Bot*> sbl;
if (ActionableBots::PopulateSBL(c, sep->arg[1], sbl, ab_mask, sep->arg[2]) == ActionableBots::ABT_None) if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, sep->arg[name_arg]) == ActionableBots::ABT_None) {
return; return;
}
sbl.remove(nullptr); sbl.remove(nullptr);
for (auto bot_iter : sbl) { for (auto bot_iter : sbl) {
if (clear) {
bot_iter->SetGuardFlag(false);
}
else {
bot_iter->SetGuardMode(); bot_iter->SetGuardMode();
} }
if (sbl.size() == 1) }
Bot::BotGroupSay(sbl.front(), "Guarding this position");
else if (sbl.size() == 1) {
c->Message(m_action, "%i of your bots are guarding their positions", sbl.size()); Bot::BotGroupSay(sbl.front(), "%suarding this position.", (clear ? "No longer g" : "G"));
}
else {
c->Message(m_action, "%i of your bots are %sguarding their positions.", sbl.size(), (clear ? "no longer " : ""));
}
} }
void bot_command_heal_rotation(Client *c, const Seperator *sep) void bot_command_heal_rotation(Client *c, const Seperator *sep)
@ -3199,23 +3243,50 @@ void bot_command_help(Client *c, const Seperator *sep)
void bot_command_hold(Client *c, const Seperator *sep) void bot_command_hold(Client *c, const Seperator *sep)
{ {
if (helper_command_alias_fail(c, "bot_command_hold", sep->arg[0], "hold")) if (helper_command_alias_fail(c, "bot_command_hold", sep->arg[0], "hold")) {
return;
if (helper_is_help_or_usage(sep->arg[1])) {
c->Message(m_usage, "usage: %s ([actionable: <any>] ([actionable_name]))", sep->arg[0]);
return; return;
} }
const int ab_mask = ActionableBots::ABM_NoFilter; if (helper_is_help_or_usage(sep->arg[1])) {
c->Message(m_usage, "usage: %s ([option: clear]) [actionable: target | byname | ownergroup | botgroup | namesgroup | healrotation | spawned] ([actionable_name])", sep->arg[0]);
return;
}
const int ab_mask = (ActionableBots::ABM_Target | ActionableBots::ABM_Type2);
bool clear = false;
int ab_arg = 1;
int name_arg = 2;
std::string clear_arg = sep->arg[1];
if (!clear_arg.compare("clear")) {
clear = true;
ab_arg = 2;
name_arg = 3;
}
std::list<Bot*> sbl; std::list<Bot*> sbl;
if (ActionableBots::PopulateSBL(c, sep->arg[1], sbl, ab_mask, sep->arg[2]) == ActionableBots::ABT_None) if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, sep->arg[name_arg]) == ActionableBots::ABT_None) {
return; return;
}
sbl.remove(nullptr); sbl.remove(nullptr);
for (auto bot_iter : sbl) for (auto bot_iter : sbl) {
bot_iter->SetPauseAI(true);
c->Message(m_action, "%i of your bots %s suspended", sbl.size(), ((sbl.size() != 1) ? ("are") : ("is"))); if (clear) {
bot_iter->SetHoldFlag(false);
}
else {
bot_iter->SetHoldMode();
}
}
if (sbl.size() == 1) {
Bot::BotGroupSay(sbl.front(), "%solding my attacks.", (clear ? "No longer h" : "H"));
}
else {
c->Message(m_action, "%i of your bots are %sholding their attacks.", sbl.size(), (clear ? "no longer " : ""));
}
} }
void bot_command_identify(Client *c, const Seperator *sep) void bot_command_identify(Client *c, const Seperator *sep)
@ -3505,70 +3576,70 @@ void bot_command_owner_option(Client *c, const Seperator *sep)
{ {
if (helper_is_help_or_usage(sep->arg[1])) { if (helper_is_help_or_usage(sep->arg[1])) {
c->Message(m_usage, "usage: %s [option] [argument | null]", sep->arg[0]); c->Message(m_usage, "usage: %s [option] [argument]", sep->arg[0]);
std::string window_title = "Bot Owner Options"; std::string window_title = "Bot Owner Options";
std::string window_text = std::string window_text =
"<table>" "<table>"
"<tr>" "<tr>"
"<td><c \"#FFFFFF\">Option</td>" "<td><c \"#FFFFFF\">Option<br>------</td>"
"<td>Argument</td>" "<td><c \"#00FF00\">Argument<br>-------</td>"
"<td>Notes</td>" "<td><c \"#AAAAAA\">Notes<br>-----</td>"
"</tr>" "</tr>"
"<tr>" "<tr>"
"<td><c \"#FFFFFF\">deathmarquee</td>" "<td><c \"#CCCCCC\">deathmarquee</td>"
"<td><c \"#00FF00\">enable</td>" "<td><c \"#00CC00\">enable <c \"#CCCCCC\">| <c \"#00CC00\">disable</td>"
"<td></td>" "<td><c \"#888888\">marquee message on death</td>"
"</tr>" "</tr>"
"<tr>" "<tr>"
"<td></td>" "<td></td>"
"<td><c \"#00FF00\">disable</td>" "<td><c \"#00CCCC\">null</td>"
"<td></td>" "<td><c \"#888888\">(toggles)</td>"
"</tr>"
"<tr>"
"<td><c \"#CCCCCC\">statsupdate</td>"
"<td><c \"#00CC00\">enable <c \"#CCCCCC\">| <c \"#00CC00\">disable</td>"
"<td><c \"#888888\">report stats on update</td>"
"</tr>" "</tr>"
"<tr>" "<tr>"
"<td></td>" "<td></td>"
"<td><c \"#00FFFF\">null</td>" "<td><c \"#00CCCC\">null</td>"
"<td><c \"#AAAAAA\">(toggles)</td>" "<td><c \"#888888\">(toggles)</td>"
"</tr>" "</tr>"
"<tr>" "<tr>"
"<td><c \"#FFFFFF\">statsupdate</td>" "<td><c \"#CCCCCC\">spawnmessage</td>"
"<td><c \"#00FF00\">enable</td>" "<td><c \"#00CC00\">say <c \"#CCCCCC\">| <c \"#00CC00\">tell <c \"#CCCCCC\">| <c \"#00CC00\">silent</td>"
"<td></td>" "<td><c \"#888888\">spawn message into channel</td>"
"</tr>" "</tr>"
"<tr>" "<tr>"
"<td></td>" "<td></td>"
"<td><c \"#00FF00\">disable</td>" "<td><c \"#00CC00\">class <c \"#CCCCCC\">| <c \"#00CC00\">default</td>"
"<td></td>" "<td><c \"#888888\">spawn with class-based message</td>"
"</tr>"
"<tr>"
"<td><c \"#CCCCCC\">altcombat</td>"
"<td><c \"#00CC00\">enable <c \"#CCCCCC\">| <c \"#00CC00\">disable</td>"
"<td><c \"#888888\">use alternate ai combat behavior</td>"
"</tr>" "</tr>"
"<tr>" "<tr>"
"<td></td>" "<td></td>"
"<td><c \"#00FFFF\">null</td>" "<td><c \"#00CCCC\">null</td>"
"<td><c \"#AAAAAA\">(toggles)</td>" "<td><c \"#888888\">(toggles)</td>"
"</tr>" "</tr>"
"<tr>" "<tr>"
"<td><c \"#FFFFFF\">spawnmessage</td>" "<td><c \"#CCCCCC\">autodefend</td>"
"<td><c \"#00FF00\">say</td>" "<td><c \"#00CC00\">enable <c \"#CCCCCC\">| <c \"#00CC00\">disable</td>"
"<td></td>" "<td><c \"#888888\">bots defend owner when aggroed</td>"
"</tr>" "</tr>"
"<tr>" "<tr>"
"<td></td>" "<td></td>"
"<td><c \"#00FF00\">tell</td>" "<td><c \"#00CCCC\">null</td>"
"<td></td>" "<td><c \"#888888\">(toggles)</td>"
"</tr>" "</tr>"
"<tr>" "<tr>"
"<td><c \"#CCCCCC\">current</td>"
"<td></td>" "<td></td>"
"<td><c \"#00FF00\">silent</td>" "<td><c \"#888888\">show current settings</td>"
"<td></td>"
"</tr>"
"<tr>"
"<td></td>"
"<td><c \"#00FF00\">class</td>"
"<td></td>"
"</tr>"
"<tr>"
"<td></td>"
"<td><c \"#00FF00\">default</td>"
"<td></td>"
"</tr>" "</tr>"
"</table>"; "</table>";
@ -3680,6 +3751,64 @@ void bot_command_owner_option(Client *c, const Seperator *sep)
c->Message(m_action, "Bot 'spawn message' is now %s.", argument.c_str()); c->Message(m_action, "Bot 'spawn message' is now %s.", argument.c_str());
} }
else if (!owner_option.compare("altcombat")) {
if (!argument.compare("enable")) {
c->SetBotOption(Client::booAltCombat, true);
}
else if (!argument.compare("disable")) {
c->SetBotOption(Client::booAltCombat, false);
}
else {
c->SetBotOption(Client::booAltCombat, !c->GetBotOption(Client::booAltCombat));
}
database.botdb.SaveOwnerOption(c->CharacterID(), Client::booAltCombat, c->GetBotOption(Client::booAltCombat));
c->Message(m_action, "Bot 'alt combat' is now %s.", (c->GetBotOption(Client::booAltCombat) == true ? "enabled" : "disabled"));
}
else if (!owner_option.compare("autodefend")) {
if (!argument.compare("enable")) {
c->SetBotOption(Client::booAutoDefend, true);
}
else if (!argument.compare("disable")) {
c->SetBotOption(Client::booAutoDefend, false);
}
else {
c->SetBotOption(Client::booAutoDefend, !c->GetBotOption(Client::booAutoDefend));
}
database.botdb.SaveOwnerOption(c->CharacterID(), Client::booAutoDefend, c->GetBotOption(Client::booAutoDefend));
c->Message(m_action, "Bot 'auto defend' is now %s.", (c->GetBotOption(Client::booAutoDefend) == true ? "enabled" : "disabled"));
}
else if (!owner_option.compare("current")) {
std::string window_title = "Current Bot Owner Options Settings";
std::string window_text = fmt::format(
"<table>"
"<tr>"
"<td><c \"#FFFFFF\">Option<br>------</td>"
"<td><c \"#00FF00\">Argument<br>-------</td>"
"</tr>"
"<tr>" "<td><c \"#CCCCCC\">deathmarquee</td>" "<td><c \"#00CC00\">{}</td>" "</tr>"
"<tr>" "<td><c \"#CCCCCC\">statsupdate</td>" "<td><c \"#00CC00\">{}</td>" "</tr>"
"<tr>" "<td><c \"#CCCCCC\">spawnmessage</td>" "<td><c \"#00CC00\">{}</td>" "</tr>"
"<tr>" "<td><c \"#CCCCCC\">spawnmessage</td>" "<td><c \"#00CC00\">{}</td>" "</tr>"
"<tr>" "<td><c \"#CCCCCC\">altcombat</td>" "<td><c \"#00CC00\">{}</td>" "</tr>"
"<tr>" "<td><c \"#CCCCCC\">autodefend</td>" "<td><c \"#00CC00\">{}</td>" "</tr>"
"</table>",
(c->GetBotOption(Client::booDeathMarquee) ? "enabled" : "disabled"),
(c->GetBotOption(Client::booStatsUpdate) ? "enabled" : "disabled"),
(c->GetBotOption(Client::booSpawnMessageSay) ? "say" : (c->GetBotOption(Client::booSpawnMessageTell) ? "tell" : "silent")),
(c->GetBotOption(Client::booSpawnMessageClassSpecific) ? "class" : "default"),
(c->GetBotOption(Client::booAltCombat) ? "enabled" : "disabled"),
(c->GetBotOption(Client::booAutoDefend) ? "enabled" : "disabled")
);
c->SendPopupToClient(window_title.c_str(), window_text.c_str());
}
else { else {
c->Message(m_fail, "Owner option '%s' is not recognized.", owner_option.c_str()); c->Message(m_fail, "Owner option '%s' is not recognized.", owner_option.c_str());
} }
@ -3761,37 +3890,112 @@ void bot_command_pick_lock(Client *c, const Seperator *sep)
c->Message(m_action, "%i door%s attempted - %i door%s successful", door_count, ((door_count != 1) ? ("s") : ("")), open_count, ((open_count != 1) ? ("s") : (""))); c->Message(m_action, "%i door%s attempted - %i door%s successful", door_count, ((door_count != 1) ? ("s") : ("")), open_count, ((open_count != 1) ? ("s") : ("")));
} }
// TODO: Rework to allow owner specificed criteria for puller
void bot_command_pull(Client *c, const Seperator *sep) void bot_command_pull(Client *c, const Seperator *sep)
{ {
if (helper_command_alias_fail(c, "bot_command_pull", sep->arg[0], "pull")) if (helper_command_alias_fail(c, "bot_command_pull", sep->arg[0], "pull")) {
return; return;
}
if (helper_is_help_or_usage(sep->arg[1])) { if (helper_is_help_or_usage(sep->arg[1])) {
c->Message(m_usage, "usage: <enemy_target> %s", sep->arg[0]); c->Message(m_usage, "usage: <enemy_target> %s", sep->arg[0]);
return; return;
} }
int ab_mask = ActionableBots::ABM_OwnerGroup; // existing behavior - need to add c->IsGrouped() check and modify code if different behavior is desired int ab_mask = ActionableBots::ABM_OwnerGroup; // existing behavior - need to add c->IsGrouped() check and modify code if different behavior is desired
std::list<Bot*> sbl; std::list<Bot*> sbl;
if (ActionableBots::PopulateSBL(c, "ownergroup", sbl, ab_mask) == ActionableBots::ABT_None) if (ActionableBots::PopulateSBL(c, "ownergroup", sbl, ab_mask) == ActionableBots::ABT_None) {
return; return;
}
sbl.remove(nullptr); sbl.remove(nullptr);
auto target_mob = ActionableTarget::VerifyEnemy(c, BCEnum::TT_Single); auto target_mob = ActionableTarget::VerifyEnemy(c, BCEnum::TT_Single);
if (!target_mob) { if (!target_mob) {
c->Message(m_fail, "Your current target is not attackable");
c->Message(m_fail, "Your current target is not attackable!");
return; return;
} }
Bot* bot_puller = nullptr; Bot* bot_puller = nullptr;
for (auto bot_iter : sbl) { for (auto bot_iter : sbl) {
if (!bot_iter->IsArcheryRange(target_mob))
continue;
Bot::BotGroupSay(bot_iter, "Attempting to pull %s..", target_mob->GetCleanName()); if (bot_iter->GetAppearance() == eaDead || bot_iter->GetBotStance() == EQEmu::constants::stancePassive) {
bot_iter->InterruptSpell(); continue;
bot_iter->BotRangedAttack(target_mob); }
switch (bot_iter->GetClass()) {
case ROGUE:
case MONK:
case BARD:
case RANGER:
bot_puller = bot_iter; bot_puller = bot_iter;
break; break;
case WARRIOR:
case SHADOWKNIGHT:
case PALADIN:
case BERSERKER:
case BEASTLORD:
if (!bot_puller) {
bot_puller = bot_iter;
continue;
}
switch (bot_puller->GetClass()) {
case DRUID:
case SHAMAN:
case CLERIC:
case WIZARD:
case NECROMANCER:
case MAGICIAN:
case ENCHANTER:
bot_puller = bot_iter;
default:
continue;
}
continue;
case DRUID:
case SHAMAN:
case CLERIC:
if (!bot_puller) {
bot_puller = bot_iter;
continue;
}
switch (bot_puller->GetClass()) {
case WIZARD:
case NECROMANCER:
case MAGICIAN:
case ENCHANTER:
bot_puller = bot_iter;
default:
continue;
}
continue;
case WIZARD:
case NECROMANCER:
case MAGICIAN:
case ENCHANTER:
if (!bot_puller) {
bot_puller = bot_iter;
}
continue;
default:
continue;
}
bot_puller = bot_iter;
break;
}
if (bot_puller) {
bot_puller->SetPullFlag();
} }
helper_no_available_bots(c, bot_puller); helper_no_available_bots(c, bot_puller);
@ -3817,7 +4021,7 @@ void bot_command_release(Client *c, const Seperator *sep)
bot_iter->SetPauseAI(false); bot_iter->SetPauseAI(false);
} }
c->Message(m_action, "%i of your bots %s unsuspended", sbl.size(), ((sbl.size() != 1) ? ("are") : ("is"))); c->Message(m_action, "%i of your bots %s released.", sbl.size(), ((sbl.size() != 1) ? ("are") : ("is")));
} }
void bot_command_resistance(Client *c, const Seperator *sep) void bot_command_resistance(Client *c, const Seperator *sep)
@ -4126,6 +4330,30 @@ void bot_command_summon_corpse(Client *c, const Seperator *sep)
helper_no_available_bots(c, my_bot); helper_no_available_bots(c, my_bot);
} }
void bot_command_suspend(Client *c, const Seperator *sep)
{
if (helper_command_alias_fail(c, "bot_command_suspend", sep->arg[0], "suspend")) {
return;
}
if (helper_is_help_or_usage(sep->arg[1])) {
c->Message(m_usage, "usage: %s ([actionable: <any>] ([actionable_name]))", sep->arg[0]);
return;
}
const int ab_mask = ActionableBots::ABM_NoFilter;
std::list<Bot*> sbl;
if (ActionableBots::PopulateSBL(c, sep->arg[1], sbl, ab_mask, sep->arg[2]) == ActionableBots::ABT_None) {
return;
}
sbl.remove(nullptr);
for (auto bot_iter : sbl) {
bot_iter->SetPauseAI(true);
}
c->Message(m_action, "%i of your bots %s suspended.", sbl.size(), ((sbl.size() != 1) ? ("are") : ("is")));
}
void bot_command_taunt(Client *c, const Seperator *sep) void bot_command_taunt(Client *c, const Seperator *sep)
{ {
if (helper_command_alias_fail(c, "bot_command_taunt", sep->arg[0], "taunt")) if (helper_command_alias_fail(c, "bot_command_taunt", sep->arg[0], "taunt"))

View File

@ -586,6 +586,7 @@ void bot_command_rune(Client *c, const Seperator *sep);
void bot_command_send_home(Client *c, const Seperator *sep); void bot_command_send_home(Client *c, const Seperator *sep);
void bot_command_size(Client *c, const Seperator *sep); void bot_command_size(Client *c, const Seperator *sep);
void bot_command_summon_corpse(Client *c, const Seperator *sep); void bot_command_summon_corpse(Client *c, const Seperator *sep);
void bot_command_suspend(Client *c, const Seperator *sep);
void bot_command_taunt(Client *c, const Seperator *sep); void bot_command_taunt(Client *c, const Seperator *sep);
void bot_command_track(Client *c, const Seperator *sep); void bot_command_track(Client *c, const Seperator *sep);
void bot_command_water_breathing(Client *c, const Seperator *sep); void bot_command_water_breathing(Client *c, const Seperator *sep);

View File

@ -580,7 +580,7 @@ bool BotDatabase::SaveNewBot(Bot* bot_inst, uint32& bot_id)
bot_inst->GetPR(), bot_inst->GetPR(),
bot_inst->GetDR(), bot_inst->GetDR(),
bot_inst->GetCorrup(), bot_inst->GetCorrup(),
BOT_FOLLOW_DISTANCE_DEFAULT, (uint32)BOT_FOLLOW_DISTANCE_DEFAULT,
(IsCasterClass(bot_inst->GetClass()) ? (uint8)RuleI(Bots, CasterStopMeleeLevel) : 255) (IsCasterClass(bot_inst->GetClass()) ? (uint8)RuleI(Bots, CasterStopMeleeLevel) : 255)
); );
auto results = database.QueryDatabase(query); auto results = database.QueryDatabase(query);
@ -2253,8 +2253,10 @@ bool BotDatabase::SaveOwnerOption(const uint32 owner_id, size_t type, const bool
switch (static_cast<Client::BotOwnerOption>(type)) { switch (static_cast<Client::BotOwnerOption>(type)) {
case Client::booDeathMarquee: case Client::booDeathMarquee:
case Client::booStatsUpdate: case Client::booStatsUpdate:
case Client::booSpawnMessageClassSpecific: { case Client::booSpawnMessageClassSpecific:
case Client::booAltCombat:
case Client::booAutoDefend:
{
query = fmt::format( query = fmt::format(
"REPLACE INTO `bot_owner_options`(`owner_id`, `option_type`, `option_value`) VALUES ('{}', '{}', '{}')", "REPLACE INTO `bot_owner_options`(`owner_id`, `option_type`, `option_value`) VALUES ('{}', '{}', '{}')",
owner_id, owner_id,
@ -2282,11 +2284,12 @@ bool BotDatabase::SaveOwnerOption(const uint32 owner_id, const std::pair<size_t,
switch (static_cast<Client::BotOwnerOption>(type.first)) { switch (static_cast<Client::BotOwnerOption>(type.first)) {
case Client::booSpawnMessageSay: case Client::booSpawnMessageSay:
case Client::booSpawnMessageTell: { case Client::booSpawnMessageTell:
{
switch (static_cast<Client::BotOwnerOption>(type.second)) { switch (static_cast<Client::BotOwnerOption>(type.second)) {
case Client::booSpawnMessageSay: case Client::booSpawnMessageSay:
case Client::booSpawnMessageTell: { case Client::booSpawnMessageTell:
{
query = fmt::format( query = fmt::format(
"REPLACE INTO `bot_owner_options`(`owner_id`, `option_type`, `option_value`) VALUES ('{}', '{}', '{}'), ('{}', '{}', '{}')", "REPLACE INTO `bot_owner_options`(`owner_id`, `option_type`, `option_value`) VALUES ('{}', '{}', '{}'), ('{}', '{}', '{}')",
owner_id, owner_id,

View File

@ -352,6 +352,10 @@ Client::Client(EQStreamInterface* ieqs)
bot_owner_options[booSpawnMessageSay] = false; bot_owner_options[booSpawnMessageSay] = false;
bot_owner_options[booSpawnMessageTell] = true; bot_owner_options[booSpawnMessageTell] = true;
bot_owner_options[booSpawnMessageClassSpecific] = true; bot_owner_options[booSpawnMessageClassSpecific] = true;
bot_owner_options[booAltCombat] = false;
bot_owner_options[booAutoDefend] = true;
SetBotPulling(false);
#endif #endif
AI_Init(); AI_Init();

View File

@ -1628,9 +1628,6 @@ private:
#ifdef BOTS #ifdef BOTS
public: public:
enum BotOwnerOption : size_t { enum BotOwnerOption : size_t {
booDeathMarquee, booDeathMarquee,
@ -1638,14 +1635,20 @@ public:
booSpawnMessageSay, booSpawnMessageSay,
booSpawnMessageTell, booSpawnMessageTell,
booSpawnMessageClassSpecific, booSpawnMessageClassSpecific,
booAltCombat,
booAutoDefend,
_booCount _booCount
}; };
bool GetBotOption(BotOwnerOption boo) const; bool GetBotOption(BotOwnerOption boo) const;
void SetBotOption(BotOwnerOption boo, bool flag = true); void SetBotOption(BotOwnerOption boo, bool flag = true);
bool GetBotPulling() { return m_bot_pulling; }
void SetBotPulling(bool flag = true) { m_bot_pulling = flag; }
private: private:
bool bot_owner_options[_booCount]; bool bot_owner_options[_booCount];
bool m_bot_pulling;
#endif #endif
}; };

View File

@ -1493,18 +1493,24 @@ void Mob::SendHPUpdate(bool skip_self /*= false*/, bool force_update_all /*= fal
} }
} }
void Mob::StopMoving() { void Mob::StopMoving()
{
StopNavigation(); StopNavigation();
if (moved)
if (moved) {
moved = false; moved = false;
} }
}
void Mob::StopMoving(float new_heading) { void Mob::StopMoving(float new_heading)
{
StopNavigation(); StopNavigation();
RotateTo(new_heading); RotateTo(new_heading);
if (moved)
if (moved) {
moved = false; moved = false;
} }
}
void Mob::SentPositionPacket(float dx, float dy, float dz, float dh, int anim, bool send_to_self) void Mob::SentPositionPacket(float dx, float dy, float dz, float dh, int anim, bool send_to_self)
{ {
@ -2684,6 +2690,27 @@ bool Mob::PlotPositionAroundTarget(Mob* target, float &x_dest, float &y_dest, fl
return Result; return Result;
} }
bool Mob::PlotPositionOnArcInFrontOfTarget(Mob* target, float& x_dest, float& y_dest, float& z_dest, float distance, float min_deg, float max_deg)
{
return false;
}
bool Mob::PlotPositionOnArcBehindTarget(Mob* target, float& x_dest, float& y_dest, float& z_dest, float distance)
{
return false;
}
bool Mob::PlotPositionBehindMeFacingTarget(Mob* target, float& x_dest, float& y_dest, float& z_dest, float min_dist, float max_dist)
{
return false;
}
bool Mob::HateSummon() { bool Mob::HateSummon() {
// check if mob has ability to summon // check if mob has ability to summon
// 97% is the offical % that summoning starts on live, not 94 // 97% is the offical % that summoning starts on live, not 94

View File

@ -593,8 +593,8 @@ public:
void MakeSpawnUpdateNoDelta(PlayerPositionUpdateServer_Struct* spu); void MakeSpawnUpdateNoDelta(PlayerPositionUpdateServer_Struct* spu);
void MakeSpawnUpdate(PlayerPositionUpdateServer_Struct* spu); void MakeSpawnUpdate(PlayerPositionUpdateServer_Struct* spu);
void SentPositionPacket(float dx, float dy, float dz, float dh, int anim, bool send_to_self = false); void SentPositionPacket(float dx, float dy, float dz, float dh, int anim, bool send_to_self = false);
void StopMoving(); virtual void StopMoving();
void StopMoving(float new_heading); virtual void StopMoving(float new_heading);
void SetSpawned() { spawned = true; }; void SetSpawned() { spawned = true; };
bool Spawned() { return spawned; }; bool Spawned() { return spawned; };
virtual bool ShouldISpawnFor(Client *c) { return true; } virtual bool ShouldISpawnFor(Client *c) { return true; }
@ -676,8 +676,10 @@ public:
void ShowStats(Client* client); void ShowStats(Client* client);
void ShowBuffs(Client* client); void ShowBuffs(Client* client);
void ShowBuffList(Client* client); void ShowBuffList(Client* client);
bool PlotPositionAroundTarget(Mob* target, float &x_dest, float &y_dest, float &z_dest, bool PlotPositionAroundTarget(Mob* target, float &x_dest, float &y_dest, float &z_dest, bool lookForAftArc = true);
bool lookForAftArc = true); bool PlotPositionOnArcInFrontOfTarget(Mob *target, float &x_dest, float &y_dest, float &z_dest, float distance, float min_deg = 5.0f, float max_deg = 150.0f);
bool PlotPositionOnArcBehindTarget(Mob *target, float &x_dest, float &y_dest, float &z_dest, float distance);
bool PlotPositionBehindMeFacingTarget(Mob *target, float &x_dest, float &y_dest, float &z_dest, float min_dist = 1.0f, float max_dist = 5.0f);
// aura functions // aura functions
void MakeAura(uint16 spell_id); void MakeAura(uint16 spell_id);