[Bot Commands] Separate Bot Commands into Individual Files (#4035)

# Notes
- Separate all bot commands into individual files like regular commands.

# Images
This commit is contained in:
Alex King
2024-02-01 05:35:33 -05:00
committed by GitHub
parent 6efd7c5177
commit 6297c56db2
66 changed files with 9293 additions and 8946 deletions
+127 -8819
View File
File diff suppressed because it is too large Load Diff
+1214 -127
View File
File diff suppressed because it is too large Load Diff
+24
View File
@@ -0,0 +1,24 @@
#include "../bot_command.h"
void bot_command_actionable(Client* c, const Seperator* sep)
{
if (helper_command_alias_fail(c, "bot_command_actionable", sep->arg[0], "actionable")) {
return;
}
c->Message(Chat::White, "Actionable command arguments:");
c->Message(Chat::White, "target - selects target as single bot .. use ^command [target] or imply by empty actionable argument");
c->Message(Chat::White, "byname [name] - selects single bot by name");
c->Message(Chat::White, "ownergroup - selects all bots in the owner's group");
c->Message(Chat::White, "ownerraid - selects all bots in the owner's raid");
c->Message(Chat::White, "targetgroup - selects all bots in target's group");
c->Message(Chat::White, "namesgroup [name] - selects all bots in name's group");
c->Message(Chat::White, "healrotation [name] - selects all member and target bots of a heal rotation where name is a member");
c->Message(Chat::White, "healrotationmembers [name] - selects all member bots of a heal rotation where name is a member");
c->Message(Chat::White, "healrotationtargets [name] - selects all target bots of a heal rotation where name is a member");
c->Message(Chat::White, "byclass - selects all bots of the chosen class");
c->Message(Chat::White, "byrace - selects all bots of the chosen rsce");
c->Message(Chat::White, "spawned - selects all spawned bots");
c->Message(Chat::White, "all - selects all spawned bots .. argument use indicates en masse database updating");
c->Message(Chat::White, "You may only select your bots as actionable");
}
+91
View File
@@ -0,0 +1,91 @@
#include "../bot_command.h"
void bot_command_aggressive(Client* c, const Seperator* sep)
{
bcst_list* local_list = &bot_command_spells[BCEnum::SpT_Stance];
if (helper_spell_list_fail(c, local_list, BCEnum::SpT_Stance) ||
helper_command_alias_fail(c, "bot_command_aggressive", sep->arg[0], "aggressive")) {
return;
}
if (helper_is_help_or_usage(sep->arg[1])) {
c->Message(
Chat::White,
"usage: %s ([actionable: target | byname | ownergroup | ownerraid | targetgroup | namesgroup | healrotationtargets | byclass | byrace | spawned] ([actionable_name]))",
sep->arg[0]
);
helper_send_usage_required_bots(c, BCEnum::SpT_Stance);
return;
}
const int ab_mask = ActionableBots::ABM_Type1;
std::string class_race_arg = sep->arg[1];
bool class_race_check = false;
if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) {
class_race_check = true;
}
std::list<Bot*> sbl;
if (ActionableBots::PopulateSBL(
c,
sep->arg[1],
sbl,
ab_mask,
!class_race_check ? sep->arg[2] : nullptr,
class_race_check ? atoi(sep->arg[2]) : 0
) == ActionableBots::ABT_None) {
return;
}
sbl.remove(nullptr);
int success_count = 0;
int candidate_count = sbl.size();
for (auto list_iter: *local_list) {
if (sbl.empty()) {
break;
}
auto local_entry = list_iter->SafeCastToStance();
if (helper_spell_check_fail(local_entry)) {
continue;
}
if (local_entry->stance_type != BCEnum::StT_Aggressive) {
continue;
}
for (auto bot_iter = sbl.begin(); bot_iter != sbl.end();) {
Bot* my_bot = *bot_iter;
if (local_entry->caster_class != my_bot->GetClass()) {
++bot_iter;
continue;
}
if (local_entry->spell_level > my_bot->GetLevel()) {
++bot_iter;
continue;
}
my_bot->InterruptSpell();
if (candidate_count == 1) {
Bot::BotGroupSay(
my_bot,
fmt::format(
"Using {}.",
spells[local_entry->spell_id].name
).c_str()
);
}
my_bot->UseDiscipline(local_entry->spell_id, my_bot->GetID());
++success_count;
bot_iter = sbl.erase(bot_iter);
}
}
c->Message(
Chat::White,
"%i of %i bots have attempted to use aggressive disciplines",
success_count,
candidate_count
);
}
+534
View File
@@ -0,0 +1,534 @@
#include "../bot_command.h"
void bot_command_appearance(Client *c, const Seperator *sep)
{
std::list<const char*> subcommand_list;
subcommand_list.push_back("botbeardcolor");
subcommand_list.push_back("botbeardstyle");
subcommand_list.push_back("botdetails");
subcommand_list.push_back("botdyearmor");
subcommand_list.push_back("boteyes");
subcommand_list.push_back("botface");
subcommand_list.push_back("bothaircolor");
subcommand_list.push_back("bothairstyle");
subcommand_list.push_back("botheritage");
subcommand_list.push_back("bottattoo");
subcommand_list.push_back("botwoad");
if (helper_command_alias_fail(c, "bot_command_appearance", sep->arg[0], "botappearance"))
return;
helper_send_available_subcommands(c, "bot appearance", subcommand_list);
}
void bot_command_beard_color(Client *c, const Seperator *sep)
{
if (helper_command_alias_fail(c, "bot_command_beard_color", sep->arg[0], "botbeardcolor"))
return;
if (helper_is_help_or_usage(sep->arg[1])) {
c->Message(Chat::White, "usage: <target_bot> %s [value: 0-n] (Dwarves or male bots only)", sep->arg[0]);
c->Message(Chat::White, "note: Actual limit is filter-based");
return;
}
auto my_bot = ActionableBots::AsTarget_ByBot(c);
if (!my_bot) {
c->Message(Chat::White, "You must <target> a bot that you own to use this command");
return;
}
if (!sep->IsNumber(1)) {
c->Message(Chat::White, "A numeric [value] is required to use this command");
return;
}
uint8 uvalue = Strings::ToInt(sep->arg[1]);
auto fail_type = BCEnum::AFT_None;
if (my_bot->GetGender() != Gender::Male && my_bot->GetRace() != DWARF)
fail_type = BCEnum::AFT_GenderRace;
else if (!PlayerAppearance::IsValidBeardColor(my_bot->GetRace(), my_bot->GetGender(), uvalue))
fail_type = BCEnum::AFT_Value;
else
my_bot->SetBeardColor(uvalue);
if (helper_bot_appearance_fail(c, my_bot, fail_type, "beard color"))
return;
helper_bot_appearance_form_final(c, my_bot);
}
void bot_command_beard_style(Client *c, const Seperator *sep)
{
if (helper_command_alias_fail(c, "bot_command_beard_style", sep->arg[0], "botbeardstyle"))
return;
if (helper_is_help_or_usage(sep->arg[1])) {
c->Message(Chat::White, "usage: <target_bot> %s [value: 0-n] (Dwarves or male bots only)", sep->arg[0]);
c->Message(Chat::White, "note: Actual limit is filter-based");
return;
}
auto my_bot = ActionableBots::AsTarget_ByBot(c);
if (!my_bot) {
c->Message(Chat::White, "You must <target> a bot that you own to use this command");
return;
}
if (!sep->IsNumber(1)) {
c->Message(Chat::White, "A numeric [value] is required to use this command");
return;
}
uint8 uvalue = Strings::ToInt(sep->arg[1]);
auto fail_type = BCEnum::AFT_None;
if (my_bot->GetGender() != Gender::Male && my_bot->GetRace() != DWARF)
fail_type = BCEnum::AFT_GenderRace;
else if (!PlayerAppearance::IsValidBeard(my_bot->GetRace(), my_bot->GetGender(), uvalue))
fail_type = BCEnum::AFT_Value;
else
my_bot->SetBeard(uvalue);
if (helper_bot_appearance_fail(c, my_bot, fail_type, "beard style"))
return;
helper_bot_appearance_form_final(c, my_bot);
}
void bot_command_details(Client *c, const Seperator *sep)
{
// TODO: Trouble-shoot model update issue
if (helper_command_alias_fail(c, "bot_command_details", sep->arg[0], "botdetails"))
return;
if (helper_is_help_or_usage(sep->arg[1])) {
c->Message(Chat::White, "usage: <target_bot> %s [value: 0-n] (Drakkin bots only)", sep->arg[0]);
c->Message(Chat::White, "note: Actual limit is filter-based");
return;
}
auto my_bot = ActionableBots::AsTarget_ByBot(c);
if (!my_bot) {
c->Message(Chat::White, "You must <target> a bot that you own to use this command");
return;
}
if (!sep->IsNumber(1)) {
c->Message(Chat::White, "A numeric [value] is required to use this command");
return;
}
uint32 uvalue = Strings::ToInt(sep->arg[1]);
auto fail_type = BCEnum::AFT_None;
if (my_bot->GetRace() != DRAKKIN)
fail_type = BCEnum::AFT_Race;
else if (!PlayerAppearance::IsValidDetail(my_bot->GetRace(), my_bot->GetGender(), uvalue))
fail_type = BCEnum::AFT_Value;
else
my_bot->SetDrakkinDetails(uvalue);
if (helper_bot_appearance_fail(c, my_bot, fail_type, "details"))
return;
helper_bot_appearance_form_final(c, my_bot);
}
void bot_command_dye_armor(Client *c, const Seperator *sep)
{
// TODO: Trouble-shoot model update issue
const std::string material_slot_message = fmt::format(
"Material Slots: * (All), {} (Head), {} (Chest), {} (Arms), {} (Wrists), {} (Hands), {} (Legs), {} (Feet)",
EQ::textures::armorHead,
EQ::textures::armorChest,
EQ::textures::armorArms,
EQ::textures::armorWrist,
EQ::textures::armorHands,
EQ::textures::armorLegs,
EQ::textures::armorFeet
);
if (helper_command_alias_fail(c, "bot_command_dye_armor", sep->arg[0], "botdyearmor")) {
return;
}
if (helper_is_help_or_usage(sep->arg[1]) || !sep->arg[1] || (sep->arg[1] && !Strings::IsNumber(sep->arg[1]))) {
c->Message(
Chat::White,
fmt::format(
"Usage: {} [Material Slot] [Red: 0-255] [Green: 0-255] [Blue: 0-255] ([actionable: target | byname | ownergroup | targetgroup | namesgroup | healrotation | spawned] ([actionable_name]))",
sep->arg[0]
).c_str()
);
c->Message(Chat::White, material_slot_message.c_str());
return;
}
const int ab_mask = ActionableBots::ABM_NoFilter;
uint8 material_slot = EQ::textures::materialInvalid;
int16 slot_id = INVALID_INDEX;
bool dye_all = (sep->arg[1][0] == '*');
if (!dye_all) {
material_slot = Strings::ToInt(sep->arg[1]);
slot_id = EQ::InventoryProfile::CalcSlotFromMaterial(material_slot);
if (!sep->IsNumber(1) || slot_id == INVALID_INDEX || material_slot > EQ::textures::LastTintableTexture) {
c->Message(Chat::White, "Valid material slots for this command are:");
c->Message(Chat::White, material_slot_message.c_str());
return;
}
}
if (!sep->IsNumber(2)) {
c->Message(Chat::White, "Valid Red values for this command are 0 to 255.");
return;
}
uint32 red_value = Strings::ToUnsignedInt(sep->arg[2]);
if (red_value > 255) {
red_value = 255;
}
if (!sep->IsNumber(3)) {
c->Message(Chat::White, "Valid Green values for this command are 0 to 255.");
return;
}
uint32 green_value = Strings::ToUnsignedInt(sep->arg[3]);
if (green_value > 255) {
green_value = 255;
}
if (!sep->IsNumber(4)) {
c->Message(Chat::White, "Valid Blue values for this command are 0 to 255.");
return;
}
uint32 blue_value = Strings::ToUnsignedInt(sep->arg[4]);
if (blue_value > 255) {
blue_value = 255;
}
uint32 rgb_value = ((uint32)red_value << 16) | ((uint32)green_value << 8) | ((uint32)blue_value);
std::list<Bot*> sbl;
auto ab_type = ActionableBots::PopulateSBL(c, sep->arg[5], sbl, ab_mask);
if (ab_type == ActionableBots::ABT_None) {
return;
}
for (auto bot_iter : sbl) {
if (!bot_iter) {
continue;
}
if (!bot_iter->DyeArmor(slot_id, rgb_value, dye_all, (ab_type != ActionableBots::ABT_All))) {
c->Message(
Chat::White,
fmt::format(
"Failed to change armor color for {} due to unknown cause.",
bot_iter->GetCleanName()
).c_str()
);
return;
}
}
if (ab_type == ActionableBots::ABT_All) {
if (dye_all) {
database.botdb.SaveAllArmorColors(c->CharacterID(), rgb_value);
} else {
database.botdb.SaveAllArmorColorBySlot(c->CharacterID(), slot_id, rgb_value);
}
}
}
void bot_command_eyes(Client *c, const Seperator *sep)
{
// TODO: Trouble-shoot model update issue
// not sure if left/right bias is allowed in pc-type entities (something is keeping them from being different)
if (helper_command_alias_fail(c, "bot_command_eyes", sep->arg[0], "boteyes"))
return;
if (helper_is_help_or_usage(sep->arg[1])) {
//c->Message(Chat::White, "usage: <target_bot> %s [value:0-n] ([option: left | right])", sep->arg[0]);
c->Message(Chat::White, "usage: <target_bot> %s [value: 0-n]", sep->arg[0]);
c->Message(Chat::White, "note: Actual limit is filter-based");
return;
}
auto my_bot = ActionableBots::AsTarget_ByBot(c);
if (!my_bot) {
c->Message(Chat::White, "You must <target> a bot that you own to use this command");
return;
}
if (!sep->IsNumber(1)) {
c->Message(Chat::White, "A numeric [value] is required to use this command");
return;
}
uint8 uvalue = Strings::ToInt(sep->arg[1]);
//uint8 eye_bias = 0;
//std::string arg2 = sep->arg[2];
//if (!arg2.compare("left"))
// eye_bias = 1;
//else if (!arg2.compare("right"))
// eye_bias = 2;
auto fail_type = BCEnum::AFT_None;
if (!PlayerAppearance::IsValidEyeColor(my_bot->GetRace(), my_bot->GetGender(), uvalue)) {
fail_type = BCEnum::AFT_Value;
}
else {
//if (eye_bias == 1) {
// my_bot->SetEyeColor1(uvalue);
//}
//else if (eye_bias == 2) {
// my_bot->SetEyeColor2(uvalue);
//}
//else {
my_bot->SetEyeColor1(uvalue);
my_bot->SetEyeColor2(uvalue);
//}
}
if (helper_bot_appearance_fail(c, my_bot, fail_type, "eyes"))
return;
c->Message(Chat::White, "This feature will update the next time your bot is spawned");
//helper_bot_appearance_form_final(c, my_bot);
}
void bot_command_face(Client *c, const Seperator *sep)
{
if (helper_command_alias_fail(c, "bot_command_face", sep->arg[0], "botface"))
return;
if (helper_is_help_or_usage(sep->arg[1])) {
c->Message(Chat::White, "usage: <target_bot> %s [value: 0-n]", sep->arg[0]);
c->Message(Chat::White, "note: Actual limit is filter-based");
return;
}
auto my_bot = ActionableBots::AsTarget_ByBot(c);
if (!my_bot) {
c->Message(Chat::White, "You must <target> a bot that you own to use this command");
return;
}
if (!sep->IsNumber(1)) {
c->Message(Chat::White, "A numeric [value] is required to use this command");
return;
}
uint8 uvalue = Strings::ToInt(sep->arg[1]);
auto fail_type = BCEnum::AFT_None;
if (!PlayerAppearance::IsValidFace(my_bot->GetRace(), my_bot->GetGender(), uvalue)) {
fail_type = BCEnum::AFT_Value;
}
else {
uint8 old_woad = 0;
if (my_bot->GetRace() == BARBARIAN)
old_woad = ((my_bot->GetLuclinFace() / 10) * 10);
my_bot->SetLuclinFace((old_woad + uvalue));
}
if (helper_bot_appearance_fail(c, my_bot, fail_type, "face"))
return;
helper_bot_appearance_form_final(c, my_bot);
}
void bot_command_hair_color(Client *c, const Seperator *sep)
{
if (helper_command_alias_fail(c, "bot_command_hair_color", sep->arg[0], "bothaircolor"))
return;
if (helper_is_help_or_usage(sep->arg[1])) {
c->Message(Chat::White, "usage: <target_bot> %s [value: 0-n]", sep->arg[0]);
c->Message(Chat::White, "note: Actual limit is filter-based");
return;
}
auto my_bot = ActionableBots::AsTarget_ByBot(c);
if (!my_bot) {
c->Message(Chat::White, "You must <target> a bot that you own to use this command");
return;
}
if (!sep->IsNumber(1)) {
c->Message(Chat::White, "A numeric [value] is required to use this command");
return;
}
uint8 uvalue = Strings::ToInt(sep->arg[1]);
auto fail_type = BCEnum::AFT_None;
if (!PlayerAppearance::IsValidHairColor(my_bot->GetRace(), my_bot->GetGender(), uvalue))
fail_type = BCEnum::AFT_Value;
else
my_bot->SetHairColor(uvalue);
if (helper_bot_appearance_fail(c, my_bot, fail_type, "hair color"))
return;
helper_bot_appearance_form_final(c, my_bot);
}
void bot_command_hairstyle(Client *c, const Seperator *sep)
{
if (helper_command_alias_fail(c, "bot_command_hairstyle", sep->arg[0], "bothairstyle"))
return;
if (helper_is_help_or_usage(sep->arg[1])) {
c->Message(Chat::White, "usage: <target_bot> %s [value: 0-n]", sep->arg[0]);
c->Message(Chat::White, "note: Actual limit is filter-based");
return;
}
auto my_bot = ActionableBots::AsTarget_ByBot(c);
if (!my_bot) {
c->Message(Chat::White, "You must <target> a bot that you own to use this command");
return;
}
if (!sep->IsNumber(1)) {
c->Message(Chat::White, "A numeric [value] is required to use this command");
return;
}
uint8 uvalue = Strings::ToInt(sep->arg[1]);
auto fail_type = BCEnum::AFT_None;
if (!PlayerAppearance::IsValidHair(my_bot->GetRace(), my_bot->GetGender(), uvalue))
fail_type = BCEnum::AFT_Value;
else
my_bot->SetHairStyle(uvalue);
if (helper_bot_appearance_fail(c, my_bot, fail_type, "hair style"))
return;
helper_bot_appearance_form_final(c, my_bot);
}
void bot_command_heritage(Client *c, const Seperator *sep)
{
// TODO: Trouble-shoot model update issue
if (helper_command_alias_fail(c, "bot_command_heritage", sep->arg[0], "botheritage"))
return;
if (helper_is_help_or_usage(sep->arg[1])) {
c->Message(Chat::White, "usage: <target_bot> %s [value: 0-n] (Drakkin bots only)", sep->arg[0]);
c->Message(Chat::White, "note: Actual limit is filter-based");
return;
}
auto my_bot = ActionableBots::AsTarget_ByBot(c);
if (!my_bot) {
c->Message(Chat::White, "You must <target> a bot that you own to use this command");
return;
}
if (!sep->IsNumber(1)) {
c->Message(Chat::White, "A numeric [value] is required to use this command");
return;
}
uint32 uvalue = Strings::ToInt(sep->arg[1]);
auto fail_type = BCEnum::AFT_None;
if (my_bot->GetRace() != DRAKKIN)
fail_type = BCEnum::AFT_Race;
else if (!PlayerAppearance::IsValidHeritage(my_bot->GetRace(), my_bot->GetGender(), uvalue))
fail_type = BCEnum::AFT_Value;
else
my_bot->SetDrakkinHeritage(uvalue);
if (helper_bot_appearance_fail(c, my_bot, fail_type, "heritage"))
return;
helper_bot_appearance_form_final(c, my_bot);
}
void bot_command_tattoo(Client *c, const Seperator *sep)
{
// TODO: Trouble-shoot model update issue
if (helper_command_alias_fail(c, "bot_command_tattoo", sep->arg[0], "bottattoo"))
return;
if (helper_is_help_or_usage(sep->arg[1])) {
c->Message(Chat::White, "usage: <target_bot> %s [value: 0-n] (Drakkin bots only)", sep->arg[0]);
c->Message(Chat::White, "note: Actual limit is filter-based");
return;
}
auto my_bot = ActionableBots::AsTarget_ByBot(c);
if (!my_bot) {
c->Message(Chat::White, "You must <target> a bot that you own to use this command");
return;
}
if (!sep->IsNumber(1)) {
c->Message(Chat::White, "A numeric [value] is required to use this command");
return;
}
uint32 uvalue = Strings::ToInt(sep->arg[1]);
auto fail_type = BCEnum::AFT_None;
if (my_bot->GetRace() != DRAKKIN)
fail_type = BCEnum::AFT_Race;
else if (!PlayerAppearance::IsValidTattoo(my_bot->GetRace(), my_bot->GetGender(), uvalue))
fail_type = BCEnum::AFT_Value;
else
my_bot->SetDrakkinTattoo(uvalue);
if (helper_bot_appearance_fail(c, my_bot, fail_type, "tattoo"))
return;
helper_bot_appearance_form_final(c, my_bot);
}
void bot_command_woad(Client *c, const Seperator *sep)
{
if (helper_command_alias_fail(c, "bot_command_woad", sep->arg[0], "botwoad"))
return;
if (helper_is_help_or_usage(sep->arg[1])) {
c->Message(Chat::White, "usage: <target_bot> %s [value: 0-n] (Barbarian bots only)", sep->arg[0]);
c->Message(Chat::White, "note: Actual limit is filter-based");
return;
}
auto my_bot = ActionableBots::AsTarget_ByBot(c);
if (!my_bot) {
c->Message(Chat::White, "You must <target> a bot that you own to use this command");
return;
}
if (!sep->IsNumber(1)) {
c->Message(Chat::White, "A numeric [value] is required to use this command");
return;
}
uint8 uvalue = Strings::ToInt(sep->arg[1]);
auto fail_type = BCEnum::AFT_None;
if (my_bot->GetRace() != BARBARIAN) {
fail_type = BCEnum::AFT_Race;
}
else if (!PlayerAppearance::IsValidWoad(my_bot->GetRace(), my_bot->GetGender(), uvalue)) {
fail_type = BCEnum::AFT_Value;
}
else {
uint8 old_face = (my_bot->GetLuclinFace() % 10);
my_bot->SetLuclinFace(((uvalue * 10) + old_face));
}
if (helper_bot_appearance_fail(c, my_bot, fail_type, "woad"))
return;
helper_bot_appearance_form_final(c, my_bot);
}
+104
View File
@@ -0,0 +1,104 @@
#include "../bot_command.h"
void bot_command_apply_poison(Client* c, const Seperator* sep)
{
if (helper_command_disabled(c, RuleB(Bots, AllowApplyPoisonCommand), "applypoison")) {
return;
}
if (helper_command_alias_fail(c, "bot_command_apply_poison", sep->arg[0], "applypoison")) {
return;
}
if (helper_is_help_or_usage(sep->arg[1])) {
c->Message(Chat::White, "usage: <rogue_bot_target> %s", sep->arg[0]);
return;
}
Bot* my_rogue_bot = nullptr;
auto t = c->GetTarget();
if (
t &&
t->IsBot() &&
t->CastToBot()->GetBotOwnerCharacterID() == c->CharacterID() &&
t->GetClass() == Class::Rogue
) {
my_rogue_bot = t->CastToBot();
}
if (!my_rogue_bot) {
c->Message(Chat::White, "You must target a rogue bot that you own to use this command!");
return;
}
if (my_rogue_bot->GetLevel() < 18) {
c->Message(
Chat::White,
"Your rogue bot must be level 18 before %s can apply poison!",
(my_rogue_bot->GetGender() == Gender::Female ? "she" : "he"));
return;
}
const auto poison_instance = c->GetInv().GetItem(EQ::invslot::slotCursor);
if (!poison_instance) {
c->Message(Chat::White, "No item found on cursor!");
return;
}
auto poison_data = poison_instance->GetItem();
if (!poison_data) {
c->Message(Chat::White, "No data found for cursor item!");
return;
}
if (poison_data->ItemType == EQ::item::ItemTypePoison) {
if ((~poison_data->Races) & GetPlayerRaceBit(my_rogue_bot->GetRace())) {
c->Message(Chat::White, "Invalid race for weapon poison!");
return;
}
if (poison_data->Proc.Level2 > my_rogue_bot->GetLevel()) {
c->Message(Chat::White, "This poison is too powerful for your intended target!");
return;
}
// generalized from client ApplyPoison handler
double ChanceRoll = zone->random.Real(0, 1);
uint16 poison_skill = 95 + ((my_rogue_bot->GetLevel() - 18) * 5);
if (poison_skill > 200) {
poison_skill = 200;
}
bool apply_poison_chance = (ChanceRoll < (.75 + poison_skill / 1000));
if (apply_poison_chance && my_rogue_bot->AddProcToWeapon(
poison_data->Proc.Effect,
false,
(my_rogue_bot->GetDEX() / 100) + 103,
POISON_PROC
)) {
c->Message(
Chat::White,
"Successfully applied %s to %s's weapon.",
poison_data->Name,
my_rogue_bot->GetCleanName());
} else {
c->Message(
Chat::White,
"Failed to apply %s to %s's weapon.",
poison_data->Name,
my_rogue_bot->GetCleanName());
}
c->DeleteItemInInventory(EQ::invslot::slotCursor, 1, true);
} else {
c->Message(Chat::White, "Item on cursor is not a weapon poison!");
return;
}
}
+80
View File
@@ -0,0 +1,80 @@
#include "../bot_command.h"
void bot_command_apply_potion(Client* c, const Seperator* sep)
{
if (helper_command_disabled(c, RuleB(Bots, AllowApplyPotionCommand), "applypotion")) {
return;
}
if (helper_command_alias_fail(c, "bot_command_apply_potion", sep->arg[0], "applypotion")) {
return;
}
if (helper_is_help_or_usage(sep->arg[1])) {
c->Message(Chat::White, "usage: <bot_target> %s", sep->arg[0]);
return;
}
Bot* my_bot = nullptr;
if (c->GetTarget() && c->GetTarget()->IsBot() && c->GetTarget()->CastToBot()->GetBotOwnerCharacterID() == c->CharacterID()) {
my_bot = c->GetTarget()->CastToBot();
}
if (!my_bot) {
c->Message(Chat::White, "You must target a bot that you own to use this command!");
return;
}
const auto potion_instance = c->GetInv().GetItem(EQ::invslot::slotCursor);
if (!potion_instance) {
c->Message(Chat::White, "No item found on cursor!");
return;
}
auto potion_data = potion_instance->GetItem();
if (!potion_data) {
c->Message(Chat::White, "No data found for cursor item!");
return;
}
if (potion_data->ItemType == EQ::item::ItemTypePotion && potion_data->Click.Effect > 0) {
if (RuleB(Bots, RestrictApplyPotionToRogue) && potion_data->Classes != player_class_bitmasks[Class::Rogue]) {
c->Message(Chat::White, "This command is restricted to rogue poison potions only!");
return;
}
if ((~potion_data->Races) & GetPlayerRaceBit(my_bot->GetRace())) {
c->Message(Chat::White, "Invalid race for potion!");
return;
}
if ((~potion_data->Classes) & GetPlayerClassBit(my_bot->GetClass())) {
c->Message(Chat::White, "Invalid class for potion!");
return;
}
if (potion_data->Click.Level2 > my_bot->GetLevel()) {
c->Message(Chat::White, "This potion is too powerful for your intended target!");
return;
}
// TODO: figure out best way to handle casting time/animation
if (my_bot->SpellFinished(potion_data->Click.Effect, my_bot, EQ::spells::CastingSlot::Item, 0)) {
c->Message(Chat::White, "Successfully applied %s to %s's buff effects.", potion_data->Name, my_bot->GetCleanName());
}
else {
c->Message(Chat::White, "Failed to apply %s to %s's buff effects.", potion_data->Name, my_bot->GetCleanName());
}
c->DeleteItemInInventory(EQ::invslot::slotCursor, 1, true);
}
else {
c->Message(Chat::White, "Item on cursor is not a potion!");
return;
}
}
+72
View File
@@ -0,0 +1,72 @@
#include "../bot_command.h"
void bot_command_attack(Client *c, const Seperator *sep)
{
if (helper_command_alias_fail(c, "bot_command_attack", sep->arg[0], "attack")) {
return;
}
if (helper_is_help_or_usage(sep->arg[1])) {
c->Message(Chat::White, "usage: <enemy_target> %s [actionable: byname | ownergroup | ownerraid | namesgroup | healrotation | byclass | byrace | default: spawned] ([actionable_name])", sep->arg[0]);
return;
}
const int ab_mask = ActionableBots::ABM_Type2;
Mob* target_mob = ActionableTarget::AsSingle_ByAttackable(c);
if (!target_mob) {
c->Message(Chat::White, "You must <target> an enemy to use this command");
return;
}
std::string ab_arg(sep->arg[1]);
if (ab_arg.empty()) {
ab_arg = "spawned";
}
std::string class_race_arg(sep->arg[1]);
bool class_race_check = false;
if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) {
class_race_check = true;
}
std::list<Bot*> sbl;
if (ActionableBots::PopulateSBL(c, ab_arg.c_str(), sbl, ab_mask, !class_race_check ? sep->arg[2] : nullptr, class_race_check ? atoi(sep->arg[2]) : 0) == ActionableBots::ABT_None) {
return;
}
size_t attacker_count = 0;
Bot *first_attacker = nullptr;
sbl.remove(nullptr);
for (auto bot_iter : sbl) {
if (bot_iter->GetAppearance() != eaDead && bot_iter->GetBotStance() != EQ::constants::stancePassive) {
if (!first_attacker) {
first_attacker = bot_iter;
}
++attacker_count;
bot_iter->SetAttackFlag();
}
}
if (attacker_count == 1 && first_attacker) {
Bot::BotGroupSay(
first_attacker,
fmt::format(
"Attacking {}.",
target_mob->GetCleanName()
).c_str()
);
} else {
c->Message(
Chat::White,
fmt::format(
"{} of your bots are attacking {}.",
sbl.size(),
target_mob->GetCleanName()
).c_str()
);
}
}
+41
View File
@@ -0,0 +1,41 @@
#include "../bot_command.h"
void bot_command_bind_affinity(Client *c, const Seperator *sep)
{
bcst_list* local_list = &bot_command_spells[BCEnum::SpT_BindAffinity];
if (helper_spell_list_fail(c, local_list, BCEnum::SpT_BindAffinity) || helper_command_alias_fail(c, "bot_command_bind_affinity", sep->arg[0], "bindaffinity"))
return;
if (helper_is_help_or_usage(sep->arg[1])) {
c->Message(Chat::White, "usage: (<friendly_target>) %s", sep->arg[0]);
helper_send_usage_required_bots(c, BCEnum::SpT_BindAffinity);
return;
}
ActionableTarget::Types actionable_targets;
Bot* my_bot = nullptr;
std::list<Bot*> sbl;
MyBots::PopulateSBL_BySpawnedBots(c, sbl);
for (auto list_iter : *local_list) {
auto local_entry = list_iter;
if (helper_spell_check_fail(local_entry))
continue;
auto target_mob = actionable_targets.Select(c, local_entry->target_type, FRIENDLY);
if (!target_mob)
continue;
my_bot = ActionableBots::Select_ByMinLevelAndClass(c, local_entry->target_type, sbl, local_entry->spell_level, local_entry->caster_class, target_mob);
if (!my_bot)
continue;
// Cast effect message is not being generated
if (helper_cast_standard_spell(my_bot, target_mob, local_entry->spell_id))
c->Message(Chat::White, "Successfully bound %s to this location", target_mob->GetCleanName());
else
c->Message(Chat::White, "Failed to bind %s to this location", target_mob->GetCleanName());
break;
}
helper_no_available_bots(c, my_bot);
}
File diff suppressed because it is too large Load Diff
+107
View File
@@ -0,0 +1,107 @@
#include "../bot_command.h"
void bot_command_caster_range(Client* c, const Seperator* sep)
{
if (helper_command_alias_fail(c, "bot_command_caster_range", sep->arg[0], "casterrange")) {
return;
}
if (helper_is_help_or_usage(sep->arg[1])) {
c->Message(Chat::White, "usage: %s [current | value: 0 - 300] ([actionable: target | byname | ownergroup | ownerraid | targetgroup | namesgroup | healrotationtargets | byclass | byrace | spawned] ([actionable_name]))", sep->arg[0]);
c->Message(Chat::White, "note: Can only be used for Casters or Hybrids.");
c->Message(Chat::White, "note: Use [current] to check the current setting.");
c->Message(Chat::White, "note: Set the value to the minimum distance you want your bot to try to remain from its target.");
c->Message(Chat::White, "note: If they are too far for a spell, it will be skipped.");
c->Message(Chat::White, "note: This is set to (90) units by default.");
return;
}
const int ab_mask = ActionableBots::ABM_Type1;
std::string arg1 = sep->arg[1];
int ab_arg = 1;
bool current_check = false;
uint32 crange = 0;
if (sep->IsNumber(1)) {
ab_arg = 2;
crange = atoi(sep->arg[1]);
if (crange < 0 || crange > 300) {
c->Message(Chat::White, "You must enter a value within the range of 0 - 300.");
return;
}
}
else if (!arg1.compare("current")) {
ab_arg = 2;
current_check = true;
}
else {
c->Message(Chat::White, "Incorrect argument, use %s help for a list of options.", sep->arg[0]);
return;
}
std::string class_race_arg = sep->arg[ab_arg];
bool class_race_check = false;
if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) {
class_race_check = true;
}
std::list<Bot*> sbl;
if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) {
return;
}
sbl.remove(nullptr);
Bot* first_found = nullptr;
int success_count = 0;
for (auto my_bot : sbl) {
if (!IsCasterClass(my_bot->GetClass()) && !IsHybridClass(my_bot->GetClass())) {
continue;
}
if (!first_found) {
first_found = my_bot;
}
if (current_check) {
c->Message(
Chat::White,
fmt::format(
"{} says, 'My current Caster Range is {}.'",
my_bot->GetCleanName(),
my_bot->GetBotCasterRange()
).c_str()
);
}
else {
my_bot->SetBotCasterRange(crange);
++success_count;
database.botdb.SaveBotCasterRange(my_bot->GetBotID(), crange);
}
}
if (!current_check) {
if (success_count == 1 && first_found) {
c->Message(
Chat::White,
fmt::format(
"{} says, 'My Caster Range was set to {}.'",
first_found->GetCleanName(),
crange
).c_str()
);
}
else {
c->Message(
Chat::White,
fmt::format(
"{} of your bots set their Caster Range to {}.",
success_count,
crange
).c_str()
);
}
}
}
+54
View File
@@ -0,0 +1,54 @@
#include "../bot_command.h"
void bot_command_charm(Client *c, const Seperator *sep)
{
auto local_list = &bot_command_spells[BCEnum::SpT_Charm];
if (helper_spell_list_fail(c, local_list, BCEnum::SpT_Charm) || helper_command_alias_fail(c, "bot_command_charm", sep->arg[0], "charm"))
return;
if (helper_is_help_or_usage(sep->arg[1])) {
c->Message(Chat::White, "usage: <enemy_target> %s ([option: dire])", sep->arg[0]);
helper_send_usage_required_bots(c, BCEnum::SpT_Charm);
return;
}
bool dire = false;
std::string dire_arg = sep->arg[1];
if (!dire_arg.compare("dire"))
dire = true;
ActionableTarget::Types actionable_targets;
Bot* my_bot = nullptr;
std::list<Bot*> sbl;
MyBots::PopulateSBL_BySpawnedBots(c, sbl);
for (auto list_iter : *local_list) {
auto local_entry = list_iter->SafeCastToCharm();
if (helper_spell_check_fail(local_entry))
continue;
if (local_entry->dire != dire)
continue;
auto target_mob = actionable_targets.Select(c, local_entry->target_type, ENEMY);
if (!target_mob)
continue;
if (target_mob->IsCharmed()) {
c->Message(Chat::White, "Your <target> is already charmed");
return;
}
if (spells[local_entry->spell_id].max_value[EFFECTIDTOINDEX(1)] < target_mob->GetLevel())
continue;
my_bot = ActionableBots::Select_ByMinLevelAndClass(c, local_entry->target_type, sbl, local_entry->spell_level, local_entry->caster_class, target_mob, true);
if (!my_bot)
continue;
uint32 dont_root_before = 0;
if (helper_cast_standard_spell(my_bot, target_mob, local_entry->spell_id, true, &dont_root_before))
target_mob->SetDontRootMeBefore(dont_root_before);
break;
}
helper_no_available_bots(c, my_bot);
}
+56
View File
@@ -0,0 +1,56 @@
#include "../bot_command.h"
void bot_command_click_item(Client* c, const Seperator* sep)
{
if (!RuleB(Bots, BotsCanClickItems)) {
c->Message(Chat::White, "The ability for bots to click equipped items is currently disabled.");
return;
}
if (helper_is_help_or_usage(sep->arg[1])) {
c->Message(Chat::White, "usage: <slot id> %s ([actionable: target | byname | ownergroup | ownerraid | targetgroup | namesgroup | byclass | byrace | spawned] ([actionable_name]))", sep->arg[0]);
c->Message(Chat::White, "This will cause the selected bots to click the item in the given slot ID.");
c->Message(Chat::White, "Use ^invlist to see their items along with slot IDs.");
return;
}
if (!sep->IsNumber(1)) {
c->Message(Chat::Yellow, "You must specify a slot ID. Use %s help for more information.", sep->arg[0]);
return;
}
const int ab_mask = ActionableBots::ABM_Type1;
int ab_arg = 1;
uint32 slot_id = 0;
if (sep->IsNumber(1)) {
ab_arg = 2;
slot_id = atoi(sep->arg[1]);
if (slot_id < EQ::invslot::EQUIPMENT_BEGIN || slot_id > EQ::invslot::EQUIPMENT_END) {
c->Message(Chat::Yellow, "You must specify a valid inventory slot from 0 to 22. Use %s help for more information", sep->arg[0]);
return;
}
}
std::string class_race_arg = sep->arg[ab_arg];
bool class_race_check = false;
if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) {
class_race_check = true;
}
std::list<Bot*> sbl;
if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) {
return;
}
sbl.remove(nullptr);
for (auto my_bot : sbl) {
if (RuleI(Bots, BotsClickItemsMinLvl) > my_bot->GetLevel()) {
c->Message(Chat::White, "%s must be level %i to use clickable items.", my_bot->GetCleanName(), RuleI(Bots, BotsClickItemsMinLvl));
continue;
}
my_bot->TryItemClick(slot_id);
}
}
+71
View File
@@ -0,0 +1,71 @@
#include "../bot_command.h"
void bot_command_cure(Client *c, const Seperator *sep)
{
bcst_list* local_list = &bot_command_spells[BCEnum::SpT_Cure];
if (helper_spell_list_fail(c, local_list, BCEnum::SpT_Cure) || helper_command_alias_fail(c, "bot_command_cure", sep->arg[0], "cure"))
return;
if (helper_is_help_or_usage(sep->arg[1])) {
c->Message(Chat::White, "usage: (<friendly_target>) %s [ailment: blindness | disease | poison | curse | corruption]", sep->arg[0]);
helper_send_usage_required_bots(c, BCEnum::SpT_Cure);
return;
}
std::string ailment_arg = sep->arg[1];
auto ailment_type = BCEnum::AT_None;
if (!ailment_arg.compare("blindness"))
ailment_type = BCEnum::AT_Blindness;
else if (!ailment_arg.compare("disease"))
ailment_type = BCEnum::AT_Disease;
else if (!ailment_arg.compare("poison"))
ailment_type = BCEnum::AT_Poison;
else if (!ailment_arg.compare("curse"))
ailment_type = BCEnum::AT_Curse;
else if (!ailment_arg.compare("corruption"))
ailment_type = BCEnum::AT_Corruption;
if (ailment_type == BCEnum::AT_None) {
c->Message(Chat::White, "You must specify a cure [ailment] to use this command");
return;
}
local_list->sort([ailment_type](STBaseEntry* l, STBaseEntry* r) {
auto _l = l->SafeCastToCure(), _r = r->SafeCastToCure();
if (_l->cure_value[AILMENTIDTOINDEX(ailment_type)] < _r->cure_value[AILMENTIDTOINDEX(ailment_type)])
return true;
if (_l->cure_value[AILMENTIDTOINDEX(ailment_type)] == _r->cure_value[AILMENTIDTOINDEX(ailment_type)] && spells[_l->spell_id].mana < spells[_r->spell_id].mana)
return true;
if (_l->cure_value[AILMENTIDTOINDEX(ailment_type)] == _r->cure_value[AILMENTIDTOINDEX(ailment_type)] && spells[_l->spell_id].mana == spells[_r->spell_id].mana && _l->cure_total < _r->cure_total)
return true;
return false;
});
ActionableTarget::Types actionable_targets;
Bot* my_bot = nullptr;
std::list<Bot*> sbl;
MyBots::PopulateSBL_BySpawnedBots(c, sbl);
bool cast_success = false;
for (auto list_iter : *local_list) {
auto local_entry = list_iter->SafeCastToCure();
if (helper_spell_check_fail(local_entry))
continue;
if (!local_entry->cure_value[AILMENTIDTOINDEX(ailment_type)])
continue;
auto target_mob = actionable_targets.Select(c, local_entry->target_type, FRIENDLY);
if (!target_mob)
continue;
my_bot = ActionableBots::Select_ByMinLevelAndClass(c, local_entry->target_type, sbl, local_entry->spell_level, local_entry->caster_class, target_mob);
if (!my_bot)
continue;
cast_success = helper_cast_standard_spell(my_bot, target_mob, local_entry->spell_id);
break;
}
helper_no_available_bots(c, my_bot);
}
+69
View File
@@ -0,0 +1,69 @@
#include "../bot_command.h"
void bot_command_defensive(Client *c, const Seperator *sep)
{
bcst_list* local_list = &bot_command_spells[BCEnum::SpT_Stance];
if (helper_spell_list_fail(c, local_list, BCEnum::SpT_Stance) || helper_command_alias_fail(c, "bot_command_defensive", sep->arg[0], "defensive"))
return;
if (helper_is_help_or_usage(sep->arg[1])) {
c->Message(Chat::White, "usage: %s ([actionable: target | byname | ownergroup | ownerraid | targetgroup | namesgroup | healrotationtargets | byclass | byrace | spawned] ([actionable_name]))", sep->arg[0]);
helper_send_usage_required_bots(c, BCEnum::SpT_Stance);
return;
}
const int ab_mask = ActionableBots::ABM_Type1;
std::string class_race_arg = sep->arg[1];
bool class_race_check = false;
if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) {
class_race_check = true;
}
std::list<Bot*> sbl;
if (ActionableBots::PopulateSBL(c, sep->arg[1], sbl, ab_mask, !class_race_check ? sep->arg[2] : nullptr, class_race_check ? atoi(sep->arg[2]) : 0) == ActionableBots::ABT_None) {
return;
}
sbl.remove(nullptr);
int success_count = 0;
int candidate_count = sbl.size();
for (auto list_iter : *local_list) {
if (sbl.empty())
break;
auto local_entry = list_iter->SafeCastToStance();
if (helper_spell_check_fail(local_entry))
continue;
if (local_entry->stance_type != BCEnum::StT_Defensive)
continue;
for (auto bot_iter = sbl.begin(); bot_iter != sbl.end(); ) {
Bot* my_bot = *bot_iter;
if (local_entry->caster_class != my_bot->GetClass()) {
++bot_iter;
continue;
}
if (local_entry->spell_level > my_bot->GetLevel()) {
++bot_iter;
continue;
}
my_bot->InterruptSpell();
if (candidate_count == 1) {
Bot::BotGroupSay(
my_bot,
fmt::format(
"Using {}.",
spells[local_entry->spell_id].name
).c_str()
);
}
my_bot->UseDiscipline(local_entry->spell_id, my_bot->GetID());
++success_count;
bot_iter = sbl.erase(bot_iter);
}
}
c->Message(Chat::White, "%i of %i bots have attempted to use defensive disciplines", success_count, candidate_count);
}
+59
View File
@@ -0,0 +1,59 @@
#include "../bot_command.h"
void bot_command_depart(Client *c, const Seperator *sep)
{
bcst_list* local_list = &bot_command_spells[BCEnum::SpT_Depart];
if (helper_spell_list_fail(c, local_list, BCEnum::SpT_Depart) || helper_command_alias_fail(c, "bot_command_depart", sep->arg[0], "depart"))
return;
if (helper_is_help_or_usage(sep->arg[1])) {
c->Message(Chat::White, "usage: %s [list | destination] ([option: single])", sep->arg[0]);
helper_send_usage_required_bots(c, BCEnum::SpT_Depart);
return;
}
bool single = false;
std::string single_arg = sep->arg[2];
if (!single_arg.compare("single"))
single = true;
std::string destination = sep->arg[1];
if (!destination.compare("list")) {
Bot* my_druid_bot = ActionableBots::AsGroupMember_ByClass(c, c, Class::Druid);
Bot* my_wizard_bot = ActionableBots::AsGroupMember_ByClass(c, c, Class::Wizard);
helper_command_depart_list(c, my_druid_bot, my_wizard_bot, local_list, single);
return;
}
else if (destination.empty()) {
c->Message(Chat::White, "A [destination] or [list] argument is required to use this command");
return;
}
ActionableTarget::Types actionable_targets;
Bot* my_bot = nullptr;
std::list<Bot*> sbl;
MyBots::PopulateSBL_BySpawnedBots(c, sbl);
bool cast_success = false;
for (auto list_iter : *local_list) {
auto local_entry = list_iter->SafeCastToDepart();
if (helper_spell_check_fail(local_entry))
continue;
if (local_entry->single != single)
continue;
if (destination.compare(spells[local_entry->spell_id].teleport_zone))
continue;
auto target_mob = actionable_targets.Select(c, local_entry->target_type, FRIENDLY);
if (!target_mob)
continue;
my_bot = ActionableBots::Select_ByMinLevelAndClass(c, local_entry->target_type, sbl, local_entry->spell_level, local_entry->caster_class, target_mob);
if (!my_bot)
continue;
cast_success = helper_cast_standard_spell(my_bot, target_mob, local_entry->spell_id);
break;
}
helper_no_available_bots(c, my_bot);
}
+44
View File
@@ -0,0 +1,44 @@
#include "../bot_command.h"
void bot_command_escape(Client *c, const Seperator *sep)
{
bcst_list* local_list = &bot_command_spells[BCEnum::SpT_Escape];
if (helper_spell_list_fail(c, local_list, BCEnum::SpT_Escape) || helper_command_alias_fail(c, "bot_command_escape", sep->arg[0], "escape"))
return;
if (helper_is_help_or_usage(sep->arg[1])) {
c->Message(Chat::White, "usage: (<friendly_target>) %s ([option: lesser])", sep->arg[0]);
helper_send_usage_required_bots(c, BCEnum::SpT_Escape);
return;
}
bool use_lesser = false;
if (!strcasecmp(sep->arg[1], "lesser"))
use_lesser = true;
ActionableTarget::Types actionable_targets;
Bot* my_bot = nullptr;
std::list<Bot*> sbl;
MyBots::PopulateSBL_BySpawnedBots(c, sbl);
bool cast_success = false;
for (auto list_iter : *local_list) {
auto local_entry = list_iter->SafeCastToEscape();
if (helper_spell_check_fail(local_entry))
continue;
if (local_entry->lesser != use_lesser)
continue;
auto target_mob = actionable_targets.Select(c, local_entry->target_type, FRIENDLY);
if (!target_mob)
continue;
my_bot = ActionableBots::Select_ByMinLevelAndClass(c, local_entry->target_type, sbl, local_entry->spell_level, local_entry->caster_class, target_mob);
if (!my_bot)
continue;
cast_success = helper_cast_standard_spell(my_bot, target_mob, local_entry->spell_id);
break;
}
helper_no_available_bots(c, my_bot);
}
+42
View File
@@ -0,0 +1,42 @@
#include "../bot_command.h"
void bot_command_find_aliases(Client *c, const Seperator *sep)
{
if (helper_command_alias_fail(c, "bot_command_find_aliases", sep->arg[0], "findaliases"))
return;
if (helper_is_help_or_usage(sep->arg[1])) {
c->Message(Chat::White, "usage: %s [alias | command]", sep->arg[0]);
return;
}
auto find_iter = bot_command_aliases.find(sep->arg[1]);
if (find_iter == bot_command_aliases.end()) {
c->Message(Chat::White, "No bot commands or aliases match '%s'", sep->arg[1]);
return;
}
auto command_iter = bot_command_list.find(find_iter->second);
if (find_iter->second.empty() || command_iter == bot_command_list.end()) {
c->Message(Chat::White, "An unknown condition has occurred...");
return;
}
c->Message(Chat::White, "Available bot command aliases for '%s':", command_iter->first.c_str());
int bot_command_aliases_shown = 0;
for (auto alias_iter : bot_command_aliases) {
if (strcasecmp(find_iter->second.c_str(), alias_iter.second.c_str()) || c->Admin() < command_iter->second->access)
continue;
c->Message(
Chat::White,
fmt::format(
"^{}",
alias_iter.first
).c_str()
);
++bot_command_aliases_shown;
}
c->Message(Chat::White, "%d bot command alias%s listed.", bot_command_aliases_shown, bot_command_aliases_shown != 1 ? "es" : "");
}
+113
View File
@@ -0,0 +1,113 @@
#include "../bot_command.h"
void bot_command_follow(Client *c, const Seperator *sep)
{
if (helper_command_alias_fail(c, "bot_command_follow", sep->arg[0], "follow"))
return;
if (helper_is_help_or_usage(sep->arg[1])) {
c->Message(Chat::White, "usage: (<friendly_target>) %s ([option: reset]) [actionable: byname | ownergroup | ownerraid | namesgroup | healrotation | byclass | byrace | spawned]] ([actionable_name])", sep->arg[0]);
c->Message(Chat::White, "usage: %s chain", sep->arg[0]);
return;
}
const int ab_mask = ActionableBots::ABM_Type2;
bool reset = false;
int ab_arg = 1;
int name_arg = 2;
Mob* target_mob = nullptr;
std::string optional_arg = sep->arg[1];
if (!optional_arg.compare("chain")) {
auto chain_count = helper_bot_follow_option_chain(c);
c->Message(Chat::White, "%i of your bots %s now chain following you", chain_count, (chain_count == 1 ? "is" : "are"));
return;
}
else if (!optional_arg.compare("reset")) {
reset = true;
ab_arg = 2;
name_arg = 3;
}
else {
target_mob = ActionableTarget::VerifyFriendly(c, BCEnum::TT_Single);
if (!target_mob) {
c->Message(Chat::White, "You must <target> a friendly mob to use this command");
return;
}
}
std::string class_race_arg = sep->arg[ab_arg];
bool class_race_check = false;
if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) {
class_race_check = true;
}
std::list<Bot*> sbl;
if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[name_arg] : nullptr, class_race_check ? atoi(sep->arg[name_arg]) : 0) == ActionableBots::ABT_None) {
return;
}
sbl.remove(nullptr);
for (auto bot_iter : sbl) {
bot_iter->WipeHateList();
auto my_group = bot_iter->GetGroup();
if (my_group) {
if (reset) {
if (!my_group->GetLeader() || my_group->GetLeader() == bot_iter)
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;
bot_iter->GetPet()->WipeHateList();
bot_iter->GetPet()->SetFollowID(bot_iter->GetID());
}
if (sbl.size() == 1) {
Mob* follow_mob = entity_list.GetMob(sbl.front()->GetFollowID());
Bot::BotGroupSay(
sbl.front(),
fmt::format(
"Following {}.",
follow_mob ? follow_mob->GetCleanName() : "no one"
).c_str()
);
} else {
if (reset) {
c->Message(
Chat::White,
fmt::format(
"{} of your bots are following their default assignments.",
sbl.size()
).c_str()
);
} else {
c->Message(
Chat::White,
fmt::format(
"{} of your bots are following {}.",
sbl.size(),
target_mob->GetCleanName()
).c_str()
);
}
}
}
+1
View File
@@ -0,0 +1 @@
#include "../bot_command.h"
+60
View File
@@ -0,0 +1,60 @@
#include "../bot_command.h"
void bot_command_guard(Client *c, const Seperator *sep)
{
if (helper_command_alias_fail(c, "bot_command_guard", sep->arg[0], "guard")) {
return;
}
if (helper_is_help_or_usage(sep->arg[1])) {
c->Message(Chat::White, "usage: %s ([option: clear]) [actionable: byname | ownergroup | ownerraid | namesgroup | healrotation | byclass | byrace | 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::string class_race_arg = sep->arg[ab_arg];
bool class_race_check = false;
if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) {
class_race_check = true;
}
std::list<Bot*> sbl;
if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[name_arg] : nullptr, class_race_check ? atoi(sep->arg[name_arg]) : 0) == ActionableBots::ABT_None) {
return;
}
sbl.remove(nullptr);
for (auto bot_iter : sbl) {
if (clear) {
bot_iter->SetGuardFlag(false);
}
else {
bot_iter->SetGuardMode();
}
}
if (sbl.size() == 1) {
Bot::BotGroupSay(
sbl.front(),
fmt::format(
"{}uarding this position.",
clear ? "No longer g" : "G"
).c_str()
);
} else {
c->Message(Chat::White, "%i of your bots are %sguarding their positions.", sbl.size(), (clear ? "no longer " : ""));
}
}
File diff suppressed because it is too large Load Diff
+36
View File
@@ -0,0 +1,36 @@
#include "../bot_command.h"
void bot_command_help(Client *c, const Seperator *sep)
{
if (helper_command_alias_fail(c, "bot_command_help", sep->arg[0], "help"))
return;
c->Message(Chat::White, "Available EQEMu bot commands:");
int bot_commands_shown = 0;
for (auto command_iter : bot_command_list) {
if (sep->arg[1][0] && command_iter.first.find(sep->arg[1]) == std::string::npos)
continue;
if (c->Admin() < command_iter.second->access)
continue;
c->Message(
Chat::White,
fmt::format(
"^{} - {}",
command_iter.first,
command_iter.second->desc ? command_iter.second->desc : "No Description"
).c_str()
);
++bot_commands_shown;
}
if (parse->PlayerHasQuestSub(EVENT_BOT_COMMAND)) {
int i = parse->EventPlayer(EVENT_BOT_COMMAND, c, sep->msg, 0);
if (i >= 1) {
bot_commands_shown += i;
}
}
c->Message(Chat::White, "%d bot command%s listed.", bot_commands_shown, bot_commands_shown != 1 ? "s" : "");
c->Message(Chat::White, "type %ccommand [help | usage] for more information", BOT_COMMAND_CHAR);
}
+67
View File
@@ -0,0 +1,67 @@
#include "../bot_command.h"
void bot_command_hold(Client *c, const Seperator *sep)
{
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(Chat::White, "usage: %s ([option: clear]) [actionable: byname | ownergroup | ownerraid | namesgroup | healrotation | byclass | byrace | 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::string class_race_arg = sep->arg[ab_arg];
bool class_race_check = false;
if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) {
class_race_check = true;
}
std::list<Bot*> sbl;
if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[name_arg] : nullptr, class_race_check ? atoi(sep->arg[name_arg]) : 0) == ActionableBots::ABT_None) {
return;
}
sbl.remove(nullptr);
for (auto bot_iter : sbl) {
if (clear) {
bot_iter->SetHoldFlag(false);
}
else {
bot_iter->SetHoldMode();
}
}
if (sbl.size() == 1) {
Bot::BotGroupSay(
sbl.front(),
fmt::format(
"{}olding my attacks.",
clear ? "No longer h" : "H"
).c_str()
);
} else {
c->Message(
Chat::White,
fmt::format(
"{} of your bots are {}holding their attacks.",
sbl.size(),
clear ? "no longer " : ""
).c_str()
);
}
}
+38
View File
@@ -0,0 +1,38 @@
#include "../bot_command.h"
void bot_command_identify(Client *c, const Seperator *sep)
{
bcst_list* local_list = &bot_command_spells[BCEnum::SpT_Identify];
if (helper_spell_list_fail(c, local_list, BCEnum::SpT_Identify) || helper_command_alias_fail(c, "bot_command_identify", sep->arg[0], "identify"))
return;
if (helper_is_help_or_usage(sep->arg[1])) {
c->Message(Chat::White, "usage: (<friendly_target>) %s", sep->arg[0]);
helper_send_usage_required_bots(c, BCEnum::SpT_Identify);
return;
}
ActionableTarget::Types actionable_targets;
Bot* my_bot = nullptr;
std::list<Bot*> sbl;
MyBots::PopulateSBL_BySpawnedBots(c, sbl);
bool cast_success = false;
for (auto list_iter : *local_list) {
auto local_entry = list_iter;
if (helper_spell_check_fail(local_entry))
continue;
auto target_mob = actionable_targets.Select(c, local_entry->target_type, FRIENDLY);
if (!target_mob)
continue;
my_bot = ActionableBots::Select_ByMinLevelAndClass(c, local_entry->target_type, sbl, local_entry->spell_level, local_entry->caster_class, target_mob);
if (!my_bot)
continue;
cast_success = helper_cast_standard_spell(my_bot, target_mob, local_entry->spell_id);
break;
}
helper_no_available_bots(c, my_bot);
}
+1
View File
@@ -0,0 +1 @@
#include "../bot_command.h"
+338
View File
@@ -0,0 +1,338 @@
#include "../bot_command.h"
void bot_command_inventory(Client *c, const Seperator *sep)
{
std::list<const char*> subcommand_list;
subcommand_list.push_back("inventorygive");
subcommand_list.push_back("inventorylist");
subcommand_list.push_back("inventoryremove");
subcommand_list.push_back("inventorywindow");
if (helper_command_alias_fail(c, "bot_command_inventory", sep->arg[0], "inventory"))
return;
helper_send_available_subcommands(c, "bot inventory", subcommand_list);
}
void bot_command_inventory_give(Client* c, const Seperator* sep)
{
if (helper_command_alias_fail(c, "bot_command_inventory_give", sep->arg[0], "inventorygive")) {
return;
}
if (helper_is_help_or_usage(sep->arg[1])) {
c->Message(
Chat::White,
fmt::format(
"Usage: {} ([actionable: target | byname] ([actionable_name]))",
sep->arg[0]
).c_str()
);
return;
}
int ab_mask = (ActionableBots::ABM_Target | ActionableBots::ABM_ByName);
std::list<Bot*> sbl;
if (ActionableBots::PopulateSBL(c, sep->arg[1], sbl, ab_mask, sep->arg[2]) == ActionableBots::ABT_None) {
return;
}
auto my_bot = sbl.front();
if (!my_bot) {
c->Message(Chat::White, "ActionableBots returned 'nullptr'");
return;
}
my_bot->FinishTrade(c, Bot::BotTradeClientNoDropNoTrade);
}
void bot_command_inventory_list(Client* c, const Seperator* sep)
{
if (helper_command_alias_fail(c, "bot_command_inventory_list", sep->arg[0], "inventorylist")) {
return;
}
if (helper_is_help_or_usage(sep->arg[1])) {
c->Message(
Chat::White,
fmt::format(
"Usage: {} ([actionable: target | byname] ([actionable_name]))",
sep->arg[0]
).c_str()
);
return;
}
int ab_mask = (ActionableBots::ABM_Target | ActionableBots::ABM_ByName);
std::list<Bot*> sbl;
if (ActionableBots::PopulateSBL(c, sep->arg[1], sbl, ab_mask, sep->arg[2]) == ActionableBots::ABT_None) {
return;
}
auto my_bot = sbl.front();
if (!my_bot) {
c->Message(Chat::White, "ActionableBots returned 'nullptr'");
return;
}
const EQ::ItemInstance* inst = nullptr;
const EQ::ItemData * item = nullptr;
bool is_2h_weapon = false;
EQ::SayLinkEngine linker;
linker.SetLinkType(EQ::saylink::SayLinkItemInst);
uint32 inventory_count = 0;
for (uint16 slot_id = EQ::invslot::EQUIPMENT_BEGIN; slot_id <= EQ::invslot::EQUIPMENT_END; ++slot_id) {
if (slot_id == EQ::invslot::slotSecondary && is_2h_weapon) {
continue;
}
inst = my_bot->CastToBot()->GetBotItem(slot_id);
if (!inst || !inst->GetItem()) {
c->Message(
Chat::White,
fmt::format(
"Slot {} ({}) | Empty",
slot_id,
EQ::invslot::GetInvPossessionsSlotName(slot_id)
).c_str()
);
continue;
}
item = inst->GetItem();
if (slot_id == EQ::invslot::slotPrimary && item->IsType2HWeapon()) {
is_2h_weapon = true;
}
linker.SetItemInst(inst);
c->Message(
Chat::White,
fmt::format(
"Slot {} ({}) | {} | {}",
slot_id,
EQ::invslot::GetInvPossessionsSlotName(slot_id),
linker.GenerateLink(),
Saylink::Silent(
fmt::format("^inventoryremove {}", slot_id),
"Remove"
)
).c_str()
);
++inventory_count;
}
uint32 database_count = 0;
database.botdb.QueryInventoryCount(my_bot->GetBotID(), database_count);
if (inventory_count != database_count) {
c->Message(
Chat::White,
fmt::format(
"Inventory-database item count mismatch, inventory has {} item{} and the database has {} item{}.",
inventory_count,
inventory_count != 1 ? "s" : "",
database_count,
database_count != 1 ? "s" : ""
).c_str()
);
}
}
void bot_command_inventory_remove(Client* c, const Seperator* sep)
{
if (helper_command_alias_fail(c, "bot_command_inventory_remove", sep->arg[0], "inventoryremove")) {
return;
}
if (helper_is_help_or_usage(sep->arg[1])) {
c->Message(
Chat::White,
fmt::format(
"Usage: {} [Slot ID: 0-22] ([actionable: target | byname] ([actionable_name]))",
sep->arg[0]
).c_str()
);
return;
}
int ab_mask = (ActionableBots::ABM_Target | ActionableBots::ABM_ByName);
if (c->GetTradeskillObject() || (c->trade->state == Trading)) {
c->MessageString(Chat::Tell, MERCHANT_BUSY);
return;
}
std::list<Bot*> sbl;
if (ActionableBots::PopulateSBL(c, sep->arg[2], sbl, ab_mask, sep->arg[3]) == ActionableBots::ABT_None) {
return;
}
auto my_bot = sbl.front();
if (!my_bot) {
c->Message(Chat::White, "ActionableBots returned 'nullptr'");
return;
}
if (!sep->IsNumber(1)) {
c->Message(Chat::White, "Slot ID must be a number.");
return;
}
auto slot_id = static_cast<uint16>(Strings::ToUnsignedInt(sep->arg[1]));
if (slot_id > EQ::invslot::EQUIPMENT_END || slot_id < EQ::invslot::EQUIPMENT_BEGIN) {
c->Message(Chat::White, "Valid slots are 0 to 22.");
return;
}
auto* inst = my_bot->GetBotItem(slot_id);
if (!inst) {
std::string slot_message = "is";
switch (slot_id) {
case EQ::invslot::slotShoulders:
case EQ::invslot::slotArms:
case EQ::invslot::slotHands:
case EQ::invslot::slotLegs:
case EQ::invslot::slotFeet:
slot_message = "are";
break;
default:
break;
}
my_bot->OwnerMessage(
fmt::format(
"My {} (Slot {}) {} already unequipped.",
EQ::invslot::GetInvPossessionsSlotName(slot_id),
slot_id,
slot_message
)
);
return;
}
const auto* itm = inst->GetItem();
if (inst && itm && c->CheckLoreConflict(itm)) {
c->MessageString(Chat::White, PICK_LORE);
return;
}
for (int m = EQ::invaug::SOCKET_BEGIN; m <= EQ::invaug::SOCKET_END; ++m) {
EQ::ItemInstance* augment = inst->GetAugment(m);
if (!augment) {
continue;
}
if (!c->CheckLoreConflict(augment->GetItem())) {
continue;
}
c->MessageString(Chat::White, PICK_LORE);
return;
}
if (itm) {
EQ::SayLinkEngine linker;
linker.SetLinkType(EQ::saylink::SayLinkItemInst);
linker.SetItemInst(inst);
c->PushItemOnCursor(*inst, true);
if (
slot_id == EQ::invslot::slotRange ||
slot_id == EQ::invslot::slotAmmo
) {
my_bot->SetBotArcherySetting(false, true);
}
my_bot->RemoveBotItemBySlot(slot_id);
my_bot->BotRemoveEquipItem(slot_id);
my_bot->CalcBotStats(c->GetBotOption(Client::booStatsUpdate));
my_bot->OwnerMessage(
fmt::format(
"I have unequipped {} from my {} (Slot {}).",
linker.GenerateLink(),
EQ::invslot::GetInvPossessionsSlotName(slot_id),
slot_id
)
);
if (parse->BotHasQuestSub(EVENT_UNEQUIP_ITEM_BOT)) {
const auto& export_string = fmt::format(
"{} {}",
inst->IsStackable() ? inst->GetCharges() : 1,
slot_id
);
std::vector<std::any> args = { inst };
parse->EventBot(EVENT_UNEQUIP_ITEM_BOT, my_bot, nullptr, export_string, inst->GetID(), &args);
}
}
}
void bot_command_inventory_window(Client* c, const Seperator* sep)
{
if (helper_command_alias_fail(c, "bot_command_inventory_window", sep->arg[0], "inventorywindow")) {
return;
}
if (helper_is_help_or_usage(sep->arg[1])) {
c->Message(
Chat::White,
fmt::format(
"Usage: {} [actionable: target]",
sep->arg[0]
).c_str()
);
return;
}
int ab_mask = ActionableBots::ABM_Target;
std::list<Bot*> sbl;
if (ActionableBots::PopulateSBL(c, sep->arg[1], sbl, ab_mask, sep->arg[2]) == ActionableBots::ABT_None) {
return;
}
auto my_bot = sbl.front();
if (!my_bot) {
c->Message(Chat::White, "ActionableBots returned 'nullptr'");
return;
}
std::string window_title = fmt::format(
"{}'s Inventory",
my_bot->GetCleanName()
);
std::string window_text = "<table>";
for (uint16 slot_id = EQ::invslot::EQUIPMENT_BEGIN; slot_id <= EQ::invslot::EQUIPMENT_END; ++slot_id) {
const EQ::ItemData * item = nullptr;
const EQ::ItemInstance* inst = my_bot->CastToBot()->GetBotItem(slot_id);
if (inst) {
item = inst->GetItem();
}
window_text.append(
fmt::format(
"<tr><td>{}</td><td>{}{}</c></td></tr>",
EQ::invslot::GetInvPossessionsSlotName(slot_id),
item ? "<c \"#00FF00\">" : "<c \"#FFFF00\">",
item ? item->Name : "Empty"
)
);
}
window_text.append("</table>");
c->SendPopupToClient(
window_title.c_str(),
window_text.c_str()
);
}
+57
View File
@@ -0,0 +1,57 @@
#include "../bot_command.h"
void bot_command_invisibility(Client *c, const Seperator *sep)
{
bcst_list* local_list = &bot_command_spells[BCEnum::SpT_Invisibility];
if (helper_spell_list_fail(c, local_list, BCEnum::SpT_Invisibility) || helper_command_alias_fail(c, "bot_command_invisibility", sep->arg[0], "invisibility"))
return;
if (helper_is_help_or_usage(sep->arg[1])) {
c->Message(Chat::White, "usage: (<friendly_target>) %s [invisibility: living | undead | animal | see]", sep->arg[0]);
helper_send_usage_required_bots(c, BCEnum::SpT_Invisibility);
return;
}
std::string invisibility = sep->arg[1];
BCEnum::IType invisibility_type = BCEnum::IT_None;
if (!invisibility.compare("living"))
invisibility_type = BCEnum::IT_Living;
else if (!invisibility.compare("undead"))
invisibility_type = BCEnum::IT_Undead;
else if (!invisibility.compare("animal"))
invisibility_type = BCEnum::IT_Animal;
else if (!invisibility.compare("see"))
invisibility_type = BCEnum::IT_See;
if (invisibility_type == BCEnum::IT_None) {
c->Message(Chat::White, "You must specify an [invisibility]");
return;
}
ActionableTarget::Types actionable_targets;
Bot* my_bot = nullptr;
std::list<Bot*> sbl;
MyBots::PopulateSBL_BySpawnedBots(c, sbl);
bool cast_success = false;
for (auto list_iter : *local_list) {
auto local_entry = list_iter->SafeCastToInvisibility();
if (helper_spell_check_fail(local_entry))
continue;
if (local_entry->invis_type != invisibility_type)
continue;
auto target_mob = actionable_targets.Select(c, local_entry->target_type, FRIENDLY);
if (!target_mob)
continue;
my_bot = ActionableBots::Select_ByMinLevelAndClass(c, local_entry->target_type, sbl, local_entry->spell_level, local_entry->caster_class, target_mob);
if (!my_bot)
continue;
cast_success = helper_cast_standard_spell(my_bot, target_mob, local_entry->spell_id);
break;
}
helper_no_available_bots(c, my_bot);
}
+211
View File
@@ -0,0 +1,211 @@
#include "../bot_command.h"
void bot_command_item_use(Client* c, const Seperator* sep)
{
if (helper_is_help_or_usage(sep->arg[1])) {
c->Message(Chat::White, "usage: [%s empty] will display only bots that can use the item in an empty slot.", sep->arg[0]);
c->Message(Chat::White, "usage: [%s byclass classID] - Example: [%s byclass 7] will display only bots that match the class that can use the item. Example is a Monk, use [^create help] for a list of class IDs.", sep->arg[0], sep->arg[0]);
c->Message(Chat::White, "usage: [%s casteronly] will display only caster bots that can use the item.", sep->arg[0]);
c->Message(Chat::White, "usage: [%s hybridonly] will display only hybrid bots that can use the item.", sep->arg[0]);
c->Message(Chat::White, "usage: [%s meleeonly] will display only melee bots that can use the item.", sep->arg[0]);
c->Message(Chat::White, "usage: [%s wiscasteronly] will display only Wisdom-based Caster bots that can use the item.", sep->arg[0]);
c->Message(Chat::White, "usage: [%s intcasteronly] will display only Intelligence-based Caster bots that can use the item.", sep->arg[0]);
c->Message(Chat::White, "usage: [%s plateonly] will display only Plate-wearing bots that can use the item.", sep->arg[0]);
c->Message(Chat::White, "usage: [%s chainonly] will display only Chain-wearing bots that can use the item.", sep->arg[0]);
c->Message(Chat::White, "usage: [%s leatheronly] will display only Leather-wearing bots that can use the item.", sep->arg[0]);
c->Message(Chat::White, "usage: [%s clothonly] will display only Cloth-wearing bots that can use the item.", sep->arg[0]);
return;
}
bool empty_only = false;
int8 class_mask = 0;
bool caster_only = false;
bool hybrid_only = false;
bool melee_only = false;
bool wis_caster_only = false;
bool int_caster_only = false;
bool plate_only = false;
bool chain_only = false;
bool leather_only = false;
bool cloth_only = false;
std::string arg1 = sep->arg[1];
std::string arg2 = sep->arg[2];
if (arg1.compare("empty") == 0) {
empty_only = true;
}
else if (arg1.compare("byclass") == 0) {
if (Strings::IsNumber(sep->arg[2])) {
class_mask = Strings::ToUnsignedInt(sep->arg[2]);
if (!(class_mask >= Class::Warrior && class_mask <= Class::Berserker)) {
c->Message(Chat::White, "Invalid class range, you must choose between 1 (Warrior) and 15 (Beastlord)");
return;
}
}
}
else if (arg1.compare("casteronly") == 0) {
caster_only = true;
}
else if (arg1.compare("hybridonly") == 0) {
hybrid_only = true;
}
else if (arg1.compare("meleeonly") == 0) {
melee_only = true;
}
else if (arg1.compare("wiscasteronly") == 0) {
wis_caster_only = true;
}
else if (arg1.compare("intcasteronly") == 0) {
int_caster_only = true;
}
else if (arg1.compare("plateonly") == 0) {
plate_only = true;
}
else if (arg1.compare("chainonly") == 0) {
chain_only = true;
}
else if (arg1.compare("leatheronly") == 0) {
leather_only = true;
}
else if (arg1.compare("clothonly") == 0) {
cloth_only = true;
}
else if (!arg1.empty()) {
c->Message(Chat::White, "Please choose the correct subtype. For help use %s help.", sep->arg[0]);
return;
}
const auto item_instance = c->GetInv().GetItem(EQ::invslot::slotCursor);
if (!item_instance) {
c->Message(Chat::White, "No item found on cursor!");
return;
}
auto item_data = item_instance->GetItem();
if (!item_data) {
c->Message(Chat::White, "No data found for cursor item!");
return;
}
if (item_data->ItemClass != EQ::item::ItemClassCommon || item_data->Slots == 0) {
c->Message(Chat::White, "'%s' is not an equipable item!", item_data->Name);
return;
}
std::vector<int16> equipable_slot_list;
for (int16 equipable_slot = EQ::invslot::EQUIPMENT_BEGIN; equipable_slot <= EQ::invslot::EQUIPMENT_END; ++equipable_slot) {
if (item_data->Slots & (1 << equipable_slot)) {
equipable_slot_list.emplace_back(equipable_slot);
}
}
EQ::SayLinkEngine linker;
linker.SetLinkType(EQ::saylink::SayLinkItemInst);
std::list<Bot*> sbl;
MyBots::PopulateSBL_BySpawnedBots(c, sbl);
if (class_mask) {
ActionableBots::Filter_ByClasses(c, sbl, GetPlayerClassBit(class_mask));
}
for (const auto& bot_iter : sbl) {
if (!bot_iter) {
continue;
}
if (caster_only && !IsCasterClass(bot_iter->GetClass())) {
continue;
}
if (hybrid_only && !IsSpellFighterClass(bot_iter->GetClass())) {
continue;
}
if (melee_only && !IsNonSpellFighterClass(bot_iter->GetClass())) {
continue;
}
if (wis_caster_only && !IsWISCasterClass(bot_iter->GetClass())) {
continue;
}
if (int_caster_only && !IsINTCasterClass(bot_iter->GetClass())) {
continue;
}
if (plate_only && !IsPlateClass(bot_iter->GetClass())) {
continue;
}
if (chain_only && !IsChainClass(bot_iter->GetClass())) {
continue;
}
if (leather_only && !IsLeatherClass(bot_iter->GetClass())) {
continue;
}
if (cloth_only && !IsClothClass(bot_iter->GetClass())) {
continue;
}
if (((~item_data->Races) & GetPlayerRaceBit(bot_iter->GetRace())) || ((~item_data->Classes) & GetPlayerClassBit(bot_iter->GetClass()))) {
continue;
}
for (const auto& slot_iter : equipable_slot_list) {
// needs more failure criteria - this should cover the bulk for now
if (slot_iter == EQ::invslot::slotSecondary && item_data->Damage && !bot_iter->CanThisClassDualWield()) {
continue;
}
auto equipped_item = bot_iter->GetInv()[slot_iter];
if (equipped_item && !empty_only) {
linker.SetItemInst(equipped_item);
c->Message(
Chat::Say,
fmt::format(
"{} says, 'I can use that for my {} instead of my {}! Would you like to {} my {}?'",
Saylink::Silent(
fmt::format(
"^inventorygive byname {}",
bot_iter->GetCleanName()
),
bot_iter->GetCleanName()
),
EQ::invslot::GetInvPossessionsSlotName(slot_iter),
linker.GenerateLink(),
Saylink::Silent(
fmt::format(
"^inventoryremove {} byname {}",
slot_iter,
bot_iter->GetCleanName()
),
"remove"
),
linker.GenerateLink()
).c_str()
);
bot_iter->DoAnim(29);
}
else if (!equipped_item) {
c->Message(
Chat::Say,
fmt::format(
"{} says, 'I can use that for my {}! Would you like to {} it to me?'",
Saylink::Silent(
fmt::format(
"^inventorygive byname {}",
bot_iter->GetCleanName()
),
bot_iter->GetCleanName()
),
EQ::invslot::GetInvPossessionsSlotName(slot_iter),
Saylink::Silent(
fmt::format(
"^inventorygive byname {}",
bot_iter->GetCleanName()
),
"give"
)
).c_str()
);
bot_iter->DoAnim(29);
}
}
}
}
+38
View File
@@ -0,0 +1,38 @@
#include "../bot_command.h"
void bot_command_levitation(Client *c, const Seperator *sep)
{
bcst_list* local_list = &bot_command_spells[BCEnum::SpT_Levitation];
if (helper_spell_list_fail(c, local_list, BCEnum::SpT_Levitation) || helper_command_alias_fail(c, "bot_command_levitation", sep->arg[0], "levitation"))
return;
if (helper_is_help_or_usage(sep->arg[1])) {
c->Message(Chat::White, "usage: (<friendly_target>) %s", sep->arg[0]);
helper_send_usage_required_bots(c, BCEnum::SpT_Levitation);
return;
}
ActionableTarget::Types actionable_targets;
Bot* my_bot = nullptr;
std::list<Bot*> sbl;
MyBots::PopulateSBL_BySpawnedBots(c, sbl);
bool cast_success = false;
for (auto list_iter : *local_list) {
auto local_entry = list_iter;
if (helper_spell_check_fail(local_entry))
continue;
auto target_mob = actionable_targets.Select(c, local_entry->target_type, FRIENDLY);
if (!target_mob)
continue;
my_bot = ActionableBots::Select_ByMinLevelAndClass(c, local_entry->target_type, sbl, local_entry->spell_level, local_entry->caster_class, target_mob);
if (!my_bot)
continue;
cast_success = helper_cast_standard_spell(my_bot, target_mob, local_entry->spell_id);
break;
}
helper_no_available_bots(c, my_bot);
}
+1
View File
@@ -0,0 +1 @@
#include "../bot_command.h"
+43
View File
@@ -0,0 +1,43 @@
#include "../bot_command.h"
void bot_command_lull(Client *c, const Seperator *sep)
{
bcst_list* local_list = &bot_command_spells[BCEnum::SpT_Lull];
if (helper_spell_list_fail(c, local_list, BCEnum::SpT_Lull) || helper_command_alias_fail(c, "bot_command_lull", sep->arg[0], "lull"))
return;
if (helper_is_help_or_usage(sep->arg[1])) {
c->Message(Chat::White, "usage: <enemy_target> %s", sep->arg[0]);
helper_send_usage_required_bots(c, BCEnum::SpT_Lull);
return;
}
ActionableTarget::Types actionable_targets;
Bot* my_bot = nullptr;
std::list<Bot*> sbl;
MyBots::PopulateSBL_BySpawnedBots(c, sbl);
for (auto list_iter : *local_list) {
auto local_entry = list_iter;
if (helper_spell_check_fail(local_entry))
continue;
auto target_mob = actionable_targets.Select(c, local_entry->target_type, ENEMY);
if (!target_mob)
continue;
//if (spells[local_entry->spell_id].max[EFFECTIDTOINDEX(3)] && spells[local_entry->spell_id].max[EFFECTIDTOINDEX(3)] < target_mob->GetLevel())
// continue;
my_bot = ActionableBots::Select_ByMinLevelAndClass(c, local_entry->target_type, sbl, local_entry->spell_level, local_entry->caster_class, target_mob);
if (!my_bot)
continue;
uint32 dont_root_before = 0;
if (helper_cast_standard_spell(my_bot, target_mob, local_entry->spell_id, true, &dont_root_before))
target_mob->SetDontRootMeBefore(dont_root_before);
break;
}
helper_no_available_bots(c, my_bot);
}
+43
View File
@@ -0,0 +1,43 @@
#include "../bot_command.h"
void bot_command_mesmerize(Client *c, const Seperator *sep)
{
bcst_list* local_list = &bot_command_spells[BCEnum::SpT_Mesmerize];
if (helper_spell_list_fail(c, local_list, BCEnum::SpT_Mesmerize) || helper_command_alias_fail(c, "bot_command_mesmerize", sep->arg[0], "mesmerize"))
return;
if (helper_is_help_or_usage(sep->arg[1])) {
c->Message(Chat::White, "usage: <enemy_target> %s", sep->arg[0]);
helper_send_usage_required_bots(c, BCEnum::SpT_Mesmerize);
return;
}
ActionableTarget::Types actionable_targets;
Bot* my_bot = nullptr;
std::list<Bot*> sbl;
MyBots::PopulateSBL_BySpawnedBots(c, sbl);
for (auto list_iter : *local_list) {
auto local_entry = list_iter;
if (helper_spell_check_fail(local_entry))
continue;
auto target_mob = actionable_targets.Select(c, local_entry->target_type, ENEMY);
if (!target_mob)
continue;
if (spells[local_entry->spell_id].max_value[EFFECTIDTOINDEX(1)] < target_mob->GetLevel())
continue;
my_bot = ActionableBots::Select_ByMinLevelAndClass(c, local_entry->target_type, sbl, local_entry->spell_level, local_entry->caster_class, target_mob);
if (!my_bot)
continue;
uint32 dont_root_before = 0;
if (helper_cast_standard_spell(my_bot, target_mob, local_entry->spell_id, true, &dont_root_before))
target_mob->SetDontRootMeBefore(dont_root_before);
break;
}
helper_no_available_bots(c, my_bot);
}
+50
View File
@@ -0,0 +1,50 @@
#include "../bot_command.h"
void bot_command_movement_speed(Client *c, const Seperator *sep)
{
bcst_list* local_list = &bot_command_spells[BCEnum::SpT_MovementSpeed];
if (helper_spell_list_fail(c, local_list, BCEnum::SpT_MovementSpeed) || helper_command_alias_fail(c, "bot_command_movement_speed", sep->arg[0], "movementspeed"))
return;
if (helper_is_help_or_usage(sep->arg[1])) {
c->Message(Chat::White, "usage: (<friendly_target>) %s ([group | sow])", sep->arg[0]);
helper_send_usage_required_bots(c, BCEnum::SpT_MovementSpeed);
return;
}
bool group = false;
bool sow = false;
std::string arg1 = sep->arg[1];
if (!arg1.compare("group"))
group = true;
else if (!arg1.compare("sow"))
sow = true;
ActionableTarget::Types actionable_targets;
Bot* my_bot = nullptr;
std::list<Bot*> sbl;
MyBots::PopulateSBL_BySpawnedBots(c, sbl);
bool cast_success = false;
for (auto list_iter : *local_list) {
auto local_entry = list_iter->SafeCastToMovementSpeed();
if (helper_spell_check_fail(local_entry))
continue;
if (!sow && (local_entry->group != group))
continue;
if (sow && (local_entry->spell_id != 278)) // '278' = single-target "Spirit of Wolf"
continue;
auto target_mob = actionable_targets.Select(c, local_entry->target_type, FRIENDLY);
if (!target_mob)
continue;
my_bot = ActionableBots::Select_ByMinLevelAndClass(c, local_entry->target_type, sbl, local_entry->spell_level, local_entry->caster_class, target_mob);
if (!my_bot)
continue;
cast_success = helper_cast_standard_spell(my_bot, target_mob, local_entry->spell_id);
break;
}
helper_no_available_bots(c, my_bot);
}
+76
View File
@@ -0,0 +1,76 @@
#include "../bot_command.h"
void bot_command_surname(Client *c, const Seperator *sep)
{
if (sep->arg[1][0] == '\0' || sep->IsNumber(1)) {
c->Message(Chat::White, "You must specify a [surname] to use this command (use _ to define spaces or -remove to clear.)");
return;
}
auto my_bot = ActionableBots::AsTarget_ByBot(c);
if (!my_bot) {
c->Message(Chat::White, "You must <target> a bot that you own to use this command");
return;
}
if (strlen(sep->arg[1]) > 31) {
c->Message(Chat::White, "Surname must be 31 characters or less.");
return;
}
std::string bot_surname = sep->arg[1];
bot_surname = (bot_surname == "-remove") ? "" : bot_surname;
std::replace(bot_surname.begin(), bot_surname.end(), '_', ' ');
my_bot->SetSurname(bot_surname);
if (database.botdb.SaveBot(my_bot)) {
c->Message(Chat::White, "Bot Surname Saved.");
}
}
void bot_command_title(Client *c, const Seperator *sep)
{
if (sep->arg[1][0] == '\0' || sep->IsNumber(1)) {
c->Message(Chat::White, "You must specify a [title] to use this command. (use _ to define spaces or -remove to clear.)");
return;
}
auto my_bot = ActionableBots::AsTarget_ByBot(c);
if (!my_bot) {
c->Message(Chat::White, "You must <target> a bot that you own to use this command");
return;
}
if (strlen(sep->arg[1]) > 31) {
c->Message(Chat::White, "Title must be 31 characters or less.");
return;
}
std::string bot_title = sep->arg[1];
bot_title = (bot_title == "-remove") ? "" : bot_title;
std::replace(bot_title.begin(), bot_title.end(), '_', ' ');
my_bot->SetTitle(bot_title);
if (database.botdb.SaveBot(my_bot)) {
c->Message(Chat::White, "Bot Title Saved.");
}
}
void bot_command_suffix(Client *c, const Seperator *sep)
{
if (sep->arg[1][0] == '\0' || sep->IsNumber(1)) {
c->Message(Chat::White, "You must specify a [suffix] to use this command. (use _ to define spaces or -remove to clear.)");
return;
}
auto my_bot = ActionableBots::AsTarget_ByBot(c);
if (!my_bot) {
c->Message(Chat::White, "You must <target> a bot that you own to use this command");
return;
}
if (strlen(sep->arg[1]) > 31) {
c->Message(Chat::White, "Suffix must be 31 characters or less.");
return;
}
std::string bot_suffix = sep->arg[1];
bot_suffix = (bot_suffix == "-remove") ? "" : bot_suffix;
std::replace(bot_suffix.begin(), bot_suffix.end(), '_', ' ');
my_bot->SetSuffix(bot_suffix);
if (database.botdb.SaveBot(my_bot)) {
c->Message(Chat::White, "Bot Suffix Saved.");
}
}
+1
View File
@@ -0,0 +1 @@
#include "../bot_command.h"
+315
View File
@@ -0,0 +1,315 @@
#include "../bot_command.h"
void bot_command_owner_option(Client *c, const Seperator *sep)
{
if (helper_is_help_or_usage(sep->arg[1])) {
c->Message(Chat::White, "usage: %s [option] [argument]", sep->arg[0]);
std::string window_title = "Bot Owner Options";
std::string window_text =
"<table>"
"<tr>"
"<td><c \"#FFFFFF\">Option<br>------</td>"
"<td><c \"#00FF00\">Argument<br>-------</td>"
"<td><c \"#AAAAAA\">Notes<br>-----</td>"
"</tr>"
"<tr>"
"<td><c \"#CCCCCC\">deathmarquee</td>"
"<td><c \"#00CC00\">enable <c \"#CCCCCC\">| <c \"#00CC00\">disable</td>"
"<td><c \"#888888\">marquee message on death</td>"
"</tr>"
"<tr>"
"<td></td>"
"<td><c \"#00CCCC\">null</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>"
"<td></td>"
"<td><c \"#00CCCC\">null</td>"
"<td><c \"#888888\">(toggles)</td>"
"</tr>"
"<tr>"
"<td><c \"#CCCCCC\">spawnmessage</td>"
"<td><c \"#00CC00\">say <c \"#CCCCCC\">| <c \"#00CC00\">tell <c \"#CCCCCC\">| <c \"#00CC00\">silent</td>"
"<td><c \"#888888\">spawn message into channel</td>"
"</tr>"
"<tr>"
"<td></td>"
"<td><c \"#00CC00\">class <c \"#CCCCCC\">| <c \"#00CC00\">default</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>"
"<td></td>"
"<td><c \"#00CCCC\">null</td>"
"<td><c \"#888888\">(toggles)</td>"
"</tr>"
"<tr>"
"<td><c \"#CCCCCC\">autodefend</td>"
"<td><c \"#00CC00\">enable <c \"#CCCCCC\">| <c \"#00CC00\">disable</td>"
"<td><c \"#888888\">bots defend owner when aggroed</td>"
"</tr>"
"<tr>"
"<td></td>"
"<td><c \"#00CCCC\">null</td>"
"<td><c \"#888888\">(toggles)</td>"
"</tr>"
"<tr>"
"<td><c \"#CCCCCC\">buffcounter</td>"
"<td><c \"#00CC00\">enable <c \"#CCCCCC\">| <c \"#00CC00\">disable</td>"
"<td><c \"#888888\">marquee message on buff counter change</td>"
"</tr>"
"<tr>"
"<td></td>"
"<td><c \"#00CCCC\">null</td>"
"<td><c \"#888888\">(toggles)</td>"
"</tr>"
"<tr>"
"<td><c \"#CCCCCC\">monkwumessage</td>"
"<td><c \"#00CC00\">enable <c \"#CCCCCC\">| <c \"#00CC00\">disable</td>"
"<td><c \"#888888\">displays monk wu trigger messages</td>"
"</tr>"
"<tr>"
"<td></td>"
"<td><c \"#00CCCC\">null</td>"
"<td><c \"#888888\">(toggles)</td>"
"</tr>"
"<tr>"
"<td><c \"#CCCCCC\">current</td>"
"<td></td>"
"<td><c \"#888888\">show current settings</td>"
"</tr>"
"</table>";
c->SendPopupToClient(window_title.c_str(), window_text.c_str());
return;
}
std::string owner_option(sep->arg[1]);
std::string argument(sep->arg[2]);
if (!owner_option.compare("deathmarquee")) {
if (!argument.compare("enable")) {
c->SetBotOption(Client::booDeathMarquee, true);
}
else if (!argument.compare("disable")) {
c->SetBotOption(Client::booDeathMarquee, false);
}
else {
c->SetBotOption(Client::booDeathMarquee, !c->GetBotOption(Client::booDeathMarquee));
}
database.botdb.SaveOwnerOption(c->CharacterID(), Client::booDeathMarquee, c->GetBotOption(Client::booDeathMarquee));
c->Message(Chat::White, "Bot 'death marquee' is now %s.", (c->GetBotOption(Client::booDeathMarquee) ? "enabled" : "disabled"));
}
else if (!owner_option.compare("statsupdate")) {
if (!argument.compare("enable")) {
c->SetBotOption(Client::booStatsUpdate, true);
}
else if (!argument.compare("disable")) {
c->SetBotOption(Client::booStatsUpdate, false);
}
else {
c->SetBotOption(Client::booStatsUpdate, !c->GetBotOption(Client::booStatsUpdate));
}
database.botdb.SaveOwnerOption(c->CharacterID(), Client::booStatsUpdate, c->GetBotOption(Client::booStatsUpdate));
c->Message(Chat::White, "Bot 'stats update' is now %s.", (c->GetBotOption(Client::booStatsUpdate) ? "enabled" : "disabled"));
}
else if (!owner_option.compare("spawnmessage")) {
Client::BotOwnerOption boo = Client::_booCount;
if (!argument.compare("say")) {
boo = Client::booSpawnMessageSay;
c->SetBotOption(Client::booSpawnMessageSay, true);
c->SetBotOption(Client::booSpawnMessageTell, false);
}
else if (!argument.compare("tell")) {
boo = Client::booSpawnMessageSay;
c->SetBotOption(Client::booSpawnMessageSay, false);
c->SetBotOption(Client::booSpawnMessageTell, true);
}
else if (!argument.compare("silent")) {
boo = Client::booSpawnMessageSay;
c->SetBotOption(Client::booSpawnMessageSay, false);
c->SetBotOption(Client::booSpawnMessageTell, false);
}
else if (!argument.compare("class")) {
boo = Client::booSpawnMessageClassSpecific;
c->SetBotOption(Client::booSpawnMessageClassSpecific, true);
}
else if (!argument.compare("default")) {
boo = Client::booSpawnMessageClassSpecific;
c->SetBotOption(Client::booSpawnMessageClassSpecific, false);
}
else {
c->Message(Chat::White, "Owner option '%s' argument '%s' is not recognized.", owner_option.c_str(), argument.c_str());
return;
}
if (boo == Client::booSpawnMessageSay) {
database.botdb.SaveOwnerOption(
c->CharacterID(),
std::pair<size_t, size_t>(
Client::booSpawnMessageSay,
Client::booSpawnMessageTell
),
std::pair<bool, bool>(
c->GetBotOption(Client::booSpawnMessageSay),
c->GetBotOption(Client::booSpawnMessageTell)
)
);
}
else if (boo == Client::booSpawnMessageClassSpecific) {
database.botdb.SaveOwnerOption(
c->CharacterID(),
Client::booSpawnMessageClassSpecific,
c->GetBotOption(Client::booSpawnMessageClassSpecific)
);
}
else {
c->Message(Chat::White, "Bot 'spawn message' is now ERROR.");
return;
}
c->Message(Chat::White, "Bot 'spawn message' is now %s.", argument.c_str());
}
else if (!owner_option.compare("altcombat")) {
if (RuleB(Bots, AllowOwnerOptionAltCombat)) {
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(Chat::White, "Bot 'alt combat' is now %s.", (c->GetBotOption(Client::booAltCombat) ? "enabled" : "disabled"));
}
else {
c->Message(Chat::White, "Bot owner option 'altcombat' is not allowed on this server.");
}
}
else if (!owner_option.compare("autodefend")) {
if (RuleB(Bots, AllowOwnerOptionAutoDefend)) {
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(Chat::White, "Bot 'auto defend' is now %s.", (c->GetBotOption(Client::booAutoDefend) ? "enabled" : "disabled"));
}
else {
c->Message(Chat::White, "Bot owner option 'autodefend' is not allowed on this server.");
}
}
else if (!owner_option.compare("buffcounter")) {
if (!argument.compare("enable")) {
c->SetBotOption(Client::booBuffCounter, true);
}
else if (!argument.compare("disable")) {
c->SetBotOption(Client::booBuffCounter, false);
}
else {
c->SetBotOption(Client::booBuffCounter, !c->GetBotOption(Client::booBuffCounter));
}
database.botdb.SaveOwnerOption(c->CharacterID(), Client::booBuffCounter, c->GetBotOption(Client::booBuffCounter));
c->Message(Chat::White, "Bot 'buff counter' is now %s.", (c->GetBotOption(Client::booBuffCounter) ? "enabled" : "disabled"));
}
else if (!owner_option.compare("monkwumessage")) {
if (!argument.compare("enable")) {
c->SetBotOption(Client::booMonkWuMessage, true);
}
else if (!argument.compare("disable")) {
c->SetBotOption(Client::booMonkWuMessage, false);
}
else {
c->SetBotOption(Client::booMonkWuMessage, !c->GetBotOption(Client::booMonkWuMessage));
}
database.botdb.SaveOwnerOption(c->CharacterID(), Client::booMonkWuMessage, c->GetBotOption(Client::booMonkWuMessage));
c->Message(
Chat::White,
"Bot 'monk wu message' is now %s.",
(c->GetBotOption(Client::booMonkWuMessage) ? "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>"
"<tr>" "<td><c \"#CCCCCC\">buffcounter</td>" "<td><c \"#00CC00\">{}</td>" "</tr>"
"<tr>" "<td><c \"#CCCCCC\">monkwumessage</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"),
(RuleB(Bots, AllowOwnerOptionAltCombat) ? (c->GetBotOption(Client::booAltCombat) ? "enabled" : "disabled") : "restricted"),
(RuleB(Bots, AllowOwnerOptionAutoDefend) ? (c->GetBotOption(Client::booAutoDefend) ? "enabled" : "disabled") : "restricted"),
(c->GetBotOption(Client::booBuffCounter) ? "enabled" : "disabled"),
(c->GetBotOption(Client::booMonkWuMessage) ? "enabled" : "disabled")
);
c->SendPopupToClient(window_title.c_str(), window_text.c_str());
}
else {
c->Message(Chat::White, "Owner option '%s' is not recognized.", owner_option.c_str());
}
}
+168
View File
@@ -0,0 +1,168 @@
#include "../bot_command.h"
void bot_command_pet(Client *c, const Seperator *sep)
{
std::list<const char*> subcommand_list;
subcommand_list.push_back("petgetlost");
subcommand_list.push_back("petremove");
subcommand_list.push_back("petsettype");
if (helper_command_alias_fail(c, "bot_command_pet", sep->arg[0], "pet"))
return;
helper_send_available_subcommands(c, "bot pet", subcommand_list);
}
void bot_command_pet_get_lost(Client *c, const Seperator *sep)
{
if (helper_command_alias_fail(c, "bot_command_pet_get_lost", sep->arg[0], "petgetlost"))
return;
if (helper_is_help_or_usage(sep->arg[1])) {
c->Message(Chat::White, "usage: %s ([actionable: target | byname | ownergroup | targetgroup | namesgroup | healrotation | spawned] ([actionable_name]))", sep->arg[0]);
return;
}
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;
int summoned_pet = 0;
for (auto bot_iter : sbl) {
if (!bot_iter->GetPet() || bot_iter->GetPet()->IsCharmed())
continue;
bot_iter->GetPet()->SayString(PET_GETLOST_STRING);
bot_iter->GetPet()->Depop(false);
bot_iter->SetPetID(0);
database.botdb.DeletePetItems(bot_iter->GetBotID());
database.botdb.DeletePetBuffs(bot_iter->GetBotID());
database.botdb.DeletePetStats(bot_iter->GetBotID());
++summoned_pet;
}
c->Message(Chat::White, "%i of your bots released their summoned pet%s", summoned_pet, (summoned_pet == 1) ? "" : "s");
}
void bot_command_pet_remove(Client *c, const Seperator *sep)
{
if (helper_command_alias_fail(c, "bot_command_pet_remove", sep->arg[0], "petremove"))
return;
if (helper_is_help_or_usage(sep->arg[1])) {
c->Message(Chat::White, "usage: %s ([actionable: target | byname] ([actionable_name]))", sep->arg[0]);
return;
}
int ab_mask = (ActionableBots::ABM_Target | ActionableBots::ABM_ByName);
std::list<Bot*> sbl;
if (ActionableBots::PopulateSBL(c, sep->arg[1], sbl, ab_mask, sep->arg[2]) == ActionableBots::ABT_None)
return;
uint16 class_mask = (player_class_bitmasks[Class::Druid] | player_class_bitmasks[Class::Necromancer] | player_class_bitmasks[Class::Enchanter]);
ActionableBots::Filter_ByClasses(c, sbl, class_mask);
if (sbl.empty()) {
c->Message(Chat::White, "You have no spawned bots capable of charming");
return;
}
sbl.remove(nullptr);
int charmed_pet = 0;
int summoned_pet = 0;
for (auto bot_iter : sbl) { // Probably needs some work to release charmed pets
if (bot_iter->IsBotCharmer()) {
bot_iter->SetBotCharmer(false);
if (sbl.size() == 1)
Bot::BotGroupSay(bot_iter, "Using a summoned pet");
++summoned_pet;
continue;
}
if (bot_iter->GetPet()) {
bot_iter->GetPet()->SayString(PET_GETLOST_STRING);
bot_iter->GetPet()->Depop(false);
bot_iter->SetPetID(0);
}
bot_iter->SetBotCharmer(true);
if (sbl.size() == 1)
Bot::BotGroupSay(bot_iter, "Available for Charming");
++charmed_pet;
}
if (sbl.size() != 1)
c->Message(Chat::White, "%i of your bots set for charming, %i of your bots set for summoned pet use", charmed_pet, summoned_pet);
}
void bot_command_pet_set_type(Client *c, const Seperator *sep)
{
if (helper_command_alias_fail(c, "bot_command_pet_set_type", sep->arg[0], "petsettype"))
return;
if (helper_is_help_or_usage(sep->arg[1])) {
c->Message(Chat::White, "usage: %s [type: water | fire | air | earth | monster] ([actionable: target | byname] ([actionable_name]))", sep->arg[0]);
c->Message(Chat::White, "requires one of the following bot classes:");
c->Message(Chat::White, "Magician(1)");
return;
}
int ab_mask = (ActionableBots::ABM_Target | ActionableBots::ABM_ByName); // this can be expanded without code modification
std::string pet_arg = sep->arg[1];
uint8 pet_type = 255;
uint8 level_req = 255;
if (!pet_arg.compare("water")) {
pet_type = 0;
level_req = 1;
}
else if (!pet_arg.compare("fire")) {
pet_type = 1;
level_req = 3;
}
else if (!pet_arg.compare("air")) {
pet_type = 2;
level_req = 4;
}
else if (!pet_arg.compare("earth")) {
pet_type = 3;
level_req = 5;
}
else if (!pet_arg.compare("monster")) {
pet_type = 4;
level_req = 30;
}
if (pet_type == 255) {
c->Message(Chat::White, "You must specify a pet [type: water | fire | air | earth | monster]");
return;
}
std::list<Bot*> sbl;
if (ActionableBots::PopulateSBL(c, sep->arg[2], sbl, ab_mask, sep->arg[3]) == ActionableBots::ABT_None)
return;
uint16 class_mask = player_class_bitmasks[Class::Magician];
ActionableBots::Filter_ByClasses(c, sbl, class_mask);
if (sbl.empty()) {
c->Message(Chat::White, "You have no spawned Magician bots");
return;
}
ActionableBots::Filter_ByMinLevel(c, sbl, level_req);
if (sbl.empty()) {
c->Message(Chat::White, "You have no spawned Magician bots capable of using this pet type: '%s'", pet_arg.c_str());
return;
}
uint16 reclaim_energy_id = 331;
for (auto bot_iter : sbl) {
if (!bot_iter)
continue;
bot_iter->SetPetChooser(true);
bot_iter->SetPetChooserID(pet_type);
if (bot_iter->GetPet()) {
auto pet_id = bot_iter->GetPetID();
bot_iter->SetPetID(0);
bot_iter->CastSpell(reclaim_energy_id, pet_id);
}
}
}
+58
View File
@@ -0,0 +1,58 @@
#include "../bot_command.h"
void bot_command_pick_lock(Client *c, const Seperator *sep)
{
if (helper_command_alias_fail(c, "bot_command_pick_lock", sep->arg[0], "picklock"))
return;
if (helper_is_help_or_usage(sep->arg[1])) {
c->Message(Chat::White, "usage: %s", sep->arg[0]);
c->Message(Chat::White, "requires one of the following bot classes:");
c->Message(Chat::White, "Rogue(5) or Bard(40)");
return;
}
std::list<Bot*> sbl;
MyBots::PopulateSBL_BySpawnedBots(c, sbl);
float pick_lock_value = 0.0f;
ActionableBots::Filter_ByHighestPickLock(c, sbl, pick_lock_value);
if (sbl.empty()) {
c->Message(Chat::White, "No bots are capable of performing this action");
return;
}
Bot* my_bot = sbl.front();
my_bot->InterruptSpell();
Bot::BotGroupSay(my_bot, "Attempting to pick the lock.");
std::list<Doors*> door_list;
entity_list.GetDoorsList(door_list);
int door_count = 0, open_count = 0;
for (auto door_iter : door_list) {
if (!door_iter || door_iter->IsDoorOpen())
continue;
glm::tvec4<float, glm::highp> diff = (c->GetPosition() - door_iter->GetPosition());
float curdist = ((diff.x * diff.x) + (diff.y * diff.y));
float curelev = (diff.z * diff.z);
#if (EQDEBUG >= 11)
if (curdist <= 130 && curelev <= 65 && curelev >= 25) // 2D limit is '130' (x^2 + y^2), 1D theoretically should be '65' (z^2)
Log(Logs::Detail, Logs::Doors, "bot_command_pick_lock(): DoorID: %i - Elevation difference failure within theoretical limit (%f <= 65.0)", door_iter->GetDoorID(), curelev);
#endif
if (curelev >= 25 || curdist > 130) // changed curelev from '10' to '25' - requiring diff.z to be less than '5'
continue;
++door_count;
if (pick_lock_value >= door_iter->GetLockpick()) {
door_iter->ForceOpen(my_bot);
++open_count;
}
else {
Bot::BotGroupSay(my_bot, "I am not skilled enough for this lock.");
}
}
c->Message(Chat::White, "%i door%s attempted - %i door%s successful", door_count, ((door_count != 1) ? ("s") : ("")), open_count, ((open_count != 1) ? ("s") : ("")));
}
+198
View File
@@ -0,0 +1,198 @@
#include "../bot_command.h"
void bot_command_pickpocket(Client *c, const Seperator *sep)
{
if (helper_command_disabled(c, RuleB(Bots, AllowPickpocketCommand), "pickpocket")) {
return;
}
if (helper_command_alias_fail(c, "bot_command_pickpocket", sep->arg[0], "pickpocket")) {
return;
}
if (helper_is_help_or_usage(sep->arg[1])) {
c->Message(Chat::White, "usage: <enemy_target>", sep->arg[0]);
return;
}
std::list<Bot *> sbl;
MyBots::PopulateSBL_BySpawnedBots(c, sbl);
// Check for capable rogue
ActionableBots::Filter_ByClasses(c, sbl, player_class_bitmasks[Class::Rogue]);
Bot *my_bot = ActionableBots::AsSpawned_ByMinLevelAndClass(c, sbl, 7, Class::Rogue);
if (!my_bot) {
c->Message(Chat::White, "No bots are capable of performing this action");
return;
}
// Make sure a mob is targetted and a valid NPC
Mob *target_mob = ActionableTarget::AsSingle_ByAttackable(c);
if (!target_mob || !target_mob->IsNPC()) {
c->Message(Chat::White, "You must <target> an enemy to use this command");
return;
}
NPC *target_npc = ActionableTarget::AsSingle_ByAttackable(c)->CastToNPC();
// Check if mob is close enough
glm::vec4 mob_distance = (c->GetPosition() - target_mob->GetPosition());
float mob_xy_distance = ((mob_distance.x * mob_distance.x) + (mob_distance.y * mob_distance.y));
float mob_z_distance = (mob_distance.z * mob_distance.z);
float z_offset_diff = target_mob->GetZOffset() - c->GetZOffset();
if (mob_z_distance >= (35-z_offset_diff) || mob_xy_distance > 250) {
c->Message(Chat::White, "You must be closer to an enemy to use this command");
return;
}
// Adapted from pickpock skill in npc.cpp
// Make sure we are allowed to target them
uint8 over_level = target_mob->GetLevel();
if (over_level > (my_bot->GetLevel() + THIEF_PICKPOCKET_OVER)) {
c->Message(Chat::Red, "You are too inexperienced to pick pocket this target");
return;
}
// Random fail roll
if (zone->random.Roll(5)) {
if (zone->CanDoCombat()) {
target_mob->AddToHateList(c, 50);
}
target_mob->Say("Stop thief!");
c->Message(Chat::Red, "You are noticed trying to steal!");
return;
}
// Setup variables for calcs
bool steal_skill = my_bot->GetSkill(EQ::skills::SkillPickPockets);
bool steal_chance = steal_skill * 100 / (5 * over_level + 5);
// Determine whether to steal money or an item.
uint32 money[6] = {
0,
((steal_skill >= 125) ? (target_npc->GetPlatinum()) : (0)),
((steal_skill >= 60) ? (target_npc->GetGold()) : (0)),
target_npc->GetSilver(),
target_npc->GetCopper(),
0
};
bool has_coin = ((money[PickPocketPlatinum] | money[PickPocketGold] | money[PickPocketSilver] | money[PickPocketCopper]) != 0);
bool steal_item = (steal_skill >= steal_chance && (zone->random.Roll(50) || !has_coin));
// Steal item
while (steal_item) {
std::vector<std::pair<const EQ::ItemData *, uint16>> loot_selection; // <const ItemData*, charges>
for (auto item_iter: target_npc->itemlist) {
if (!item_iter || !item_iter->item_id) {
continue;
}
auto item_test = database.GetItem(item_iter->item_id);
if (item_test->Magic || !item_test->NoDrop || item_test->IsClassBag() || c->CheckLoreConflict(item_test) ||
item_iter->equip_slot != EQ::invslot::SLOT_INVALID) {
continue;
}
loot_selection.emplace_back(
std::make_pair(
item_test,
((item_test->Stackable) ? (1) : (item_iter->charges))
)
);
}
if (loot_selection.empty()) {
steal_item = false;
break;
}
int random = zone->random.Int(0, (loot_selection.size() - 1));
int16 slot_id = c->GetInv().FindFreeSlot(
false,
true,
(loot_selection[random].first->Size),
(loot_selection[random].first->ItemType == EQ::item::ItemTypeArrow)
);
if (slot_id == INVALID_INDEX) {
steal_item = false;
break;
}
auto item_inst = database.CreateItem(loot_selection[random].first, loot_selection[random].second);
if (item_inst == nullptr) {
steal_item = false;
break;
}
// Successful item pickpocket
if (item_inst->IsStackable() && RuleB(Character, UseStackablePickPocketing)) {
if (!c->TryStacking(item_inst, ItemPacketTrade, false, false)) {
c->PutItemInInventory(slot_id, *item_inst);
c->SendItemPacket(slot_id, item_inst, ItemPacketTrade);
}
}
else {
c->PutItemInInventory(slot_id, *item_inst);
c->SendItemPacket(slot_id, item_inst, ItemPacketTrade);
}
target_npc->RemoveItem(item_inst->GetID());
c->Message(Chat::White, "You stole an item.");
safe_delete(item_inst);
return;
}
// no items, try money
while (!steal_item && has_coin) {
uint32 coin_amount = zone->random.Int(1, (steal_skill / 25) + 1);
int coin_type = PickPocketPlatinum;
while (coin_type <= PickPocketCopper) {
if (money[coin_type]) {
if (coin_amount > money[coin_type]) {
coin_amount = money[coin_type];
}
break;
}
++coin_type;
}
if (coin_type > PickPocketCopper) {
break;
}
memset(money, 0, (sizeof(int) * 6));
money[coin_type] = coin_amount;
if (zone->random.Roll(steal_chance)) { // Successful coin pickpocket
switch (coin_type) {
case PickPocketPlatinum:
target_npc->SetPlatinum(target_npc->GetPlatinum() - coin_amount);
break;
case PickPocketGold:
target_npc->SetGold(target_npc->GetGold() - coin_amount);
break;
case PickPocketSilver:
target_npc->SetSilver(target_npc->GetSilver() - coin_amount);
break;
case PickPocketCopper:
target_npc->SetCopper(target_npc->GetCopper() - coin_amount);
break;
default: // has_coin..but, doesn't have coin?
c->Message(Chat::Red, "You failed to pickpocket.");
return;
}
c->Message(Chat::White, "You stole money.");
c->AddMoneyToPP(
money[PickPocketCopper],
money[PickPocketSilver],
money[PickPocketGold],
money[PickPocketPlatinum],
true
);
return;
}
c->Message(Chat::Red, "You failed to pickpocket.");
return;
}
c->Message(Chat::White, "This target's pockets are empty");
}
+33
View File
@@ -0,0 +1,33 @@
#include "../bot_command.h"
void bot_command_precombat(Client* c, const Seperator* sep)
{
if (helper_command_alias_fail(c, "bot_command_precombat", sep->arg[0], "precombat")) {
return;
}
if (helper_is_help_or_usage(sep->arg[1])) {
c->Message(Chat::White, "usage: %s ([set | clear])", sep->arg[0]);
return;
}
if (!c->GetTarget() || !c->IsAttackAllowed(c->GetTarget())) {
c->Message(Chat::White, "This command requires an attackable target.");
return;
}
std::string argument(sep->arg[1]);
if (!argument.compare("set")) {
c->SetBotPrecombat(true);
}
else if (!argument.compare("clear")) {
c->SetBotPrecombat(false);
}
else {
c->SetBotPrecombat(!c->GetBotPrecombat());
}
c->Message(Chat::White, "Precombat flag is now %s.", (c->GetBotPrecombat() ? "set" : "clear"));
}
+117
View File
@@ -0,0 +1,117 @@
#include "../bot_command.h"
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])) {
c->Message(Chat::White, "usage: <enemy_target> %s", sep->arg[0]);
return;
}
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;
if (ActionableBots::PopulateSBL(c, "ownergroup", sbl, ab_mask) == ActionableBots::ABT_None) {
return;
}
sbl.remove(nullptr);
auto target_mob = ActionableTarget::VerifyEnemy(c, BCEnum::TT_Single);
if (!target_mob) {
c->Message(Chat::White, "Your current target is not attackable!");
return;
}
if (target_mob->IsNPC() && target_mob->GetHateList().size()) {
c->Message(Chat::White, "Your current target is already engaged!");
return;
}
Bot* bot_puller = nullptr;
for (auto bot_iter : sbl) {
if (bot_iter->GetAppearance() == eaDead || bot_iter->GetBotStance() == EQ::constants::stancePassive) {
continue;
}
switch (bot_iter->GetClass()) {
case Class::Rogue:
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_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;
default:
continue;
}
bot_puller = bot_iter;
break;
}
if (bot_puller) {
bot_puller->SetPullFlag();
}
helper_no_available_bots(c, bot_puller);
}
+24
View File
@@ -0,0 +1,24 @@
#include "../bot_command.h"
void bot_command_release(Client *c, const Seperator *sep)
{
if (helper_command_alias_fail(c, "bot_command_release", sep->arg[0], "release"))
return;
if (helper_is_help_or_usage(sep->arg[1])) {
c->Message(Chat::White, "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->WipeHateList();
bot_iter->SetPauseAI(false);
}
c->Message(Chat::White, "%i of your bots %s released.", sbl.size(), ((sbl.size() != 1) ? ("are") : ("is")));
}
+1
View File
@@ -0,0 +1 @@
#include "../bot_command.h"
+73
View File
@@ -0,0 +1,73 @@
#include "../bot_command.h"
void bot_command_resistance(Client *c, const Seperator *sep)
{
bcst_list* local_list = &bot_command_spells[BCEnum::SpT_Resistance];
if (helper_spell_list_fail(c, local_list, BCEnum::SpT_Resistance) || helper_command_alias_fail(c, "bot_command_resistance", sep->arg[0], "resistance"))
return;
if (helper_is_help_or_usage(sep->arg[1])) {
c->Message(Chat::White, "usage: (<friendly_target>) %s [resistance: fire | cold | poison | disease | magic | corruption]", sep->arg[0]);
helper_send_usage_required_bots(c, BCEnum::SpT_Resistance);
return;
}
std::string resistance_arg = sep->arg[1];
auto resistance_type = BCEnum::RT_None;
if (!resistance_arg.compare("fire"))
resistance_type = BCEnum::RT_Fire;
else if (!resistance_arg.compare("cold"))
resistance_type = BCEnum::RT_Cold;
else if (!resistance_arg.compare("poison"))
resistance_type = BCEnum::RT_Poison;
else if (!resistance_arg.compare("disease"))
resistance_type = BCEnum::RT_Disease;
else if (!resistance_arg.compare("magic"))
resistance_type = BCEnum::RT_Magic;
else if (!resistance_arg.compare("corruption"))
resistance_type = BCEnum::RT_Corruption;
if (resistance_type == BCEnum::RT_None) {
c->Message(Chat::White, "You must specify a [resistance]");
return;
}
local_list->sort([resistance_type](STBaseEntry* l, STBaseEntry* r) {
auto _l = l->SafeCastToResistance(), _r = r->SafeCastToResistance();
if (_l->resist_value[RESISTANCEIDTOINDEX(resistance_type)] > _r->resist_value[RESISTANCEIDTOINDEX(resistance_type)])
return true;
if (_l->resist_value[RESISTANCEIDTOINDEX(resistance_type)] == _r->resist_value[RESISTANCEIDTOINDEX(resistance_type)] && spells[_l->spell_id].mana < spells[_r->spell_id].mana)
return true;
if (_l->resist_value[RESISTANCEIDTOINDEX(resistance_type)] == _r->resist_value[RESISTANCEIDTOINDEX(resistance_type)] && spells[_l->spell_id].mana == spells[_r->spell_id].mana && _l->resist_total > _r->resist_total)
return true;
return false;
});
ActionableTarget::Types actionable_targets;
Bot* my_bot = nullptr;
std::list<Bot*> sbl;
MyBots::PopulateSBL_BySpawnedBots(c, sbl);
bool cast_success = false;
for (auto list_iter : *local_list) {
auto local_entry = list_iter->SafeCastToResistance();
if (helper_spell_check_fail(local_entry))
continue;
if (!local_entry->resist_value[RESISTANCEIDTOINDEX(resistance_type)])
continue;
auto target_mob = actionable_targets.Select(c, local_entry->target_type, FRIENDLY);
if (!target_mob)
continue;
my_bot = ActionableBots::Select_ByMinLevelAndClass(c, local_entry->target_type, sbl, local_entry->spell_level, local_entry->caster_class, target_mob);
if (!my_bot)
continue;
cast_success = helper_cast_standard_spell(my_bot, target_mob, local_entry->spell_id);
break;
}
helper_no_available_bots(c, my_bot);
}
+57
View File
@@ -0,0 +1,57 @@
#include "../bot_command.h"
void bot_command_resurrect(Client *c, const Seperator *sep)
{
// Obscure bot spell code prohibits the aoe portion from working correctly...
bcst_list* local_list = &bot_command_spells[BCEnum::SpT_Resurrect];
if (helper_spell_list_fail(c, local_list, BCEnum::SpT_Resurrect) || helper_command_alias_fail(c, "bot_command_resurrect", sep->arg[0], "resurrect"))
return;
if (helper_is_help_or_usage(sep->arg[1])) {
//c->Message(Chat::White, "usage: <corpse_target> %s ([option: aoe])", sep->arg[0]);
c->Message(Chat::White, "usage: <corpse_target> %s", sep->arg[0]);
helper_send_usage_required_bots(c, BCEnum::SpT_Resurrect);
return;
}
bool aoe = false;
//std::string aoe_arg = sep->arg[1];
//if (!aoe_arg.compare("aoe"))
// aoe = true;
ActionableTarget::Types actionable_targets;
Bot* my_bot = nullptr;
std::list<Bot*> sbl;
MyBots::PopulateSBL_BySpawnedBots(c, sbl);
for (auto list_iter : *local_list) {
auto local_entry = list_iter->SafeCastToResurrect();
if (helper_spell_check_fail(local_entry))
continue;
//if (local_entry->aoe != aoe)
// continue;
if (local_entry->aoe)
continue;
auto target_mob = actionable_targets.Select(c, local_entry->target_type, FRIENDLY);
//if (!target_mob && !local_entry->aoe)
// continue;
if (!target_mob)
continue;
my_bot = ActionableBots::Select_ByMinLevelAndClass(c, local_entry->target_type, sbl, local_entry->spell_level, local_entry->caster_class, target_mob);
if (!my_bot)
continue;
//if (local_entry->aoe)
// target_mob = my_bot;
uint32 dont_root_before = 0;
if (helper_cast_standard_spell(my_bot, target_mob, local_entry->spell_id, true, &dont_root_before))
target_mob->SetDontRootMeBefore(dont_root_before);
break;
}
helper_no_available_bots(c, my_bot);
}
+38
View File
@@ -0,0 +1,38 @@
#include "../bot_command.h"
void bot_command_rune(Client *c, const Seperator *sep)
{
bcst_list* local_list = &bot_command_spells[BCEnum::SpT_Rune];
if (helper_spell_list_fail(c, local_list, BCEnum::SpT_Rune) || helper_command_alias_fail(c, "bot_command_rune", sep->arg[0], "rune"))
return;
if (helper_is_help_or_usage(sep->arg[1])) {
c->Message(Chat::White, "usage: (<friendly_target>) %s", sep->arg[0]);
helper_send_usage_required_bots(c, BCEnum::SpT_Rune);
return;
}
ActionableTarget::Types actionable_targets;
Bot* my_bot = nullptr;
std::list<Bot*> sbl;
MyBots::PopulateSBL_BySpawnedBots(c, sbl);
bool cast_success = false;
for (auto list_iter : *local_list) {
auto local_entry = list_iter;
if (helper_spell_check_fail(local_entry))
continue;
auto target_mob = actionable_targets.Select(c, local_entry->target_type, FRIENDLY);
if (!target_mob)
continue;
my_bot = ActionableBots::Select_ByMinLevelAndClass(c, local_entry->target_type, sbl, local_entry->spell_level, local_entry->caster_class, target_mob);
if (!my_bot)
continue;
cast_success = helper_cast_standard_spell(my_bot, target_mob, local_entry->spell_id);
break;
}
helper_no_available_bots(c, my_bot);
}
+47
View File
@@ -0,0 +1,47 @@
#include "../bot_command.h"
void bot_command_send_home(Client *c, const Seperator *sep)
{
// Obscure bot spell code prohibits the aoe portion from working correctly...
bcst_list* local_list = &bot_command_spells[BCEnum::SpT_SendHome];
if (helper_spell_list_fail(c, local_list, BCEnum::SpT_SendHome) || helper_command_alias_fail(c, "bot_command_send_home", sep->arg[0], "sendhome"))
return;
if (helper_is_help_or_usage(sep->arg[1])) {
c->Message(Chat::White, "usage: (<friendly_target>) %s ([option: group])", sep->arg[0]);
helper_send_usage_required_bots(c, BCEnum::SpT_SendHome);
return;
}
bool group = false;
std::string group_arg = sep->arg[1];
if (!group_arg.compare("group"))
group = true;
ActionableTarget::Types actionable_targets;
Bot* my_bot = nullptr;
std::list<Bot*> sbl;
MyBots::PopulateSBL_BySpawnedBots(c, sbl);
bool cast_success = false;
for (auto list_iter : *local_list) {
auto local_entry = list_iter->SafeCastToSendHome();
if (helper_spell_check_fail(local_entry))
continue;
if (local_entry->group != group)
continue;
auto target_mob = actionable_targets.Select(c, local_entry->target_type, FRIENDLY);
if (!target_mob)
continue;
my_bot = ActionableBots::Select_ByMinLevelAndClass(c, local_entry->target_type, sbl, local_entry->spell_level, local_entry->caster_class, target_mob);
if (!my_bot)
continue;
cast_success = helper_cast_standard_spell(my_bot, target_mob, local_entry->spell_id);
break;
}
helper_no_available_bots(c, my_bot);
}
+50
View File
@@ -0,0 +1,50 @@
#include "../bot_command.h"
void bot_command_size(Client *c, const Seperator *sep)
{
bcst_list* local_list = &bot_command_spells[BCEnum::SpT_Size];
if (helper_spell_list_fail(c, local_list, BCEnum::SpT_Size) || helper_command_alias_fail(c, "bot_command_size", sep->arg[0], "size"))
return;
if (helper_is_help_or_usage(sep->arg[1])) {
c->Message(Chat::White, "usage: (<friendly_target>) %s [grow | shrink]", sep->arg[0]);
helper_send_usage_required_bots(c, BCEnum::SpT_Size);
return;
}
std::string size_arg = sep->arg[1];
auto size_type = BCEnum::SzT_Reduce;
if (!size_arg.compare("grow")) {
size_type = BCEnum::SzT_Enlarge;
}
else if (size_arg.compare("shrink")) {
c->Message(Chat::White, "This command requires a [grow | shrink] argument");
return;
}
ActionableTarget::Types actionable_targets;
Bot* my_bot = nullptr;
std::list<Bot*> sbl;
MyBots::PopulateSBL_BySpawnedBots(c, sbl);
bool cast_success = false;
for (auto list_iter : *local_list) {
auto local_entry = list_iter->SafeCastToSize();
if (helper_spell_check_fail(local_entry))
continue;
if (local_entry->size_type != size_type)
continue;
auto target_mob = actionable_targets.Select(c, local_entry->target_type, FRIENDLY);
if (!target_mob)
continue;
my_bot = ActionableBots::Select_ByMinLevelAndClass(c, local_entry->target_type, sbl, local_entry->spell_level, local_entry->caster_class, target_mob);
if (!my_bot)
continue;
cast_success = helper_cast_standard_spell(my_bot, target_mob, local_entry->spell_id);
break;
}
helper_no_available_bots(c, my_bot);
}
+570
View File
@@ -0,0 +1,570 @@
#include "../bot_command.h"
void bot_command_spell_list(Client* c, const Seperator *sep)
{
if (helper_command_alias_fail(c, "bot_command_spell_list", sep->arg[0], "spells")) {
return;
}
if (helper_is_help_or_usage(sep->arg[1])) {
c->Message(
Chat::White,
fmt::format(
"Usage: {} [Min Level] (Level is optional)",
sep->arg[0]
).c_str()
);
return;
}
auto my_bot = ActionableBots::AsTarget_ByBot(c);
if (!my_bot) {
c->Message(Chat::White, "You must target a bot that you own to use this command.");
return;
}
uint8 min_level = 0;
if (sep->IsNumber(1)) {
min_level = static_cast<uint8>(Strings::ToUnsignedInt(sep->arg[1]));
}
my_bot->ListBotSpells(min_level);
}
void bot_command_spell_settings_add(Client *c, const Seperator *sep)
{
if (helper_command_alias_fail(c, "bot_command_spell_settings_add", sep->arg[0], "spellsettingsadd")) {
return;
}
if (helper_is_help_or_usage(sep->arg[1])) {
c->Message(
Chat::White,
fmt::format(
"Usage: {} [Spell ID] [Priority] [Min HP] [Max HP]",
sep->arg[0]
).c_str()
);
return;
}
auto my_bot = ActionableBots::AsTarget_ByBot(c);
if (!my_bot) {
c->Message(Chat::White, "You must target a bot that you own to use this command.");
return;
}
auto arguments = sep->argnum;
if (
arguments < 4 ||
!sep->IsNumber(1) ||
!sep->IsNumber(2) ||
!sep->IsNumber(3) ||
!sep->IsNumber(4)
) {
c->Message(
Chat::White,
fmt::format(
"Usage: {} [Spell ID] [Priority] [Min HP] [Max HP]",
sep->arg[0]
).c_str()
);
return;
}
auto spell_id = static_cast<uint16>(Strings::ToUnsignedInt(sep->arg[1]));
if (!IsValidSpell(spell_id)) {
c->Message(
Chat::White,
fmt::format(
"Spell ID {} is invalid or could not be found.",
spell_id
).c_str()
);
return;
}
if (my_bot->GetBotSpellSetting(spell_id)) {
c->Message(
Chat::White,
fmt::format(
"{} already has a spell setting for {} ({}), trying using {} instead.",
my_bot->GetCleanName(),
spells[spell_id].name,
spell_id,
Saylink::Silent("^spellsettingsupdate")
).c_str()
);
return;
}
auto priority = static_cast<int16>(Strings::ToInt(sep->arg[2]));
auto min_hp = static_cast<int8>(EQ::Clamp(Strings::ToInt(sep->arg[3]), -1, 99));
auto max_hp = static_cast<int8>(EQ::Clamp(Strings::ToInt(sep->arg[4]), -1, 100));
BotSpellSetting bs;
bs.priority = priority;
bs.min_hp = min_hp;
bs.max_hp = max_hp;
if (!my_bot->AddBotSpellSetting(spell_id, &bs)) {
c->Message(
Chat::White,
fmt::format(
"Failed to add spell setting for {}.",
my_bot->GetCleanName()
).c_str()
);
return;
}
my_bot->AI_AddBotSpells(my_bot->GetBotSpellID());
c->Message(
Chat::White,
fmt::format(
"Successfully added spell setting for {}.",
my_bot->GetCleanName()
).c_str()
);
c->Message(
Chat::White,
fmt::format(
"Spell Setting Added | Spell: {} ({}) ",
spells[spell_id].name,
spell_id
).c_str()
);
c->Message(
Chat::White,
fmt::format(
"Spell Setting Added | Priority: {} Health: {}",
priority,
my_bot->GetHPString(min_hp, max_hp)
).c_str()
);
}
void bot_command_spell_settings_delete(Client *c, const Seperator *sep)
{
if (helper_command_alias_fail(c, "bot_command_spell_settings_delete", sep->arg[0], "spellsettingsdelete")) {
return;
}
if (helper_is_help_or_usage(sep->arg[1])) {
c->Message(
Chat::White,
fmt::format(
"Usage: {} [Spell ID]",
sep->arg[0]
).c_str()
);
return;
}
auto my_bot = ActionableBots::AsTarget_ByBot(c);
if (!my_bot) {
c->Message(Chat::White, "You must target a bot that you own to use this command.");
return;
}
auto arguments = sep->argnum;
if (
arguments < 1 ||
!sep->IsNumber(1)
) {
c->Message(
Chat::White,
fmt::format(
"Usage: {} [Spell ID]",
sep->arg[0]
).c_str()
);
return;
}
auto spell_id = static_cast<uint16>(Strings::ToUnsignedInt(sep->arg[1]));
if (!IsValidSpell(spell_id)) {
c->Message(
Chat::White,
fmt::format(
"Spell ID {} is invalid or could not be found.",
spell_id
).c_str()
);
return;
}
if (!my_bot->DeleteBotSpellSetting(spell_id)) {
c->Message(
Chat::White,
fmt::format(
"Failed to delete spell setting for {}.",
my_bot->GetCleanName()
).c_str()
);
return;
}
my_bot->AI_AddBotSpells(my_bot->GetBotSpellID());
c->Message(
Chat::White,
fmt::format(
"Successfully deleted spell setting for {}.",
my_bot->GetCleanName()
).c_str()
);
c->Message(
Chat::White,
fmt::format(
"Spell Setting Deleted | Spell: {} ({})",
spells[spell_id].name,
spell_id
).c_str()
);
}
void bot_command_spell_settings_list(Client *c, const Seperator *sep)
{
if (helper_command_alias_fail(c, "bot_command_spell_settings_list", sep->arg[0], "spellsettings")) {
return;
}
if (helper_is_help_or_usage(sep->arg[1])) {
c->Message(
Chat::White,
fmt::format(
"Usage: {}",
sep->arg[0]
).c_str()
);
return;
}
auto my_bot = ActionableBots::AsTarget_ByBot(c);
if (!my_bot) {
c->Message(Chat::White, "You must target a bot that you own to use this command.");
return;
}
my_bot->ListBotSpellSettings();
}
void bot_command_spell_settings_toggle(Client *c, const Seperator *sep)
{
if (helper_command_alias_fail(c, "bot_command_spell_settings_toggle", sep->arg[0], "spellsettingstoggle")) {
return;
}
if (helper_is_help_or_usage(sep->arg[1])) {
c->Message(
Chat::White,
fmt::format(
"Usage: {} [Spell ID] [Toggle]",
sep->arg[0]
).c_str()
);
return;
}
auto my_bot = ActionableBots::AsTarget_ByBot(c);
if (!my_bot) {
c->Message(Chat::White, "You must target a bot that you own to use this command.");
return;
}
auto arguments = sep->argnum;
if (
arguments < 2 ||
!sep->IsNumber(1)
) {
c->Message(
Chat::White,
fmt::format(
"Usage: {} [Spell ID] [Toggle]",
sep->arg[0]
).c_str()
);
return;
}
auto spell_id = static_cast<uint16>(Strings::ToUnsignedInt(sep->arg[1]));
if (!IsValidSpell(spell_id)) {
c->Message(
Chat::White,
fmt::format(
"Spell ID {} is invalid or could not be found.",
spell_id
).c_str()
);
return;
}
bool toggle = (
sep->IsNumber(2) ?
Strings::ToInt(sep->arg[2]) != 0 :
atobool(sep->arg[2])
);
auto obs = my_bot->GetBotSpellSetting(spell_id);
if (!obs) {
return;
}
BotSpellSetting bs;
bs.priority = obs->priority;
bs.min_hp = obs->min_hp;
bs.max_hp = obs->max_hp;
bs.is_enabled = toggle;
if (!my_bot->UpdateBotSpellSetting(spell_id, &bs)) {
c->Message(
Chat::White,
fmt::format(
"Failed to {}able spell for {}.",
toggle ? "en" : "dis",
my_bot->GetCleanName()
).c_str()
);
return;
}
my_bot->AI_AddBotSpells(my_bot->GetBotSpellID());
c->Message(
Chat::White,
fmt::format(
"Successfully {}abled spell for {}.",
toggle ? "en" : "dis",
my_bot->GetCleanName()
).c_str()
);
c->Message(
Chat::White,
fmt::format(
"Spell {}abled | Spell: {} ({})",
toggle ? "En" : "Dis",
spells[spell_id].name,
spell_id
).c_str()
);
}
void bot_command_spell_settings_update(Client *c, const Seperator *sep)
{
if (helper_command_alias_fail(c, "bot_command_spell_settings_update", sep->arg[0], "spellsettingsupdate")) {
return;
}
if (helper_is_help_or_usage(sep->arg[1])) {
c->Message(
Chat::White,
fmt::format(
"Usage: {} [Spell ID] [Priority] [Min HP] [Max HP]",
sep->arg[0]
).c_str()
);
return;
}
auto my_bot = ActionableBots::AsTarget_ByBot(c);
if (!my_bot) {
c->Message(Chat::White, "You must target a bot that you own to use this command.");
return;
}
auto arguments = sep->argnum;
if (
arguments < 4 ||
!sep->IsNumber(1) ||
!sep->IsNumber(2) ||
!sep->IsNumber(3) ||
!sep->IsNumber(4)
) {
c->Message(
Chat::White,
fmt::format(
"Usage: {} [Spell ID] [Priority] [Min HP] [Max HP]",
sep->arg[0]
).c_str()
);
return;
}
auto spell_id = static_cast<uint16>(Strings::ToUnsignedInt(sep->arg[1]));
if (!IsValidSpell(spell_id)) {
c->Message(
Chat::White,
fmt::format(
"Spell ID {} is invalid or could not be found.",
spell_id
).c_str()
);
return;
}
auto priority = static_cast<int16>(Strings::ToInt(sep->arg[2]));
auto min_hp = static_cast<int8>(EQ::Clamp(Strings::ToInt(sep->arg[3]), -1, 99));
auto max_hp = static_cast<int8>(EQ::Clamp(Strings::ToInt(sep->arg[4]), -1, 100));
BotSpellSetting bs;
bs.priority = priority;
bs.min_hp = min_hp;
bs.max_hp = max_hp;
if (!my_bot->UpdateBotSpellSetting(spell_id, &bs)) {
c->Message(
Chat::White,
fmt::format(
"Failed to update spell setting for {}.",
my_bot->GetCleanName()
).c_str()
);
return;
}
my_bot->AI_AddBotSpells(my_bot->GetBotSpellID());
c->Message(
Chat::White,
fmt::format(
"Successfully updated spell setting for {}.",
my_bot->GetCleanName()
).c_str()
);
c->Message(
Chat::White,
fmt::format(
"Spell Setting Updated | Spell: {} ({})",
spells[spell_id].name,
spell_id
).c_str()
);
c->Message(
Chat::White,
fmt::format(
"Spell Setting Updated | Priority: {} Health: {}",
priority,
my_bot->GetHPString(min_hp, max_hp)
).c_str()
);
}
void bot_spell_info_dialogue_window(Client* c, const Seperator *sep)
{
if (helper_command_alias_fail(c, "bot_spell_info_dialogue_window", sep->arg[0], "spellinfo")) {
return;
}
auto arguments = sep->argnum;
if (
arguments < 1 ||
!sep->IsNumber(1)
) {
c->Message(
Chat::White,
fmt::format(
"Usage: {} [Spell ID]",
sep->arg[0]
).c_str()
);
return;
}
auto my_bot = ActionableBots::AsTarget_ByBot(c);
if (!my_bot) {
c->Message(Chat::White, "You must target a bot that you own to use this command.");
return;
}
auto spell_id = static_cast<uint16>(Strings::ToUnsignedInt(sep->arg[1]));
auto min_level = spells[spell_id].classes;
auto class_level = min_level[my_bot->GetBotClass() - 1];
if (class_level > my_bot->GetLevel()) {
c->Message(Chat::White, "This is not a usable spell by your bot.");
return;
}
auto results = database.QueryDatabase(
fmt::format(
"SELECT value FROM db_str WHERE id = {} and type = 6 LIMIT 1",
spells[spell_id].effect_description_id
)
);
if (!results.Success() || !results.RowCount()) {
c->Message(Chat::White, "No Spell Information Available for this.");
return;
}
auto row = results.begin();
std::string spell_desc = row[0];
auto m = DialogueWindow::TableRow(
DialogueWindow::TableCell("Spell Effect: ") +
DialogueWindow::TableCell(spell_desc)
);
m += DialogueWindow::TableRow(
DialogueWindow::TableCell("Spell Level: ") +
DialogueWindow::TableCell(fmt::format("{}", class_level))
);
c->SendPopupToClient(
fmt::format(
"Spell: {}", spells[spell_id].name
).c_str(),
DialogueWindow::Table(m).c_str()
);
}
void bot_command_enforce_spell_list(Client* c, const Seperator *sep)
{
if (helper_command_alias_fail(c, "bot_command_enforce_spell_list", sep->arg[0], "enforcespellsettings")) {
return;
}
if (helper_is_help_or_usage(sep->arg[1])) {
c->Message(
Chat::White,
fmt::format(
"Usage: {} [True|False] (Blank to toggle]",
sep->arg[0]
).c_str()
);
return;
}
auto my_bot = ActionableBots::AsTarget_ByBot(c);
if (!my_bot) {
c->Message(Chat::White, "You must target a bot that you own to use this command.");
return;
}
bool enforce_state = (sep->argnum > 0) ? Strings::ToBool(sep->arg[1]) : !my_bot->GetBotEnforceSpellSetting();
my_bot->SetBotEnforceSpellSetting(enforce_state, true);
c->Message(
Chat::White,
fmt::format(
"{}'s Spell Settings List entries are now {}.",
my_bot->GetCleanName(),
my_bot->GetBotEnforceSpellSetting() ? "enforced" : "optional"
).c_str()
);
}
+1
View File
@@ -0,0 +1 @@
#include "../bot_command.h"
+1
View File
@@ -0,0 +1 @@
#include "../bot_command.h"
+1
View File
@@ -0,0 +1 @@
#include "../bot_command.h"
+1
View File
@@ -0,0 +1 @@
#include "../bot_command.h"
+47
View File
@@ -0,0 +1,47 @@
#include "../bot_command.h"
void bot_command_summon_corpse(Client *c, const Seperator *sep)
{
// Same methodology as old command..but, does not appear to work... (note: didn't work there, either...)
// temp
c->Message(Chat::White, "This command is currently unavailable...");
return;
bcst_list* local_list = &bot_command_spells[BCEnum::SpT_SummonCorpse];
if (helper_spell_list_fail(c, local_list, BCEnum::SpT_SummonCorpse) || helper_command_alias_fail(c, "bot_command_summon_corpse", sep->arg[0], "summoncorpse"))
return;
if (helper_is_help_or_usage(sep->arg[1])) {
c->Message(Chat::White, "usage: <friendly_target> %s", sep->arg[0]);
helper_send_usage_required_bots(c, BCEnum::SpT_SummonCorpse);
return;
}
Bot* my_bot = nullptr;
std::list<Bot*> sbl;
MyBots::PopulateSBL_BySpawnedBots(c, sbl);
bool cast_success = false;
for (auto list_iter : *local_list) {
auto local_entry = list_iter;
if (helper_spell_check_fail(local_entry))
continue;
auto target_mob = ActionableTarget::AsSingle_ByPlayer(c);
if (!target_mob)
continue;
if (spells[local_entry->spell_id].base_value[EFFECTIDTOINDEX(1)] < target_mob->GetLevel())
continue;
my_bot = ActionableBots::Select_ByMinLevelAndClass(c, local_entry->target_type, sbl, local_entry->spell_level, local_entry->caster_class, target_mob);
if (!my_bot)
continue;
cast_success = helper_cast_standard_spell(my_bot, target_mob, local_entry->spell_id);
break;
}
helper_no_available_bots(c, my_bot);
}
+1
View File
@@ -0,0 +1 @@
#include "../bot_command.h"
+25
View File
@@ -0,0 +1,25 @@
#include "../bot_command.h"
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(Chat::White, "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(Chat::White, "%i of your bots %s suspended.", sbl.size(), ((sbl.size() != 1) ? ("are") : ("is")));
}
+118
View File
@@ -0,0 +1,118 @@
#include "../bot_command.h"
void bot_command_taunt(Client *c, const Seperator *sep)
{
if (helper_command_alias_fail(c, "bot_command_taunt", sep->arg[0], "taunt"))
return;
if (helper_is_help_or_usage(sep->arg[1])) {
c->Message(Chat::White, "usage: %s ([option: on | off]) ([actionable: target | byname | ownergroup | ownerraid | targetgroup | namesgroup | healrotationtargets | byclass | byrace | spawned] ([actionable_name]))", sep->arg[0]);
return;
}
const int ab_mask = ActionableBots::ABM_Type1;
std::string arg1 = sep->arg[1];
bool taunt_state = false;
bool toggle_taunt = true;
int ab_arg = 1;
if (!arg1.compare("on")) {
taunt_state = true;
toggle_taunt = false;
ab_arg = 2;
}
else if (!arg1.compare("off")) {
toggle_taunt = false;
ab_arg = 2;
}
std::string class_race_arg = sep->arg[ab_arg];
bool class_race_check = false;
if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) {
class_race_check = true;
}
std::list<Bot*> sbl;
if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[(ab_arg + 1)] : nullptr, class_race_check ? atoi(sep->arg[(ab_arg + 1)]) : 0) == ActionableBots::ABT_None) {
return;
}
sbl.remove(nullptr);
int taunting_count = 0;
for (auto bot_iter : sbl) {
if (!bot_iter->GetSkill(EQ::skills::SkillTaunt)) {
continue;
}
if (toggle_taunt) {
bot_iter->SetTaunting(!bot_iter->IsTaunting());
} else {
bot_iter->SetTaunting(taunt_state);
}
if (sbl.size() == 1) {
Bot::BotGroupSay(
bot_iter,
fmt::format(
"I am {} taunting.",
bot_iter->IsTaunting() ? "now" : "no longer"
).c_str()
);
}
++taunting_count;
}
for (auto bot_iter : sbl) {
if (!bot_iter->HasPet()) {
continue;
}
if (!bot_iter->GetPet()->GetSkill(EQ::skills::SkillTaunt)) {
continue;
}
if (toggle_taunt) {
bot_iter->GetPet()->CastToNPC()->SetTaunting(!bot_iter->GetPet()->CastToNPC()->IsTaunting());
} else {
bot_iter->GetPet()->CastToNPC()->SetTaunting(taunt_state);
}
if (sbl.size() == 1) {
Bot::BotGroupSay(
bot_iter,
fmt::format(
"My Pet is {} taunting.",
bot_iter->GetPet()->CastToNPC()->IsTaunting() ? "now" : "no longer"
).c_str()
);
}
++taunting_count;
}
if (taunting_count) {
if (toggle_taunt) {
c->Message(
Chat::White,
fmt::format(
"{} of your bots and their pets {} toggled their taunting state",
taunting_count,
taunting_count != 1 ? "have" : "has"
).c_str()
);
} else {
c->Message(
Chat::White,
fmt::format(
"{} of your bots and their pets {} {} taunting.",
taunting_count,
taunting_count != 1 ? "have" : "has",
taunt_state ? "started" : "stopped"
).c_str()
);
}
}
else {
c->Message(Chat::White, "None of your bots are capable of taunting");
}
}
+119
View File
@@ -0,0 +1,119 @@
#include "../bot_command.h"
void bot_command_circle(Client *c, const Seperator *sep)
{
bcst_list* local_list = &bot_command_spells[BCEnum::SpT_Depart];
if (helper_spell_list_fail(c, local_list, BCEnum::SpT_Depart) || helper_command_alias_fail(c, "bot_command_circle", sep->arg[0], "circle"))
return;
if (helper_is_help_or_usage(sep->arg[1])) {
c->Message(Chat::White, "usage: %s [list | destination] ([option: single])", sep->arg[0]);
helper_send_usage_required_bots(c, BCEnum::SpT_Depart, Class::Druid);
return;
}
bool single = false;
std::string single_arg = sep->arg[2];
if (!single_arg.compare("single"))
single = true;
std::string destination = sep->arg[1];
if (!destination.compare("list")) {
auto my_druid_bot = ActionableBots::AsGroupMember_ByClass(c, c, Class::Druid);
helper_command_depart_list(c, my_druid_bot, nullptr, local_list, single);
return;
}
else if (destination.empty()) {
c->Message(Chat::White, "A [destination] or [list] argument is required to use this command");
return;
}
ActionableTarget::Types actionable_targets;
Bot* my_bot = nullptr;
std::list<Bot*> sbl;
MyBots::PopulateSBL_BySpawnedBots(c, sbl);
bool cast_success = false;
for (auto list_iter : *local_list) {
auto local_entry = list_iter->SafeCastToDepart();
if (helper_spell_check_fail(local_entry))
continue;
if (local_entry->caster_class != Class::Druid)
continue;
if (local_entry->single != single)
continue;
if (destination.compare(spells[local_entry->spell_id].teleport_zone))
continue;
auto target_mob = actionable_targets.Select(c, local_entry->target_type, FRIENDLY);
if (!target_mob)
continue;
my_bot = ActionableBots::Select_ByMinLevelAndClass(c, local_entry->target_type, sbl, local_entry->spell_level, local_entry->caster_class, target_mob);
if (!my_bot)
continue;
cast_success = helper_cast_standard_spell(my_bot, target_mob, local_entry->spell_id);
break;
}
helper_no_available_bots(c, my_bot);
}
void bot_command_portal(Client *c, const Seperator *sep)
{
bcst_list* local_list = &bot_command_spells[BCEnum::SpT_Depart];
if (helper_spell_list_fail(c, local_list, BCEnum::SpT_Depart) || helper_command_alias_fail(c, "bot_command_portal", sep->arg[0], "portal"))
return;
if (helper_is_help_or_usage(sep->arg[1])) {
c->Message(Chat::White, "usage: %s [list | destination] ([option: single])", sep->arg[0]);
helper_send_usage_required_bots(c, BCEnum::SpT_Depart, Class::Wizard);
return;
}
bool single = false;
std::string single_arg = sep->arg[2];
if (!single_arg.compare("single"))
single = true;
std::string destination = sep->arg[1];
if (!destination.compare("list")) {
auto my_wizard_bot = ActionableBots::AsGroupMember_ByClass(c, c, Class::Wizard);
helper_command_depart_list(c, nullptr, my_wizard_bot, local_list, single);
return;
}
else if (destination.empty()) {
c->Message(Chat::White, "A [destination] or [list] argument is required to use this command");
return;
}
ActionableTarget::Types actionable_targets;
Bot* my_bot = nullptr;
std::list<Bot*> sbl;
MyBots::PopulateSBL_BySpawnedBots(c, sbl);
bool cast_success = false;
for (auto list_iter : *local_list) {
auto local_entry = list_iter->SafeCastToDepart();
if (helper_spell_check_fail(local_entry))
continue;
if (local_entry->caster_class != Class::Wizard)
continue;
if (local_entry->single != single)
continue;
if (destination.compare(spells[local_entry->spell_id].teleport_zone))
continue;
auto target_mob = actionable_targets.Select(c, local_entry->target_type, FRIENDLY);
if (!target_mob)
continue;
my_bot = ActionableBots::Select_ByMinLevelAndClass(c, local_entry->target_type, sbl, local_entry->spell_level, local_entry->caster_class, target_mob);
if (!my_bot)
continue;
cast_success = helper_cast_standard_spell(my_bot, target_mob, local_entry->spell_id);
break;
}
helper_no_available_bots(c, my_bot);
}
+189
View File
@@ -0,0 +1,189 @@
#include "../bot_command.h"
void bot_command_timer(Client* c, const Seperator* sep)
{
if (helper_command_alias_fail(c, "bot_command_timer", sep->arg[0], "timer"))
return;
if (helper_is_help_or_usage(sep->arg[1])) {
c->Message(Chat::White, "usage: %s [clear | has | set] [disc | item | spell] [timer ID | item ID | spell ID | all] [optional ms for set] [actionable].", sep->arg[0]);
c->Message(Chat::White, "When setting, you can leave the value blank to use the default for the item or specify a value in ms to set the timer to.");
c->Message(Chat::White, "Returns or sets the provided timer(s) for the selected bot(s) or clears the selected timer(s) for the selected bot(s).");
return;
}
const int ab_mask = ActionableBots::ABM_Type1;
std::string arg1 = sep->arg[1];
std::string arg2 = sep->arg[2];
std::string arg3 = sep->arg[3];
int ab_arg = 4;
bool clear = false;
bool has = false;
bool set = false;
bool disc = false;
bool item = false;
bool spell = false;
uint32 timer_id = 0;
uint32 timer_value = 0;
bool all = false;
if (!arg1.compare("clear")) {
clear = true;
}
else if (!arg1.compare("has")) {
has = true;
}
else if (!arg1.compare("set")) {
set = true;
}
else {
c->Message(Chat::White, "Incorrect argument, use %s help for a list of options.", sep->arg[0]);
return;
}
if (!arg2.compare("disc")) {
disc = true;
}
else if (!arg2.compare("item")) {
item = true;
}
else if (!arg2.compare("spell")) {
spell = true;
}
else {
c->Message(Chat::White, "Incorrect timer type, use %s help for a list of options.", sep->arg[0]);
return;
}
if (sep->IsNumber(3)) {
timer_id = atoi(sep->arg[3]);
if (timer_id < 0) {
c->Message(Chat::White, "You cannot use negative numbers.");
return;
}
}
else if (!arg3.compare("all")) {
if (has || set) {
c->Message(Chat::White, "You can only use 'all' for clearing timers.");
return;
}
all = true;
}
else {
c->Message(Chat::White, "Incorrect ID option, use %s help for a list of options.", sep->arg[0]);
return;
}
if (set) {
if (sep->IsNumber(4)) {
ab_arg = 5;
timer_value = atoi(sep->arg[4]);
if (timer_value <= 0) {
c->Message(Chat::White, "You cannot use 0 or negative numbers.");
return;
}
}
}
std::string class_race_arg = sep->arg[ab_arg];
bool class_race_check = false;
if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) {
class_race_check = true;
}
std::list<Bot*> sbl;
if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) {
return;
}
sbl.remove(nullptr);
for (auto my_bot : sbl) {
bool found = false;
if (clear) {
c->Message(
Chat::White,
fmt::format(
"{} says, 'Clearing {} timer{}'",
my_bot->GetCleanName(),
disc ? "Discipline" : item ? "Item" : "Spell",
(all ? "s." : ".")
).c_str()
);
if (disc) {
my_bot->ClearDisciplineReuseTimer(timer_id);
}
else if (item) {
my_bot->ClearItemReuseTimer(timer_id);
}
else if (spell) {
my_bot->ClearSpellRecastTimer(timer_id);
}
}
else if (has) {
uint32 remaining_time;
std::string time_string = "";
if (disc) {
if (!my_bot->CheckDisciplineReuseTimer(timer_id)) {
remaining_time = my_bot->GetDisciplineReuseRemainingTime(timer_id) / 1000;
time_string = Strings::SecondsToTime(remaining_time);
found = true;
}
}
else if (item) {
if (!my_bot->CheckItemReuseTimer(timer_id)) {
remaining_time = my_bot->GetItemReuseRemainingTime(timer_id) / 1000;
time_string = Strings::SecondsToTime(remaining_time);
found = true;
}
}
else if (spell) {
if (!my_bot->CheckSpellRecastTimer(timer_id)) {
remaining_time = my_bot->GetSpellRecastRemainingTime(timer_id) / 1000;
time_string = Strings::SecondsToTime(remaining_time);
found = true;
}
}
c->Message(
Chat::White,
fmt::format(
"{} says, 'I {}{}{}'",
my_bot->GetCleanName(),
(!found ? " do not have that timer currently" : " have "),
(!found ? "" : time_string),
(!found ? "." : " remaining.")
).c_str()
);
}
else if (set) {
c->Message(
Chat::White,
fmt::format(
"{} says, 'Setting {} timer{} for {} to {}.'",
my_bot->GetCleanName(),
disc ? "Discipline" : item ? "Item" : "Spell",
(all ? "s" : ""),
timer_id,
timer_value ? std::to_string(timer_value) : "the default value"
).c_str()
);
if (disc) {
my_bot->ClearDisciplineReuseTimer(timer_id);
my_bot->SetDisciplineReuseTimer(timer_id, timer_value);
}
else if (item) {
my_bot->ClearItemReuseTimer(timer_id);
my_bot->SetItemReuseTimer(timer_id, timer_value);
}
else if (spell) {
my_bot->ClearSpellRecastTimer(timer_id);
my_bot->SetSpellRecastTimer(timer_id, timer_value);
}
}
}
}
+1
View File
@@ -0,0 +1 @@
#include "../bot_command.h"
+1
View File
@@ -0,0 +1 @@
#include "../bot_command.h"
+72
View File
@@ -0,0 +1,72 @@
#include "../bot_command.h"
void bot_command_track(Client *c, const Seperator *sep)
{
if (helper_command_alias_fail(c, "bot_command_track", sep->arg[0], "track"))
return;
if (helper_is_help_or_usage(sep->arg[1])) {
c->Message(Chat::White, "usage: %s (Ranger: [option=all: all | rare | local])", sep->arg[0]);
c->Message(Chat::White, "requires one of the following bot classes:");
c->Message(Chat::White, "Ranger(1), Druid(20) or Bard(35)");
return;
}
std::string tracking_scope = sep->arg[1];
std::list<Bot*> sbl;
MyBots::PopulateSBL_BySpawnedBots(c, sbl);
uint16 class_mask = (player_class_bitmasks[Class::Ranger] | player_class_bitmasks[Class::Druid] | player_class_bitmasks[Class::Bard]);
ActionableBots::Filter_ByClasses(c, sbl, class_mask);
Bot* my_bot = ActionableBots::AsSpawned_ByMinLevelAndClass(c, sbl, 1, Class::Ranger);
if (tracking_scope.empty()) {
if (!my_bot)
my_bot = ActionableBots::AsSpawned_ByMinLevelAndClass(c, sbl, 20, Class::Druid);
if (!my_bot)
my_bot = ActionableBots::AsSpawned_ByMinLevelAndClass(c, sbl, 35, Class::Bard);
}
if (!my_bot) {
c->Message(Chat::White, "No bots are capable of performing this action");
return;
}
int base_distance = 0;
bool track_named = false;
std::string tracking_msg;
switch (my_bot->GetClass()) {
case Class::Ranger:
if (!tracking_scope.compare("local")) {
base_distance = 30;
tracking_msg = "Local tracking...";
}
else if (!tracking_scope.compare("rare")) {
base_distance = 80;
bool track_named = true;
tracking_msg = "Master tracking...";
}
else { // default to 'all'
base_distance = 80;
tracking_msg = "Advanced tracking...";
}
break;
case Class::Druid:
base_distance = 30;
tracking_msg = "Local tracking...";
break;
case Class::Bard:
base_distance = 20;
tracking_msg = "Near tracking...";
break;
default:
return;
}
if (!base_distance) {
c->Message(Chat::White, "An unknown codition has occurred");
return;
}
my_bot->InterruptSpell();
Bot::BotGroupSay(my_bot, tracking_msg.c_str());
entity_list.ShowSpawnWindow(c, (c->GetLevel() * base_distance), track_named);
}
+1
View File
@@ -0,0 +1 @@
#include "../bot_command.h"
+116
View File
@@ -0,0 +1,116 @@
#include "../bot_command.h"
void bot_command_view_combos(Client *c, const Seperator *sep)
{
const std::string class_substrs[17] = {
"",
"WAR", "CLR", "PAL", "RNG",
"SHD", "DRU", "MNK", "BRD",
"ROG", "SHM", "NEC", "WIZ",
"MAG", "ENC", "BST", "BER"
};
const std::string race_substrs[17] = {
"",
"HUM", "BAR", "ERU", "ELF",
"HIE", "DEF", "HEF", "DWF",
"TRL", "OGR", "HFL", "GNM",
"IKS", "VAH", "FRG", "DRK"
};
const uint16 race_values[17] = {
Race::Doug,
Race::Human, Race::Barbarian, Race::Erudite, Race::WoodElf,
Race::HighElf, Race::DarkElf, Race::HalfElf, Race::Dwarf,
Race::Troll, Race::Ogre, Race::Halfling, Race::Gnome,
Race::Iksar, Race::VahShir, Race::Froglok2, Race::Drakkin
};
if (helper_command_alias_fail(c, "bot_command_view_combos", sep->arg[0], "viewcombos")) {
return;
}
if (helper_is_help_or_usage(sep->arg[1])) {
std::string window_text;
std::string message_separator = " ";
c->Message(Chat::White, fmt::format("Usage: {} [Race]", sep->arg[0]).c_str());
window_text.append("<c \"#FFFF\">");
for (int race_id = 0; race_id <= 15; ++race_id) {
window_text.append(message_separator);
window_text.append(
fmt::format(
"{} ({})",
race_substrs[race_id + 1],
race_values[race_id + 1]
)
);
message_separator = ", ";
}
c->SendPopupToClient("Bot Races", window_text.c_str());
return;
}
if (sep->arg[1][0] == '\0' || !sep->IsNumber(1)) {
c->Message(Chat::White, "Invalid Race!");
return;
}
const uint16 bot_race = static_cast<uint16>(Strings::ToUnsignedInt(sep->arg[1]));
const std::string race_name = GetRaceIDName(bot_race);
if (!IsPlayerRace(bot_race)) {
c->Message(
Chat::White,
fmt::format(
"{} ({}) is not a race bots can use.",
race_name,
bot_race
).c_str()
);
return;
}
const auto classes_bitmask = database.botdb.GetRaceClassBitmask(bot_race);
std::string window_text;
std::string message_separator = " ";
window_text.append("<c \"#FFFF\">");
const int object_max = 4;
auto object_count = 0;
for (int class_id = 0; class_id <= 15; ++class_id) {
if (classes_bitmask & GetPlayerClassBit(class_id)) {
window_text.append(message_separator);
if (object_count >= object_max) {
window_text.append(DialogueWindow::Break());
object_count = 0;
}
window_text.append(
fmt::format(
"{} ({})",
class_substrs[class_id],
class_id
)
);
++object_count;
message_separator = ", ";
}
}
c->SendPopupToClient(
fmt::format(
"Bot Classes for {} ({})",
race_name,
bot_race
).c_str(),
window_text.c_str()
);
}
+38
View File
@@ -0,0 +1,38 @@
#include "../bot_command.h"
void bot_command_water_breathing(Client *c, const Seperator *sep)
{
bcst_list* local_list = &bot_command_spells[BCEnum::SpT_WaterBreathing];
if (helper_spell_list_fail(c, local_list, BCEnum::SpT_WaterBreathing) || helper_command_alias_fail(c, "bot_command_water_breathing", sep->arg[0], "waterbreathing"))
return;
if (helper_is_help_or_usage(sep->arg[1])) {
c->Message(Chat::White, "usage: (<friendly_target>) %s", sep->arg[0]);
helper_send_usage_required_bots(c, BCEnum::SpT_WaterBreathing);
return;
}
ActionableTarget::Types actionable_targets;
Bot* my_bot = nullptr;
std::list<Bot*> sbl;
MyBots::PopulateSBL_BySpawnedBots(c, sbl);
bool cast_success = false;
for (auto list_iter : *local_list) {
auto local_entry = list_iter;
if (helper_spell_check_fail(local_entry))
continue;
auto target_mob = actionable_targets.Select(c, local_entry->target_type, FRIENDLY);
if (!target_mob)
continue;
my_bot = ActionableBots::Select_ByMinLevelAndClass(c, local_entry->target_type, sbl, local_entry->spell_level, local_entry->caster_class, target_mob);
if (!my_bot)
continue;
cast_success = helper_cast_standard_spell(my_bot, target_mob, local_entry->spell_id);
break;
}
helper_no_available_bots(c, my_bot);
}