[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
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
66 changed files with 9293 additions and 8946 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

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");
}

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
);
}

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);
}

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;
}
}

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;
}
}

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()
);
}
}

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);
}

1427
zone/bot_commands/bot.cpp Normal file

File diff suppressed because it is too large Load Diff

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()
);
}
}
}

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);
}

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);
}
}

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);
}

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);
}

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);
}

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);
}

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" : "");
}

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()
);
}
}
}

View File

@ -0,0 +1 @@
#include "../bot_command.h"

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

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);
}

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()
);
}
}

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);
}

View File

@ -0,0 +1 @@
#include "../bot_command.h"

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()
);
}

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);
}

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);
}
}
}
}

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);
}

View File

@ -0,0 +1 @@
#include "../bot_command.h"

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);
}

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);
}

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);
}

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.");
}
}

View File

@ -0,0 +1 @@
#include "../bot_command.h"

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
zone/bot_commands/pet.cpp Normal file
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);
}
}
}

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") : ("")));
}

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");
}

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
zone/bot_commands/pull.cpp Normal file
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);
}

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")));
}

View File

@ -0,0 +1 @@
#include "../bot_command.h"

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);
}

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);
}

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);
}

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);
}

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
zone/bot_commands/spell.cpp Normal file
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()
);
}

View File

@ -0,0 +1 @@
#include "../bot_command.h"

View File

@ -0,0 +1 @@
#include "../bot_command.h"

View File

@ -0,0 +1 @@
#include "../bot_command.h"

View File

@ -0,0 +1 @@
#include "../bot_command.h"

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);
}

View File

@ -0,0 +1 @@
#include "../bot_command.h"

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
zone/bot_commands/taunt.cpp Normal file
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");
}
}

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
zone/bot_commands/timer.cpp Normal file
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);
}
}
}
}

View File

@ -0,0 +1 @@
#include "../bot_command.h"

View File

@ -0,0 +1 @@
#include "../bot_command.h"

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);
}

View File

@ -0,0 +1 @@
#include "../bot_command.h"

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()
);
}

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);
}