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