[Bots] Bot Overhaul (#4580)

* Implement spell AI pulling, fix throw stone

* more pull tweaks

* holding check at start of ai process

* fully implement ^pull logic to always return, can still be overidden by ^attack

* Rewrite ^pull logic and handling. **MORE**

Add ^setassistee command to set who your bots will assist. Bots will always assist you first before anyone else.

If the rule Bots, AllowCrossGroupRaidAssist is enabled bots will assist the group or raid main assists.

Rewrites logic in handling of pull and returning to ensure bots make it back to their location.

* Move HateLine to a better ID

* cleanup ST_Self logic in CastChecks

* Removed unused BotSpellTypeRequiresLoS

* Move fizzle message to define

* add timer checks to Idle/Engaged/Pursue CastCheck to early terminate

* Add back !IsBotNonSpellFighter() check to the different CastCheck

* Correct IsValidSpellRange

* Implement AAs and harmtouch/layonhands to ^cast --- fix IsValidSpellRange

* Add PetDamageShields and PetResistBuffs to IsPetBotSpellType()

* Add priorities to HateLine inserts for db update

* Remove SpellTypeRequiresCastChecks

* Add bot check to DetermineSpellTargets for IsIllusionSpell

* merge with previous

* Correct bot checks for ST_GroupClientAndPet

* Remove misc target_type checks

* Add lull/aelull to ^cast

* Add more checks for CommandedSubTypes::AETarget

* remove unneeded checks on IsValidSpellTypeBySpellID

* add to aelull

* rewrite GetCorrectSpellType

* Add IsBlockedBuff to CastChecks

* Add spellid option to ^cast to allow casting of a specific spell by ID

* ^cast adjustments for spellid casts

* Add missing alert round for ranged attacks

* More castcheck improvements

* CanUseBotSpell for ^cast

* remove ht/loh from attack ai

* remove SetCombatRoundForAlerts that triggered every engagement

* Add RangedAttackImmunity checks before trying to ranged attack

* move bot backstab to mob

* fix MinStatusToBypassCreateLimit

* more backstab to mob cleanup

* add bot checks to tryheadshot / tryassassinate

* adjust version number for bots

* add back m_mob_check_moving_timer, necessary?

* add sanity checks for classattacks

* Get rid of Bots:BotGroupXP and change logic to support Bots:SameRaidGroupForXP

Bots won't do anything if not in the same group so this should more accurately control only when in the same raid group.

* add "confirm" check to ^delete

* Update bot.cpp

* Remove `id` from bot_settings, correct types

* Implement blocked_buffs and blocked_pet_buffs

* more blocked buff tweaks

* add beneficial check to ^blockedbuffs

* command grammar

* missing )

* Move getnames for categories and settings to mob, rename hptomed/manatomed

* add GetBotSpellCategoryIDByShortName and CopyBotBlockedPetBuffs, update ^defaultsettings command

* cls cleanup

* Allow bots to clear HasProjectIllusion flag

* Add PercentChanceToCastGroupCure

* Implmenet PetCures, add some missing types for defaults/chance to cast

* Change GetRaidByBotName to GetRaidByBot

* Typo on PetBuffs implement

* Change GetSpellListSpellType to GetParentSpellType

* missing from GetChanceToCastBySpellType

* Fix performance in IsValidSpellRange by flipping HasProjectIllusion

* merge with prev

* merge with cls cleanup

* Reorder IsTargetAlreadyReceivingSpell/CheckSpellLevelRestriction/IsBlockedBuff

* Combine GatherGroupSpellTargets and GatherSpellTargets

* Cleanup IsTargetAlreadyReceivingSpell

* Fix ^petsettype to account for usable levels of spells and remove hardcoded level limits.

* Remove Bot_AICheckCloseBeneficialSpells and use AttemptCloseBeneficialSpells for better performance

* remove default hold for resist buffa

* move IsValidSpellRange further down castchecks

* raid optimizations

* correct name checking to match players

* more name checks and add proper soft deletes to bots

* organize some checks in IsImmuneToBotSpell

* Fix GetRaidByBotName and GetRaidByBot checks to not loop unnecessarily

* Move GatherSpellTargets to mob

* Change GetPrioritizedBotSpellsBySpellType to vector

Some slipped through in "organize some checks in IsImmuneToBotSpell"

* Move GatherSpellTargets and Raid to stored variables.

Missing some in "organize some checks in IsImmuneToBotSpell"

* comment out precheck, delays, thresholds, etc logging

missed some in "organize some checks in IsImmuneToBotSpell"

* Missing IsInGroupOrRaid cleanup

* Implement AIBot_spells_by_type to reduce looping when searching for spells

* Add _tempSpellType as placeholder for any future passthru

* todo

* Move bot_list from std::list to std::unordered_map like other entities

* Fix missing raid assignment for GetStoredRaid in IsInGroupOrRaid

* TempPet owned by bots that get the kill will now give exp like a client would

* Remove unnecessary checks in bot process (closescanmoving timer, verify raid, send hp/mana/end packet

* Fix client spell commands from saving the wrong setting

* Cleanup ^copysettings command and add new commands

* Add pet option to ^taunt

No longer has toggle, required on/off option and an optional "pet" option to control pets' taunting state

* Allow pet types to ^cast, prevent failure spam, add cure check

* more raid optimizations, should be final.

10 clients, 710 bots, 10 raids, ~250 pets sits around 3.5% CPU idle

* Move spell range check to proper location

* Implement ^discipline

* remove ^aggressive/^defensive

* remove this for a separate PR

* cleanup

* Add BotGroupSay method

* todo list

* Add missing bot_blocked_buffs to schema

* Remove plural on ^spelltypeidsand ^spelltypenames

* Move spelltype names, spell subtypes, category names and setting names to maps.

* move los checks to mob.cpp

* Bot CampAll fix

* Bots special_attacks.cpp fix

* Add zero check for bot spawn limits

If the spawn limit rule is set to 0 and spawn limit is set by bucket, if no class buckets are set, it defaults to the rule of 0 and renders the player unable to spawn bots.

This adds a check where if the rule and class bucket are 0, it will check for the spawn limit bucket

* Add HasSkill checks to bot special abilities (kick/bash/etc)

* code cleanup 1

* code cleanup 2

* code cleanup 3

* code cleanup 4

* fix ^cast wirh commanded types

* Remove bcspells, fix helper_send_usage_required_bots

* linux build fix

* remove completed todo

* Allow inventory give to specific ID slots

* Update TODO

* Correct slot ranges for inventorygive

* Add zone specific spawn limits and zone specific forced spawn limits

* remove bd. from update queries where it doesn't exist

* Rename _spellSettings to m_bot_spell_settings

* Add IsPetOwnerOfClientBot(), add Lua and Perl methods

* Make botOwnerCharacterID snakecase

* Throw bot_camp_timer behind Bots:Enabled rule

* Move various Bot<>Checks logging to BotSpellChecks

* Remove from LogCategoryName

* Consolidate IsInGroupOrRaid

* Consolidate GatherSpellTargets

* Add missing Bot Spell Type Checks to log

* Add GetParentSpellType when checking spelltypes for idle, engaged, pursue CastChecks.

* Consolidate AttemptForcedCastSpell

* Consolidate SetBotBlockedBuff/SetBotBlockedPetBuff

* Add list option to ^spellpriority commands.

* Move client functions to client_bot

* Move mob functions to mob_bot

* Move bot spdat functions to spdat_bot

* Move SendCommandHelpWindow to SendBotCommandHelpWindow and simplify

* Change char_id to character_id for bot_settings

* update todo

* Fix typo on merge conflict

* Cleanup command format changes, remove hardcoded class IDs in examples.

* Set #illusionblock for players to guide access

* Move client commands for bot spells from gm commands to existing bot commands

* Fix alignment issues

* More alignment fixes

* More cleanup 1

* More cleanup 2

* Fix BotMeditate to med at proper percentages

* Correct GetStopMeleeLevel checks for some buff checks

* Add back hpmanaend update to bot raid, force timer update to prevent spamming

* Remove log

* Cleanup ranged and ammo calculations - Adds throwing check for match

* Add check in distance calculations to stay at range if set even if no ammo or ranged

* Move melee distance calculations to better function

* Add GetBuffTargets helper

* Missing p_item, s_item in CombatRangeInput

* Linux test?

* Reduce GetCorrectBotSpellType branching slightly

This is still an ugly ass function but my brain is melted

* Line fixes

* Make bot pets only do half damage in pvp

* Add bot pet pvp damage to tune

* Add bot pet check for AIYellForHelp

* Add bots to UseSpellImpliedTargeting

* Move toggleranged, togglehelm and illusionblock to new help window. Add actionable support

* Add bot and bot pet checks to various spells, auras and targeting checks that were missing.

* update todo

* New lines

* Correct DoLosChecks

* Remove Log TestDebug

* Remove _Struct from struct declarations

* Add bot check to IsAttackAllowed for GetUltimateOwner to skip entity list where possible

* Wrap SaveBotSettings in Bots Enabled check

* Remove comment

* Wrap bot setting loading for clients in bots enabled rule

* Cleanup BlockedBuffs logic in SpellOnTarget

* Rename BotSpells_Struct/BotSpells_Struct_wIndex

* Rename spawn/create status bypass rules, fix return for spawn limit

* Remove unnecessary return in CanBuffStack, cleanup

* Enable recastdelay support for clients

* Remove unused variables

* Rename _assistee to bot_assistee

* hardcode BotCommandHelpWindow colors

* todo

* Fix ^cast summoncorpse

* todo

* Reimplement secondary colors to BotSendCommandHelpWindow

* Give ^copysettings/^defaultsettings more options, cleanup.

* Cleanup some commands

* Add comment to CheckLosCheat/CheckLosCheatExempt

* Make struct BotSpellSettings snake case

* Allow duplicate casts of same spell on target for heals and cures

* Add default delay to cures

* Remove unused methods

* Implement missing ^spellresistlimits/^resistlimits command

* Move functions out of mob.h and cleanup

* Return for GetRawBotList

This checks offline bots too

* Rename BotGroupSay to RaidGroupSay

* Prevent bots from forming their own group if a bot that is a group leader is removed from the raid

* Linux fix?

* IsPetOwner fixes

* Add remove option to list for ^blockedbuffs / ^blockedpetbuffs

* Implement ^spellannouncecasts to toggle announcing casts of spell types

* Remove rule Bots:BardsAnnounceCasts

* Update bot.h

* Remove unused no_pets option from GatherSpellTargets

* Move ^attack response back to normal chat window (other)

* Set lower limit of spell delays to 100 rather than 1

* Correct pet checks on GetUltimateSpell functions

* Add rules (Bots, AICastSpellTypeDelay, Bots, AICastSpellTypeHeldDelay) to prevent spamming of failed spell type AI casts

* Correct pet buff type logic to catch DS/Resists with other spell effects in them

* Fix defaults for clients

* Add more logic for necros/shaman for default heal thresholds due to lich and canni

* Rename SpellHold, SpellDelay, SpellMinThreshold, SpellMaxThreshold, SpellRecastDelay to fit SpellType style naming

* Use GetTempSpellType() for announce check in RaidGroupSay

* Make all spell shortnames plural where applicable

* Update bot.cpp

* Bots:BotsUseLiveBlockedMessage filter to spell failure

* Move GetSpellTargetList to only get called when necessary to reduce overhead

* formatting

* Formatting

* Simplify case SE_Illusion and SE_IllusionCopy for GetIllusionBlock

* Clean up InterruptSpell

* Cleanup IsBot() checks for DetermineSpellTargets->ST_GroupClientAndPet

* Cleanup range/aoe_range check in SpellFinished

* Cleanup DetermineSpellTargets->ST_GroupNoPets

* Cleanup DetermineSpellTargets->ST_Self for bot summon corpse

* Cleanup DetermineSpellTargets->ST_Pet

* Cleanup bot logic in TryBackstab

* Cleanup IsAttackAllowed checks for bots and their pets

* Cleanup StopMoving for bots

* Cleanup CanThisClassTripleAttack

* Fix casting for GetIllusionBlock checks

* Formatting

* Fix DetermineSpellTargets for group spells (this also wasn't properly checking the rule Character:EnableTGB in master)

* Cleanup spelltarget grabbing logic, consolidate group heals in to GetNumberNeedingHealedInGroup

* Throw added client los pet checks behind LoS cheat rule for bots

* CLeanup give_exp on npc death logic and ensure client pets always pass.

* Undo unintended rename from previous refactor

* Remove pointless Bots, SameRaidGroupForXP rule

* Revision to 0690783a9d

---------

Co-authored-by: Akkadius <akkadius1@gmail.com>
This commit is contained in:
nytmyr
2025-02-03 04:02:42 -06:00
committed by GitHub
parent 4fda3c045e
commit f466964db8
141 changed files with 24337 additions and 9163 deletions
+30 -15
View File
@@ -3,22 +3,37 @@
void bot_command_actionable(Client* c, const Seperator* sep)
{
if (helper_command_alias_fail(c, "bot_command_actionable", sep->arg[0], "actionable")) {
c->Message(Chat::White, "note: Lists actionable command arguments and use descriptions");
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");
BotCommandHelpParams p;
p.description = { "Lists actionable command arguments and use descriptions." };
p.notes = {
"[target] - uses the command on the target. Some commands will default to target if no actionable is selected.",
"[byname] [name] - selects a bot by name their name.",
"[ownergroup] - selects all bots in the owner's group.",
"[ownerraid] - selects all bots in the owner's raid.",
"[targetgroup] - selects all bots in the target's group.",
"[namesgroup] [name] - selects all bots in [name]'s group.",
"[healrotation] [name] - selects all member and target bots of a heal rotation where [name] is a member.",
"[healrotationmembers] [name] - selects all member bots of a heal rotation where [name] is a member.",
"[healrotationtargets] [name] - selects all target bots of a heal rotation where [name] is a member.",
"[mmr] - selects all bots that are currently at max melee range.",
"[byclass] - selects all bots of the chosen class.",
"[byrace] - selects all bots of the chosen race.",
"[spawned] - selects all spawned bots.",
"[all] - selects all spawned bots.",
"<br>",
"You may only select your own bots."
};
std::string popup_text = c->SendBotCommandHelpWindow(p);
popup_text = DialogueWindow::Table(popup_text);
c->SendPopupToClient(sep->arg[0], popup_text.c_str());
return;
}
-91
View File
@@ -1,91 +0,0 @@
#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
);
}
+41 -41
View File
@@ -2,19 +2,19 @@
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");
std::vector<const char*> subcommand_list = {
"botbeardcolor",
"botbeardstyle",
"botdetails",
"botdyearmor",
"boteyes",
"botface",
"bothaircolor",
"bothairstyle",
"botheritage",
"bottattoo",
"botwoad"
};
if (helper_command_alias_fail(c, "bot_command_appearance", sep->arg[0], "botappearance"))
return;
@@ -45,11 +45,11 @@ void bot_command_beard_color(Client *c, const Seperator *sep)
uint8 uvalue = Strings::ToInt(sep->arg[1]);
auto fail_type = BCEnum::AFT_None;
auto fail_type = AFT_None;
if (my_bot->GetGender() != Gender::Male && my_bot->GetRace() != DWARF)
fail_type = BCEnum::AFT_GenderRace;
fail_type = AFT_GenderRace;
else if (!PlayerAppearance::IsValidBeardColor(my_bot->GetRace(), my_bot->GetGender(), uvalue))
fail_type = BCEnum::AFT_Value;
fail_type = AFT_Value;
else
my_bot->SetBeardColor(uvalue);
@@ -82,11 +82,11 @@ void bot_command_beard_style(Client *c, const Seperator *sep)
uint8 uvalue = Strings::ToInt(sep->arg[1]);
auto fail_type = BCEnum::AFT_None;
auto fail_type = AFT_None;
if (my_bot->GetGender() != Gender::Male && my_bot->GetRace() != DWARF)
fail_type = BCEnum::AFT_GenderRace;
fail_type = AFT_GenderRace;
else if (!PlayerAppearance::IsValidBeard(my_bot->GetRace(), my_bot->GetGender(), uvalue))
fail_type = BCEnum::AFT_Value;
fail_type = AFT_Value;
else
my_bot->SetBeard(uvalue);
@@ -121,11 +121,11 @@ void bot_command_details(Client *c, const Seperator *sep)
uint32 uvalue = Strings::ToInt(sep->arg[1]);
auto fail_type = BCEnum::AFT_None;
auto fail_type = AFT_None;
if (my_bot->GetRace() != DRAKKIN)
fail_type = BCEnum::AFT_Race;
fail_type = AFT_Race;
else if (!PlayerAppearance::IsValidDetail(my_bot->GetRace(), my_bot->GetGender(), uvalue))
fail_type = BCEnum::AFT_Value;
fail_type = AFT_Value;
else
my_bot->SetDrakkinDetails(uvalue);
@@ -158,7 +158,7 @@ void bot_command_dye_armor(Client *c, const Seperator *sep)
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]))",
"Usage: {} [Material Slot] [Red: 0-255] [Green: 0-255] [Blue: 0-255] ([actionable: target | byname | ownergroup | ownerraid | targetgroup | namesgroup | healrotationmembers | healrotationtargets | mmr | byclass | byrace | spawned] ([actionable_name]))",
sep->arg[0]
).c_str()
);
@@ -214,7 +214,7 @@ void bot_command_dye_armor(Client *c, const Seperator *sep)
uint32 rgb_value = ((uint32)red_value << 16) | ((uint32)green_value << 8) | ((uint32)blue_value);
std::list<Bot*> sbl;
std::vector<Bot*> sbl;
auto ab_type = ActionableBots::PopulateSBL(c, sep->arg[5], sbl, ab_mask);
if (ab_type == ActionableBots::ABT_None) {
return;
@@ -280,9 +280,9 @@ void bot_command_eyes(Client *c, const Seperator *sep)
//else if (!arg2.compare("right"))
// eye_bias = 2;
auto fail_type = BCEnum::AFT_None;
auto fail_type = AFT_None;
if (!PlayerAppearance::IsValidEyeColor(my_bot->GetRace(), my_bot->GetGender(), uvalue)) {
fail_type = BCEnum::AFT_Value;
fail_type = AFT_Value;
}
else {
//if (eye_bias == 1) {
@@ -327,9 +327,9 @@ void bot_command_face(Client *c, const Seperator *sep)
uint8 uvalue = Strings::ToInt(sep->arg[1]);
auto fail_type = BCEnum::AFT_None;
auto fail_type = AFT_None;
if (!PlayerAppearance::IsValidFace(my_bot->GetRace(), my_bot->GetGender(), uvalue)) {
fail_type = BCEnum::AFT_Value;
fail_type = AFT_Value;
}
else {
uint8 old_woad = 0;
@@ -367,9 +367,9 @@ void bot_command_hair_color(Client *c, const Seperator *sep)
uint8 uvalue = Strings::ToInt(sep->arg[1]);
auto fail_type = BCEnum::AFT_None;
auto fail_type = AFT_None;
if (!PlayerAppearance::IsValidHairColor(my_bot->GetRace(), my_bot->GetGender(), uvalue))
fail_type = BCEnum::AFT_Value;
fail_type = AFT_Value;
else
my_bot->SetHairColor(uvalue);
@@ -402,9 +402,9 @@ void bot_command_hairstyle(Client *c, const Seperator *sep)
uint8 uvalue = Strings::ToInt(sep->arg[1]);
auto fail_type = BCEnum::AFT_None;
auto fail_type = AFT_None;
if (!PlayerAppearance::IsValidHair(my_bot->GetRace(), my_bot->GetGender(), uvalue))
fail_type = BCEnum::AFT_Value;
fail_type = AFT_Value;
else
my_bot->SetHairStyle(uvalue);
@@ -439,11 +439,11 @@ void bot_command_heritage(Client *c, const Seperator *sep)
uint32 uvalue = Strings::ToInt(sep->arg[1]);
auto fail_type = BCEnum::AFT_None;
auto fail_type = AFT_None;
if (my_bot->GetRace() != DRAKKIN)
fail_type = BCEnum::AFT_Race;
fail_type = AFT_Race;
else if (!PlayerAppearance::IsValidHeritage(my_bot->GetRace(), my_bot->GetGender(), uvalue))
fail_type = BCEnum::AFT_Value;
fail_type = AFT_Value;
else
my_bot->SetDrakkinHeritage(uvalue);
@@ -478,11 +478,11 @@ void bot_command_tattoo(Client *c, const Seperator *sep)
uint32 uvalue = Strings::ToInt(sep->arg[1]);
auto fail_type = BCEnum::AFT_None;
auto fail_type = AFT_None;
if (my_bot->GetRace() != DRAKKIN)
fail_type = BCEnum::AFT_Race;
fail_type = AFT_Race;
else if (!PlayerAppearance::IsValidTattoo(my_bot->GetRace(), my_bot->GetGender(), uvalue))
fail_type = BCEnum::AFT_Value;
fail_type = AFT_Value;
else
my_bot->SetDrakkinTattoo(uvalue);
@@ -515,12 +515,12 @@ void bot_command_woad(Client *c, const Seperator *sep)
uint8 uvalue = Strings::ToInt(sep->arg[1]);
auto fail_type = BCEnum::AFT_None;
auto fail_type = AFT_None;
if (my_bot->GetRace() != BARBARIAN) {
fail_type = BCEnum::AFT_Race;
fail_type = AFT_Race;
}
else if (!PlayerAppearance::IsValidWoad(my_bot->GetRace(), my_bot->GetGender(), uvalue)) {
fail_type = BCEnum::AFT_Value;
fail_type = AFT_Value;
}
else {
uint8 old_face = (my_bot->GetLuclinFace() % 10);
+1
View File
@@ -11,6 +11,7 @@ void bot_command_apply_poison(Client* c, const Seperator* sep)
if (helper_is_help_or_usage(sep->arg[1])) {
c->Message(Chat::White, "usage: <rogue_bot_target> %s", sep->arg[0]);
c->Message(Chat::White, "note: Applies cursor-held poison to a rogue bot's weapon");
return;
}
+18 -8
View File
@@ -5,17 +5,25 @@ 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]);
c->Message(Chat::White, "usage: <enemy_target> %s [actionable: byname | ownergroup | ownerraid | namesgroup | healrotation | mmr | byclass | byrace | default: spawned] ([actionable_name])", sep->arg[0]);
c->Message(Chat::White, "note: Orders bots to attack a designated target");
return;
}
const int ab_mask = ActionableBots::ABM_Type2;
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");
c->Message(Chat::Yellow, "You must <target> an enemy to use this command");
return;
}
if (!c->DoLosChecks(target_mob)) {
c->Message(Chat::Red, "You must have Line of Sight to use this command.");
return;
}
@@ -26,11 +34,13 @@ void bot_command_attack(Client *c, const Seperator *sep)
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;
std::vector<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;
}
@@ -42,7 +52,7 @@ void bot_command_attack(Client *c, const Seperator *sep)
size_t attacker_count = 0;
Bot *first_attacker = nullptr;
sbl.remove(nullptr);
sbl.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end());
for (auto bot_iter : sbl) {
if (bot_iter->GetAppearance() != eaDead && bot_iter->GetBotStance() != Stance::Passive) {
@@ -57,8 +67,8 @@ void bot_command_attack(Client *c, const Seperator *sep)
}
if (attacker_count == 1 && first_attacker) {
Bot::BotGroupSay(
first_attacker,
c->Message(
Chat::Green,
fmt::format(
"Attacking {}.",
target_mob->GetCleanName()
@@ -66,7 +76,7 @@ void bot_command_attack(Client *c, const Seperator *sep)
);
} else {
c->Message(
Chat::White,
Chat::Green,
fmt::format(
"{} of your bots are attacking {}.",
sbl.size(),
+146
View File
@@ -0,0 +1,146 @@
#include "../bot_command.h"
void bot_command_behind_mob(Client* c, const Seperator* sep)
{
if (helper_command_alias_fail(c, "bot_command_behind_mob", sep->arg[0], "behindmob")) {
c->Message(Chat::White, "note: Toggles whether or not bots will stay behind the mob during combat.");
return;
}
if (helper_is_help_or_usage(sep->arg[1])) {
BotCommandHelpParams p;
p.description = { "Toggles whether or not bots will stay behind the mob during combat." };
p.example_format = { fmt::format("{} [value] [actionable]", sep->arg[0]) };
p.examples_one =
{
"To set Monks to stay behind the mob:",
fmt::format("{} 1 byclass {}", sep->arg[0], Class::Monk)
};
p.examples_two =
{
"To force all bots to stay behind mobs:",
fmt::format("{} 1 spawned", sep->arg[0])
};
p.examples_three =
{
"To check the behind mob status of all bots:",
fmt::format("{} current spawned", sep->arg[0])
};
p.actionables = { "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" };
std::string popup_text = c->SendBotCommandHelpWindow(p);
popup_text = DialogueWindow::Table(popup_text);
c->SendPopupToClient(sep->arg[0], popup_text.c_str());
if (RuleB(Bots, SendClassRaceOnHelp)) {
c->Message(
Chat::Yellow,
fmt::format(
"Use {} for information about race/class IDs.",
Saylink::Silent("^classracelist")
).c_str()
);
}
return;
}
std::string arg1 = sep->arg[1];
int ab_arg = 1;
bool current_check = false;
uint32 type_value = 0;
if (sep->IsNumber(1)) {
type_value = atoi(sep->arg[1]);
++ab_arg;
if (type_value < 0 || type_value > 1) {
c->Message(Chat::Yellow, "You must enter either 0 for disabled or 1 for enabled.");
return;
}
}
else if (!arg1.compare("current")) {
++ab_arg;
current_check = true;
}
else {
c->Message(
Chat::Yellow,
fmt::format(
"Incorrect argument, use {} for information regarding this command.",
Saylink::Silent(
fmt::format("{} help", sep->arg[0])
)
).c_str()
);
return;
}
const int ab_mask = ActionableBots::ABM_Type1;
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::vector<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.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end());
Bot* first_found = nullptr;
int success_count = 0;
for (auto my_bot : sbl) {
if (my_bot->BotPassiveCheck()) {
continue;
}
if (!first_found) {
first_found = my_bot;
}
if (current_check) {
c->Message(
Chat::Green,
fmt::format(
"{} says, 'I {} stay behind mobs.'",
my_bot->GetCleanName(),
my_bot->GetBehindMob() ? "will" : "will not"
).c_str()
);
}
else {
my_bot->SetBehindMob(type_value);
++success_count;
}
}
if (!current_check) {
if (success_count == 1 && first_found) {
c->Message(
Chat::Green,
fmt::format(
"{} says, 'I {} stay behind mobs.'",
first_found->GetCleanName(),
first_found->GetBehindMob() ? "will now" : "will no longer"
).c_str()
);
}
else {
c->Message(
Chat::Green,
fmt::format(
"{} of your bots {} stay behind mobs.",
success_count,
type_value ? "will now" : "will no longer"
).c_str()
);
}
}
}
-41
View File
@@ -1,41 +0,0 @@
#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);
}
+460
View File
@@ -0,0 +1,460 @@
#include "../bot_command.h"
void bot_command_blocked_buffs(Client* c, const Seperator* sep)
{
if (!RuleB(Bots, AllowBotBlockedBuffs)) {
c->Message(Chat::Yellow, "This command is disabled.");
return;
}
if (helper_command_alias_fail(c, "bot_command_blocked_buffs", sep->arg[0], "blockedbuffs")) {
c->Message(Chat::White, "note: Allows you to set, view and wipe blocked buffs for the selected bots.");
return;
}
if (helper_is_help_or_usage(sep->arg[1])) {
BotCommandHelpParams p;
p.description = { "Allows you to set, view and wipe blocked buffs for the selected bots." };
p.notes = { "- You can 'set' spells to be blocked, 'remove' spells from the blocked list, 'list' the current blocked spells or 'wipe' the entire list." };
p.example_format = { fmt::format("{} [add [ID] | remove [ID] | list | wipe] [actionable, default: target]", sep->arg[0]) };
p.examples_one =
{
"To add Courage(Spell ID #202) to the targeted bot's blocked list:",
fmt::format("{} add 202", sep->arg[0])
};
p.examples_two =
{
"To view the targeted bot's blocked buff list:",
fmt::format("{} list", sep->arg[0])
};
p.examples_three =
{
"To wipe all Warriors bots' blocked buff list:",
fmt::format( "{} wipe byclass {}", sep->arg[0], Class::Warrior)
};
p.actionables = { "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" };
std::string popup_text = c->SendBotCommandHelpWindow(p);
popup_text = DialogueWindow::Table(popup_text);
c->SendPopupToClient(sep->arg[0], popup_text.c_str());
c->Message(
Chat::Yellow,
fmt::format(
"You can also control bot buffs ({}).",
Saylink::Silent("^blockedbuffs help", "^blockedbuffs")
).c_str()
);
if (RuleB(Bots, SendClassRaceOnHelp)) {
c->Message(
Chat::Yellow,
fmt::format(
"Use {} for information about race/class IDs.",
Saylink::Silent("^classracelist")
).c_str()
);
}
return;
}
std::string arg1 = sep->arg[1];
std::string arg2 = sep->arg[2];
int ab_arg = 2;
bool add = false;
bool remove = false;
bool list = false;
bool wipe = false;
uint16 spell_id;
if (!arg1.compare("add")) {
if (!sep->IsNumber(2) || !IsValidSpell(atoi(sep->arg[2])) || !IsBeneficialSpell(atoi(sep->arg[2]))) {
c->Message(Chat::Yellow, "You must enter a valid spell ID.");
return;
}
add = true;
spell_id = atoi(sep->arg[2]);
++ab_arg;
}
else if (!arg1.compare("remove")) {
if (!sep->IsNumber(2) || !IsValidSpell(atoi(sep->arg[2]))) {
c->Message(Chat::Yellow, "You must enter a valid spell ID.");
return;
}
remove = true;
spell_id = atoi(sep->arg[2]);
++ab_arg;
}
else if (!arg1.compare("list")) {
list = true;
}
else if (!arg1.compare("wipe")) {
wipe = true;
}
else {
c->Message(
Chat::Yellow,
fmt::format(
"Incorrect argument, use {} for information regarding this command.",
Saylink::Silent(
fmt::format("{} help", sep->arg[0])
)
).c_str()
);
return;
}
const int ab_mask = ActionableBots::ABM_Type1;
std::string actionable_arg = sep->arg[ab_arg];
if (actionable_arg.empty()) {
actionable_arg = "target";
}
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::vector<Bot*> sbl;
if (ActionableBots::PopulateSBL(c, actionable_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.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end());
bool is_success = false;
uint16 success_count = 0;
Bot* first_found = nullptr;
for (auto bot_iter : sbl) {
if (!bot_iter->IsInGroupOrRaid(c)) {
continue;
}
if (!first_found) {
first_found = bot_iter;
}
if (add) {
bot_iter->SetBotBlockedBuff(spell_id, true);
}
else if (remove) {
bot_iter->SetBotBlockedBuff(spell_id, false);
}
else if (list) {
std::vector<BotBlockedBuffs> blocked_buffs = bot_iter->GetBotBlockedBuffs();
bool found = false;
if (!blocked_buffs.empty()) {
for (auto& blocked_buff : blocked_buffs) {
if (blocked_buff.blocked == 1 && IsValidSpell(blocked_buff.spell_id)) {
found = true;
c->Message(
Chat::Yellow,
fmt::format(
"{} says, '{} [#{}] is currently blocked. [{}]'",
bot_iter->GetCleanName(),
spells[blocked_buff.spell_id].name,
blocked_buff.spell_id,
Saylink::Silent(fmt::format("^blockedbuffs remove {}", blocked_buff.spell_id),"Remove")
).c_str()
);
}
}
}
if (!found) {
c->Message(
Chat::Yellow,
fmt::format(
"{} says, 'I am not currently blocking any buffs.'",
bot_iter->GetCleanName()
).c_str()
);
}
}
else if (wipe) {
bot_iter->ClearBotBlockedBuffs();
c->Message(
Chat::Yellow,
fmt::format(
"{} says, I have wiped my blocked buffs list.'",
bot_iter->GetCleanName()
).c_str()
);
}
is_success = true;
++success_count;
}
if (!is_success) {
c->Message(Chat::Yellow, "No bots were selected.");
}
else {
if (add || remove) {
c->Message(
Chat::Yellow,
fmt::format(
"{} {} {} blocking {} [#{}]",
((success_count == 1 && first_found) ? first_found->GetCleanName() : (fmt::format("{}", success_count).c_str())),
((success_count == 1 && first_found) ? "is" : "of your bots"),
(add ? "now" : "no longer"),
spells[spell_id].name,
spell_id
).c_str()
);
}
}
}
void bot_command_blocked_pet_buffs(Client* c, const Seperator* sep)
{
if (!RuleB(Bots, AllowBotBlockedBuffs)) {
c->Message(Chat::Yellow, "This command is disabled.");
return;
}
if (helper_command_alias_fail(c, "bot_command_blocked_pet_buffs", sep->arg[0], "blockedpetbuffs")) {
c->Message(Chat::White, "note: Allows you to set, view and wipe blocked pet buffs for the selected bots.");
return;
}
if (helper_is_help_or_usage(sep->arg[1])) {
BotCommandHelpParams p;
p.description = { "Allows you to set, view and wipe blocked pet buffs for the selected bots." };
p.notes =
{
"- You can 'set' spells to be blocked, 'remove' spells from the blocked list, 'list' the current blocked spells or 'wipe' the entire list.",
"- This controls whether or not any pet the selected bot(s) own will prevent certain beneficial buffs from landing on them."
};
p.example_format = { fmt::format("{} [add [ID] | remove [ID] | list | wipe] [actionable, default: target]", sep->arg[0]) };
p.examples_one =
{
"To add Courage (Spell ID #202) to the targeted bot's blocked list:",
fmt::format(
"{} add 202",
sep->arg[0],
Bot::GetSpellTypeShortNameByID(BotSpellTypes::Nuke)
)
};
p.examples_two =
{
"To view the targeted bot's blocked buff list:",
fmt::format(
"{} list",
sep->arg[0]
)
};
p.examples_three =
{
"To wipe all Warriors bots' blocked buff list:",
fmt::format(
"{} wipe byclass {}",
sep->arg[0],
Class::Warrior
)
};
p.actionables = { "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" };
std::string popup_text = c->SendBotCommandHelpWindow(p);
popup_text = DialogueWindow::Table(popup_text);
c->SendPopupToClient(sep->arg[0], popup_text.c_str());
c->Message(
Chat::Yellow,
fmt::format(
"You can also control pet buffs ({}).",
Saylink::Silent("^blockedpetbuffs help", "^blockedpetbuffs")
).c_str()
);
if (RuleB(Bots, SendClassRaceOnHelp)) {
c->Message(
Chat::Yellow,
fmt::format(
"Use {} for information about race/class IDs.",
Saylink::Silent("^classracelist")
).c_str()
);
}
return;
}
std::string arg1 = sep->arg[1];
std::string arg2 = sep->arg[2];
int ab_arg = 2;
bool add = false;
bool remove = false;
bool list = false;
bool wipe = false;
uint16 spell_id;
if (!arg1.compare("add")) {
if (!sep->IsNumber(2) || !IsValidSpell(atoi(sep->arg[2])) || !IsBeneficialSpell(atoi(sep->arg[2]))) {
c->Message(Chat::Yellow, "You must enter a valid spell ID.");
return;
}
add = true;
spell_id = atoi(sep->arg[2]);
++ab_arg;
}
else if (!arg1.compare("remove")) {
if (!sep->IsNumber(2) || !IsValidSpell(atoi(sep->arg[2]))) {
c->Message(Chat::Yellow, "You must enter a valid spell ID.");
return;
}
remove = true;
spell_id = atoi(sep->arg[2]);
++ab_arg;
}
else if (!arg1.compare("list")) {
list = true;
}
else if (!arg1.compare("wipe")) {
wipe = true;
}
else {
c->Message(
Chat::Yellow,
fmt::format(
"Incorrect argument, use {} for information regarding this command.",
Saylink::Silent(
fmt::format("{} help", sep->arg[0])
)
).c_str()
);
return;
}
const int ab_mask = ActionableBots::ABM_Type1;
std::string actionable_arg = sep->arg[ab_arg];
if (actionable_arg.empty()) {
actionable_arg = "target";
}
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::vector<Bot*> sbl;
if (ActionableBots::PopulateSBL(c, actionable_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.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end());
bool is_success = false;
uint16 success_count = 0;
Bot* first_found = nullptr;
for (auto bot_iter : sbl) {
if (!bot_iter->IsInGroupOrRaid(c)) {
continue;
}
if (!first_found) {
first_found = bot_iter;
}
if (add) {
bot_iter->SetBotBlockedPetBuff(spell_id, true);
}
else if (remove) {
bot_iter->SetBotBlockedPetBuff(spell_id, false);
}
else if (list) {
std::vector<BotBlockedBuffs> blocked_buffs = bot_iter->GetBotBlockedBuffs();
bool found = false;
if (!blocked_buffs.empty()) {
for (auto& blocked_buff : blocked_buffs) {
if (blocked_buff.blocked_pet == 1 && IsValidSpell(blocked_buff.spell_id)) {
found = true;
c->Message(
Chat::Yellow,
fmt::format(
"{} says, '{} [#{}] is currently blocked for my pet. [{}]'",
bot_iter->GetCleanName(),
spells[blocked_buff.spell_id].name,
blocked_buff.spell_id,
Saylink::Silent(fmt::format("^blockedpetbuffs remove {}", blocked_buff.spell_id), "Remove")
).c_str()
);
}
}
}
if (!found) {
c->Message(
Chat::Yellow,
fmt::format(
"{} says, 'I am not currently blocking any pet buffs.'",
bot_iter->GetCleanName()
).c_str()
);
}
}
else if (wipe) {
bot_iter->ClearBotBlockedBuffs();
c->Message(
Chat::Yellow,
fmt::format(
"{} says, I have wiped my blocked buffs list.'",
bot_iter->GetCleanName()
).c_str()
);
}
is_success = true;
++success_count;
}
if (!is_success) {
c->Message(Chat::Yellow, "No bots were selected.");
}
else {
if (add || remove) {
c->Message(
Chat::Yellow,
fmt::format(
"{} {} {} blocking {} [#{}] on their pet.",
((success_count == 1 && first_found) ? first_found->GetCleanName() : (fmt::format("{}", success_count).c_str())),
((success_count == 1 && first_found) ? "is" : "of your bots"),
(add ? "now" : "no longer"),
spells[spell_id].name,
spell_id
).c_str()
);
}
}
}
+855 -304
View File
File diff suppressed because it is too large Load Diff
+47
View File
@@ -0,0 +1,47 @@
#include "../bot_command.h"
void bot_command_bot_settings(Client* c, const Seperator* sep)
{
std::vector<const char*> subcommand_list = {
"behindmob",
"blockedbuffs",
"blockedpetbuffs",
"distanceranged",
"copysettings",
"defaultsettings",
"enforcespelllist",
"follow",
"followdistance",
"illusionblock",
"maxmeleerange",
"owneroption",
"petsettype",
"sithppercent",
"sitincombat",
"sitmanapercent",
"spellaggrochecks",
"spellannouncecasts",
"spelldelays",
"spellengagedpriority",
"spellholds",
"spellidlepriority",
"spellmaxhppct",
"spellmaxmanapct",
"spellmaxthresholds",
"spellminhppct",
"spellminmanapct",
"spellminthresholds",
"spellpursuepriority",
"spellresistlimits",
"spelltargetcount",
"spelllist",
"stance",
"togglehelm",
"bottoggleranged"
};
if (helper_command_alias_fail(c, "bot_command_bot_settings", sep->arg[0], "botsettings"))
return;
helper_send_available_subcommands(c, "botsettings", subcommand_list);
}
+625
View File
@@ -0,0 +1,625 @@
#include "../bot_command.h"
void bot_command_cast(Client* c, const Seperator* sep)
{
if (helper_command_alias_fail(c, "bot_command_cast", sep->arg[0], "cast")) {
c->Message(Chat::White, "note: Commands bots to force cast a specific spell type, ignoring all settings (holds, delays, thresholds, etc).");
return;
}
if (helper_is_help_or_usage(sep->arg[1])) {
BotCommandHelpParams p;
p.description = { "Commands bots to force cast a specific spell type, ignoring all settings (holds, delays, thresholds, etc)." };
p.notes =
{
"- This will interrupt any spell currently being cast by bots told to use the command",
"- Bots will still check to see if they have the spell in their spell list, whether the target is immune, spell is allowed and all other sanity checks for spells",
fmt::format(
"- You can use {} aa # to cast any clickable AA or specifically {} harmtouch / {} layonhands"
, sep->arg[0]
, sep->arg[0]
, sep->arg[0]
)
};
p.example_format =
{
fmt::format("{} [Type Shortname] [actionable, default: spawned]", sep->arg[0]),
fmt::format("{} [Type ID] [actionable, default: spawned]", sep->arg[0])
};
p.examples_one =
{
"To tell everyone to Nuke the target:",
fmt::format(
"{} {}",
sep->arg[0],
Bot::GetSpellTypeShortNameByID(BotSpellTypes::Nuke)
),
fmt::format(
"{} {}",
sep->arg[0],
BotSpellTypes::Nuke
)
};
p.examples_two =
{
"To tell Skbot to Harm Touch the target:",
fmt::format(
"{} aa 6000 byname Skbot",
sep->arg[0]
),
fmt::format(
"{} harmtouch byname Skbot",
sep->arg[0]
)
};
p.examples_three =
{
"To tell all bots to try to cast spell #93 (Burst of Flame)",
fmt::format(
"{} spellid 93",
sep->arg[0]
)
};
p.actionables = { "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" };
std::string popup_text = c->SendBotCommandHelpWindow(p);
popup_text = DialogueWindow::Table(popup_text);
c->SendPopupToClient(sep->arg[0], popup_text.c_str());
c->SendSpellTypePrompts(true);
c->Message(
Chat::Yellow,
fmt::format(
"Use help after any command type for more subtypes to use, for example: {}.",
Saylink::Silent("^cast invisibility help")
).c_str()
);
if (RuleB(Bots, SendClassRaceOnHelp)) {
c->Message(
Chat::Yellow,
fmt::format(
"Use {} for information about race/class IDs.",
Saylink::Silent("^classracelist")
).c_str()
);
}
return;
}
std::string arg1 = sep->arg[1];
std::string arg2 = sep->arg[2];
//AA help
if (!arg1.compare("aa") && !arg2.compare("help")) {
c->Message(Chat::Yellow, "Enter the ID of an AA to attempt to cast.", sep->arg[0]);
}
//Commanded type help prompts
if (!arg2.compare("help")) {
c->Message(Chat::Yellow, "You can also use [single], [group], [ae]. Ex: ^cast movementspeed group.", sep->arg[0]);
}
if (!arg1.compare("invisibility") && !arg2.compare("help")) {
c->Message(
Chat::Yellow,
fmt::format(
"Available options for {} are: {}, {}, {}, {}.",
sep->arg[0],
Saylink::Silent("^cast invisibility see", "see"),
Saylink::Silent("^cast invisibility invis", "invis"),
Saylink::Silent("^cast invisibility undead", "undead"),
Saylink::Silent("^cast invisibility animals", "animals")
).c_str()
);
return;
}
if (!arg1.compare("size") && !arg2.compare("help")) {
c->Message(
Chat::Yellow,
fmt::format(
"Available options for {} are: {}, {}.",
sep->arg[0],
Saylink::Silent("^cast size grow", "grow"),
Saylink::Silent("^cast size shrink", "shrink")
).c_str()
);
return;
}
if (!arg1.compare("movementspeed") && !arg2.compare("help")) {
c->Message(
Chat::Yellow,
fmt::format(
"Available options for {} are: {}, {}.",
sep->arg[0],
Saylink::Silent("^cast movementspeed selo"), "selo"
).c_str()
);
return;
}
if (!arg2.compare("help")) {
c->Message(Chat::Yellow, "There are no additional options for {}.", sep->arg[0]);
return;
}
int ab_arg = 2;
uint16 spell_type = UINT16_MAX;
uint16 sub_type = UINT16_MAX;
uint16 sub_target_type = UINT16_MAX;
bool aa_type = false;
int aa_id = 0;
bool by_spell_id = false;
uint16 chosen_spell_id = UINT16_MAX;
if (!arg1.compare("aa") || !arg1.compare("harmtouch") || !arg1.compare("layonhands")) {
if (!RuleB(Bots, AllowCastAAs)) {
c->Message(Chat::Yellow, "This commanded type is currently disabled.");
return;
}
if (!arg1.compare("harmtouch")) {
aa_id = zone->GetAlternateAdvancementAbilityByRank(aaHarmTouch)->id;
}
else if (!arg1.compare("layonhands")) {
aa_id = zone->GetAlternateAdvancementAbilityByRank(aaLayonHands)->id;
}
else if (!sep->IsNumber(2) || !zone->GetAlternateAdvancementAbility(Strings::ToInt(arg2))) {
c->Message(Chat::Yellow, "You must enter an AA ID.");
return;
}
else {
++ab_arg;
aa_id = Strings::ToInt(arg2);
}
aa_type = true;
}
if (!arg1.compare("spellid")) {
if (!RuleB(Bots, AllowForcedCastsBySpellID)) {
c->Message(Chat::Yellow, "This commanded type is currently disabled.");
return;
}
if (sep->IsNumber(2) && IsValidSpell(atoi(sep->arg[2]))) {
++ab_arg;
chosen_spell_id = atoi(sep->arg[2]);
by_spell_id = true;
}
else {
c->Message(Chat::Yellow, "You must enter a valid spell ID.");
return;
}
}
if (!aa_type && !by_spell_id) {
if (sep->IsNumber(1)) {
spell_type = atoi(sep->arg[1]);
if (!Bot::IsValidBotSpellType(spell_type)) {
c->Message(
Chat::Yellow,
fmt::format(
"You must choose a valid spell type. Use {} for information regarding this command.",
Saylink::Silent(
fmt::format("{} help", sep->arg[0])
)
).c_str()
);
return;
}
}
else {
if (Bot::GetSpellTypeIDByShortName(arg1) != UINT16_MAX) {
spell_type = Bot::GetSpellTypeIDByShortName(arg1);
}
else {
c->Message(
Chat::Yellow,
fmt::format(
"Incorrect argument, use {} for information regarding this command.",
Saylink::Silent(
fmt::format("{} help", sep->arg[0])
)
).c_str()
);
return;
}
}
switch (spell_type) { //Allowed command checks
case BotSpellTypes::Charm:
if (!RuleB(Bots, AllowCommandedCharm)) {
c->Message(Chat::Yellow, "This commanded type is currently disabled.");
return;
}
break;
case BotSpellTypes::AEMez:
case BotSpellTypes::Mez:
if (!RuleB(Bots, AllowCommandedMez)) {
c->Message(Chat::Yellow, "This commanded type is currently disabled.");
return;
}
break;
case BotSpellTypes::Resurrect:
if (!RuleB(Bots, AllowCommandedResurrect)) {
c->Message(Chat::Yellow, "This commanded type is currently disabled.");
return;
}
break;
case BotSpellTypes::AELull:
case BotSpellTypes::Lull:
if (!RuleB(Bots, AllowCommandedLull)) {
c->Message(Chat::Yellow, "This commanded type is currently disabled.");
return;
}
break;
case BotSpellTypes::SummonCorpse:
if (!RuleB(Bots, AllowCommandedSummonCorpse)) {
c->Message(Chat::Yellow, "This commanded type is currently disabled.");
return;
}
break;
default:
break;
}
std::string arg_string = sep->arg[ab_arg];
if (!arg_string.compare("shrink")) {
sub_type = CommandedSubTypes::Shrink;
++ab_arg;
}
else if (!arg_string.compare("grow")) {
sub_type = CommandedSubTypes::Grow;
++ab_arg;
}
else if (!arg_string.compare("see")) {
sub_type = CommandedSubTypes::SeeInvis;
++ab_arg;
}
else if (!arg_string.compare("invis")) {
sub_type = CommandedSubTypes::Invis;
++ab_arg;
}
else if (!arg_string.compare("undead")) {
sub_type = CommandedSubTypes::InvisUndead;
++ab_arg;
}
else if (!arg_string.compare("animals")) {
sub_type = CommandedSubTypes::InvisAnimals;
++ab_arg;
}
else if (!arg_string.compare("selo")) {
sub_type = CommandedSubTypes::Selo;
++ab_arg;
}
arg_string = sep->arg[ab_arg];
if (!arg_string.compare("single")) {
sub_target_type = CommandedSubTypes::SingleTarget;
++ab_arg;
}
else if (!arg_string.compare("group")) {
sub_target_type = CommandedSubTypes::GroupTarget;
++ab_arg;
}
else if (!arg_string.compare("ae")) {
sub_target_type = CommandedSubTypes::AETarget;
++ab_arg;
}
}
Mob* tar = c->GetTarget();
if (!tar) {
if ((!aa_type && !by_spell_id) && spell_type != BotSpellTypes::Escape && spell_type != BotSpellTypes::Pet) {
c->Message(Chat::Yellow, "You need a target for that.");
return;
}
}
if (!aa_type && !by_spell_id) {
if (IsPetBotSpellType(spell_type) && !tar->IsPet()) {
c->Message(
Chat::Yellow,
fmt::format(
"[{}] is an invalid target. {} requires a pet to be targeted.",
tar->GetCleanName(),
Bot::GetSpellTypeNameByID(spell_type)
).c_str()
);
return;
}
switch (spell_type) { //Target Checks
case BotSpellTypes::Resurrect:
if (!tar->IsCorpse() || !tar->CastToCorpse()->IsPlayerCorpse()) {
c->Message(
Chat::Yellow,
fmt::format(
"[{}] is not a player's corpse.",
tar->GetCleanName()
).c_str()
);
return;
}
break;
case BotSpellTypes::Identify:
case BotSpellTypes::SendHome:
case BotSpellTypes::BindAffinity:
case BotSpellTypes::SummonCorpse:
if (!tar->IsClient() || !c->IsInGroupOrRaid(tar)) {
c->Message(
Chat::Yellow,
fmt::format(
"[{}] is an invalid target. Only players in your group or raid are eligible targets.",
tar->GetCleanName()
).c_str()
);
return;
}
break;
default:
if (
(IsBotSpellTypeDetrimental(spell_type) && !c->IsAttackAllowed(tar)) ||
(
spell_type == BotSpellTypes::Charm &&
(
tar->IsClient() ||
tar->IsCorpse() ||
tar->GetOwner()
)
)
) {
c->Message(
Chat::Yellow,
fmt::format(
"You cannot attack [{}].",
tar->GetCleanName()
).c_str()
);
return;
}
if (IsBotSpellTypeBeneficial(spell_type)) {
if (
(tar->IsNPC() && !tar->GetOwner()) ||
(tar->GetOwner() && tar->GetOwner()->IsOfClientBot() && !c->IsInGroupOrRaid(tar->GetOwner())) ||
(tar->IsOfClientBot() && !c->IsInGroupOrRaid(tar))
) {
c->Message(
Chat::Yellow,
fmt::format(
"[{}] is an invalid target. Only players or their pet in your group or raid are eligible targets.",
tar->GetCleanName()
).c_str()
);
return;
}
}
break;
}
}
if (
(spell_type == BotSpellTypes::Cure || spell_type == BotSpellTypes::GroupCures || spell_type == BotSpellTypes::PetCures) &&
!c->CastToBot()->GetNeedsCured(tar)
) {
c->Message(
Chat::Yellow,
fmt::format(
"[{}] doesn't have anything that needs to be cured.",
tar->GetCleanName()
).c_str()
);
return;
}
const int ab_mask = ActionableBots::ABM_Type1;
std::string actionable_arg = sep->arg[ab_arg];
if (actionable_arg.empty()) {
actionable_arg = "spawned";
}
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::vector<Bot*> sbl;
if (ActionableBots::PopulateSBL(c, actionable_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.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end());
BotSpell bot_spell;
bot_spell.SpellId = 0;
bot_spell.SpellIndex = 0;
bot_spell.ManaCost = 0;
bool is_success = false;
uint16 success_count = 0;
Bot* first_found = nullptr;
for (auto bot_iter : sbl) {
if (!bot_iter->IsInGroupOrRaid(c)) {
continue;
}
if (bot_iter->GetBotStance() == Stance::Passive || bot_iter->GetHoldFlag() || bot_iter->GetAppearance() == eaDead || bot_iter->IsFeared() || bot_iter->IsSilenced() || bot_iter->IsAmnesiad() || bot_iter->GetHP() < 0) {
continue;
}
Mob* new_tar = tar;
if (!aa_type && !by_spell_id) {
if (!BotSpellTypeRequiresTarget(spell_type)) {
new_tar = bot_iter;
}
if (!new_tar) {
continue;
}
if (
IsBotSpellTypeBeneficial(spell_type) &&
!RuleB(Bots, CrossRaidBuffingAndHealing) &&
!bot_iter->IsInGroupOrRaid(new_tar, true)
) {
continue;
}
if (IsBotSpellTypeDetrimental(spell_type) && !bot_iter->IsAttackAllowed(new_tar)) {
bot_iter->RaidGroupSay(
bot_iter,
fmt::format(
"I cannot attack [{}].",
new_tar->GetCleanName()
).c_str()
);
continue;
}
}
if (aa_type) {
if (!bot_iter->GetAA(zone->GetAlternateAdvancementAbility(aa_id)->first_rank_id)) {
continue;
}
AA::Rank* temp_rank = nullptr;
AA::Rank*& rank = temp_rank;
uint16 spell_id = bot_iter->GetSpellByAA(aa_id, rank);
if (!IsValidSpell(spell_id)) {
continue;
}
if (!bot_iter->AttemptAACastSpell(tar, spell_id, rank)) {
continue;
}
is_success = true;
++success_count;
continue;
}
else if (by_spell_id) {
if (!bot_iter->CanUseBotSpell(chosen_spell_id)) {
continue;
}
if (!tar || (spells[chosen_spell_id].target_type == ST_Self && tar != bot_iter)) {
tar = bot_iter;
}
if (bot_iter->AttemptForcedCastSpell(tar, chosen_spell_id)) {
if (!first_found) {
first_found = bot_iter;
}
is_success = true;
++success_count;
}
continue;
}
else {
bot_iter->SetCommandedSpell(true);
if (bot_iter->AICastSpell(new_tar, 100, spell_type, sub_target_type, sub_type)) {
if (!first_found) {
first_found = bot_iter;
}
is_success = true;
++success_count;
}
bot_iter->SetCommandedSpell(false);
continue;
}
continue;
}
std::string type = "";
if (aa_type) {
type = zone->GetAAName(zone->GetAlternateAdvancementAbility(aa_id)->first_rank_id);
}
else if (by_spell_id) {
type = "Forced";
}
else {
if (sub_type == UINT16_MAX) {
type = Bot::GetSpellTypeNameByID(spell_type);
}
else {
type = Bot::GetSubTypeNameByID(sub_type);
}
}
if (!is_success) {
c->Message(
Chat::Yellow,
fmt::format(
"No bots are capable of casting [{}] on {}. This could be due to this to any number of things: range, mana, immune, target type, etc.",
(by_spell_id ? spells[chosen_spell_id].name : type),
tar ? tar->GetCleanName() : "your target"
).c_str()
);
if (!aa_type && !by_spell_id) {
helper_send_usage_required_bots(c, spell_type);
}
}
else {
c->Message(
Chat::Yellow,
fmt::format(
"{} {} [{}]{}",
((success_count == 1 && first_found) ? first_found->GetCleanName() : (fmt::format("{}", success_count).c_str())),
((success_count == 1 && first_found) ? "casted" : "of your bots casted"),
(by_spell_id ? spells[chosen_spell_id].name : type),
tar ? (fmt::format(" on {}.", tar->GetCleanName()).c_str()) : "."
).c_str()
);
}
}
-107
View File
@@ -1,107 +0,0 @@
#include "../bot_command.h"
void bot_command_caster_range(Client* c, const Seperator* sep)
{
if (helper_command_alias_fail(c, "bot_command_caster_range", sep->arg[0], "casterrange")) {
return;
}
if (helper_is_help_or_usage(sep->arg[1])) {
c->Message(Chat::White, "usage: %s [current | value: 0 - 300] ([actionable: target | byname | ownergroup | ownerraid | targetgroup | namesgroup | healrotationtargets | byclass | byrace | spawned] ([actionable_name]))", sep->arg[0]);
c->Message(Chat::White, "note: Can only be used for Casters or Hybrids.");
c->Message(Chat::White, "note: Use [current] to check the current setting.");
c->Message(Chat::White, "note: Set the value to the minimum distance you want your bot to try to remain from its target.");
c->Message(Chat::White, "note: If they are too far for a spell, it will be skipped.");
c->Message(Chat::White, "note: This is set to (90) units by default.");
return;
}
const int ab_mask = ActionableBots::ABM_Type1;
std::string arg1 = sep->arg[1];
int ab_arg = 1;
bool current_check = false;
uint32 crange = 0;
if (sep->IsNumber(1)) {
ab_arg = 2;
crange = atoi(sep->arg[1]);
if (crange < 0 || crange > 300) {
c->Message(Chat::White, "You must enter a value within the range of 0 - 300.");
return;
}
}
else if (!arg1.compare("current")) {
ab_arg = 2;
current_check = true;
}
else {
c->Message(Chat::White, "Incorrect argument, use %s help for a list of options.", sep->arg[0]);
return;
}
std::string class_race_arg = sep->arg[ab_arg];
bool class_race_check = false;
if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) {
class_race_check = true;
}
std::list<Bot*> sbl;
if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) {
return;
}
sbl.remove(nullptr);
Bot* first_found = nullptr;
int success_count = 0;
for (auto my_bot : sbl) {
if (!IsCasterClass(my_bot->GetClass()) && !IsHybridClass(my_bot->GetClass())) {
continue;
}
if (!first_found) {
first_found = my_bot;
}
if (current_check) {
c->Message(
Chat::White,
fmt::format(
"{} says, 'My current Caster Range is {}.'",
my_bot->GetCleanName(),
my_bot->GetBotCasterRange()
).c_str()
);
}
else {
my_bot->SetBotCasterRange(crange);
++success_count;
database.botdb.SaveBotCasterRange(my_bot->GetBotID(), crange);
}
}
if (!current_check) {
if (success_count == 1 && first_found) {
c->Message(
Chat::White,
fmt::format(
"{} says, 'My Caster Range was set to {}.'",
first_found->GetCleanName(),
crange
).c_str()
);
}
else {
c->Message(
Chat::White,
fmt::format(
"{} of your bots set their Caster Range to {}.",
success_count,
crange
).c_str()
);
}
}
}
-54
View File
@@ -1,54 +0,0 @@
#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);
}
+113
View File
@@ -0,0 +1,113 @@
#include "../bot_command.h"
void bot_command_class_race_list(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
};
std::string window_text;
std::string message_separator;
int object_count = 0;
const int object_max = 4;
window_text.append(
fmt::format(
"<c \"#EDDA74\">Classes{}<c \"#357EC7\">",
DialogueWindow::Break()
)
);
window_text.append(
fmt::format(
"<c \"#D4A017\">--------------------------------------------------------------------<c \"#357EC7\">",
DialogueWindow::Break()
)
);
window_text.append(DialogueWindow::Break());
message_separator = " ";
object_count = 0;
for (int i = 0; i <= 15; ++i) {
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[i + 1],
(i + 1)
)
);
++object_count;
message_separator = ", ";
}
window_text.append(DialogueWindow::Break(2));
window_text.append(
fmt::format(
"<c \"#EDDA74\">Races{}<c \"#357EC7\">",
DialogueWindow::Break()
)
);
window_text.append(
fmt::format(
"<c \"#D4A017\">--------------------------------------------------------------------<c \"#357EC7\">",
DialogueWindow::Break()
)
);
window_text.append(DialogueWindow::Break());
message_separator = " ";
object_count = 0;
for (int i = 0; i <= 15; ++i) {
window_text.append(message_separator);
if (object_count >= object_max) {
window_text.append(DialogueWindow::Break());
object_count = 0;
}
window_text.append(
fmt::format("{} ({})",
race_substrs[i + 1],
race_values[i + 1]
)
);
++object_count;
message_separator = ", ";
}
c->SendPopupToClient("Bot Creation Options", window_text.c_str());
return;
}
+21 -4
View File
@@ -8,7 +8,7 @@ void bot_command_click_item(Client* c, const Seperator* sep)
}
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, "usage: %s <slot id> ([actionable: target | byname | ownergroup | ownerraid | targetgroup | namesgroup | healrotationtargets | mmr | 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;
@@ -25,7 +25,7 @@ void bot_command_click_item(Client* c, const Seperator* sep)
uint32 slot_id = 0;
if (sep->IsNumber(1)) {
ab_arg = 2;
++ab_arg;
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]);
@@ -39,13 +39,30 @@ void bot_command_click_item(Client* c, const Seperator* sep)
class_race_check = true;
}
std::list<Bot*> sbl;
std::vector<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);
sbl.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end());
Mob* tar = c->GetTarget();
for (auto my_bot : sbl) {
if (my_bot->BotPassiveCheck()) {
continue;
}
if (
tar &&
tar != c &&
tar->GetOwner() != c &&
!c->DoLosChecks(tar)
) {
continue;
}
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;
+376
View File
@@ -0,0 +1,376 @@
#include "../bot_command.h"
void bot_command_copy_settings(Client* c, const Seperator* sep)
{
if (helper_command_alias_fail(c, "bot_command_copy_settings", sep->arg[0], "copysettings")) {
c->Message(Chat::White, "note: Copies settings from one bot to another.");
return;
}
if (helper_is_help_or_usage(sep->arg[1])) {
BotCommandHelpParams p;
p.description =
{
"Copies settings from one bot to another."
};
p.notes =
{
"- You can put a spell type ID or shortname after any option except [all], [misc] and [spellsettings] to restore that specifc spell type only"
};
p.example_format = { fmt::format("{} [from] [to] [option] [optional: spelltype id/short name]", sep->arg[0]) };
p.examples_one =
{
"To copy all settings from BotA to BotB:",
fmt::format("{} BotA BotB all", sep->arg[0])
};
p.examples_two =
{
"To copy only Nuke spelltypesettings from BotA to BotB:",
fmt::format(
"{} BotA BotB spelltypesettings {}",
sep->arg[0],
Bot::GetSpellTypeShortNameByID(BotSpellTypes::Nuke)
),
fmt::format(
"{} BotA BotB spelltypesettings {}",
sep->arg[0],
Bot::GetSpellTypeShortNameByID(BotSpellTypes::Nuke)
),
};
p.examples_three =
{
"To copy only spellsettings from BotA to BotB:",
fmt::format(
"{} BotA BotB spellsettings",
sep->arg[0]
),
fmt::format(
"{} BotA BotB spellsettings",
sep->arg[0]
),
};
p.actionables = { "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" };
p.options = { "all, misc, spellsettings, spelltypesettings, spellholds, spelldelays, spellminthresholds, spellmaxthresholds, spellminmanapct, spellmaxmanapct, spellminhppct, spellmaxhppct, spellidlepriority, spellengagedpriority, spellpursuepriority, spellaggrochecks, spelltargetcounts, spellresistlimits, spellannouncecasts, blockedbuffs, blockedpetbuffs" };
p.options_one =
{
"[spellsettings] will copy ^spellsettings options",
"[spelltypesettings] copies all spell type settings",
"[all] copies all settings"
};
p.options_two =
{
"[misc] copies all miscellaneous options such as:",
"- ^showhelm, ^followd, ^stopmeleelevel, ^enforcespellsettings, ^bottoggleranged, ^petsettype, ^behindmob, ^distanceranged, ^illusionblock, ^sitincombat, ^sithppercent, ^sitmanapercent, ^blockedbuffs, ^blockedpetbuffs"
};
p.options_three = { "The remaining options copy that specific type" };
std::string popup_text = c->SendBotCommandHelpWindow(p);
popup_text = DialogueWindow::Table(popup_text);
c->SendPopupToClient(sep->arg[0], popup_text.c_str());
c->SendSpellTypePrompts();
if (RuleB(Bots, SendClassRaceOnHelp)) {
c->Message(
Chat::Yellow,
fmt::format(
"Use {} for information about race/class IDs.",
Saylink::Silent("^classracelist")
).c_str()
);
}
return;
}
int spell_type_arg_int = 4;
std::string spell_type_arg = sep->arg[spell_type_arg_int];
int ab_arg = 2;
bool valid_option = false;
uint16 spell_type = UINT16_MAX;
uint16 setting_type = UINT16_MAX;
std::vector<std::string> options =
{
"all",
"misc",
"spellsettings",
"spelltypesettings",
"spellholds",
"spelldelays",
"spellminthresholds",
"spellmaxthresholds",
"spellminmanapct",
"spellmaxmanapct",
"spellminhppct",
"spellmaxhppct",
"spellidlepriority",
"spellengagedpriority",
"spellpursuepriority",
"spellaggrochecks",
"spelltargetcounts",
"spellresistlimits",
"spellannouncecasts",
"blockedbuffs",
"blockedpetbuffs"
};
if (sep->IsNumber(spell_type_arg_int)) {
spell_type = atoi(sep->arg[spell_type_arg_int]);
if (!Bot::IsValidBotSpellType(spell_type)) {
c->Message(
Chat::Yellow,
fmt::format(
"You must choose a valid spell type. Use {} for information regarding this command.",
Saylink::Silent(
fmt::format("{} help", sep->arg[0])
)
).c_str()
);
return;
}
}
else if (!spell_type_arg.empty()) {
if (Bot::GetSpellTypeIDByShortName(spell_type_arg) != UINT16_MAX) {
spell_type = Bot::GetSpellTypeIDByShortName(spell_type_arg);
}
else {
c->Message(
Chat::Yellow,
fmt::format(
"You must choose a valid spell type. Use {} for information regarding this command.",
Saylink::Silent(
fmt::format("{} help", sep->arg[0])
)
).c_str()
);
return;
}
}
for (int i = 0; i < options.size(); i++) {
if (sep->arg[3] == options[i]) {
setting_type = Bot::GetBotSpellCategoryIDByShortName(sep->arg[3]);
valid_option = true;
break;
}
}
if (!valid_option) {
c->Message(
Chat::Yellow,
fmt::format(
"Incorrect argument, use {} for information regarding this command.",
Saylink::Silent(
fmt::format("{} help", sep->arg[0])
)
).c_str()
);
return;
}
auto from = entity_list.GetBotByBotName(sep->arg[1]);
if (!from) {
c->Message(Chat::Yellow, "Could not find %s.", sep->arg[1]);
return;
}
if (!from->IsBot()) {
c->Message(Chat::Yellow, "%s is not a bot.", from->GetCleanName());
return;
}
if (!from->GetOwner()) {
c->Message(Chat::Yellow, "Could not find %s's owner.", from->GetCleanName());
}
if (RuleB(Bots, CopySettingsOwnBotsOnly) && from->GetOwner() != c) {
c->Message(Chat::Yellow, "You name a bot you own to use this command.");
return;
}
if (!RuleB(Bots, AllowCopySettingsAnon) && from->GetOwner() != c && from->GetOwner()->CastToClient()->GetAnon()) {
c->Message(Chat::Yellow, "You name a bot you own to use this command.");
return;
}
auto to = ActionableBots::AsNamed_ByBot(c, sep->arg[2]);
if (!to) {
c->Message(Chat::Yellow, "Could not find %s.", sep->arg[1]);
return;
}
if (!to->IsBot()) {
c->Message(Chat::Yellow, "%s is not a bot.", to->GetCleanName());
return;
}
if (!to->GetOwner()) {
c->Message(Chat::Yellow, "Could not find %s's owner.", to->GetCleanName());
}
if (to->GetOwner() != c) {
c->Message(Chat::Yellow, "You must name a spawned bot that you own to use this command.");
return;
}
if (to == from) {
c->Message(Chat::Yellow, "You cannot copy to the same bot that you're copying from.");
return;
}
std::string output = "";
if (setting_type != UINT16_MAX) {
if (spell_type != UINT16_MAX) {
from->CopySettings(to, setting_type, spell_type);
}
else {
for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) {
from->CopySettings(to, setting_type, i);
}
}
output = Bot::GetBotSpellCategoryName(setting_type);
}
else {
if (!strcasecmp(sep->arg[3], "misc")) {
from->CopySettings(to, BotSettingCategories::BaseSetting);
output = "Miscellaneous";
}
else if (!strcasecmp(sep->arg[3], "spellsettings")) {
from->CopyBotSpellSettings(to);
output = "^spellsettings";
}
else if (!strcasecmp(sep->arg[3], "spelltypesettings")) {
if (spell_type != UINT16_MAX) {
from->CopySettings(to, BotSettingCategories::SpellHold, spell_type);
from->CopySettings(to, BotSettingCategories::SpellDelay, spell_type);
from->CopySettings(to, BotSettingCategories::SpellMinThreshold, spell_type);
from->CopySettings(to, BotSettingCategories::SpellMaxThreshold, spell_type);
from->CopySettings(to, BotSettingCategories::SpellTypeAggroCheck, spell_type);
from->CopySettings(to, BotSettingCategories::SpellTypeResistLimit, spell_type);
from->CopySettings(to, BotSettingCategories::SpellTypeMinManaPct, spell_type);
from->CopySettings(to, BotSettingCategories::SpellTypeMaxManaPct, spell_type);
from->CopySettings(to, BotSettingCategories::SpellTypeMinHPPct, spell_type);
from->CopySettings(to, BotSettingCategories::SpellTypeMaxHPPct, spell_type);
from->CopySettings(to, BotSettingCategories::SpellTypeIdlePriority, spell_type);
from->CopySettings(to, BotSettingCategories::SpellTypeEngagedPriority, spell_type);
from->CopySettings(to, BotSettingCategories::SpellTypePursuePriority, spell_type);
from->CopySettings(to, BotSettingCategories::SpellTypeAEOrGroupTargetCount, spell_type);
from->CopySettings(to, BotSettingCategories::SpellTypeAnnounceCast, spell_type);
}
else {
for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) {
from->CopySettings(to, BotSettingCategories::SpellHold, i);
from->CopySettings(to, BotSettingCategories::SpellDelay, i);
from->CopySettings(to, BotSettingCategories::SpellMinThreshold, i);
from->CopySettings(to, BotSettingCategories::SpellMaxThreshold, i);
from->CopySettings(to, BotSettingCategories::SpellTypeAggroCheck, i);
from->CopySettings(to, BotSettingCategories::SpellTypeResistLimit, i);
from->CopySettings(to, BotSettingCategories::SpellTypeMinManaPct, i);
from->CopySettings(to, BotSettingCategories::SpellTypeMaxManaPct, i);
from->CopySettings(to, BotSettingCategories::SpellTypeMinHPPct, i);
from->CopySettings(to, BotSettingCategories::SpellTypeMaxHPPct, i);
from->CopySettings(to, BotSettingCategories::SpellTypeIdlePriority, i);
from->CopySettings(to, BotSettingCategories::SpellTypeEngagedPriority, i);
from->CopySettings(to, BotSettingCategories::SpellTypePursuePriority, i);
from->CopySettings(to, BotSettingCategories::SpellTypeAEOrGroupTargetCount, i);
from->CopySettings(to, BotSettingCategories::SpellTypeAnnounceCast, i);
}
}
output = "spell type";
}
else if (!strcasecmp(sep->arg[3], "blockedbuffs")) {
from->CopyBotBlockedBuffs(to);
}
else if (!strcasecmp(sep->arg[3], "blockedpetbuffs")) {
from->CopyBotBlockedPetBuffs(to);
}
else if (!strcasecmp(sep->arg[3], "all")) {
from->CopySettings(to, BotSettingCategories::BaseSetting);
if (spell_type != UINT16_MAX) {
from->CopySettings(to, BotSettingCategories::SpellHold, spell_type);
from->CopySettings(to, BotSettingCategories::SpellDelay, spell_type);
from->CopySettings(to, BotSettingCategories::SpellMinThreshold, spell_type);
from->CopySettings(to, BotSettingCategories::SpellMaxThreshold, spell_type);
from->CopySettings(to, BotSettingCategories::SpellTypeAggroCheck, spell_type);
from->CopySettings(to, BotSettingCategories::SpellTypeResistLimit, spell_type);
from->CopySettings(to, BotSettingCategories::SpellTypeMinManaPct, spell_type);
from->CopySettings(to, BotSettingCategories::SpellTypeMaxManaPct, spell_type);
from->CopySettings(to, BotSettingCategories::SpellTypeMinHPPct, spell_type);
from->CopySettings(to, BotSettingCategories::SpellTypeMaxHPPct, spell_type);
from->CopySettings(to, BotSettingCategories::SpellTypeIdlePriority, spell_type);
from->CopySettings(to, BotSettingCategories::SpellTypeEngagedPriority, spell_type);
from->CopySettings(to, BotSettingCategories::SpellTypePursuePriority, spell_type);
from->CopySettings(to, BotSettingCategories::SpellTypeAEOrGroupTargetCount, spell_type);
from->CopySettings(to, BotSettingCategories::SpellTypeAnnounceCast, spell_type);
}
else {
for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) {
from->CopySettings(to, BotSettingCategories::SpellHold, i);
from->CopySettings(to, BotSettingCategories::SpellDelay, i);
from->CopySettings(to, BotSettingCategories::SpellMinThreshold, i);
from->CopySettings(to, BotSettingCategories::SpellMaxThreshold, i);
from->CopySettings(to, BotSettingCategories::SpellTypeAggroCheck, i);
from->CopySettings(to, BotSettingCategories::SpellTypeResistLimit, i);
from->CopySettings(to, BotSettingCategories::SpellTypeMinManaPct, i);
from->CopySettings(to, BotSettingCategories::SpellTypeMaxManaPct, i);
from->CopySettings(to, BotSettingCategories::SpellTypeMinHPPct, i);
from->CopySettings(to, BotSettingCategories::SpellTypeMaxHPPct, i);
from->CopySettings(to, BotSettingCategories::SpellTypeIdlePriority, i);
from->CopySettings(to, BotSettingCategories::SpellTypeEngagedPriority, i);
from->CopySettings(to, BotSettingCategories::SpellTypePursuePriority, i);
from->CopySettings(to, BotSettingCategories::SpellTypeAEOrGroupTargetCount, i);
from->CopySettings(to, BotSettingCategories::SpellTypeAnnounceCast, i);
}
}
from->CopyBotSpellSettings(to);
from->CopyBotBlockedBuffs(to);
from->CopyBotBlockedPetBuffs(to);
output = "spell type";
}
else {
c->Message(
Chat::Yellow,
fmt::format(
"Incorrect argument, use {} for information regarding this command.",
Saylink::Silent(
fmt::format("{} help", sep->arg[0])
)
).c_str()
);
return;
}
}
to->Save();
c->Message(
Chat::Green,
fmt::format(
"{}'s{}{} settings were copied to {}.",
from->GetCleanName(),
(
spell_type != UINT16_MAX ?
fmt::format(" [{}] ",
Bot::GetSpellTypeNameByID(spell_type)
)
: " "
),
output,
to->GetCleanName()
).c_str()
);
}
-71
View File
@@ -1,71 +0,0 @@
#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);
}
+516
View File
@@ -0,0 +1,516 @@
#include "../bot_command.h"
void bot_command_default_settings(Client* c, const Seperator* sep)
{
if (helper_command_alias_fail(c, "bot_command_default_settings", sep->arg[0], "defaultsettings")) {
c->Message(Chat::White, "note: Restores a bot's setting(s) to defaults.");
return;
}
if (helper_is_help_or_usage(sep->arg[1])) {
BotCommandHelpParams p;
p.description = { "Restores a bot's setting(s) to defaults" };
p.notes = { "- You can put a spell type ID or shortname after any option except [all], [misc] and [spellsettings] to restore that specifc spell type only"};
p.example_format = { fmt::format("{} [option] [optional: spelltype id/short name] [actionable]", sep->arg[0]) };
p.examples_one =
{
"To restore delays for Clerics:",
fmt::format(
"{} delays byclass {}",
sep->arg[0],
Class::Cleric
)
};
p.examples_two =
{
"To restore only Snare delays for BotA:",
fmt::format(
"{} delays {} byname BotA",
sep->arg[0],
Bot::GetSpellTypeShortNameByID(BotSpellTypes::Snare)
),
fmt::format(
"{} delays {} byname BotA",
sep->arg[0],
BotSpellTypes::Snare
)
};
p.actionables = { "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" };
p.options = { "all, misc, spellsettings, spelltypesettings, spellholds, spelldelays, spellminthresholds, spellmaxthresholds, spellminmanapct, spellmaxmanapct, spellminhppct, spellmaxhppct, spellidlepriority, spellengagedpriority, spellpursuepriority, spellaggrocheck, spelltargetcounts, spellresistlimits, spellannouncecasts" };
p.options_one =
{
"[spellsettings] will restore ^spellsettings options",
"[spelltypesettings] restores all spell type settings",
"[all] restores all settings"
};
p.options_two =
{
"[misc] restores all miscellaneous options such as:",
"- ^showhelm, ^followd, ^stopmeleelevel, ^enforcespellsettings, ^bottoggleranged, ^petsettype, ^behindmob, ^distanceranged, ^illusionblock, ^sitincombat, ^sithppercent and ^sitmanapercent",
};
p.options_three =
{
"<br>",
"**The remaining options restore that specific type**"
};
std::string popup_text = c->SendBotCommandHelpWindow(p);
popup_text = DialogueWindow::Table(popup_text);
c->SendPopupToClient(sep->arg[0], popup_text.c_str());
c->SendSpellTypePrompts();
if (RuleB(Bots, SendClassRaceOnHelp)) {
c->Message(
Chat::Yellow,
fmt::format(
"Use {} for information about race/class IDs.",
Saylink::Silent("^classracelist")
).c_str()
);
}
return;
}
int spell_type_arg_int = 2;
std::string spell_type_arg = sep->arg[spell_type_arg_int];
int ab_arg = 2;
bool valid_option = false;
uint16 spell_type = UINT16_MAX;
uint16 setting_type = UINT16_MAX;
std::vector<std::string> options =
{
"all",
"misc",
"spellsettings",
"spelltypesettings",
"spellholds",
"spelldelays",
"spellminthresholds",
"spellmaxthresholds",
"spellminmanapct",
"spellmaxmanapct",
"spellminhppct",
"spellmaxhppct",
"spellidlepriority",
"spellengagedpriority",
"spellpursuepriority",
"spellaggrochecks",
"spelltargetcounts",
"spellresistlimits",
"spellannouncecasts"
};
if (sep->IsNumber(spell_type_arg_int)) {
spell_type = atoi(sep->arg[spell_type_arg_int]);
if (!EQ::ValueWithin(spell_type, BotSpellTypes::START, BotSpellTypes::END)) {
c->Message(
Chat::Yellow,
fmt::format(
"You must choose a valid spell type. Use {} for information regarding this command.",
Saylink::Silent(
fmt::format("{} help", sep->arg[0])
)
).c_str()
);
return;
}
++ab_arg;
}
else if (!spell_type_arg.empty()) {
if (Bot::GetSpellTypeIDByShortName(spell_type_arg) != UINT16_MAX) {
spell_type = Bot::GetSpellTypeIDByShortName(spell_type_arg);
}
else {
c->Message(
Chat::Yellow,
fmt::format(
"You must choose a valid spell type. Use {} for information regarding this command.",
Saylink::Silent(
fmt::format("{} help", sep->arg[0])
)
).c_str()
);
return;
}
++ab_arg;
}
for (int i = 0; i < options.size(); i++) {
if (sep->arg[1] == options[i]) {
setting_type = Bot::GetBotSpellCategoryIDByShortName(sep->arg[1]);
valid_option = true;
break;
}
}
if (!valid_option) {
c->Message(
Chat::Yellow,
fmt::format(
"Incorrect argument, use {} for information regarding this command.",
Saylink::Silent(
fmt::format("{} help", sep->arg[0])
)
).c_str()
);
return;
}
const int ab_mask = ActionableBots::ABM_Type1;
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::vector<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.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end());
Bot* first_found = nullptr;
int success_count = 0;
std::string output = "";
uint8 bot_stance = 2;
for (auto my_bot : sbl) {
if (!first_found) {
first_found = my_bot;
}
bot_stance = my_bot->GetBotStance();
if (setting_type != UINT16_MAX) {
if (spell_type != UINT16_MAX) {
my_bot->SetBotSetting(setting_type, spell_type, my_bot->GetDefaultSetting(setting_type, spell_type, bot_stance));
}
else {
for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) {
my_bot->SetBotSetting(setting_type, i, my_bot->GetDefaultSetting(setting_type, i, bot_stance));
}
}
output = (spell_type != UINT16_MAX ? Bot::GetSpellTypeNameByID(spell_type) : "");
output += sep->arg[3];
}
else if (!strcasecmp(sep->arg[1], "misc")) {
for (uint16 i = BotBaseSettings::START; i <= BotBaseSettings::END; ++i) {
my_bot->SetBotBaseSetting(i, my_bot->GetDefaultBotBaseSetting(i, bot_stance));
output = "miscellanous settings";
}
}
else if (!strcasecmp(sep->arg[1], "spellsettings")) {
my_bot->ResetBotSpellSettings();
output = "^spellsettings";
}
else if (!strcasecmp(sep->arg[1], "spelltypesettings")) {
if (spell_type != UINT16_MAX) {
my_bot->SetSpellTypeHold(spell_type, my_bot->GetDefaultSpellTypeHold(spell_type, bot_stance));
my_bot->SetSpellTypeDelay(spell_type, my_bot->GetDefaultSpellTypeDelay(spell_type, bot_stance));
my_bot->SetSpellTypeMinThreshold(
spell_type,
my_bot->GetDefaultSpellTypeMinThreshold(spell_type, bot_stance));
my_bot->SetSpellTypeMaxThreshold(
spell_type,
my_bot->GetDefaultSpellTypeMaxThreshold(spell_type, bot_stance));
my_bot->SetSpellTypeAggroCheck(spell_type, my_bot->GetDefaultSpellTypeAggroCheck(spell_type, bot_stance));
my_bot->SetSpellTypeResistLimit(spell_type, my_bot->GetDefaultSpellTypeResistLimit(spell_type, bot_stance));
my_bot->SetSpellTypeMinManaLimit(spell_type, my_bot->GetDefaultSpellTypeMinManaLimit(spell_type, bot_stance));
my_bot->SetSpellTypeMaxManaLimit(spell_type, my_bot->GetDefaultSpellTypeMaxManaLimit(spell_type, bot_stance));
my_bot->SetSpellTypeMinHPLimit(spell_type, my_bot->GetDefaultSpellTypeMinHPLimit(spell_type, bot_stance));
my_bot->SetSpellTypeMaxHPLimit(spell_type, my_bot->GetDefaultSpellTypeMaxHPLimit(spell_type, bot_stance));
my_bot->SetSpellTypePriority(spell_type, BotPriorityCategories::Idle, my_bot->GetDefaultSpellTypePriority(spell_type, BotPriorityCategories::Idle, my_bot->GetClass(), bot_stance));
my_bot->SetSpellTypePriority(spell_type, BotPriorityCategories::Engaged, my_bot->GetDefaultSpellTypePriority(spell_type, BotPriorityCategories::Engaged, my_bot->GetClass(), bot_stance));
my_bot->SetSpellTypePriority(spell_type, BotPriorityCategories::Pursue, my_bot->GetDefaultSpellTypePriority(spell_type, BotPriorityCategories::Pursue, my_bot->GetClass(), bot_stance));
my_bot->SetSpellTypeAEOrGroupTargetCount(spell_type, my_bot->GetDefaultSpellTypeAEOrGroupTargetCount(spell_type, bot_stance));
my_bot->SetSpellTypeAnnounceCast(spell_type, my_bot->GetDefaultSpellTypeAnnounceCast(spell_type, bot_stance));
}
else {
for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) {
my_bot->SetSpellTypeHold(i, my_bot->GetDefaultSpellTypeHold(i, bot_stance));
my_bot->SetSpellTypeDelay(i, my_bot->GetDefaultSpellTypeDelay(i, bot_stance));
my_bot->SetSpellTypeMinThreshold(i, my_bot->GetDefaultSpellTypeMinThreshold(i, bot_stance));
my_bot->SetSpellTypeMaxThreshold(i, my_bot->GetDefaultSpellTypeMaxThreshold(i, bot_stance));
my_bot->SetSpellTypeAggroCheck(i, my_bot->GetDefaultSpellTypeAggroCheck(i, bot_stance));
my_bot->SetSpellTypeResistLimit(i, my_bot->GetDefaultSpellTypeResistLimit(i, bot_stance));
my_bot->SetSpellTypeMinManaLimit(i, my_bot->GetDefaultSpellTypeMinManaLimit(i, bot_stance));
my_bot->SetSpellTypeMaxManaLimit(i, my_bot->GetDefaultSpellTypeMaxManaLimit(i, bot_stance));
my_bot->SetSpellTypeMinHPLimit(i, my_bot->GetDefaultSpellTypeMinHPLimit(i, bot_stance));
my_bot->SetSpellTypeMaxHPLimit(i, my_bot->GetDefaultSpellTypeMaxHPLimit(i, bot_stance));
my_bot->SetSpellTypePriority(i, BotPriorityCategories::Idle, my_bot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Idle, my_bot->GetClass(), bot_stance));
my_bot->SetSpellTypePriority(i, BotPriorityCategories::Engaged, my_bot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Engaged, my_bot->GetClass(), bot_stance));
my_bot->SetSpellTypePriority(i, BotPriorityCategories::Pursue, my_bot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Pursue, my_bot->GetClass(), bot_stance));
my_bot->SetSpellTypeAEOrGroupTargetCount(i, my_bot->GetDefaultSpellTypeAEOrGroupTargetCount(i, bot_stance));
my_bot->SetSpellTypeAnnounceCast(i, my_bot->GetDefaultSpellTypeAnnounceCast(i, bot_stance));
}
}
output = "spell type settings";
}
else if (!strcasecmp(sep->arg[1], "all")) {
for (uint16 i = BotBaseSettings::START; i <= BotBaseSettings::END; ++i) {
my_bot->SetBotBaseSetting(i, my_bot->GetDefaultBotBaseSetting(i, bot_stance));
}
for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) {
my_bot->SetSpellTypeHold(i, my_bot->GetDefaultSpellTypeHold(i, bot_stance));
my_bot->SetSpellTypeDelay(i, my_bot->GetDefaultSpellTypeDelay(i, bot_stance));
my_bot->SetSpellTypeMinThreshold(i, my_bot->GetDefaultSpellTypeMinThreshold(i, bot_stance));
my_bot->SetSpellTypeMaxThreshold(i, my_bot->GetDefaultSpellTypeMaxThreshold(i, bot_stance));
my_bot->SetSpellTypeAggroCheck(i, my_bot->GetDefaultSpellTypeAggroCheck(i, bot_stance));
my_bot->SetSpellTypeResistLimit(i, my_bot->GetDefaultSpellTypeResistLimit(i, bot_stance));
my_bot->SetSpellTypeMinManaLimit(i, my_bot->GetDefaultSpellTypeMinManaLimit(i, bot_stance));
my_bot->SetSpellTypeMaxManaLimit(i, my_bot->GetDefaultSpellTypeMaxManaLimit(i, bot_stance));
my_bot->SetSpellTypeMinHPLimit(i, my_bot->GetDefaultSpellTypeMinHPLimit(i, bot_stance));
my_bot->SetSpellTypeMaxHPLimit(i, my_bot->GetDefaultSpellTypeMaxHPLimit(i, bot_stance));
my_bot->SetSpellTypePriority(i, BotPriorityCategories::Idle, my_bot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Idle, my_bot->GetClass(), bot_stance));
my_bot->SetSpellTypePriority(i, BotPriorityCategories::Engaged, my_bot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Engaged, my_bot->GetClass(), bot_stance));
my_bot->SetSpellTypePriority(i, BotPriorityCategories::Pursue, my_bot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Pursue, my_bot->GetClass(), bot_stance));
my_bot->SetSpellTypeAEOrGroupTargetCount(i, my_bot->GetDefaultSpellTypeAEOrGroupTargetCount(i, bot_stance));
my_bot->SetSpellTypeAnnounceCast(i, my_bot->GetDefaultSpellTypeAnnounceCast(i, bot_stance));
};
my_bot->ResetBotSpellSettings();
my_bot->ClearBotBlockedBuffs();
output = "settings";
}
else if (!strcasecmp(sep->arg[1], "holds")) {
if (spell_type != UINT16_MAX) {
my_bot->SetSpellTypeHold(spell_type, my_bot->GetDefaultSpellTypeHold(spell_type, bot_stance));
}
else {
for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) {
my_bot->SetSpellTypeHold(i, my_bot->GetDefaultSpellTypeHold(i, bot_stance));
}
}
output = "hold settings";
}
else if (!strcasecmp(sep->arg[1], "delays")) {
if (spell_type != UINT16_MAX) {
my_bot->SetSpellTypeDelay(spell_type, my_bot->GetDefaultSpellTypeDelay(spell_type, bot_stance));
}
else {
for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) {
my_bot->SetSpellTypeDelay(i, my_bot->GetDefaultSpellTypeDelay(i, bot_stance));
}
}
output = "delay settings";
}
else if (!strcasecmp(sep->arg[1], "minthresholds")) {
if (spell_type != UINT16_MAX) {
my_bot->SetSpellTypeMinThreshold(
spell_type,
my_bot->GetDefaultSpellTypeMinThreshold(spell_type, bot_stance));
}
else {
for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) {
my_bot->SetSpellTypeMinThreshold(i, my_bot->GetDefaultSpellTypeMinThreshold(i, bot_stance));
}
}
output = "minimum threshold settings";
}
else if (!strcasecmp(sep->arg[1], "maxthresholds")) {
if (spell_type != UINT16_MAX) {
my_bot->SetSpellTypeMaxThreshold(
spell_type,
my_bot->GetDefaultSpellTypeMaxThreshold(spell_type, bot_stance));
}
else {
for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) {
my_bot->SetSpellTypeMaxThreshold(i, my_bot->GetDefaultSpellTypeMaxThreshold(i, bot_stance));
}
}
output = "maximum threshold settings";
}
else if (!strcasecmp(sep->arg[1], "aggrochecks")) {
if (spell_type != UINT16_MAX) {
my_bot->SetSpellTypeAggroCheck(spell_type, my_bot->GetDefaultSpellTypeAggroCheck(spell_type, bot_stance));
}
else {
for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) {
my_bot->SetSpellTypeAggroCheck(i, my_bot->GetDefaultSpellTypeAggroCheck(i, bot_stance));
}
}
output = "aggro check settings";
}
else if (!strcasecmp(sep->arg[1], "resist limit")) {
if (spell_type != UINT16_MAX) {
my_bot->SetSpellTypeAEOrGroupTargetCount(spell_type, my_bot->GetDefaultSpellTypeAEOrGroupTargetCount(spell_type, bot_stance));
}
else {
for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) {
my_bot->SetSpellTypeAEOrGroupTargetCount(i, my_bot->GetDefaultSpellTypeAEOrGroupTargetCount(i, bot_stance));
}
}
output = "resist limit settings";
}
else if (!strcasecmp(sep->arg[1], "minmanapct")) {
if (spell_type != UINT16_MAX) {
my_bot->SetSpellTypeMinManaLimit(spell_type, my_bot->GetDefaultSpellTypeMinManaLimit(spell_type, bot_stance));
}
else {
for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) {
my_bot->SetSpellTypeMinManaLimit(i, my_bot->GetDefaultSpellTypeMinManaLimit(i, bot_stance));
}
}
output = "min mana settings";
}
else if (!strcasecmp(sep->arg[1], "maxmanapct")) {
if (spell_type != UINT16_MAX) {
my_bot->SetSpellTypeMaxManaLimit(spell_type, my_bot->GetDefaultSpellTypeMaxManaLimit(spell_type, bot_stance));
}
else {
for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) {
my_bot->SetSpellTypeMaxManaLimit(i, my_bot->GetDefaultSpellTypeMaxManaLimit(i, bot_stance));
}
}
output = "max mana settings";
}
else if (!strcasecmp(sep->arg[1], "minhppct")) {
if (spell_type != UINT16_MAX) {
my_bot->SetSpellTypeMinHPLimit(spell_type, my_bot->GetDefaultSpellTypeMinHPLimit(spell_type, bot_stance));
}
else {
for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) {
my_bot->SetSpellTypeMinHPLimit(i, my_bot->GetDefaultSpellTypeMinHPLimit(i, bot_stance));
}
}
output = "min hp settings";
}
else if (!strcasecmp(sep->arg[1], "maxhppct")) {
if (spell_type != UINT16_MAX) {
my_bot->SetSpellTypeMaxHPLimit(spell_type, my_bot->GetDefaultSpellTypeMaxHPLimit(spell_type, bot_stance));
}
else {
for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) {
my_bot->SetSpellTypeMaxHPLimit(i, my_bot->GetDefaultSpellTypeMaxHPLimit(i, bot_stance));
}
}
output = "max hp settings";
}
else if (!strcasecmp(sep->arg[1], "idlepriority")) {
if (spell_type != UINT16_MAX) {
my_bot->SetSpellTypePriority(spell_type, BotPriorityCategories::Idle, my_bot->GetDefaultSpellTypePriority(spell_type, BotPriorityCategories::Idle, my_bot->GetClass(), bot_stance));
}
else {
for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) {
my_bot->SetSpellTypePriority(i, BotPriorityCategories::Idle, my_bot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Idle, my_bot->GetClass(), bot_stance));
}
}
output = "idle priority settings";
}
else if (!strcasecmp(sep->arg[1], "engagedpriority")) {
if (spell_type != UINT16_MAX) {
my_bot->SetSpellTypePriority(spell_type, BotPriorityCategories::Engaged, my_bot->GetDefaultSpellTypePriority(spell_type, BotPriorityCategories::Engaged, my_bot->GetClass(), bot_stance));
}
else {
for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) {
my_bot->SetSpellTypePriority(i, BotPriorityCategories::Engaged, my_bot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Engaged, my_bot->GetClass(), bot_stance));
}
}
output = "engaged priority settings";
}
else if (!strcasecmp(sep->arg[1], "pursuepriority")) {
if (spell_type != UINT16_MAX) {
my_bot->SetSpellTypePriority(spell_type, BotPriorityCategories::Pursue, my_bot->GetDefaultSpellTypePriority(spell_type, BotPriorityCategories::Pursue, my_bot->GetClass(), bot_stance));
}
else {
for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) {
my_bot->SetSpellTypePriority(i, BotPriorityCategories::Pursue, my_bot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Pursue, my_bot->GetClass(), bot_stance));
}
}
output = "pursue priority settings";
}
else if (!strcasecmp(sep->arg[1], "targetcounts")) {
if (spell_type != UINT16_MAX) {
my_bot->SetSpellTypeAEOrGroupTargetCount(spell_type, my_bot->GetDefaultSpellTypeAEOrGroupTargetCount(spell_type, bot_stance));
}
else {
for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) {
my_bot->SetSpellTypeAEOrGroupTargetCount(i, my_bot->GetDefaultSpellTypeAEOrGroupTargetCount(i, bot_stance));
}
}
output = "target count settings";
}
else if (!strcasecmp(sep->arg[1], "announcecast")) {
if (spell_type != UINT16_MAX) {
my_bot->SetSpellTypeAEOrGroupTargetCount(spell_type, my_bot->GetDefaultSpellTypeAEOrGroupTargetCount(spell_type, bot_stance));
}
else {
for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) {
my_bot->SetSpellTypeAEOrGroupTargetCount(i, my_bot->GetDefaultSpellTypeAEOrGroupTargetCount(i, bot_stance));
}
}
output = "announce cast settings";
}
my_bot->Save();
++success_count;
}
if (success_count == 1) {
c->Message(
Chat::Green,
fmt::format(
"{} says, '{}{} were restored.'",
first_found->GetCleanName(),
(
spell_type != UINT16_MAX ?
fmt::format("My [{}] ",
Bot::GetSpellTypeNameByID(spell_type)
)
: "My "
),
output
).c_str()
);
}
else {
c->Message(
Chat::Green,
fmt::format(
"{} of your bot's{}{} were restored.",
success_count,
(
spell_type != UINT16_MAX ?
fmt::format(" [{}] ",
Bot::GetSpellTypeNameByID(spell_type)
)
: " "
),
output
).c_str()
);
}
}
-69
View File
@@ -1,69 +0,0 @@
#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);
}
+249 -36
View File
@@ -1,59 +1,272 @@
#include "../bot_command.h"
void bot_command_depart(Client *c, const Seperator *sep)
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"))
if (helper_command_alias_fail(c, "bot_command_depart", sep->arg[0], "depart")) {
c->Message(Chat::White, "note: Tells bots to list their port locations or port to a specific location.");
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);
BotCommandHelpParams p;
p.description = { "Tells bots to list their port locations or port to a specific location." };
p.notes =
{
"- This will interrupt any spell currently being cast by bots told to use the command.",
"- Bots will still check to see if they have the spell in their spell list, whether the target is immune, spell is allowed and all other sanity checks for spells"
};
p.example_format = { fmt::format("{} [list | zone shortname] [optional: single | group | ae] [actionable, default: spawned]", sep->arg[0]) };
p.examples_one =
{
"To tell everyone to list their portable locations:",
fmt::format("{} list spawned",
sep->arg[0]
)
};
p.examples_two =
{
"To tell all bots to port to Nexus:",
fmt::format(
"{} nexus spawned",
sep->arg[0]
)
};
p.examples_three =
{
"To tell Druidbot to single target port to Butcher:",
fmt::format(
"{} butcher single byname Druidbot",
sep->arg[0]
)
};
p.actionables = { "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" };
std::string popup_text = c->SendBotCommandHelpWindow(p);
popup_text = DialogueWindow::Table(popup_text);
c->SendPopupToClient(sep->arg[0], popup_text.c_str());
if (RuleB(Bots, SendClassRaceOnHelp)) {
c->Message(
Chat::Yellow,
fmt::format(
"Use {} for information about race/class IDs.",
Saylink::Silent("^classracelist")
).c_str()
);
}
return;
}
bool single = false;
bool group = true;
bool ae = false;
std::string single_arg = sep->arg[2];
if (!single_arg.compare("single"))
single = true;
bool list = false;
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;
int ab_arg = 2;
if (!single_arg.compare("single")) {
++ab_arg;
single = true;
group = false;
}
else if (destination.empty()) {
c->Message(Chat::White, "A [destination] or [list] argument is required to use this command");
else if (!single_arg.compare("group")) {
++ab_arg;
single = false;
group = true;
}
else if (!single_arg.compare("ae")) {
++ab_arg;
single = false;
group = false;
ae = true;
}
if (!destination.compare("list") || destination.empty()) {
list = true;
if (destination.empty()) {
c->Message(
Chat::Yellow,
fmt::format(
"Use {} for information regarding this command.",
Saylink::Silent(
fmt::format("{} help", sep->arg[0])
)
).c_str()
);
}
}
Mob* tar = c->GetTarget();
if (!tar) {
tar = c;
}
std::string arg_string = sep->arg[ab_arg];
const int ab_mask = ActionableBots::ABM_Type1;
std::string actionable_arg = sep->arg[ab_arg];
if (actionable_arg.empty()) {
actionable_arg = "spawned";
}
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::vector<Bot*> sbl;
if (ActionableBots::PopulateSBL(c, actionable_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;
}
ActionableTarget::Types actionable_targets;
Bot* my_bot = nullptr;
std::list<Bot*> sbl;
MyBots::PopulateSBL_BySpawnedBots(c, sbl);
sbl.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end());
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;
BotSpell bot_spell;
bot_spell.SpellId = 0;
bot_spell.SpellIndex = 0;
bot_spell.ManaCost = 0;
auto target_mob = actionable_targets.Select(c, local_entry->target_type, FRIENDLY);
if (!target_mob)
continue;
bool is_success = false;
std::map<std::string, std::pair<uint8_t, uint8_t>> list_zones;
my_bot = ActionableBots::Select_ByMinLevelAndClass(c, local_entry->target_type, sbl, local_entry->spell_level, local_entry->caster_class, target_mob);
if (!my_bot)
for (auto bot_iter : sbl) {
if (!bot_iter->IsInGroupOrRaid(tar, !single)) {
continue;
}
cast_success = helper_cast_standard_spell(my_bot, target_mob, local_entry->spell_id);
break;
if (bot_iter->GetBotStance() == Stance::Passive || bot_iter->GetHoldFlag() || bot_iter->GetAppearance() == eaDead || bot_iter->IsFeared() || bot_iter->IsSilenced() || bot_iter->IsAmnesiad() || bot_iter->GetHP() < 0) {
continue;
}
std::vector<BotSpell_wPriority> bot_spell_list_itr = bot_iter->GetPrioritizedBotSpellsBySpellType(bot_iter, BotSpellTypes::Teleport, tar);
for (std::vector<BotSpell_wPriority>::iterator itr = bot_spell_list_itr.begin(); itr != bot_spell_list_itr.end(); ++itr) {
if (!IsValidSpell(itr->SpellId)) {
continue;
}
if (
(single && spells[itr->SpellId].target_type != ST_Target) ||
(group && !IsGroupSpell(itr->SpellId)) ||
(ae && !IsAnyAESpell(itr->SpellId))
) {
continue;
}
if (list) {
auto it = list_zones.find(spells[itr->SpellId].teleport_zone);
if (it != list_zones.end()) {
const auto& [val1, val2] = it->second;
if (val1 == spells[itr->SpellId].target_type && val2 == bot_iter->GetClass()) {
continue;
}
}
Bot::RaidGroupSay(
bot_iter,
fmt::format(
"I can port you to {}.",
Saylink::Silent(
fmt::format(
"{} {} {} byname {}",
sep->arg[0],
spells[itr->SpellId].teleport_zone,
(spells[itr->SpellId].target_type == ST_Target ? "single" : (IsGroupSpell(itr->SpellId) ? "group" : "ae")),
bot_iter->GetCleanName()
).c_str()
, ZoneLongName(ZoneID(spells[itr->SpellId].teleport_zone)))
).c_str()
);
list_zones.insert({ spells[itr->SpellId].teleport_zone, {spells[itr->SpellId].target_type, bot_iter->GetClass()} });
continue;
}
if (destination.compare(spells[itr->SpellId].teleport_zone)) {
continue;
}
bot_iter->SetCommandedSpell(true);
if (!IsValidSpellAndLoS(itr->SpellId, bot_iter->HasLoS())) {
continue;
}
if (IsInvulnerabilitySpell(itr->SpellId)) {
tar = bot_iter; //target self for invul type spells
}
if (bot_iter->IsCommandedSpell() && bot_iter->IsCasting()) {
Bot::RaidGroupSay(
bot_iter,
fmt::format(
"Interrupting {}. I have been commanded to try to cast a [{}] spell, {} on {}.",
bot_iter->CastingSpellID() ? spells[bot_iter->CastingSpellID()].name : "my spell",
bot_iter->GetSpellTypeNameByID(BotSpellTypes::Teleport),
spells[itr->SpellId].name,
tar->GetCleanName()
).c_str()
);
bot_iter->InterruptSpell();
}
if (bot_iter->CastSpell(itr->SpellId, tar->GetID(), EQ::spells::CastingSlot::Gem2, -1, -1)) {
if (IsBotSpellTypeOtherBeneficial(BotSpellTypes::Teleport)) {
bot_iter->SetCastedSpellType(UINT16_MAX);
}
else {
bot_iter->SetCastedSpellType(BotSpellTypes::Teleport);
}
Bot::RaidGroupSay(
bot_iter,
fmt::format(
"Casting {} [{}] on {}.",
GetSpellName(itr->SpellId),
bot_iter->GetSpellTypeNameByID(BotSpellTypes::Teleport),
(tar == bot_iter ? "myself" : tar->GetCleanName())
).c_str()
);
is_success = true;
}
bot_iter->SetCommandedSpell(false);
if (is_success) {
return;
}
else {
continue;
}
}
}
helper_no_available_bots(c, my_bot);
if (
(list && list_zones.empty()) ||
(!list && !is_success)
) {
c->Message(
Chat::Yellow,
fmt::format(
"No bots are capable of that on {}. Be sure they are in the same group, raid or raid group if necessary.",
tar ? tar->GetCleanName() : "you"
).c_str()
);
}
}
+273
View File
@@ -0,0 +1,273 @@
#include "../bot_command.h"
void bot_command_discipline(Client* c, const Seperator* sep)
{
if (helper_command_alias_fail(c, "bot_command_discipline", sep->arg[0], "discipline")) {
c->Message(Chat::White, "note: Tells applicable bots to use the specified disciplines.");
return;
}
if (helper_is_help_or_usage(sep->arg[1])) {
BotCommandHelpParams p;
p.description =
{
"Tells applicable bots to use the specified disciplines."
};
p.notes = { "Aside from Lay On Hands and Harm Touch, you will need to know the spell ID of the discipline to tell a bot to attempt to use it." };
p.example_format = { fmt::format("{} [aggressive | defensive | spell ID] [actionable, default: spawned]", sep->arg[0]) };
p.examples_one =
{
"To tell all bots to use an aggressive discipline:",
fmt::format(
"{} aggressive spawned",
sep->arg[0]
)
};
p.examples_two =
{
"To tell Warrior bots to use a defensive discipline:",
fmt::format(
"{} defensive byclass {}",
sep->arg[0],
Class::Warrior
)
};
p.examples_three =
{
"To tell all bots to use their Fearless discipline:",
fmt::format(
"{} 4587 spawned",
sep->arg[0]
)
};
p.actionables = { "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" };
std::string popup_text = c->SendBotCommandHelpWindow(p);
popup_text = DialogueWindow::Table(popup_text);
c->SendPopupToClient(sep->arg[0], popup_text.c_str());
if (RuleB(Bots, SendClassRaceOnHelp)) {
c->Message(
Chat::Yellow,
fmt::format(
"Use {} for information about race/class IDs.",
Saylink::Silent("^classracelist")
).c_str()
);
}
return;
}
std::string arg1 = sep->arg[1];
std::string arg2 = sep->arg[2];
int ab_arg = 2;
bool aggressive = false;
bool defensive = false;
Mob* tar = c->GetTarget();
uint16 spell_id = UINT16_MAX;
if (!arg1.compare("aggressive")) {
aggressive = true;
}
else if (!arg1.compare("defensive")) {
defensive = true;
}
else if (sep->IsNumber(1)) {
if (!IsValidSpell(atoi(sep->arg[1]))) {
c->Message(Chat::Yellow, "You must enter a valid spell ID.");
return;
}
spell_id = atoi(sep->arg[1]);
}
else {
c->Message(
Chat::Yellow,
fmt::format(
"Incorrect argument, use {} for information regarding this command.",
Saylink::Silent(
fmt::format("{} help", sep->arg[0])
)
).c_str()
);
return;
}
const int ab_mask = ActionableBots::ABM_Type1;
std::string actionable_arg = sep->arg[ab_arg];
if (actionable_arg.empty()) {
actionable_arg = "spawned";
}
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::vector<Bot*> sbl;
if (ActionableBots::PopulateSBL(c, actionable_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.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end());
bool is_success = false;
uint16 success_count = 0;
Bot* first_found = nullptr;
for (auto bot_iter : sbl) {
if (!bot_iter->IsInGroupOrRaid(c)) {
continue;
}
if (bot_iter->GetBotStance() == Stance::Passive || bot_iter->GetHoldFlag() || bot_iter->GetAppearance() == eaDead || bot_iter->IsFeared() || bot_iter->IsSilenced() || bot_iter->IsAmnesiad() || bot_iter->GetHP() < 0) {
continue;
}
if (spell_id == UINT16_MAX) { // Aggressive/Defensive type
std::vector<BotSpells_wIndex> bot_spell_list;
if (aggressive) {
bot_spell_list = bot_iter->BotGetSpellsByType(BotSpellTypes::DiscAggressive);
}
else if (defensive) {
bot_spell_list = bot_iter->BotGetSpellsByType(BotSpellTypes::DiscDefensive);
}
for (int i = bot_spell_list.size() - 1; i >= 0; i--) {
if (!IsValidSpell(bot_spell_list[i].spellid)) {
continue;
}
if (!bot_iter->CheckDisciplineReuseTimer(bot_spell_list[i].spellid)) {
uint32 remaining_time = (bot_iter->GetDisciplineReuseRemainingTime(bot_spell_list[i].spellid) / 1000);
bot_iter->OwnerMessage(
fmt::format(
"I can use this discipline in {}.",
Strings::SecondsToTime(remaining_time)
)
);
continue;
}
if (bot_iter->GetEndurance() < spells[bot_spell_list[i].spellid].endurance_cost) {
continue;
}
if (bot_iter->DivineAura() && !IsCastNotStandingSpell(bot_spell_list[i].spellid)) {
continue;
}
if (spells[bot_spell_list[i].spellid].buff_duration_formula != 0 && spells[bot_spell_list[i].spellid].target_type == ST_Self && bot_iter->HasDiscBuff()) {
continue;
}
if (!tar || (spells[bot_spell_list[i].spellid].target_type == ST_Self && tar != bot_iter)) {
tar = bot_iter;
}
if (bot_iter->AttemptForcedCastSpell(tar, bot_spell_list[i].spellid, true)) {
if (!first_found) {
first_found = bot_iter;
}
is_success = true;
++success_count;
spell_id = bot_spell_list[i].spellid;
}
}
}
else { // Direct spell ID
if (!IsValidSpell(spell_id)) {
continue;
}
SPDat_Spell_Struct spell = spells[spell_id];
if (!bot_iter->CanUseBotSpell(spell_id)) {
continue;
}
if (!bot_iter->CheckDisciplineReuseTimer(spell_id)) {
uint32 remaining_time = (bot_iter->GetDisciplineReuseRemainingTime(spell_id) / 1000);
bot_iter->OwnerMessage(
fmt::format(
"I can use this item in {}.",
Strings::SecondsToTime(remaining_time)
)
);
continue;
}
if (bot_iter->GetEndurance() < spell.endurance_cost) {
continue;
}
if (bot_iter->DivineAura() && !IsCastNotStandingSpell(spell_id)) {
continue;
}
if (spell.buff_duration_formula != 0 && spell.target_type == ST_Self && bot_iter->HasDiscBuff()) {
continue;
}
if (!tar || (spell.target_type == ST_Self && tar != bot_iter)) {
tar = bot_iter;
}
if (bot_iter->AttemptForcedCastSpell(tar, spell_id, true)) {
if (!first_found) {
first_found = bot_iter;
}
is_success = true;
++success_count;
}
}
continue;
}
if (!is_success) {
c->Message(Chat::Yellow, "No bots were selected.");
}
else {
if (aggressive || defensive) {
c->Message(
Chat::Yellow,
fmt::format(
"{} {} {} {} discipline.",
((success_count == 1 && first_found) ? first_found->GetCleanName() : (fmt::format("{}", success_count).c_str())),
((success_count == 1 && first_found) ? "used" : "of your bots used"),
(aggressive ? "an" : "a"),
(aggressive ? "aggressive" : "defensive")
).c_str()
);
}
else {
c->Message(
Chat::Yellow,
fmt::format(
"{} {} their {} [#{}] discipline.",
((success_count == 1 && first_found) ? first_found->GetCleanName() : (fmt::format("{}", success_count).c_str())),
((success_count == 1 && first_found) ? "used" : "of your bots used"),
spells[spell_id].name,
spell_id
).c_str()
);
}
}
}
+157
View File
@@ -0,0 +1,157 @@
#include "../bot_command.h"
void bot_command_distance_ranged(Client* c, const Seperator* sep)
{
if (helper_command_alias_fail(c, "bot_command_distance_ranged", sep->arg[0], "distanceranged")) {
c->Message(Chat::White, "note: Sets the distance bots will attempt to stay away from their target to cast or use ranged items.");
return;
}
if (helper_is_help_or_usage(sep->arg[1])) {
BotCommandHelpParams p;
p.description = { "Sets the distance bots will attempt to stay away from their target to cast or use ranged items." };
p.notes =
{
"- Bots will stay between half the value of the setting and the current value. IE, if set to 60, bots will stay between 30 and 60.",
"- Casters will never go closer than their maximum melee range.",
"- Throwing bots will never get closer than the minimum value for ranged to work, or beyond the range of their items."
};
p.example_format = { fmt::format("{} [value] [actionable]", sep->arg[0]) };
p.examples_one = {
"To set Wizards to a range of 100:",
fmt::format(
"{} 100 byclass {}",
sep->arg[0],
Class::Wizard
)
};
p.examples_two = {
"To set Rangers to a range of 175:",
fmt::format(
"{} 175 byclass {}",
sep->arg[0],
Class::Ranger
)
};
p.examples_three = {
"To view the current setting of all bots:",
fmt::format(
"{} current spawned",
sep->arg[0]
)
};
p.actionables = { "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" };
std::string popup_text = c->SendBotCommandHelpWindow(p);
popup_text = DialogueWindow::Table(popup_text);
c->SendPopupToClient(sep->arg[0], popup_text.c_str());
if (RuleB(Bots, SendClassRaceOnHelp)) {
c->Message(
Chat::Yellow,
fmt::format(
"Use {} for information about race/class IDs.",
Saylink::Silent("^classracelist")
).c_str()
);
}
return;
c->Message(Chat::White, "usage: %s [current | value: 0 - 300] ([actionable: target | byname | ownergroup | ownerraid | targetgroup | namesgroup | healrotationtargets | mmr | byclass | byrace | spawned] ([actionable_name]))", sep->arg[0]);
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.");
}
const int ab_mask = ActionableBots::ABM_Type1;
std::string arg1 = sep->arg[1];
int ab_arg = 1;
bool current_check = false;
uint32 value = 0;
if (sep->IsNumber(1)) {
++ab_arg;
value = atoi(sep->arg[1]);
if (value < 0 || value > RuleI(Bots, MaxDistanceRanged)) {
c->Message(Chat::Yellow, "You must enter a value within the range of 0 - 300.");
return;
}
}
else if (!arg1.compare("current")) {
++ab_arg;
current_check = true;
}
else {
c->Message(Chat::Yellow, "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::vector<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.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end());
Bot* first_found = nullptr;
int success_count = 0;
for (auto my_bot : sbl) {
if (my_bot->BotPassiveCheck()) {
continue;
}
if (!first_found) {
first_found = my_bot;
}
if (current_check) {
c->Message(
Chat::Green,
fmt::format(
"{} says, 'My current Distance Ranged is {}.'",
my_bot->GetCleanName(),
my_bot->GetBotDistanceRanged()
).c_str()
);
}
else {
my_bot->SetBotDistanceRanged(value);
++success_count;
}
}
if (!current_check) {
if (success_count == 1 && first_found) {
c->Message(
Chat::Green,
fmt::format(
"{} says, 'My Distance Ranged was set to {}.'",
first_found->GetCleanName(),
value
).c_str()
);
}
else {
c->Message(
Chat::Green,
fmt::format(
"{} of your bots set their Distance Ranged to {}.",
success_count,
value
).c_str()
);
}
}
}
-44
View File
@@ -1,44 +0,0 @@
#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);
}
+178 -55
View File
@@ -1,40 +1,85 @@
#include "../bot_command.h"
void bot_command_follow(Client *c, const Seperator *sep)
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]);
if (helper_command_alias_fail(c, "bot_command_follow", sep->arg[0], "follow")) {
c->Message(Chat::White, "note: Sets bots of your choosing to follow your target, view their current following state or reset their following state.");
return;
}
if (helper_is_help_or_usage(sep->arg[1])) {
BotCommandHelpParams p;
p.description = { "Sets bots of your choosing to follow your target, view their current following state or reset their following state." };
p.notes = { "- You can only follow players, bots or mercenaries belonging to your group or raid." };
p.example_format = { fmt::format("{} [optional] [actionable]", sep->arg[0]) };
p.examples_one =
{
"To set all Clerics to follow your target:",
fmt::format(
"{} byclass {}",
sep->arg[0],
Class::Cleric
)
};
p.examples_two =
{
"To check the current state of all bots:",
fmt::format(
"{} current spawned",
sep->arg[0]
)
};
p.examples_three =
{
"To reset all bots:",
fmt::format(
"{} reset spawned",
sep->arg[0]
)
};
p.actionables = { "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" };
std::string popup_text = c->SendBotCommandHelpWindow(p);
popup_text = DialogueWindow::Table(popup_text);
c->SendPopupToClient(sep->arg[0], popup_text.c_str());
return;
}
const int ab_mask = ActionableBots::ABM_Type2;
bool chain = false;
bool reset = false;
bool current_check = 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")) {
if (!optional_arg.compare("reset")) {
target_mob = c;
reset = true;
ab_arg = 2;
name_arg = 3;
++ab_arg;
}
else if (!optional_arg.compare("current")) {
current_check = true;
++ab_arg;
}
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");
target_mob = c->GetTarget();
if (!target_mob || !target_mob->IsOfClientBotMerc() || !c->IsInGroupOrRaid(target_mob)) {
c->Message(Chat::Yellow, "You must <target> a friendly player, bot or merc within your group or raid to use this command");
return;
}
if (!optional_arg.compare("chain")) {
chain = true;
++ab_arg;
}
}
std::string class_race_arg = sep->arg[ab_arg];
@@ -43,69 +88,147 @@ void bot_command_follow(Client *c, const Seperator *sep)
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) {
std::vector<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 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());
sbl.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end());
auto bot_count = sbl.size();
Mob* follow_mob = nullptr;
std::list<Bot*> chain_list;
std::list<Bot*>::const_iterator it = chain_list.begin();
uint16 count = 0;
for (auto bot_iter : sbl) {
if (current_check) {
follow_mob = entity_list.GetMob(bot_iter->GetFollowID());
c->Message(
Chat::Green,
fmt::format(
"{} says, 'I am currently following {}.'",
bot_iter->GetCleanName(),
follow_mob ? follow_mob->GetCleanName() : "no one"
).c_str()
);
if (!follow_mob && RuleB(Bots, DoResponseAnimations)) {
bot_iter->DoAnim(28);
}
continue;
}
if (bot_iter == target_mob) {
if (bot_count == 1) {
c->Message(
Chat::Yellow,
fmt::format(
"{} says, 'I cannot follow myself, you want me to run circles?",
bot_iter->GetCleanName()
).c_str()
);
if (RuleB(Bots, DoResponseAnimations)) {
bot_iter->DoAnim(60);
}
return;
}
bot_iter->WipeHateList();
--bot_count;
continue;
}
if (!bot_iter->IsInGroupOrRaid(target_mob)) {
--bot_count;
continue;
}
bot_iter->WipeHateList();
if (!bot_iter->GetGroup() && !bot_iter->GetRaid()) {
bot_iter->SetFollowID(0);
bot_iter->SetManualFollow(false);
}
else {
if (reset) {
bot_iter->SetFollowID(c->GetID());
bot_iter->SetManualFollow(false);
}
else {
if (bot_iter == target_mob)
bot_iter->SetFollowID(c->GetID());
else
if (chain) {
Mob* next_tar = target_mob;
if (count > 0) {
next_tar = *it;
if (!next_tar) {
next_tar = target_mob;
}
}
chain_list.push_back(bot_iter);
++it;
++count;
bot_iter->SetFollowID(next_tar->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())
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(),
if (current_check || !bot_count) {
return;
}
follow_mob = target_mob;
if (bot_count == 1) {
follow_mob = entity_list.GetMob(sbl.front()->GetFollowID());
c->Message(
Chat::Green,
fmt::format(
"Following {}.",
follow_mob ? follow_mob->GetCleanName() : "no one"
"{} says, 'Following {}.'",
sbl.front()->GetCleanName(),
follow_mob ? follow_mob->GetCleanName() : "you"
).c_str()
);
} else {
}
else {
if (reset) {
c->Message(
Chat::White,
Chat::Green,
fmt::format(
"{} of your bots are following their default assignments.",
sbl.size()
"{} of your bots are following you.",
bot_count
).c_str()
);
} else {
}
else {
c->Message(
Chat::White,
Chat::Green,
fmt::format(
"{} of your bots are following {}.",
sbl.size(),
target_mob->GetCleanName()
"{} of your bots are {} {}.",
bot_count,
chain ? "chain following" : "following",
follow_mob ? follow_mob->GetCleanName() : "you"
).c_str()
);
}
+6 -6
View File
@@ -7,7 +7,7 @@ void bot_command_guard(Client *c, const Seperator *sep)
}
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]);
c->Message(Chat::White, "usage: %s ([option: clear]) [actionable: byname | ownergroup | ownerraid | namesgroup | healrotation | mmr | byclass | byrace | default: spawned] ([actionable_name])", sep->arg[0]);
return;
}
const int ab_mask = (ActionableBots::ABM_Target | ActionableBots::ABM_Type2);
@@ -20,8 +20,8 @@ void bot_command_guard(Client *c, const Seperator *sep)
if (!clear_arg.compare("clear")) {
clear = true;
ab_arg = 2;
name_arg = 3;
++ab_arg;
++name_arg;
}
std::string class_race_arg = sep->arg[ab_arg];
@@ -30,12 +30,12 @@ void bot_command_guard(Client *c, const Seperator *sep)
class_race_check = true;
}
std::list<Bot*> sbl;
std::vector<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);
sbl.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end());
for (auto bot_iter : sbl) {
if (clear) {
@@ -47,7 +47,7 @@ void bot_command_guard(Client *c, const Seperator *sep)
}
if (sbl.size() == 1) {
Bot::BotGroupSay(
Bot::RaidGroupSay(
sbl.front(),
fmt::format(
"{}uarding this position.",
+42 -42
View File
@@ -2,28 +2,28 @@
void bot_command_heal_rotation(Client *c, const Seperator *sep)
{
std::list<const char*> subcommand_list;
subcommand_list.push_back("healrotationadaptivetargeting");
subcommand_list.push_back("healrotationaddmember");
subcommand_list.push_back("healrotationaddtarget");
subcommand_list.push_back("healrotationadjustcritical");
subcommand_list.push_back("healrotationadjustsafe");
subcommand_list.push_back("healrotationcastoverride");
subcommand_list.push_back("healrotationchangeinterval");
subcommand_list.push_back("healrotationclearhot");
subcommand_list.push_back("healrotationcleartargets");
subcommand_list.push_back("healrotationcreate");
subcommand_list.push_back("healrotationdelete");
subcommand_list.push_back("healrotationfastheals");
subcommand_list.push_back("healrotationlist");
subcommand_list.push_back("healrotationremovemember");
subcommand_list.push_back("healrotationremovetarget");
subcommand_list.push_back("healrotationresetlimits");
subcommand_list.push_back("healrotationsave");
subcommand_list.push_back("healrotationsethot");
subcommand_list.push_back("healrotationstart");
subcommand_list.push_back("healrotationstop");
std::vector<const char*> subcommand_list = {
"healrotationadaptivetargeting",
"healrotationaddmember",
"healrotationaddtarget",
"healrotationadjustcritical",
"healrotationadjustsafe",
"healrotationcastoverride",
"healrotationchangeinterval",
"healrotationclearhot",
"healrotationcleartargets",
"healrotationcreate",
"healrotationdelete",
"healrotationfastheals",
"healrotationlist",
"healrotationremovemember",
"healrotationremovetarget",
"healrotationresetlimits",
"healrotationsave",
"healrotationsethot",
"healrotationstart",
"healrotationstop"
};
if (helper_command_alias_fail(c, "bot_command_heal_rotation", sep->arg[0], "healrotation"))
return;
@@ -63,7 +63,7 @@ void bot_command_heal_rotation_adaptive_targeting(Client* c, const Seperator* se
std::string adaptive_targeting_arg;
std::list<Bot*> sbl;
std::vector<Bot*> sbl;
MyBots::PopulateSBL_ByNamedBot(c, sbl, sep->arg[1]);
if (!sbl.empty()) {
adaptive_targeting_arg = sep->arg[2];
@@ -123,7 +123,7 @@ void bot_command_heal_rotation_add_member(Client* c, const Seperator* sep)
return;
}
std::list<Bot*> sbl;
std::vector<Bot*> sbl;
MyBots::PopulateSBL_ByNamedBot(c, sbl, sep->arg[1]);
if (sbl.empty()) {
c->Message(Chat::White, "You must [name] a new member as a bot that you own to use this command");
@@ -191,7 +191,7 @@ void bot_command_heal_rotation_add_target(Client* c, const Seperator* sep)
return;
}
std::list<Bot*> sbl;
std::vector<Bot*> sbl;
MyBots::PopulateSBL_ByNamedBot(c, sbl, sep->arg[2]);
if (sbl.empty()) {
MyBots::PopulateSBL_ByTargetedBot(c, sbl);
@@ -283,7 +283,7 @@ void bot_command_heal_rotation_adjust_critical(Client* c, const Seperator* sep)
return;
}
std::list<Bot*> sbl;
std::vector<Bot*> sbl;
MyBots::PopulateSBL_ByNamedBot(c, sbl, sep->arg[3]);
if (sbl.empty()) {
MyBots::PopulateSBL_ByTargetedBot(c, sbl);
@@ -383,7 +383,7 @@ void bot_command_heal_rotation_adjust_safe(Client* c, const Seperator* sep)
return;
}
std::list<Bot*> sbl;
std::vector<Bot*> sbl;
MyBots::PopulateSBL_ByNamedBot(c, sbl, sep->arg[3]);
if (sbl.empty()) {
MyBots::PopulateSBL_ByTargetedBot(c, sbl);
@@ -460,7 +460,7 @@ void bot_command_heal_rotation_casting_override(Client* c, const Seperator* sep)
std::string casting_override_arg;
std::list<Bot*> sbl;
std::vector<Bot*> sbl;
MyBots::PopulateSBL_ByNamedBot(c, sbl, sep->arg[1]);
if (!sbl.empty()) {
casting_override_arg = sep->arg[2];
@@ -534,7 +534,7 @@ void bot_command_heal_rotation_change_interval(Client* c, const Seperator* sep)
std::string change_interval_arg;
std::list<Bot*> sbl;
std::vector<Bot*> sbl;
MyBots::PopulateSBL_ByNamedBot(c, sbl, sep->arg[1]);
if (!sbl.empty()) {
change_interval_arg = sep->arg[2];
@@ -603,7 +603,7 @@ void bot_command_heal_rotation_clear_hot(Client* c, const Seperator* sep)
return;
}
std::list<Bot*> sbl;
std::vector<Bot*> sbl;
MyBots::PopulateSBL_ByNamedBot(c, sbl, sep->arg[1]);
if (sbl.empty()) {
MyBots::PopulateSBL_ByTargetedBot(c, sbl);
@@ -649,7 +649,7 @@ void bot_command_heal_rotation_clear_targets(Client* c, const Seperator* sep)
return;
}
std::list<Bot*> sbl;
std::vector<Bot*> sbl;
MyBots::PopulateSBL_ByNamedBot(c, sbl, sep->arg[1]);
if (sbl.empty()) {
MyBots::PopulateSBL_ByTargetedBot(c, sbl);
@@ -703,7 +703,7 @@ void bot_command_heal_rotation_create(Client* c, const Seperator* sep)
std::string adaptive_targeting_arg;
std::string casting_override_arg;
std::list<Bot*> sbl;
std::vector<Bot*> sbl;
MyBots::PopulateSBL_ByNamedBot(c, sbl, sep->arg[1]);
if (!sbl.empty()) {
interval_arg = sep->arg[2];
@@ -858,7 +858,7 @@ void bot_command_heal_rotation_delete(Client* c, const Seperator* sep)
return;
}
std::list<Bot*> sbl;
std::vector<Bot*> sbl;
MyBots::PopulateSBL_ByNamedBot(c, sbl, sep->arg[name_arg]);
if (sbl.empty()) {
MyBots::PopulateSBL_ByTargetedBot(c, sbl);
@@ -896,7 +896,7 @@ void bot_command_heal_rotation_fast_heals(Client* c, const Seperator* sep)
std::string fast_heals_arg;
std::list<Bot*> sbl;
std::vector<Bot*> sbl;
MyBots::PopulateSBL_ByNamedBot(c, sbl, sep->arg[1]);
if (!sbl.empty()) {
fast_heals_arg = sep->arg[2];
@@ -956,7 +956,7 @@ void bot_command_heal_rotation_list(Client* c, const Seperator* sep)
return;
}
std::list<Bot*> sbl;
std::vector<Bot*> sbl;
MyBots::PopulateSBL_ByNamedBot(c, sbl, sep->arg[1]);
if (sbl.empty()) {
MyBots::PopulateSBL_ByTargetedBot(c, sbl);
@@ -1076,7 +1076,7 @@ void bot_command_heal_rotation_remove_member(Client* c, const Seperator* sep)
return;
}
std::list<Bot*> sbl;
std::vector<Bot*> sbl;
MyBots::PopulateSBL_ByNamedBot(c, sbl, sep->arg[1]);
if (sbl.empty()) {
MyBots::PopulateSBL_ByTargetedBot(c, sbl);
@@ -1123,7 +1123,7 @@ void bot_command_heal_rotation_remove_target(Client* c, const Seperator* sep)
return;
}
std::list<Bot*> sbl;
std::vector<Bot*> sbl;
MyBots::PopulateSBL_ByNamedBot(c, sbl, sep->arg[2]);
if (sbl.empty()) {
MyBots::PopulateSBL_ByTargetedBot(c, sbl);
@@ -1181,7 +1181,7 @@ void bot_command_heal_rotation_reset_limits(Client* c, const Seperator* sep)
return;
}
std::list<Bot*> sbl;
std::vector<Bot*> sbl;
MyBots::PopulateSBL_ByNamedBot(c, sbl, sep->arg[1]);
if (sbl.empty()) {
MyBots::PopulateSBL_ByTargetedBot(c, sbl);
@@ -1223,7 +1223,7 @@ void bot_command_heal_rotation_save(Client* c, const Seperator* sep)
return;
}
std::list<Bot*> sbl;
std::vector<Bot*> sbl;
MyBots::PopulateSBL_ByNamedBot(c, sbl, sep->arg[1]);
if (sbl.empty()) {
MyBots::PopulateSBL_ByTargetedBot(c, sbl);
@@ -1272,7 +1272,7 @@ void bot_command_heal_rotation_set_hot(Client* c, const Seperator* sep)
return;
}
std::list<Bot*> sbl;
std::vector<Bot*> sbl;
MyBots::PopulateSBL_ByNamedBot(c, sbl, sep->arg[2]);
if (sbl.empty()) {
MyBots::PopulateSBL_ByTargetedBot(c, sbl);
@@ -1337,7 +1337,7 @@ void bot_command_heal_rotation_start(Client* c, const Seperator* sep)
return;
}
std::list<Bot*> sbl;
std::vector<Bot*> sbl;
MyBots::PopulateSBL_ByNamedBot(c, sbl, sep->arg[1]);
if (sbl.empty()) {
MyBots::PopulateSBL_ByTargetedBot(c, sbl);
@@ -1384,7 +1384,7 @@ void bot_command_heal_rotation_stop(Client* c, const Seperator* sep)
return;
}
std::list<Bot*> sbl;
std::vector<Bot*> sbl;
MyBots::PopulateSBL_ByNamedBot(c, sbl, sep->arg[1]);
if (sbl.empty()) {
MyBots::PopulateSBL_ByTargetedBot(c, sbl);
+6 -6
View File
@@ -7,7 +7,7 @@ void bot_command_hold(Client *c, const Seperator *sep)
}
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]);
c->Message(Chat::White, "usage: %s ([option: clear]) [actionable: byname | ownergroup | ownerraid | namesgroup | healrotation | mmr | byclass | byrace | default: spawned] ([actionable_name])", sep->arg[0]);
return;
}
const int ab_mask = (ActionableBots::ABM_Target | ActionableBots::ABM_Type2);
@@ -20,8 +20,8 @@ void bot_command_hold(Client *c, const Seperator *sep)
if (!clear_arg.compare("clear")) {
clear = true;
ab_arg = 2;
name_arg = 3;
++ab_arg;
++name_arg;
}
std::string class_race_arg = sep->arg[ab_arg];
@@ -30,12 +30,12 @@ void bot_command_hold(Client *c, const Seperator *sep)
class_race_check = true;
}
std::list<Bot*> sbl;
std::vector<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);
sbl.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end());
for (auto bot_iter : sbl) {
if (clear) {
@@ -47,7 +47,7 @@ void bot_command_hold(Client *c, const Seperator *sep)
}
if (sbl.size() == 1) {
Bot::BotGroupSay(
Bot::RaidGroupSay(
sbl.front(),
fmt::format(
"{}olding my attacks.",
-38
View File
@@ -1,38 +0,0 @@
#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);
}
+157
View File
@@ -0,0 +1,157 @@
#include "../bot_command.h"
void bot_command_illusion_block(Client* c, const Seperator* sep)
{
if (helper_command_alias_fail(c, "bot_command_illusion_block", sep->arg[0], "illusionblock")) {
c->Message(Chat::White, "note: Toggles whether or not bots will block the illusion effects of spells cast by players or bots.");
return;
}
if (helper_is_help_or_usage(sep->arg[1])) {
BotCommandHelpParams p;
p.description = { "Toggles whether or not bots will block the illusion effects of spells cast by players or bots." };
p.example_format = { fmt::format("{} [value] [actionable]", sep->arg[0]) };
p.examples_one =
{
"To set BotA to block illusions:",
fmt::format(
"{} 1 byname BotA",
sep->arg[0]
)
};
p.examples_two =
{
"To set all bots to block illusions:",
fmt::format(
"{} 1 spawned",
sep->arg[0]
)
};
p.examples_three =
{
"To check the illusion block status for all bots:",
fmt::format(
"{} current spawned",
sep->arg[0]
)
};
p.actionables = { "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" };
std::string popup_text = c->SendBotCommandHelpWindow(p);
popup_text = DialogueWindow::Table(popup_text);
c->SendPopupToClient(sep->arg[0], popup_text.c_str());
if (RuleB(Bots, SendClassRaceOnHelp)) {
c->Message(
Chat::Yellow,
fmt::format(
"Use {} for information about race/class IDs.",
Saylink::Silent("^classracelist")
).c_str()
);
}
return;
}
std::string arg1 = sep->arg[1];
int ab_arg = 1;
bool current_check = false;
uint32 type_value = 0;
if (sep->IsNumber(1)) {
type_value = atoi(sep->arg[1]);
++ab_arg;
if (type_value < 0 || type_value > 1) {
c->Message(Chat::Yellow, "You must enter either 0 for disabled or 1 for enabled.");
return;
}
}
else if (!arg1.compare("current")) {
++ab_arg;
current_check = true;
}
else {
c->Message(
Chat::Yellow,
fmt::format(
"Incorrect argument, use {} for information regarding this command.",
Saylink::Silent(
fmt::format("{} help", sep->arg[0])
)
).c_str()
);
return;
}
const int ab_mask = ActionableBots::ABM_Type1;
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::vector<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.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end());
Bot* first_found = nullptr;
int success_count = 0;
for (auto my_bot : sbl) {
if (my_bot->BotPassiveCheck()) {
continue;
}
if (!first_found) {
first_found = my_bot;
}
if (current_check) {
c->Message(
Chat::Green,
fmt::format(
"{} says, 'I {} block illusions.'",
my_bot->GetCleanName(),
my_bot->GetIllusionBlock() ? "will" : "will not"
).c_str()
);
}
else {
my_bot->SetIllusionBlock(type_value);
++success_count;
}
}
if (!current_check) {
if (success_count == 1 && first_found) {
c->Message(
Chat::Green,
fmt::format(
"{} says, 'I {} block illusions.'",
first_found->GetCleanName(),
first_found->GetIllusionBlock() ? "will now" : "will no longer"
).c_str()
);
}
else {
c->Message(
Chat::Green,
fmt::format(
"{} of your bots {} block illusions.",
success_count,
type_value ? "will now" : "will no longer"
).c_str()
);
}
}
}
+58 -17
View File
@@ -2,12 +2,12 @@
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");
std::vector<const char*> subcommand_list = {
"inventorygive",
"inventorylist",
"inventoryremove",
"inventorywindow"
};
if (helper_command_alias_fail(c, "bot_command_inventory", sep->arg[0], "inventory"))
return;
@@ -25,7 +25,7 @@ void bot_command_inventory_give(Client* c, const Seperator* sep)
c->Message(
Chat::White,
fmt::format(
"Usage: {} ([actionable: target | byname] ([actionable_name]))",
"Usage: {} ([actionable: target | byname] ([actionable_name])) [optional: slot ID]",
sep->arg[0]
).c_str()
);
@@ -33,19 +33,45 @@ void bot_command_inventory_give(Client* c, const Seperator* sep)
}
int ab_mask = (ActionableBots::ABM_Target | ActionableBots::ABM_ByName);
int ab_arg = 1;
int slot_arg = 1;
int16 chosen_slot = INVALID_INDEX;
bool byname = false;
std::list<Bot*> sbl;
if (ActionableBots::PopulateSBL(c, sep->arg[1], sbl, ab_mask, sep->arg[2]) == ActionableBots::ABT_None) {
std::string byname_arg = sep->arg[ab_arg];
if (!byname_arg.compare("byname")) {
byname = true;
slot_arg = ab_arg + 2;
}
if (sep->IsNumber(slot_arg)) {
chosen_slot = atoi(sep->arg[slot_arg]);
if (!EQ::ValueWithin(chosen_slot, EQ::invslot::EQUIPMENT_BEGIN, EQ::invslot::EQUIPMENT_END)) {
c->Message(Chat::Yellow, "Please enter a valid inventory slot.");
return;
}
if (!byname) {
++ab_arg;
}
}
std::vector<Bot*> sbl;
if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, sep->arg[ab_arg + 1]) == ActionableBots::ABT_None) {
return;
}
auto my_bot = sbl.front();
if (!my_bot) {
c->Message(Chat::White, "ActionableBots returned 'nullptr'");
c->Message(Chat::Yellow, "ActionableBots returned 'nullptr'");
return;
}
my_bot->FinishTrade(c, Bot::BotTradeClientNoDropNoTrade);
my_bot->FinishTrade(c, Bot::BotTradeClientNoDropNoTrade, chosen_slot);
}
void bot_command_inventory_list(Client* c, const Seperator* sep)
@@ -67,7 +93,7 @@ void bot_command_inventory_list(Client* c, const Seperator* sep)
int ab_mask = (ActionableBots::ABM_Target | ActionableBots::ABM_ByName);
std::list<Bot*> sbl;
std::vector<Bot*> sbl;
if (ActionableBots::PopulateSBL(c, sep->arg[1], sbl, ab_mask, sep->arg[2]) == ActionableBots::ABT_None) {
return;
}
@@ -168,7 +194,7 @@ void bot_command_inventory_remove(Client* c, const Seperator* sep)
return;
}
std::list<Bot*> sbl;
std::vector<Bot*> sbl;
if (ActionableBots::PopulateSBL(c, sep->arg[2], sbl, ab_mask, sep->arg[3]) == ActionableBots::ABT_None) {
return;
}
@@ -217,9 +243,18 @@ void bot_command_inventory_remove(Client* c, const Seperator* sep)
}
const auto* itm = inst->GetItem();
EQ::SayLinkEngine linker;
linker.SetLinkType(EQ::saylink::SayLinkItemInst);
linker.SetItemInst(inst);
if (inst && itm && c->CheckLoreConflict(itm)) {
c->MessageString(Chat::White, PICK_LORE);
c->Message(
Chat::White,
fmt::format(
"You cannot pick up {} because it is a lore item you already possess.",
linker.GenerateLink()
).c_str()
);
return;
}
@@ -233,7 +268,13 @@ void bot_command_inventory_remove(Client* c, const Seperator* sep)
continue;
}
c->MessageString(Chat::White, PICK_LORE);
c->Message(
Chat::White,
fmt::format(
"You cannot pick up {} because it is a lore item you already possess.",
linker.GenerateLink()
).c_str()
);
return;
}
@@ -247,7 +288,7 @@ void bot_command_inventory_remove(Client* c, const Seperator* sep)
slot_id == EQ::invslot::slotRange ||
slot_id == EQ::invslot::slotAmmo
) {
my_bot->SetBotArcherySetting(false, true);
my_bot->SetBotRangedSetting(false);
}
my_bot->RemoveBotItemBySlot(slot_id);
@@ -296,7 +337,7 @@ void bot_command_inventory_window(Client* c, const Seperator* sep)
int ab_mask = ActionableBots::ABM_Target;
std::list<Bot*> sbl;
std::vector<Bot*> sbl;
if (ActionableBots::PopulateSBL(c, sep->arg[1], sbl, ab_mask, sep->arg[2]) == ActionableBots::ABT_None) {
return;
}
-57
View File
@@ -1,57 +0,0 @@
#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);
}
+186 -70
View File
@@ -4,16 +4,17 @@ 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]);
c->Message(Chat::White, "usage: [%s caster] will display only caster bots that can use the item.", sep->arg[0]);
c->Message(Chat::White, "usage: [%s hybrid] will display only hybrid bots that can use the item.", sep->arg[0]);
c->Message(Chat::White, "usage: [%s melee] will display only melee bots that can use the item.", sep->arg[0]);
c->Message(Chat::White, "usage: [%s wiscaster] will display only Wisdom-based Caster bots that can use the item.", sep->arg[0]);
c->Message(Chat::White, "usage: [%s intcaster] will display only Intelligence-based Caster bots that can use the item.", sep->arg[0]);
c->Message(Chat::White, "usage: [%s plate] will display only Plate-wearing bots that can use the item.", sep->arg[0]);
c->Message(Chat::White, "usage: [%s chain] will display only Chain-wearing bots that can use the item.", sep->arg[0]);
c->Message(Chat::White, "usage: [%s leather] will display only Leather-wearing bots that can use the item.", sep->arg[0]);
c->Message(Chat::White, "usage: [%s cloth] will display only Cloth-wearing bots that can use the item.", sep->arg[0]);
c->Message(Chat::White, "usage: [%s haste] will display bots that have no or lesser haste than the item.", sep->arg[0]);
c->Message(Chat::White, "usage: You can also use empty or haste as an argument to narrow down further, for example [%s caster empty], [%s plate haste] or even [%s empty haste]", sep->arg[0], sep->arg[0], sep->arg[0]);
return;
}
@@ -28,136 +29,252 @@ void bot_command_item_use(Client* c, const Seperator* sep)
bool chain_only = false;
bool leather_only = false;
bool cloth_only = false;
bool haste_only = false;
int haste_value = 0;
int ab_arg = 2;
std::string arg1 = sep->arg[1];
std::string arg2 = sep->arg[2];
if (arg1.compare("empty") == 0) {
if (arg1.compare("empty") == 0 || arg2.compare("empty") == 0) {
empty_only = true;
if (arg2.compare("empty") == 0) {
++ab_arg;
}
}
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)");
if (arg1.compare("haste") == 0 || arg2.compare("haste") == 0) {
haste_only = true;
if (arg2.compare("haste") == 0) {
++ab_arg;
}
}
if (arg1.compare("caster") == 0) {
caster_only = true;
}
else if (arg1.compare("hybrid") == 0) {
hybrid_only = true;
}
else if (arg1.compare("melee") == 0) {
melee_only = true;
}
else if (arg1.compare("wiscaster") == 0) {
wis_caster_only = true;
}
else if (arg1.compare("intcaster") == 0) {
int_caster_only = true;
}
else if (arg1.compare("plate") == 0) {
plate_only = true;
}
else if (arg1.compare("chain") == 0) {
chain_only = true;
}
else if (arg1.compare("leather") == 0) {
leather_only = true;
}
else if (arg1.compare("cloth") == 0) {
cloth_only = true;
}
else {
if (arg1.empty()) {
--ab_arg;
}
else {
if (!(arg1.compare("empty") == 0) && !(arg1.compare("haste") == 0)) {
c->Message(Chat::White, "Please choose the correct subtype. For help use %s help.", sep->arg[0]);
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!");
c->Message(Chat::Yellow, "No item found on cursor! For help use %s help.", sep->arg[0]);
return;
}
auto item_data = item_instance->GetItem();
if (!item_data) {
c->Message(Chat::White, "No data found for cursor item!");
c->Message(Chat::Yellow, "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);
c->Message(Chat::Yellow, "'%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);
const int ab_mask = ActionableBots::ABM_Type1;
std::string actionable_arg = sep->arg[ab_arg];
std::list<Bot*> sbl;
MyBots::PopulateSBL_BySpawnedBots(c, sbl);
if (class_mask) {
ActionableBots::Filter_ByClasses(c, sbl, GetPlayerClassBit(class_mask));
if (actionable_arg.empty()) {
actionable_arg = "spawned";
}
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::vector<Bot*> sbl;
if (ActionableBots::PopulateSBL(c, actionable_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.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end());
EQ::SayLinkEngine linker;
linker.SetLinkType(EQ::saylink::SayLinkItemData);
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()))) {
if (
(!RuleB(Bots, AllowBotEquipAnyRaceGear) && ((~item_data->Races) & GetPlayerRaceBit(bot_iter->GetRace()))) ||
(!RuleB(Bots, AllowBotEquipAnyClassGear) && ((~item_data->Classes) & GetPlayerClassBit(bot_iter->GetClass())))
) {
continue;
}
std::list<int16> refined_equipable_slot_list;
bool skip_bot = false;
const EQ::ItemData* equipped_item = nullptr;
const EQ::ItemInstance* equipped_inst = nullptr;
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 (item_data->ReqLevel > bot_iter->GetLevel()) {
continue;
}
if (equipped_item && !empty_only) {
linker.SetItemInst(equipped_item);
haste_value = 0;
equipped_item = nullptr;
equipped_inst = nullptr;
for (int16 equipable_slot = EQ::invslot::EQUIPMENT_BEGIN; equipable_slot <= EQ::invslot::EQUIPMENT_END; ++equipable_slot) {
equipped_inst = bot_iter->GetInv()[equipable_slot];
if (equipped_inst && equipped_inst->GetItem()) {
equipped_item = equipped_inst->GetItem();
if (item_data->CheckLoreConflict(equipped_item)) {
skip_bot = true;
break;
}
if (haste_only) {
if (equipped_item->Haste > haste_value) {
haste_value = equipped_item->Haste;
}
}
}
}
if (skip_bot) {
break;
}
if (haste_only && item_data->Haste < haste_value) {
continue;
}
equipped_inst = bot_iter->GetInv()[slot_iter];
if (equipped_inst && empty_only) {
continue;
}
refined_equipable_slot_list.push_back(slot_iter);
}
if (skip_bot) {
continue;
}
if (refined_equipable_slot_list.empty()) {
continue;
}
for (auto slot_iter : refined_equipable_slot_list) {
equipped_item = nullptr;
equipped_inst = nullptr;
equipped_inst = bot_iter->GetInv()[slot_iter];
if (equipped_inst && equipped_inst->GetItem()) {
equipped_item = equipped_inst->GetItem();
}
if (equipped_item) {
linker.SetItemData(equipped_item);
c->Message(
Chat::Say,
fmt::format(
"{} says, 'I can use that for my {} instead of my {}! Would you like to {} my {}?'",
"{} says, 'I can use that for my {} instead of my {}! Would you like to {}?'",
Saylink::Silent(
fmt::format(
"^inventorygive byname {}",
@@ -173,19 +290,16 @@ void bot_command_item_use(Client* c, const Seperator* sep)
slot_iter,
bot_iter->GetCleanName()
),
"remove"
),
linker.GenerateLink()
"remove my item"
)
).c_str()
);
bot_iter->DoAnim(29);
}
else if (!equipped_item) {
else {
c->Message(
Chat::Say,
fmt::format(
"{} says, 'I can use that for my {}! Would you like to {} it to me?'",
"{} says, 'I can use that for my {}! Would you like to {}?'",
Saylink::Silent(
fmt::format(
"^inventorygive byname {}",
@@ -199,11 +313,13 @@ void bot_command_item_use(Client* c, const Seperator* sep)
"^inventorygive byname {}",
bot_iter->GetCleanName()
),
"give"
"give it to me"
)
).c_str()
);
}
if (RuleB(Bots, DoResponseAnimations)) {
bot_iter->DoAnim(29);
}
}
-38
View File
@@ -1,38 +0,0 @@
#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);
}
-43
View File
@@ -1,43 +0,0 @@
#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);
}
+155
View File
@@ -0,0 +1,155 @@
#include "../bot_command.h"
void bot_command_max_melee_range(Client* c, const Seperator* sep)
{
if (helper_command_alias_fail(c, "bot_command_max_melee_range", sep->arg[0], "maxmeleerange")) {
c->Message(Chat::White, "note: Toggles whether or not bots will stay at max melee range during combat.");
return;
}
if (helper_is_help_or_usage(sep->arg[1])) {
BotCommandHelpParams p;
p.description = { "Toggles whether or not bots will stay at max melee range during combat."};
p.example_format ={ fmt::format("{} [value] [actionable]", sep->arg[0]) };
p.examples_one =
{
"To set BotA to stay at max melee range:",
fmt::format(
"{} 1 byname BotA",
sep->arg[0]
)
};
p.examples_two =
{
"To set all bots to stay at max melee range:",
fmt::format(
"{} 1 spawned",
sep->arg[0]
)
};
p.examples_three =
{
"To check the max melee range status for all bots:",
fmt::format(
"{} current spawned",
sep->arg[0]
)
};
p.actionables = { "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" };
std::string popup_text = c->SendBotCommandHelpWindow(p);
popup_text = DialogueWindow::Table(popup_text);
c->SendPopupToClient(sep->arg[0], popup_text.c_str());
if (RuleB(Bots, SendClassRaceOnHelp)) {
c->Message(
Chat::Yellow,
fmt::format(
"Use {} for information about race/class IDs.",
Saylink::Silent("^classracelist")
).c_str()
);
}
return;
}
std::string arg1 = sep->arg[1];
int ab_arg = 1;
bool current_check = false;
uint32 type_value = 0;
if (sep->IsNumber(1)) {
type_value = atoi(sep->arg[1]);
++ab_arg;
if (type_value < 0 || type_value > 1) {
c->Message(Chat::Yellow, "You must enter either 0 for disabled or 1 for enabled.");
return;
}
}
else if (!arg1.compare("current")) {
++ab_arg;
current_check = true;
}
else {
c->Message(
Chat::Yellow,
fmt::format(
"Incorrect argument, use {} for information regarding this command.",
Saylink::Silent(
fmt::format("{} help", sep->arg[0])
)
).c_str()
);
return;
}
const int ab_mask = ActionableBots::ABM_Type1;
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::vector<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.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end());
Bot* first_found = nullptr;
int success_count = 0;
for (auto my_bot : sbl) {
if (my_bot->BotPassiveCheck()) {
continue;
}
if (!first_found) {
first_found = my_bot;
}
if (current_check) {
c->Message(
Chat::Green,
fmt::format(
"{} says, 'I {} stay at max melee range.'",
my_bot->GetCleanName(),
my_bot->GetMaxMeleeRange() ? "will" : "will not"
).c_str()
);
}
else {
my_bot->SetMaxMeleeRange(type_value);
++success_count;
}
}
if (!current_check) {
if (success_count == 1 && first_found) {
c->Message(
Chat::Green,
fmt::format(
"{} says, 'I {} stay at max melee range.'",
first_found->GetCleanName(),
first_found->GetMaxMeleeRange() ? "will now" : "will no longer"
).c_str()
);
}
else {
c->Message(
Chat::Green,
fmt::format(
"{} of your bots {} stay at max melee range.",
success_count,
type_value ? "will now" : "will no longer"
).c_str()
);
}
}
}
-43
View File
@@ -1,43 +0,0 @@
#include "../bot_command.h"
void bot_command_mesmerize(Client *c, const Seperator *sep)
{
bcst_list* local_list = &bot_command_spells[BCEnum::SpT_Mesmerize];
if (helper_spell_list_fail(c, local_list, BCEnum::SpT_Mesmerize) || helper_command_alias_fail(c, "bot_command_mesmerize", sep->arg[0], "mesmerize"))
return;
if (helper_is_help_or_usage(sep->arg[1])) {
c->Message(Chat::White, "usage: <enemy_target> %s", sep->arg[0]);
helper_send_usage_required_bots(c, BCEnum::SpT_Mesmerize);
return;
}
ActionableTarget::Types actionable_targets;
Bot* my_bot = nullptr;
std::list<Bot*> sbl;
MyBots::PopulateSBL_BySpawnedBots(c, sbl);
for (auto list_iter : *local_list) {
auto local_entry = list_iter;
if (helper_spell_check_fail(local_entry))
continue;
auto target_mob = actionable_targets.Select(c, local_entry->target_type, ENEMY);
if (!target_mob)
continue;
if (spells[local_entry->spell_id].max_value[EFFECTIDTOINDEX(1)] < target_mob->GetLevel())
continue;
my_bot = ActionableBots::Select_ByMinLevelAndClass(c, local_entry->target_type, sbl, local_entry->spell_level, local_entry->caster_class, target_mob);
if (!my_bot)
continue;
uint32 dont_root_before = 0;
if (helper_cast_standard_spell(my_bot, target_mob, local_entry->spell_id, true, &dont_root_before))
target_mob->SetDontRootMeBefore(dont_root_before);
break;
}
helper_no_available_bots(c, my_bot);
}
-50
View File
@@ -1,50 +0,0 @@
#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);
}
+297 -46
View File
@@ -1,12 +1,13 @@
#include "../bot_command.h"
#include "../bot.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");
std::vector<const char*> subcommand_list = {
"petgetlost",
"petremove",
"petsettype"
};
if (helper_command_alias_fail(c, "bot_command_pet", sep->arg[0], "pet"))
return;
@@ -19,12 +20,12 @@ 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]);
c->Message(Chat::White, "usage: %s ([actionable: target | byname | ownergroup | ownerraid | targetgroup | namesgroup | healrotationmembers | healrotationtargets | mmr | byclass | byrace | spawned] ([actionable_name]))", sep->arg[0]);
return;
}
int ab_mask = ActionableBots::ABM_NoFilter;
std::list<Bot*> sbl;
std::vector<Bot*> sbl;
if (ActionableBots::PopulateSBL(c, sep->arg[1], sbl, ab_mask, sep->arg[2]) == ActionableBots::ABT_None)
return;
@@ -55,7 +56,7 @@ void bot_command_pet_remove(Client *c, const Seperator *sep)
}
int ab_mask = (ActionableBots::ABM_Target | ActionableBots::ABM_ByName);
std::list<Bot*> sbl;
std::vector<Bot*> sbl;
if (ActionableBots::PopulateSBL(c, sep->arg[1], sbl, ab_mask, sep->arg[2]) == ActionableBots::ABT_None)
return;
@@ -65,7 +66,7 @@ void bot_command_pet_remove(Client *c, const Seperator *sep)
c->Message(Chat::White, "You have no spawned bots capable of charming");
return;
}
sbl.remove(nullptr);
sbl.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end());
int charmed_pet = 0;
int summoned_pet = 0;
@@ -73,7 +74,7 @@ void bot_command_pet_remove(Client *c, const Seperator *sep)
if (bot_iter->IsBotCharmer()) {
bot_iter->SetBotCharmer(false);
if (sbl.size() == 1)
Bot::BotGroupSay(bot_iter, "Using a summoned pet");
Bot::RaidGroupSay(bot_iter, "Using a summoned pet");
++summoned_pet;
continue;
}
@@ -85,7 +86,7 @@ void bot_command_pet_remove(Client *c, const Seperator *sep)
}
bot_iter->SetBotCharmer(true);
if (sbl.size() == 1)
Bot::BotGroupSay(bot_iter, "Available for Charming");
Bot::RaidGroupSay(bot_iter, "Available for Charming");
++charmed_pet;
}
@@ -95,74 +96,324 @@ void bot_command_pet_remove(Client *c, const Seperator *sep)
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)");
if (helper_command_alias_fail(c, "bot_command_pet_set_type", sep->arg[0], "petsettype")) {
c->Message(Chat::White, "note: Allows you to change the type of pet Magician bots will cast.");
return;
}
int ab_mask = (ActionableBots::ABM_Target | ActionableBots::ABM_ByName); // this can be expanded without code modification
std::string pet_arg = sep->arg[1];
if (helper_is_help_or_usage(sep->arg[1])) {
BotCommandHelpParams p;
p.description = { "Allows you to change the type of pet Magician bots will cast." };
p.example_format = { fmt::format("{} [current | water | fire | air | earth | monster | epic] [actionable, default: target]", sep->arg[0]) };
p.examples_one =
{
"To set all spawned bots to use Water pets:",
fmt::format(
"{} fire spawned",
sep->arg[0]
)
};
p.examples_two =
{
"To set Magelulz to use Fire pets:",
fmt::format(
"{} fire byname Magelulz",
sep->arg[0]
)
};
p.examples_three =
{
"To check the current pet type for all bots:",
fmt::format(
"{} current spawned",
sep->arg[0]
)
};
p.actionables = { "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" };
std::string popup_text = c->SendBotCommandHelpWindow(p);
popup_text = DialogueWindow::Table(popup_text);
c->SendPopupToClient(sep->arg[0], popup_text.c_str());
if (RuleB(Bots, SendClassRaceOnHelp)) {
c->Message(
Chat::Yellow,
fmt::format(
"Use {} for information about race/class IDs.",
Saylink::Silent("^classracelist")
).c_str()
);
}
return;
}
std::string arg1 = sep->arg[1];
int ab_arg = 2;
bool current_check = false;
uint8 pet_type = 255;
uint8 level_req = 255;
if (!pet_arg.compare("water")) {
if (!arg1.compare("auto")) {
pet_type = 0;
level_req = 1;
}
else if (!pet_arg.compare("fire")) {
else if (!arg1.compare("water")) {
pet_type = 1;
level_req = 3;
}
else if (!pet_arg.compare("air")) {
else if (!arg1.compare("fire")) {
pet_type = 2;
level_req = 4;
}
else if (!pet_arg.compare("earth")) {
else if (!arg1.compare("air")) {
pet_type = 3;
level_req = 5;
}
else if (!pet_arg.compare("monster")) {
else if (!arg1.compare("earth")) {
pet_type = 4;
level_req = 30;
}
else if (!arg1.compare("monster")) {
pet_type = 5;
}
else if (!arg1.compare("epic")) {
if (!RuleB(Bots, AllowMagicianEpicPet)) {
c->Message(Chat::Yellow, "Epic pets are currently disabled for bots.");
return;
}
pet_type = 6;
}
else if (!arg1.compare("current")) {
current_check = true;
}
if (pet_type == 255) {
c->Message(Chat::White, "You must specify a pet [type: water | fire | air | earth | monster]");
if (!current_check && pet_type == 255) {
c->Message(
Chat::Yellow,
fmt::format(
"You must specify a pet [type: auto | water | fire | air | earth | monster | epic]. Use {} for information regarding this command.",
Saylink::Silent(
fmt::format("{} help", sep->arg[0])
)
).c_str()
);
return;
}
std::list<Bot*> sbl;
if (ActionableBots::PopulateSBL(c, sep->arg[2], sbl, ab_mask, sep->arg[3]) == ActionableBots::ABT_None)
return;
const int ab_mask = ActionableBots::ABM_Type1;
std::string actionable_arg = sep->arg[ab_arg];
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");
if (actionable_arg.empty()) {
actionable_arg = "target";
}
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::vector<Bot*> sbl;
if (ActionableBots::PopulateSBL(c, actionable_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;
}
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;
}
sbl.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end());
std::string current_type;
uint16 reclaim_energy_id = RuleI(Bots, ReclaimEnergySpellID);
bool is_success = false;
uint16 success_count = 0;
Bot* first_found = nullptr;
uint16 reclaim_energy_id = 331;
for (auto bot_iter : sbl) {
if (!bot_iter)
if (bot_iter->GetClass() != Class::Magician) {
continue;
}
if (!bot_iter->IsInGroupOrRaid(c)) {
continue;
}
if (pet_type == 6 && RuleI(Bots, RequiredMagicianEpicPetItemID) > 0) {
bool has_item = bot_iter->HasBotItem(RuleI(Bots, RequiredMagicianEpicPetItemID)) != INVALID_INDEX;
if (!has_item) {
c->Message(
Chat::Say,
fmt::format(
"{} says, 'I require {} to cast an epic pet which I do not currently possess.'",
bot_iter->GetCleanName(),
(database.GetItem(RuleI(Bots, RequiredMagicianEpicPetItemID)) ? database.CreateItemLink(RuleI(Bots, RequiredMagicianEpicPetItemID)) : "an item")
).c_str()
);
continue;
}
}
if (current_check) {
switch (bot_iter->GetPetChooserID()) {
case 0:
current_type = "auto";
break;
case SumWater:
current_type = "water";
break;
case SumFire:
current_type = "fire";
break;
case SumAir:
current_type = "air";
break;
case SumEarth:
current_type = "earth";
break;
case MonsterSum:
current_type = "monster";
break;
case SumMageMultiElement:
current_type = "epic";
break;
default:
current_type = "null";
break;
}
c->Message(
Chat::Green,
fmt::format(
"{} says, 'I'm currently summoning {} pets.'",
bot_iter->GetCleanName(),
current_type
).c_str()
);
continue;
}
uint8 air_min_level = 255;
uint8 fire_min_level = 255;
uint8 water_min_level = 255;
uint8 earth_min_level = 255;
uint8 monster_min_level = 255;
uint8 epic_min_level = 255;
std::list<BotSpell> bot_spell_list = bot_iter->GetBotSpellsBySpellType(bot_iter, BotSpellTypes::Pet);
for (const auto& s : bot_spell_list) {
if (!IsValidSpell(s.SpellId)) {
continue;
}
if (!IsEffectInSpell(s.SpellId, SE_SummonPet)) {
continue;
}
auto spell = spells[s.SpellId];
if (!strncmp(spell.teleport_zone, "SumWater", 8) && spell.classes[Class::Magician - 1] < water_min_level) {
water_min_level = spell.classes[Class::Magician - 1];
}
else if (!strncmp(spell.teleport_zone, "SumFire", 7) && spell.classes[Class::Magician - 1] < fire_min_level) {
fire_min_level = spell.classes[Class::Magician - 1];
}
else if (!strncmp(spell.teleport_zone, "SumAir", 6) && spell.classes[Class::Magician - 1] < air_min_level) {
air_min_level = spell.classes[Class::Magician - 1];
}
else if (!strncmp(spell.teleport_zone, "SumEarth", 8) && spell.classes[Class::Magician - 1] < earth_min_level) {
earth_min_level = spell.classes[Class::Magician - 1];
}
else if (!strncmp(spell.teleport_zone, "MonsterSum", 10) && spell.classes[Class::Magician - 1] < monster_min_level) {
monster_min_level = spell.classes[Class::Magician - 1];
}
}
uint8 min_level = std::min({
water_min_level,
fire_min_level,
air_min_level,
earth_min_level,
monster_min_level
});
epic_min_level = RuleI(Bots, AllowMagicianEpicPetLevel);
switch (pet_type) {
case 0:
level_req = min_level;
break;
case SumWater:
level_req = water_min_level;
break;
case SumFire:
level_req = fire_min_level;
break;
case SumAir:
level_req = air_min_level;
break;
case SumEarth:
level_req = earth_min_level;
break;
case MonsterSum:
level_req = monster_min_level;
break;
case SumMageMultiElement:
level_req = epic_min_level;
break;
default:
break;
}
if (bot_iter->GetLevel() < level_req) {
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);
}
if (!first_found) {
first_found = bot_iter;
}
is_success = true;
++success_count;
}
if (current_check) {
return;
}
if (!is_success) {
c->Message(Chat::Yellow, "No bots were selected.");
return;
}
if (success_count == 1 && first_found) {
c->Message(
Chat::Green,
fmt::format(
"{} says, 'I will now summon {} pets.'",
first_found->GetCleanName(),
current_type
).c_str()
);
}
else {
c->Message(
Chat::Green,
fmt::format(
"{} of your bots will now summon {} pets.",
success_count,
arg1
).c_str()
);
}
}
+3 -3
View File
@@ -11,7 +11,7 @@ void bot_command_pick_lock(Client *c, const Seperator *sep)
return;
}
std::list<Bot*> sbl;
std::vector<Bot*> sbl;
MyBots::PopulateSBL_BySpawnedBots(c, sbl);
float pick_lock_value = 0.0f;
@@ -24,7 +24,7 @@ void bot_command_pick_lock(Client *c, const Seperator *sep)
Bot* my_bot = sbl.front();
my_bot->InterruptSpell();
Bot::BotGroupSay(my_bot, "Attempting to pick the lock.");
Bot::RaidGroupSay(my_bot, "Attempting to pick the lock.");
std::list<Doors*> door_list;
entity_list.GetDoorsList(door_list);
@@ -51,7 +51,7 @@ void bot_command_pick_lock(Client *c, const Seperator *sep)
++open_count;
}
else {
Bot::BotGroupSay(my_bot, "I am not skilled enough for this lock.");
Bot::RaidGroupSay(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") : ("")));
+1 -1
View File
@@ -15,7 +15,7 @@ void bot_command_pickpocket(Client *c, const Seperator *sep)
return;
}
std::list<Bot *> sbl;
std::vector<Bot*> sbl;
MyBots::PopulateSBL_BySpawnedBots(c, sbl);
// Check for capable rogue
+9 -2
View File
@@ -5,15 +5,22 @@ 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])) {
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;
}
if (!c->DoLosChecks(c->GetTarget())) {
c->Message(Chat::Red, "You must have Line of Sight to use this command.");
return;
}
+37 -8
View File
@@ -7,21 +7,50 @@ void bot_command_pull(Client *c, const Seperator *sep)
}
if (helper_is_help_or_usage(sep->arg[1])) {
c->Message(Chat::White, "usage: <enemy_target> %s", sep->arg[0]);
c->Message(Chat::White, "usage: <enemy_target> %s ([actionable: target | byname | ownergroup | ownerraid | targetgroup | namesgroup | healrotationtargets | mmr | byclass | byrace | spawned] ([actionable_name]))", 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) {
const int ab_mask = ActionableBots::ABM_Type1;
std::string arg1 = sep->arg[1];
int ab_arg = 1;
std::string actionable_arg = sep->arg[ab_arg];
if (actionable_arg.empty()) {
actionable_arg = "spawned";
}
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::vector<Bot*> sbl;
if (ActionableBots::PopulateSBL(c, actionable_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);
auto target_mob = ActionableTarget::VerifyEnemy(c, BCEnum::TT_Single);
if (!target_mob) {
sbl.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end());
auto target_mob = c->GetTarget();
if (
!target_mob ||
target_mob == c ||
!c->IsAttackAllowed(target_mob)
) {
c->Message(Chat::White, "Your current target is not attackable!");
return;
}
if (!c->DoLosChecks(target_mob)) {
c->Message(Chat::Red, "You must have Line of Sight to use this command.");
return;
}
@@ -32,8 +61,8 @@ void bot_command_pull(Client *c, const Seperator *sep)
}
Bot* bot_puller = nullptr;
for (auto bot_iter : sbl) {
for (auto bot_iter : sbl) {
if (bot_iter->GetAppearance() == eaDead || bot_iter->GetBotStance() == Stance::Passive) {
continue;
}
+3 -3
View File
@@ -5,16 +5,16 @@ 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]);
c->Message(Chat::White, "usage: %s ([actionable: target | byname | ownergroup | ownerraid | targetgroup | namesgroup | healrotationmembers | healrotationtargets | mmr | byclass | byrace | spawned] ([actionable_name]))", sep->arg[0]);
return;
}
const int ab_mask = ActionableBots::ABM_NoFilter;
std::list<Bot*> sbl;
std::vector<Bot*> sbl;
if (ActionableBots::PopulateSBL(c, sep->arg[1], sbl, ab_mask, sep->arg[2]) == ActionableBots::ABT_None)
return;
sbl.remove(nullptr);
sbl.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end());
for (auto bot_iter : sbl) {
bot_iter->WipeHateList();
bot_iter->SetPauseAI(false);
-73
View File
@@ -1,73 +0,0 @@
#include "../bot_command.h"
void bot_command_resistance(Client *c, const Seperator *sep)
{
bcst_list* local_list = &bot_command_spells[BCEnum::SpT_Resistance];
if (helper_spell_list_fail(c, local_list, BCEnum::SpT_Resistance) || helper_command_alias_fail(c, "bot_command_resistance", sep->arg[0], "resistance"))
return;
if (helper_is_help_or_usage(sep->arg[1])) {
c->Message(Chat::White, "usage: (<friendly_target>) %s [resistance: fire | cold | poison | disease | magic | corruption]", sep->arg[0]);
helper_send_usage_required_bots(c, BCEnum::SpT_Resistance);
return;
}
std::string resistance_arg = sep->arg[1];
auto resistance_type = BCEnum::RT_None;
if (!resistance_arg.compare("fire"))
resistance_type = BCEnum::RT_Fire;
else if (!resistance_arg.compare("cold"))
resistance_type = BCEnum::RT_Cold;
else if (!resistance_arg.compare("poison"))
resistance_type = BCEnum::RT_Poison;
else if (!resistance_arg.compare("disease"))
resistance_type = BCEnum::RT_Disease;
else if (!resistance_arg.compare("magic"))
resistance_type = BCEnum::RT_Magic;
else if (!resistance_arg.compare("corruption"))
resistance_type = BCEnum::RT_Corruption;
if (resistance_type == BCEnum::RT_None) {
c->Message(Chat::White, "You must specify a [resistance]");
return;
}
local_list->sort([resistance_type](STBaseEntry* l, STBaseEntry* r) {
auto _l = l->SafeCastToResistance(), _r = r->SafeCastToResistance();
if (_l->resist_value[RESISTANCEIDTOINDEX(resistance_type)] > _r->resist_value[RESISTANCEIDTOINDEX(resistance_type)])
return true;
if (_l->resist_value[RESISTANCEIDTOINDEX(resistance_type)] == _r->resist_value[RESISTANCEIDTOINDEX(resistance_type)] && spells[_l->spell_id].mana < spells[_r->spell_id].mana)
return true;
if (_l->resist_value[RESISTANCEIDTOINDEX(resistance_type)] == _r->resist_value[RESISTANCEIDTOINDEX(resistance_type)] && spells[_l->spell_id].mana == spells[_r->spell_id].mana && _l->resist_total > _r->resist_total)
return true;
return false;
});
ActionableTarget::Types actionable_targets;
Bot* my_bot = nullptr;
std::list<Bot*> sbl;
MyBots::PopulateSBL_BySpawnedBots(c, sbl);
bool cast_success = false;
for (auto list_iter : *local_list) {
auto local_entry = list_iter->SafeCastToResistance();
if (helper_spell_check_fail(local_entry))
continue;
if (!local_entry->resist_value[RESISTANCEIDTOINDEX(resistance_type)])
continue;
auto target_mob = actionable_targets.Select(c, local_entry->target_type, FRIENDLY);
if (!target_mob)
continue;
my_bot = ActionableBots::Select_ByMinLevelAndClass(c, local_entry->target_type, sbl, local_entry->spell_level, local_entry->caster_class, target_mob);
if (!my_bot)
continue;
cast_success = helper_cast_standard_spell(my_bot, target_mob, local_entry->spell_id);
break;
}
helper_no_available_bots(c, my_bot);
}
-57
View File
@@ -1,57 +0,0 @@
#include "../bot_command.h"
void bot_command_resurrect(Client *c, const Seperator *sep)
{
// Obscure bot spell code prohibits the aoe portion from working correctly...
bcst_list* local_list = &bot_command_spells[BCEnum::SpT_Resurrect];
if (helper_spell_list_fail(c, local_list, BCEnum::SpT_Resurrect) || helper_command_alias_fail(c, "bot_command_resurrect", sep->arg[0], "resurrect"))
return;
if (helper_is_help_or_usage(sep->arg[1])) {
//c->Message(Chat::White, "usage: <corpse_target> %s ([option: aoe])", sep->arg[0]);
c->Message(Chat::White, "usage: <corpse_target> %s", sep->arg[0]);
helper_send_usage_required_bots(c, BCEnum::SpT_Resurrect);
return;
}
bool aoe = false;
//std::string aoe_arg = sep->arg[1];
//if (!aoe_arg.compare("aoe"))
// aoe = true;
ActionableTarget::Types actionable_targets;
Bot* my_bot = nullptr;
std::list<Bot*> sbl;
MyBots::PopulateSBL_BySpawnedBots(c, sbl);
for (auto list_iter : *local_list) {
auto local_entry = list_iter->SafeCastToResurrect();
if (helper_spell_check_fail(local_entry))
continue;
//if (local_entry->aoe != aoe)
// continue;
if (local_entry->aoe)
continue;
auto target_mob = actionable_targets.Select(c, local_entry->target_type, FRIENDLY);
//if (!target_mob && !local_entry->aoe)
// continue;
if (!target_mob)
continue;
my_bot = ActionableBots::Select_ByMinLevelAndClass(c, local_entry->target_type, sbl, local_entry->spell_level, local_entry->caster_class, target_mob);
if (!my_bot)
continue;
//if (local_entry->aoe)
// target_mob = my_bot;
uint32 dont_root_before = 0;
if (helper_cast_standard_spell(my_bot, target_mob, local_entry->spell_id, true, &dont_root_before))
target_mob->SetDontRootMeBefore(dont_root_before);
break;
}
helper_no_available_bots(c, my_bot);
}
-38
View File
@@ -1,38 +0,0 @@
#include "../bot_command.h"
void bot_command_rune(Client *c, const Seperator *sep)
{
bcst_list* local_list = &bot_command_spells[BCEnum::SpT_Rune];
if (helper_spell_list_fail(c, local_list, BCEnum::SpT_Rune) || helper_command_alias_fail(c, "bot_command_rune", sep->arg[0], "rune"))
return;
if (helper_is_help_or_usage(sep->arg[1])) {
c->Message(Chat::White, "usage: (<friendly_target>) %s", sep->arg[0]);
helper_send_usage_required_bots(c, BCEnum::SpT_Rune);
return;
}
ActionableTarget::Types actionable_targets;
Bot* my_bot = nullptr;
std::list<Bot*> sbl;
MyBots::PopulateSBL_BySpawnedBots(c, sbl);
bool cast_success = false;
for (auto list_iter : *local_list) {
auto local_entry = list_iter;
if (helper_spell_check_fail(local_entry))
continue;
auto target_mob = actionable_targets.Select(c, local_entry->target_type, FRIENDLY);
if (!target_mob)
continue;
my_bot = ActionableBots::Select_ByMinLevelAndClass(c, local_entry->target_type, sbl, local_entry->spell_level, local_entry->caster_class, target_mob);
if (!my_bot)
continue;
cast_success = helper_cast_standard_spell(my_bot, target_mob, local_entry->spell_id);
break;
}
helper_no_available_bots(c, my_bot);
}
-47
View File
@@ -1,47 +0,0 @@
#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);
}
+42
View File
@@ -0,0 +1,42 @@
#include "../bot_command.h"
void bot_command_set_assistee(Client* c, const Seperator* sep)
{
if (helper_command_alias_fail(c, "bot_command_set_assistee", sep->arg[0], "setassistee")) {
c->Message(Chat::White, "note: Sets your bots to assist your target in addition to yourself.");
return;
}
if (helper_is_help_or_usage(sep->arg[1])) {
BotCommandHelpParams p;
p.description = { "Sets your bots to assist your target in addition to yourself." };
p.notes =
{
"- Your target must be another player in your group or raid.",
"- This needs to be set on every zone/camp you do.",
"- If a Raid or Group assist is set and you do not want your bots to auto assist that person, set yourself as the assistee."
};
std::string popup_text = c->SendBotCommandHelpWindow(p);
popup_text = DialogueWindow::Table(popup_text);
c->SendPopupToClient(sep->arg[0], popup_text.c_str());
return;
}
Mob* assistee = c->GetTarget();
if (assistee && assistee->IsClient() && c->IsInGroupOrRaid(assistee)) {
c->SetAssistee(assistee->CastToClient()->CharacterID());
c->Message(Chat::Green, "Your bots will now assist %s.", assistee->GetCleanName());
return;
}
c->Message(Chat::Yellow, "You can only set your bots to assist clients that are in your group or raid.");
return;
}
+156
View File
@@ -0,0 +1,156 @@
#include "../bot_command.h"
void bot_command_sit_hp_percent(Client* c, const Seperator* sep)
{
if (helper_command_alias_fail(c, "bot_command_sit_hp_percent", sep->arg[0], "sithppercent")) {
c->Message(Chat::White, "note: HP % threshold when bots will sit in combat if allowed.");
return;
}
if (helper_is_help_or_usage(sep->arg[1])) {
BotCommandHelpParams p;
p.description = { "HP % threshold when bots will sit in combat if allowed." };
p.example_format = { fmt::format("{} [value] [actionable]", sep->arg[0]) };
p.examples_one =
{
"To set Clerics to sit at 45% HP:",
fmt::format(
"{} 45 byclass {}",
sep->arg[0],
Class::Cleric
)
};
p.examples_two =
{
"To set all bots to sit at 50% HP:",
fmt::format(
"{} 50 spawned",
sep->arg[0]
)
};
p.examples_three =
{
"To check the HP threshold for all bots:",
fmt::format(
"{} current spawned",
sep->arg[0]
)
};
p.actionables = { "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" };
std::string popup_text = c->SendBotCommandHelpWindow(p);
popup_text = DialogueWindow::Table(popup_text);
c->SendPopupToClient(sep->arg[0], popup_text.c_str());
if (RuleB(Bots, SendClassRaceOnHelp)) {
c->Message(
Chat::Yellow,
fmt::format(
"Use {} for information about race/class IDs.",
Saylink::Silent("^classracelist")
).c_str()
);
}
return;
}
std::string arg1 = sep->arg[1];
int ab_arg = 1;
bool current_check = false;
uint32 type_value = 0;
if (sep->IsNumber(1)) {
type_value = atoi(sep->arg[1]);
++ab_arg;
if (type_value < 0 || type_value > 100) {
c->Message(Chat::Yellow, "You must enter a value between 0-100 (0%% to 100%% of health).");
return;
}
}
else if (!arg1.compare("current")) {
++ab_arg;
current_check = true;
}
else {
c->Message(
Chat::Yellow,
fmt::format(
"Incorrect argument, use {} for information regarding this command.",
Saylink::Silent(
fmt::format("{} help", sep->arg[0])
)
).c_str()
);
return;
}
const int ab_mask = ActionableBots::ABM_Type1;
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::vector<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.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end());
Bot* first_found = nullptr;
int success_count = 0;
for (auto my_bot : sbl) {
if (my_bot->BotPassiveCheck()) {
continue;
}
if (!first_found) {
first_found = my_bot;
}
if (current_check) {
c->Message(
Chat::Green,
fmt::format(
"{} says, 'I sit in combat whem at or below [{}%%] HP.'",
my_bot->GetCleanName(),
my_bot->GetSitHPPct()
).c_str()
);
}
else {
my_bot->SetSitHPPct(type_value);
++success_count;
}
}
if (!current_check) {
if (success_count == 1 && first_found) {
c->Message(
Chat::Green,
fmt::format(
"{} says, 'I will now sit in combat whem at or below [{}%%] HP.'",
first_found->GetCleanName(),
first_found->GetSitHPPct()
).c_str()
);
}
else {
c->Message(
Chat::Green,
fmt::format(
"{} of your bots will now sit in combat whem at or below [{}%%] HP.'",
success_count,
type_value
).c_str()
);
}
}
}
+156
View File
@@ -0,0 +1,156 @@
#include "../bot_command.h"
void bot_command_sit_in_combat(Client* c, const Seperator* sep)
{
if (helper_command_alias_fail(c, "bot_command_sit_in_combat", sep->arg[0], "sitincombat")) {
c->Message(Chat::White, "note: Toggles whether or not bots will sit in combat to heal or med.");
return;
}
if (helper_is_help_or_usage(sep->arg[1])) {
BotCommandHelpParams p;
p.description = { "Toggles whether or not bots will sit in combat to heal or med." };
p.example_format = { fmt::format("{} [value] [actionable]", sep->arg[0]) };
p.examples_one =
{
"To set Clerics to sit in combat:",
fmt::format(
"{} 1 byclass {}",
sep->arg[0],
Class::Cleric
)
};
p.examples_two =
{
"To set all bots to sit/med in combat:",
fmt::format(
"{} 1 spawned",
sep->arg[0]
)
};
p.examples_three =
{
"To check the sit in combat state for all bots:",
fmt::format(
"{} current spawned",
sep->arg[0]
)
};
p.actionables = { "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" };
std::string popup_text = c->SendBotCommandHelpWindow(p);
popup_text = DialogueWindow::Table(popup_text);
c->SendPopupToClient(sep->arg[0], popup_text.c_str());
if (RuleB(Bots, SendClassRaceOnHelp)) {
c->Message(
Chat::Yellow,
fmt::format(
"Use {} for information about race/class IDs.",
Saylink::Silent("^classracelist")
).c_str()
);
}
return;
}
std::string arg1 = sep->arg[1];
int ab_arg = 1;
bool current_check = false;
uint32 type_value = 0;
if (sep->IsNumber(1)) {
type_value = atoi(sep->arg[1]);
++ab_arg;
if (type_value < 0 || type_value > 1) {
c->Message(Chat::Yellow, "You must enter either 0 for disabled or 1 for enabled.");
return;
}
}
else if (!arg1.compare("current")) {
++ab_arg;
current_check = true;
}
else {
c->Message(
Chat::Yellow,
fmt::format(
"Incorrect argument, use {} for information regarding this command.",
Saylink::Silent(
fmt::format("{} help", sep->arg[0])
)
).c_str()
);
return;
}
const int ab_mask = ActionableBots::ABM_Type1;
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::vector<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.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end());
Bot* first_found = nullptr;
int success_count = 0;
for (auto my_bot : sbl) {
if (my_bot->BotPassiveCheck()) {
continue;
}
if (!first_found) {
first_found = my_bot;
}
if (current_check) {
c->Message(
Chat::Green,
fmt::format(
"{} says, 'I {} sit in combat.'",
my_bot->GetCleanName(),
my_bot->GetMedInCombat() ? "will" : "will not"
).c_str()
);
}
else {
my_bot->SetMedInCombat(type_value);
++success_count;
}
}
if (!current_check) {
if (success_count == 1 && first_found) {
c->Message(
Chat::Green,
fmt::format(
"{} says, 'I {} sit in combat.'",
first_found->GetCleanName(),
first_found->GetMedInCombat() ? "will now" : "will no longer"
).c_str()
);
}
else {
c->Message(
Chat::Green,
fmt::format(
"{} of your bots {} sit in combat.",
success_count,
type_value ? "will now" : "will no longer"
).c_str()
);
}
}
}
+156
View File
@@ -0,0 +1,156 @@
#include "../bot_command.h"
void bot_command_sit_mana_percent(Client* c, const Seperator* sep)
{
if (helper_command_alias_fail(c, "bot_command_sit_mana_percent", sep->arg[0], "sitmanapercent")) {
c->Message(Chat::White, "note: Mana % threshold when bots will sit in combat if allowed.");
return;
}
if (helper_is_help_or_usage(sep->arg[1])) {
BotCommandHelpParams p;
p.description = { "Mana % threshold when bots will sit in combat if allowed." };
p.example_format = { fmt::format("{} [value] [actionable]", sep->arg[0]) };
p.examples_one =
{
"To set Clerics to sit at 45% Mana:",
fmt::format(
"{} 45 byclass {}",
sep->arg[0],
Class::Cleric
)
};
p.examples_two =
{
"To set all bots to sit at 50% Mana:",
fmt::format(
"{} 50 spawned",
sep->arg[0]
)
};
p.examples_three =
{
"To check the Mana threshold for all bots:",
fmt::format(
"{} current spawned",
sep->arg[0]
)
};
p.actionables = { "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" };
std::string popup_text = c->SendBotCommandHelpWindow(p);
popup_text = DialogueWindow::Table(popup_text);
c->SendPopupToClient(sep->arg[0], popup_text.c_str());
if (RuleB(Bots, SendClassRaceOnHelp)) {
c->Message(
Chat::Yellow,
fmt::format(
"Use {} for information about race/class IDs.",
Saylink::Silent("^classracelist")
).c_str()
);
}
return;
}
std::string arg1 = sep->arg[1];
int ab_arg = 1;
bool current_check = false;
uint32 type_value = 0;
if (sep->IsNumber(1)) {
type_value = atoi(sep->arg[1]);
++ab_arg;
if (type_value < 0 || type_value > 100) {
c->Message(Chat::Yellow, "You must enter a value between 0-100 (0%% to 100%% of health).");
return;
}
}
else if (!arg1.compare("current")) {
++ab_arg;
current_check = true;
}
else {
c->Message(
Chat::Yellow,
fmt::format(
"Incorrect argument, use {} for information regarding this command.",
Saylink::Silent(
fmt::format("{} help", sep->arg[0])
)
).c_str()
);
return;
}
const int ab_mask = ActionableBots::ABM_Type1;
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::vector<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.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end());
Bot* first_found = nullptr;
int success_count = 0;
for (auto my_bot : sbl) {
if (my_bot->BotPassiveCheck()) {
continue;
}
if (!first_found) {
first_found = my_bot;
}
if (current_check) {
c->Message(
Chat::Green,
fmt::format(
"{} says, 'I sit in combat whem at or below [{}%%] mana.'",
my_bot->GetCleanName(),
my_bot->GetSitManaPct()
).c_str()
);
}
else {
my_bot->SetSitManaPct(type_value);
++success_count;
}
}
if (!current_check) {
if (success_count == 1 && first_found) {
c->Message(
Chat::Green,
fmt::format(
"{} says, 'I will now sit in combat whem at or below [{}%%] mana.'",
first_found->GetCleanName(),
first_found->GetSitManaPct()
).c_str()
);
}
else {
c->Message(
Chat::Green,
fmt::format(
"{} of your bots will now sit in combat whem at or below [{}%%] mana.'",
success_count,
type_value
).c_str()
);
}
}
}
-50
View File
@@ -1,50 +0,0 @@
#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);
}
+2 -2
View File
@@ -503,7 +503,7 @@ void bot_spell_info_dialogue_window(Client* c, const Seperator *sep)
auto results = database.QueryDatabase(
fmt::format(
"SELECT value FROM db_str WHERE id = {} and type = 6 LIMIT 1",
spells[spell_id].effect_description_id
spells[spell_id].description_id
)
);
@@ -557,7 +557,7 @@ void bot_command_enforce_spell_list(Client* c, const Seperator *sep)
}
bool enforce_state = (sep->argnum > 0) ? Strings::ToBool(sep->arg[1]) : !my_bot->GetBotEnforceSpellSetting();
my_bot->SetBotEnforceSpellSetting(enforce_state, true);
my_bot->SetBotEnforceSpellSetting(enforce_state);
c->Message(
Chat::White,
+222
View File
@@ -0,0 +1,222 @@
#include "../bot_command.h"
void bot_command_spell_aggro_checks(Client* c, const Seperator* sep)
{
if (helper_command_alias_fail(c, "bot_command_spell_aggro_checks", sep->arg[0], "spellaggrochecks")) {
c->Message(Chat::White, "note: Toggles whether or not bots will cast a spell type if they think it will get them aggro.");
return;
}
if (helper_is_help_or_usage(sep->arg[1])) {
BotCommandHelpParams p;
p.description = { "Toggles whether or not bots will cast a spell type if they think it will get them aggro." };
p.example_format =
{
fmt::format("{} [Type Shortname] [value] [actionable]", sep->arg[0]),
fmt::format("{} [Type ID] [value] [actionable]", sep->arg[0])
};
p.examples_one =
{
"To set all bots to check aggro on nukes:",
fmt::format(
"{} {} 1 spawned",
sep->arg[0],
Bot::GetSpellTypeShortNameByID(BotSpellTypes::Nuke)
),
fmt::format(
"{} {} 1 spawned",
sep->arg[0],
BotSpellTypes::Nuke
)
};
p.examples_two =
{
"To set Shadowknights to ignore aggro checks on snares:",
fmt::format(
"{} {} 0 byclass {}",
sep->arg[0],
Bot::GetSpellTypeShortNameByID(BotSpellTypes::Snare),
Class::ShadowKnight
),
fmt::format(
"{} {} 0 byclass {}",
sep->arg[0],
BotSpellTypes::Snare,
Class::ShadowKnight
)
};
p.examples_three =
{
"To check the current DoT aggro check on all bots:",
fmt::format(
"{} {} current spawned",
sep->arg[0],
Bot::GetSpellTypeShortNameByID(BotSpellTypes::DOT)
),
fmt::format(
"{} {} current spawned",
sep->arg[0],
BotSpellTypes::DOT
)
};
p.actionables = { "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" };
std::string popup_text = c->SendBotCommandHelpWindow(p);
popup_text = DialogueWindow::Table(popup_text);
c->SendPopupToClient(sep->arg[0], popup_text.c_str());
c->SendSpellTypePrompts();
if (RuleB(Bots, SendClassRaceOnHelp)) {
c->Message(
Chat::Yellow,
fmt::format(
"Use {} for information about race/class IDs.",
Saylink::Silent("^classracelist")
).c_str()
);
}
return;
}
std::string arg1 = sep->arg[1];
std::string arg2 = sep->arg[2];
int ab_arg = 2;
bool current_check = false;
uint16 spell_type = 0;
uint32 type_value = 0;
// String/Int type checks
if (sep->IsNumber(1)) {
spell_type = atoi(sep->arg[1]);
if (!EQ::ValueWithin(spell_type, BotSpellTypes::START, BotSpellTypes::END)) {
c->Message(
Chat::Yellow,
fmt::format(
"You must choose a valid spell type. Use {} for information regarding this command.",
Saylink::Silent(
fmt::format("{} help", sep->arg[0])
)
).c_str()
);
return;
}
}
else {
if (Bot::GetSpellTypeIDByShortName(arg1) != UINT16_MAX) {
spell_type = Bot::GetSpellTypeIDByShortName(arg1);
}
else {
c->Message(
Chat::Yellow,
fmt::format(
"Incorrect argument, use {} for information regarding this command.",
Saylink::Silent(
fmt::format("{} help", sep->arg[0])
)
).c_str()
);
return;
}
}
if (sep->IsNumber(2)) {
type_value = atoi(sep->arg[2]);
++ab_arg;
if (type_value < 0 || type_value > 1) {
c->Message(Chat::Yellow, "You must enter either 0 for disabled or 1 for enabled.");
return;
}
}
else if (!arg2.compare("current")) {
++ab_arg;
current_check = true;
}
else {
c->Message(
Chat::Yellow,
fmt::format(
"Incorrect argument, use {} for information regarding this command.",
Saylink::Silent(
fmt::format("{} help", sep->arg[0])
)
).c_str()
);
return;
}
const int ab_mask = ActionableBots::ABM_Type1;
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::vector<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.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end());
Bot* first_found = nullptr;
int success_count = 0;
for (auto my_bot : sbl) {
if (my_bot->BotPassiveCheck()) {
continue;
}
if (!first_found) {
first_found = my_bot;
}
if (current_check) {
c->Message(
Chat::Green,
fmt::format(
"{} says, 'My [{}] aggro check is currently [{}].'",
my_bot->GetCleanName(),
Bot::GetSpellTypeNameByID(spell_type),
my_bot->GetSpellTypeAggroCheck(spell_type) ? "enabled" : "disabled"
).c_str()
);
}
else {
my_bot->SetSpellTypeAggroCheck(spell_type, type_value);
++success_count;
}
}
if (!current_check) {
if (success_count == 1 && first_found) {
c->Message(
Chat::Green,
fmt::format(
"{} says, 'My [{}] aggro check was [{}].'",
first_found->GetCleanName(),
Bot::GetSpellTypeNameByID(spell_type),
first_found->GetSpellTypeAggroCheck(spell_type) ? "enabled" : "disabled"
).c_str()
);
}
else {
c->Message(
Chat::Green,
fmt::format(
"{} of your bots [{}] their [{}] aggro check.",
success_count,
Bot::GetSpellTypeNameByID(spell_type),
type_value ? "enabled" : "disabled"
).c_str()
);
}
}
}
+214
View File
@@ -0,0 +1,214 @@
#include "../bot_command.h"
void bot_command_spell_announce_cast(Client* c, const Seperator* sep) {
if (helper_command_alias_fail(c, "bot_command_spell_announce_cast", sep->arg[0], "spellannouncecasts")) {
c->Message(Chat::White, "note: Allows you to enable or disable cast announcements for bots by spell type.");
return;
}
if (helper_is_help_or_usage(sep->arg[1])) {
BotCommandHelpParams p;
p.description = { "Allows you to enable or disable cast announcements for bots by spell type." };
p.example_format =
{
fmt::format("{} [Type Shortname] [value] [actionable]", sep->arg[0]),
fmt::format("{} [Type ID] [value] [actionable]", sep->arg[0])
};
p.examples_one =
{
"To set all bots to stop announcing dispels:",
fmt::format(
"{} {} 0 spawned",
sep->arg[0],
Bot::GetSpellTypeShortNameByID(BotSpellTypes::Dispel)
),
fmt::format(
"{} {} 0 spawned",
sep->arg[0],
BotSpellTypes::Dispel
)
};
p.examples_two =
{
"To set Wizards to not announce nukes:",
fmt::format(
"{} {} 0 byclass {}",
sep->arg[0],
Bot::GetSpellTypeShortNameByID(BotSpellTypes::Nuke),
Class::Wizard
),
fmt::format(
"{} {} 0 byclass {}",
sep->arg[0],
BotSpellTypes::Nuke,
Class::Wizard
)
};
p.examples_three =
{
"To check the current announcement setting for debuffs:",
fmt::format(
"{} {} current spawned",
sep->arg[0],
Bot::GetSpellTypeShortNameByID(BotSpellTypes::Debuff)
),
fmt::format(
"{} {} current spawned",
sep->arg[0],
BotSpellTypes::Debuff
)
};
p.actionables = { "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" };
std::string popup_text = c->SendBotCommandHelpWindow(p);
popup_text = DialogueWindow::Table(popup_text);
c->SendPopupToClient(sep->arg[0], popup_text.c_str());
c->SendSpellTypePrompts();
if (RuleB(Bots, SendClassRaceOnHelp)) {
c->Message(
Chat::Yellow,
fmt::format(
"Use {} for information about race/class IDs.",
Saylink::Silent("^classracelist")
).c_str()
);
}
return;
}
std::string arg1 = sep->arg[1];
std::string arg2 = sep->arg[2];
int ab_arg = 2;
bool current_check = false;
uint16 spell_type = 0;
uint32 type_value = 0;
// String/Int type checks
if (sep->IsNumber(1)) {
spell_type = atoi(sep->arg[1]);
if (!EQ::ValueWithin(spell_type, BotSpellTypes::START, BotSpellTypes::END)) {
c->Message(Chat::Yellow, "You must choose a valid spell type. Spell types range from %i to %i", BotSpellTypes::START, BotSpellTypes::END);
return;
}
}
else {
if (Bot::GetSpellTypeIDByShortName(arg1) != UINT16_MAX) {
spell_type = Bot::GetSpellTypeIDByShortName(arg1);
}
else {
c->Message(
Chat::Yellow,
fmt::format(
"Incorrect argument, use {} for information regarding this command.",
Saylink::Silent(
fmt::format("{} help", sep->arg[0])
)
).c_str()
);
return;
}
}
if (sep->IsNumber(2)) {
type_value = atoi(sep->arg[2]);
++ab_arg;
if (type_value != 0 && type_value != 1) {
c->Message(Chat::Yellow, "You must enter either 0 for [Disabled] or 1 for [Enabled].");
return;
}
}
else if (!arg2.compare("current")) {
++ab_arg;
current_check = true;
}
else {
c->Message(
Chat::Yellow,
fmt::format(
"Incorrect argument, use {} for information regarding this command.",
Saylink::Silent(
fmt::format("{} help", sep->arg[0])
)
).c_str()
);
return;
}
const int ab_mask = ActionableBots::ABM_Type1;
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::vector<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.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end());
Bot* first_found = nullptr;
int success_count = 0;
for (auto my_bot : sbl) {
if (my_bot->BotPassiveCheck()) {
continue;
}
if (!first_found) {
first_found = my_bot;
}
if (current_check) {
c->Message(
Chat::Green,
fmt::format(
"{} says, 'I currently {} announce [{}] casts.'",
my_bot->GetCleanName(),
(my_bot->GetSpellTypeAnnounceCast(spell_type) ? "do" : "do not"),
Bot::GetSpellTypeNameByID(spell_type)
).c_str()
);
}
else {
my_bot->SetSpellTypeAnnounceCast(spell_type, type_value);
++success_count;
}
}
if (!current_check) {
if (success_count == 1 && first_found) {
c->Message(
Chat::Green,
fmt::format(
"{} says, 'I will {} announce [{}] casts.'",
first_found->GetCleanName(),
(first_found->GetSpellTypeAnnounceCast(spell_type) ? "now" : "no longer"),
Bot::GetSpellTypeNameByID(spell_type)
).c_str()
);
}
else {
c->Message(
Chat::Green,
fmt::format(
"{} of your bots will {} announce [{}] casts.",
success_count,
(type_value ? "now" : "no longer"),
Bot::GetSpellTypeNameByID(spell_type)
).c_str()
);
}
}
}
+282
View File
@@ -0,0 +1,282 @@
#include "../bot_command.h"
void bot_command_spell_delays(Client* c, const Seperator* sep) {
if (helper_command_alias_fail(c, "bot_command_spell_delays", sep->arg[0], "spelldelays")) {
c->Message(Chat::White, "note: Controls how long a bot will wait between casts of different spell types.");
return;
}
if (helper_is_help_or_usage(sep->arg[1])) {
BotCommandHelpParams p;
p.description = { "Controls how long a bot will wait between casts of different spell types." };
p.notes =
{
"- Targeting yourself for this command will allow you to control your own settings for how bots cast on you",
"- All pet types are based off the pet's owner's setting",
"- Any remaining types use the owner's setting when a pet is the target",
"- All Heals, Cures, Buffs (DS and resists included) are based off the target's setting, not the caster",
"- e.g., BotA is healing BotB using BotB's settings"
};
p.example_format =
{
fmt::format("{} [Type Shortname] [value] [actionable, default: target]", sep->arg[0]),
fmt::format("{} [Type ID] [value] [actionable, default: target]", sep->arg[0])
};
p.examples_one =
{
"To set all Necromancers to an 8s DoT delay:",
fmt::format(
"{} {} 8000 byclass {}",
sep->arg[0],
Bot::GetSpellTypeShortNameByID(BotSpellTypes::DOT),
Class::Necromancer
),
fmt::format(
"{} {} 8000 byclass {}",
sep->arg[0],
BotSpellTypes::DOT,
Class::Necromancer
)
};
p.examples_two =
{
"To set all Warriors to receive Fast Heals every 2.5s:",
fmt::format(
"{} {} 2500 byclass {}",
sep->arg[0],
Bot::GetSpellTypeShortNameByID(BotSpellTypes::FastHeals),
Class::Warrior
),
fmt::format(
"{} {} 2500 byclass {}",
sep->arg[0],
BotSpellTypes::FastHeals,
Class::Warrior
)
};
p.examples_three =
{
"To check the current Nuke delay on all bots:",
fmt::format(
"{} {} current spawned",
sep->arg[0],
Bot::GetSpellTypeShortNameByID(BotSpellTypes::Nuke)
),
fmt::format(
"{} {} current spawned",
sep->arg[0],
BotSpellTypes::Nuke
)
};
p.actionables = { "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" };
std::string popup_text = c->SendBotCommandHelpWindow(p);
popup_text = DialogueWindow::Table(popup_text);
c->SendPopupToClient(sep->arg[0], popup_text.c_str());
c->SendSpellTypePrompts();
c->SendSpellTypePrompts(false, true);
if (RuleB(Bots, SendClassRaceOnHelp)) {
c->Message(
Chat::Yellow,
fmt::format(
"Use {} for information about race/class IDs.",
Saylink::Silent("^classracelist")
).c_str()
);
}
return;
}
std::string arg1 = sep->arg[1];
std::string arg2 = sep->arg[2];
int ab_arg = 2;
bool current_check = false;
uint16 spell_type = 0;
uint32 type_value = 0;
Mob* target = c->GetTarget();
bool clientSetting = (target && target == c);
if (sep->IsNumber(1)) {
spell_type = atoi(sep->arg[1]);
if (
(clientSetting && !IsClientBotSpellType(spell_type)) ||
(!clientSetting && !EQ::ValueWithin(spell_type, BotSpellTypes::START, BotSpellTypes::END))
) {
c->Message(
Chat::Yellow,
fmt::format(
"{}. Use {} for information regarding this command.",
(!clientSetting ? "You must choose a valid spell type" : "You must choose a valid client spell type"),
Saylink::Silent(
fmt::format("{} help", sep->arg[0])
)
).c_str()
);
if (clientSetting) {
c->SendSpellTypePrompts(false, true);
}
return;
}
}
else {
if (Bot::GetSpellTypeIDByShortName(arg1) != UINT16_MAX) {
spell_type = Bot::GetSpellTypeIDByShortName(arg1);
if (clientSetting && !IsClientBotSpellType(spell_type)) {
c->Message(Chat::Yellow, "Invalid spell type for clients.");
c->SendSpellTypePrompts(false, true);
}
}
else {
c->Message(
Chat::Yellow,
fmt::format(
"Incorrect argument, use {} for information regarding this command.",
Saylink::Silent(
fmt::format("{} help", sep->arg[0])
)
).c_str()
);
return;
}
}
if (sep->IsNumber(2)) {
type_value = atoi(sep->arg[2]);
++ab_arg;
if (type_value < 100 || type_value > 60000) {
c->Message(Chat::Yellow, "You must enter a value between 100-60000 (100ms to 60s).");
return;
}
}
else if (!arg2.compare("current")) {
++ab_arg;
current_check = true;
}
else {
c->Message(
Chat::Yellow,
fmt::format(
"Incorrect argument, use {} for information regarding this command.",
Saylink::Silent(
fmt::format("{} help", sep->arg[0])
)
).c_str()
);
return;
}
const int ab_mask = ActionableBots::ABM_Type1;
std::string actionable_arg = sep->arg[ab_arg];
if (actionable_arg.empty()) {
actionable_arg = "target";
}
if (!clientSetting) {
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::vector<Bot*> sbl;
if (ActionableBots::PopulateSBL(c, actionable_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.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end());
Bot* first_found = nullptr;
int success_count = 0;
for (auto my_bot : sbl) {
if (my_bot->BotPassiveCheck()) {
continue;
}
if (!first_found) {
first_found = my_bot;
}
if (current_check) {
c->Message(
Chat::Green,
fmt::format(
"{} says, 'My [{}] spell delay is currently [{}] seconds.'",
my_bot->GetCleanName(),
Bot::GetSpellTypeNameByID(spell_type),
my_bot->GetSpellTypeDelay(spell_type) / 1000.00
).c_str()
);
}
else {
my_bot->SetSpellTypeDelay(spell_type, type_value);
++success_count;
}
}
if (!current_check) {
if (success_count == 1 && first_found) {
c->Message(
Chat::Green,
fmt::format(
"{} says, 'My [{}] spell delay was set to [{}] seconds.'",
first_found->GetCleanName(),
Bot::GetSpellTypeNameByID(spell_type),
first_found->GetSpellTypeDelay(spell_type) / 1000.00
).c_str()
);
}
else {
c->Message(
Chat::Green,
fmt::format(
"{} of your bots set their [{}] spell delay to [{}] seconds.",
success_count,
Bot::GetSpellTypeNameByID(spell_type),
type_value / 1000.00
).c_str()
);
}
}
}
else {
if (current_check) {
c->Message(
Chat::Green,
fmt::format(
"Your [{}] spell delay is currently [{}] seconds.",
Bot::GetSpellTypeNameByID(spell_type),
c->GetSpellTypeDelay(spell_type) / 1000.00
).c_str()
);
}
else {
c->SetSpellTypeDelay(spell_type, type_value);
c->Message(
Chat::Green,
fmt::format(
"Your [{}] spell delay was set to [{}] seconds.",
Bot::GetSpellTypeNameByID(spell_type),
c->GetSpellTypeDelay(spell_type) / 1000.00
).c_str()
);
}
}
}
@@ -0,0 +1,248 @@
#include "../bot_command.h"
void bot_command_spell_engaged_priority(Client* c, const Seperator* sep)
{
if (helper_command_alias_fail(c, "bot_command_spell_engaged_priority", sep->arg[0], "spellengagedpriority")) {
c->Message(Chat::White, "note: Sets the order of spell casts when engaged in combat by spell type.");
return;
}
if (helper_is_help_or_usage(sep->arg[1])) {
BotCommandHelpParams p;
p.description = { "Sets the order of spell casts when engaged in combat by spell type." };
p.notes =
{
"- Setting a spell type to 0 will prevent that type from being cast.",
"- If 2 or more are set to the same priority they will sort by spell type ID."
};
p.example_format =
{
fmt::format("{} [Type Shortname] [value] [actionable]", sep->arg[0]),
fmt::format("{} [Type ID] [value] [actionable]", sep->arg[0])
};
p.examples_one =
{
"To set all Shaman to cast slows first:",
fmt::format(
"{} {} 1 byclass {}",
sep->arg[0],
Bot::GetSpellTypeShortNameByID(BotSpellTypes::Slow),
Class::Shaman
),
fmt::format(
"{} {} 1 byclass {}",
sep->arg[0],
BotSpellTypes::Slow,
Class::Shaman
)
};
p.examples_two =
{
"To set all bots to not cast snares:",
fmt::format(
"{} {} 0 spawned",
sep->arg[0],
Bot::GetSpellTypeShortNameByID(BotSpellTypes::Snare)
),
fmt::format(
"{} {} 0 spawned",
sep->arg[0],
BotSpellTypes::Snare
)
};
p.examples_three =
{
"To check the current engaged priority of dispels on all bots:",
fmt::format(
"{} {} current spawned",
sep->arg[0],
Bot::GetSpellTypeShortNameByID(BotSpellTypes::Dispel)
),
fmt::format(
"{} {} current spawned",
sep->arg[0],
BotSpellTypes::Dispel
)
};
p.actionables = { "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" };
std::string popup_text = c->SendBotCommandHelpWindow(p);
popup_text = DialogueWindow::Table(popup_text);
c->SendPopupToClient(sep->arg[0], popup_text.c_str());
c->SendSpellTypePrompts();
if (RuleB(Bots, SendClassRaceOnHelp)) {
c->Message(
Chat::Yellow,
fmt::format(
"Use {} for information about race/class IDs.",
Saylink::Silent("^classracelist")
).c_str()
);
}
return;
}
std::string arg1 = sep->arg[1];
std::string arg2 = sep->arg[2];
int ab_arg = 2;
bool current_check = false;
bool list_check = false;
uint16 spell_type = 0;
uint32 type_value = 0;
// String/Int type checks
if (sep->IsNumber(1)) {
spell_type = atoi(sep->arg[1]);
if (!EQ::ValueWithin(spell_type, BotSpellTypes::START, BotSpellTypes::END)) {
c->Message(Chat::Yellow, "You must choose a valid spell type. Spell types range from %i to %i", BotSpellTypes::START, BotSpellTypes::END);
return;
}
}
else {
if (Bot::GetSpellTypeIDByShortName(arg1) != UINT16_MAX) {
spell_type = Bot::GetSpellTypeIDByShortName(arg1);
}
else if (!arg1.compare("list")) {
++ab_arg;
list_check = true;
}
else {
c->Message(
Chat::Yellow,
fmt::format(
"Incorrect argument, use {} for information regarding this command.",
Saylink::Silent(
fmt::format("{} help", sep->arg[0])
)
).c_str()
);
return;
}
}
if (sep->IsNumber(2)) {
type_value = atoi(sep->arg[2]);
++ab_arg;
if (EQ::ValueWithin(type_value, BotSpellTypes::START, BotSpellTypes::END)) {
c->Message(Chat::Yellow, "You must enter a value between {} and {}.", BotSpellTypes::START, BotSpellTypes::END);
return;
}
}
else if (!arg2.compare("current")) {
++ab_arg;
current_check = true;
}
else if (!list_check) {
c->Message(
Chat::Yellow,
fmt::format(
"Incorrect argument, use {} for information regarding this command.",
Saylink::Silent(
fmt::format("{} help", sep->arg[0])
)
).c_str()
);
return;
}
const int ab_mask = ActionableBots::ABM_Type1;
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::vector<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.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end());
Bot* first_found = nullptr;
int success_count = 0;
for (auto my_bot : sbl) {
if (my_bot->BotPassiveCheck()) {
continue;
}
if (!first_found) {
first_found = my_bot;
}
if (current_check) {
c->Message(
Chat::Green,
fmt::format(
"{} says, 'My [{}] engaged cast priority is currently [{}].'",
my_bot->GetCleanName(),
Bot::GetSpellTypeNameByID(spell_type),
my_bot->GetSpellTypePriority(spell_type, BotPriorityCategories::Engaged)
).c_str()
);
}
else if (list_check) {
auto cast_order = my_bot->GetSpellTypesPrioritized(BotPriorityCategories::Engaged);
for (auto& current_cast : cast_order) {
c->Message(
Chat::Green,
fmt::format(
"{} says, 'My [{}] engaged cast priority for is currently [{}].'",
my_bot->GetCleanName(),
Bot::GetSpellTypeNameByID(current_cast.spellType),
(current_cast.priority == 0 ? "disabled (0)" : std::to_string(current_cast.priority))
).c_str()
);
}
c->Message(
Chat::Green,
fmt::format(
"{} says, 'Anything not listed is currently disabled (0).'",
my_bot->GetCleanName()
).c_str()
);
return;
}
else {
my_bot->SetSpellTypePriority(spell_type, BotPriorityCategories::Engaged, type_value);
++success_count;
}
}
if (!current_check) {
if (success_count == 1 && first_found) {
c->Message(
Chat::Green,
fmt::format(
"{} says, 'My [{}] engaged cast priority was set to [{}].'",
first_found->GetCleanName(),
Bot::GetSpellTypeNameByID(spell_type),
first_found->GetSpellTypePriority(spell_type, BotPriorityCategories::Engaged)
).c_str()
);
}
else {
c->Message(
Chat::Green,
fmt::format(
"{} of your bots set their [{}] engaged cast priority to [{}].",
success_count,
Bot::GetSpellTypeNameByID(spell_type),
type_value
).c_str()
);
}
}
}
+193
View File
@@ -0,0 +1,193 @@
#include "../bot_command.h"
void bot_command_spell_holds(Client* c, const Seperator* sep)
{
if (helper_command_alias_fail(c, "bot_command_spell_holds", sep->arg[0], "spellholds")) {
c->Message(Chat::White, "note: Toggles whether or not bots can cast certain spell types.");
return;
}
if (helper_is_help_or_usage(sep->arg[1])) {
BotCommandHelpParams p;
p.description = { "Toggles whether or not bots can cast certain spell types." };
p.notes = { "- All pet types are based off the pet owner's setting when a pet is the target" };
p.example_format =
{
fmt::format("{} [Type Shortname] [value] [actionable]", sep->arg[0]),
fmt::format("{} [Type ID] [value] [actionable]", sep->arg[0])
};
p.examples_one =
{
"To set all bots to hold DoTs:",
fmt::format(
"{} {} 1 spawned",
sep->arg[0],
Bot::GetSpellTypeShortNameByID(BotSpellTypes::DOT)
),
fmt::format(
"{} {} 1 spawned",
sep->arg[0],
BotSpellTypes::DOT
)
};
p.examples_two =
{
"To check the current DoT settings on all bots:",
fmt::format(
"{} {} current spawned",
sep->arg[0],
Bot::GetSpellTypeShortNameByID(BotSpellTypes::DOT)
),
fmt::format(
"{} {} current spawned",
sep->arg[0],
BotSpellTypes::DOT
)
};
p.actionables = { "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" };
std::string popup_text = c->SendBotCommandHelpWindow(p);
popup_text = DialogueWindow::Table(popup_text);
c->SendPopupToClient(sep->arg[0], popup_text.c_str());
c->SendSpellTypePrompts();
if (RuleB(Bots, SendClassRaceOnHelp)) {
c->Message(
Chat::Yellow,
fmt::format(
"Use {} for information about race/class IDs.",
Saylink::Silent("^classracelist")
).c_str()
);
}
return;
}
std::string arg1 = sep->arg[1];
std::string arg2 = sep->arg[2];
int ab_arg = 2;
bool current_check = false;
uint16 spell_type = 0;
uint32 type_value = 0;
// String/Int type checks
if (sep->IsNumber(1)) {
spell_type = atoi(sep->arg[1]);
if (!EQ::ValueWithin(spell_type, BotSpellTypes::START, BotSpellTypes::END)) {
c->Message(Chat::Yellow, "You must choose a valid spell type. Spell types range from %i to %i", BotSpellTypes::START, BotSpellTypes::END);
return;
}
}
else {
if (Bot::GetSpellTypeIDByShortName(arg1) != UINT16_MAX) {
spell_type = Bot::GetSpellTypeIDByShortName(arg1);
}
else {
c->Message(
Chat::Yellow,
fmt::format(
"Incorrect argument, use {} for information regarding this command.",
Saylink::Silent(
fmt::format("{} help", sep->arg[0])
)
).c_str()
);
return;
}
}
if (sep->IsNumber(2)) {
type_value = atoi(sep->arg[2]);
++ab_arg;
if (type_value < 0 || type_value > 1) {
c->Message(Chat::Yellow, "You must enter either 0 for disabled or 1 for enabled.");
return;
}
}
else if (!arg2.compare("current")) {
++ab_arg;
current_check = true;
}
else {
c->Message(
Chat::Yellow,
fmt::format(
"Incorrect argument, use {} for information regarding this command.",
Saylink::Silent(
fmt::format("{} help", sep->arg[0])
)
).c_str()
);
return;
}
const int ab_mask = ActionableBots::ABM_Type1;
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::vector<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.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end());
Bot* first_found = nullptr;
int success_count = 0;
for (auto my_bot : sbl) {
if (!first_found) {
first_found = my_bot;
}
if (current_check) {
c->Message(
Chat::Green,
fmt::format(
"{} says, 'My [{}] spell hold is currently [{}].'",
my_bot->GetCleanName(),
Bot::GetSpellTypeNameByID(spell_type),
my_bot->GetSpellTypeHold(spell_type) ? "enabled" : "disabled"
).c_str()
);
}
else {
my_bot->SetSpellTypeHold(spell_type, type_value);
++success_count;
}
}
if (!current_check) {
if (success_count == 1 && first_found) {
c->Message(
Chat::Green,
fmt::format(
"{} says, 'My [{}] spell hold was [{}].'",
first_found->GetCleanName(),
Bot::GetSpellTypeNameByID(spell_type),
first_found->GetSpellTypeHold(spell_type) ? "enabled" : "disabled"
).c_str()
);
}
else {
c->Message(
Chat::Green,
fmt::format(
"{} of your bots [{}] their [{}] spell hold.",
success_count,
type_value ? "enabled" : "disabled",
Bot::GetSpellTypeNameByID(spell_type)
).c_str()
);
}
}
}
+248
View File
@@ -0,0 +1,248 @@
#include "../bot_command.h"
void bot_command_spell_idle_priority(Client* c, const Seperator* sep)
{
if (helper_command_alias_fail(c, "bot_command_spell_idle_priority", sep->arg[0], "spellidlepriority")) {
c->Message(Chat::White, "note: Sets the order of spell casts when not in combat by spell type.");
return;
}
if (helper_is_help_or_usage(sep->arg[1])) {
BotCommandHelpParams p;
p.description = { "Sets the order of spell casts when not in combat by spell type." };
p.notes =
{
"- Setting a spell type to 0 will prevent that type from being cast.",
"- If 2 or more are set to the same priority they will sort by spell type ID."
};
p.example_format =
{
fmt::format("{} [Type Shortname] [value] [actionable]", sep->arg[0]),
fmt::format("{} [Type ID] [value] [actionable]", sep->arg[0])
};
p.examples_one =
{
"To set all Clerics to cast fast heals third:",
fmt::format(
"{} {} 3 byclass {}",
sep->arg[0],
Bot::GetSpellTypeShortNameByID(BotSpellTypes::FastHeals),
Class::Cleric
),
fmt::format(
"{} {} 3 byclass {}",
sep->arg[0],
BotSpellTypes::FastHeals,
Class::Cleric
)
};
p.examples_two =
{
"To set all bots to not cast cures:",
fmt::format(
"{} {} 0 spawned",
sep->arg[0],
Bot::GetSpellTypeShortNameByID(BotSpellTypes::Cure)
),
fmt::format(
"{} {} 0 spawned",
sep->arg[0],
BotSpellTypes::Cure
)
};
p.examples_three =
{
"To check the current idle priority of buffs on all bots:",
fmt::format(
"{} {} current spawned",
sep->arg[0],
Bot::GetSpellTypeShortNameByID(BotSpellTypes::Buff)
),
fmt::format(
"{} {} current spawned",
sep->arg[0],
BotSpellTypes::Buff
)
};
p.actionables = { "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" };
std::string popup_text = c->SendBotCommandHelpWindow(p);
popup_text = DialogueWindow::Table(popup_text);
c->SendPopupToClient(sep->arg[0], popup_text.c_str());
c->SendSpellTypePrompts();
if (RuleB(Bots, SendClassRaceOnHelp)) {
c->Message(
Chat::Yellow,
fmt::format(
"Use {} for information about race/class IDs.",
Saylink::Silent("^classracelist")
).c_str()
);
}
return;
}
std::string arg1 = sep->arg[1];
std::string arg2 = sep->arg[2];
int ab_arg = 2;
bool current_check = false;
bool list_check = false;
uint16 spell_type = 0;
uint32 type_value = 0;
// String/Int type checks
if (sep->IsNumber(1)) {
spell_type = atoi(sep->arg[1]);
if (!EQ::ValueWithin(spell_type, BotSpellTypes::START, BotSpellTypes::END)) {
c->Message(Chat::Yellow, "You must choose a valid spell type. Spell types range from %i to %i", BotSpellTypes::START, BotSpellTypes::END);
return;
}
}
else {
if (Bot::GetSpellTypeIDByShortName(arg1) != UINT16_MAX) {
spell_type = Bot::GetSpellTypeIDByShortName(arg1);
}
else if (!arg1.compare("list")) {
++ab_arg;
list_check = true;
}
else {
c->Message(
Chat::Yellow,
fmt::format(
"Incorrect argument, use {} for information regarding this command.",
Saylink::Silent(
fmt::format("{} help", sep->arg[0])
)
).c_str()
);
return;
}
}
if (sep->IsNumber(2)) {
type_value = atoi(sep->arg[2]);
++ab_arg;
if (EQ::ValueWithin(type_value, BotSpellTypes::START, BotSpellTypes::END)) {
c->Message(Chat::Yellow, "You must enter a value between {} and {}.", BotSpellTypes::START, BotSpellTypes::END);
return;
}
}
else if (!arg2.compare("current")) {
++ab_arg;
current_check = true;
}
else if (!list_check) {
c->Message(
Chat::Yellow,
fmt::format(
"Incorrect argument, use {} for information regarding this command.",
Saylink::Silent(
fmt::format("{} help", sep->arg[0])
)
).c_str()
);
return;
}
const int ab_mask = ActionableBots::ABM_Type1;
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::vector<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.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end());
Bot* first_found = nullptr;
int success_count = 0;
for (auto my_bot : sbl) {
if (my_bot->BotPassiveCheck()) {
continue;
}
if (!first_found) {
first_found = my_bot;
}
if (current_check) {
c->Message(
Chat::Green,
fmt::format(
"{} says, 'My [{}] idle cast priority is currently [{}].'",
my_bot->GetCleanName(),
Bot::GetSpellTypeNameByID(spell_type),
my_bot->GetSpellTypePriority(spell_type, BotPriorityCategories::Idle)
).c_str()
);
}
else if (list_check) {
auto cast_order = my_bot->GetSpellTypesPrioritized(BotPriorityCategories::Idle);
for (auto& current_cast : cast_order) {
c->Message(
Chat::Green,
fmt::format(
"{} says, 'My [{}] idle cast priority for is currently [{}].'",
my_bot->GetCleanName(),
Bot::GetSpellTypeNameByID(current_cast.spellType),
(current_cast.priority == 0 ? "disabled (0)" : std::to_string(current_cast.priority))
).c_str()
);
}
c->Message(
Chat::Green,
fmt::format(
"{} says, 'Anything not listed is currently disabled (0).'",
my_bot->GetCleanName()
).c_str()
);
return;
}
else {
my_bot->SetSpellTypePriority(spell_type, BotPriorityCategories::Idle, type_value);
++success_count;
}
}
if (!current_check) {
if (success_count == 1 && first_found) {
c->Message(
Chat::Green,
fmt::format(
"{} says, 'My [{}] idle cast priority was set to [{}].'",
first_found->GetCleanName(),
Bot::GetSpellTypeNameByID(spell_type),
first_found->GetSpellTypePriority(spell_type, BotPriorityCategories::Idle)
).c_str()
);
}
else {
c->Message(
Chat::Green,
fmt::format(
"{} of your bots set their [{}] idle cast priority to [{}].",
success_count,
Bot::GetSpellTypeNameByID(spell_type),
type_value
).c_str()
);
}
}
}
+211
View File
@@ -0,0 +1,211 @@
#include "../bot_command.h"
void bot_command_spell_max_hp_pct(Client* c, const Seperator* sep)
{
if (helper_command_alias_fail(c, "bot_command_spell_max_hp_pct", sep->arg[0], "spellmaxhppct")) {
c->Message(Chat::White, "note: Controls at what health percentage a bot will start casting different spell types.");
return;
}
if (helper_is_help_or_usage(sep->arg[1])) {
BotCommandHelpParams p;
p.description = { "Controls at what health percentage a bot will start casting different spell types." };
p.example_format =
{
fmt::format("{} [Type Shortname] [value] [actionable]", sep->arg[0]),
fmt::format("{} [Type ID] [value] [actionable]", sep->arg[0])
};
p.examples_one =
{
"To set all bots to allow snaring when their health is at or below 100% HP:",
fmt::format(
"{} {} 100 spawned",
sep->arg[0],
Bot::GetSpellTypeShortNameByID(BotSpellTypes::Snare)
),
fmt::format(
"{} {} 10 spawned",
sep->arg[0],
BotSpellTypes::Snare
)
};
p.examples_two =
{
"To set BotA to allow casting of fast heals at 30% HP:",
fmt::format(
"{} {} 30 byname BotA",
sep->arg[0],
Bot::GetSpellTypeShortNameByID(BotSpellTypes::FastHeals)
),
fmt::format(
"{} {} 30 byname BotA",
sep->arg[0],
BotSpellTypes::FastHeals
)
};
p.examples_three =
{
"To check the current Stun max HP percent on all bots:",
fmt::format(
"{} {} current spawned",
sep->arg[0],
Bot::GetSpellTypeShortNameByID(BotSpellTypes::Stun)
),
fmt::format(
"{} {} current spawned",
sep->arg[0],
BotSpellTypes::Stun
)
};
p.actionables = { "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" };
std::string popup_text = c->SendBotCommandHelpWindow(p);
popup_text = DialogueWindow::Table(popup_text);
c->SendPopupToClient(sep->arg[0], popup_text.c_str());
c->SendSpellTypePrompts();
if (RuleB(Bots, SendClassRaceOnHelp)) {
c->Message(
Chat::Yellow,
fmt::format(
"Use {} for information about race/class IDs.",
Saylink::Silent("^classracelist")
).c_str()
);
}
return;
}
std::string arg1 = sep->arg[1];
std::string arg2 = sep->arg[2];
int ab_arg = 2;
bool current_check = false;
uint16 spell_type = 0;
uint32 type_value = 0;
// String/Int type checks
if (sep->IsNumber(1)) {
spell_type = atoi(sep->arg[1]);
if (!EQ::ValueWithin(spell_type, BotSpellTypes::START, BotSpellTypes::END)) {
c->Message(Chat::Yellow, "You must choose a valid spell type. Spell types range from %i to %i", BotSpellTypes::START, BotSpellTypes::END);
return;
}
}
else {
if (Bot::GetSpellTypeIDByShortName(arg1) != UINT16_MAX) {
spell_type = Bot::GetSpellTypeIDByShortName(arg1);
}
else {
c->Message(
Chat::Yellow,
fmt::format(
"Incorrect argument, use {} for information regarding this command.",
Saylink::Silent(
fmt::format("{} help", sep->arg[0])
)
).c_str()
);
return;
}
}
if (sep->IsNumber(2)) {
type_value = atoi(sep->arg[2]);
++ab_arg;
if (type_value < 0 || type_value > 100) {
c->Message(Chat::Yellow, "You must enter a value between 0-100 (0%% to 100%% of health).");
return;
}
}
else if (!arg2.compare("current")) {
++ab_arg;
current_check = true;
}
else {
c->Message(
Chat::Yellow,
fmt::format(
"Incorrect argument, use {} for information regarding this command.",
Saylink::Silent(
fmt::format("{} help", sep->arg[0])
)
).c_str()
);
return;
}
const int ab_mask = ActionableBots::ABM_Type1;
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::vector<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.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end());
Bot* first_found = nullptr;
int success_count = 0;
for (auto my_bot : sbl) {
if (my_bot->BotPassiveCheck()) {
continue;
}
if (!first_found) {
first_found = my_bot;
}
if (current_check) {
c->Message(
Chat::Green,
fmt::format(
"{} says, 'My [{}] maximum HP is currently [{}%%].'",
my_bot->GetCleanName(),
Bot::GetSpellTypeNameByID(spell_type),
my_bot->GetSpellTypeMaxHPLimit(spell_type)
).c_str()
);
}
else {
my_bot->SetSpellTypeMaxHPLimit(spell_type, type_value);
++success_count;
}
}
if (!current_check) {
if (success_count == 1 && first_found) {
c->Message(
Chat::Green,
fmt::format(
"{} says, 'My [{}] maximum HP was set to [{}%%].'",
first_found->GetCleanName(),
Bot::GetSpellTypeNameByID(spell_type),
first_found->GetSpellTypeMaxHPLimit(spell_type)
).c_str()
);
}
else {
c->Message(
Chat::Green,
fmt::format(
"{} of your bots set their [{}] maximum HP to [{}%%].",
success_count,
Bot::GetSpellTypeNameByID(spell_type),
type_value
).c_str()
);
}
}
}
+211
View File
@@ -0,0 +1,211 @@
#include "../bot_command.h"
void bot_command_spell_max_mana_pct(Client* c, const Seperator* sep)
{
if (helper_command_alias_fail(c, "bot_command_spell_max_mana_pct", sep->arg[0], "spellmaxmanapct")) {
c->Message(Chat::White, "note: Controls at what mana percentage a bot will stop casting different spell types.");
return;
}
if (helper_is_help_or_usage(sep->arg[1])) {
BotCommandHelpParams p;
p.description = { "Controls at what mana percentage a bot will stop casting different spell types." };
p.example_format =
{
fmt::format("{} [Type Shortname] [value] [actionable]", sep->arg[0]),
fmt::format("{} [Type ID] [value] [actionable]", sep->arg[0])
};
p.examples_one =
{
"To set all bots to allow snaring when their mana is at or below 100% HP:",
fmt::format(
"{} {} 10 spawned",
sep->arg[0],
Bot::GetSpellTypeShortNameByID(BotSpellTypes::Snare)
),
fmt::format(
"{} {} 10 spawned",
sep->arg[0],
BotSpellTypes::Snare
)
};
p.examples_two =
{
"To set BotA to allow casting of fast heals at 90% mana:",
fmt::format(
"{} {} 90 byname BotA",
sep->arg[0],
Bot::GetSpellTypeShortNameByID(BotSpellTypes::FastHeals)
),
fmt::format(
"{} {} 90 byname BotA",
sep->arg[0],
BotSpellTypes::FastHeals
)
};
p.examples_three =
{
"To check the current Stun max mana percent on all bots:",
fmt::format(
"{} {} current spawned",
sep->arg[0],
Bot::GetSpellTypeShortNameByID(BotSpellTypes::Stun)
),
fmt::format(
"{} {} current spawned",
sep->arg[0],
BotSpellTypes::Stun
)
};
p.actionables = { "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" };
std::string popup_text = c->SendBotCommandHelpWindow(p);
popup_text = DialogueWindow::Table(popup_text);
c->SendPopupToClient(sep->arg[0], popup_text.c_str());
c->SendSpellTypePrompts();
if (RuleB(Bots, SendClassRaceOnHelp)) {
c->Message(
Chat::Yellow,
fmt::format(
"Use {} for information about race/class IDs.",
Saylink::Silent("^classracelist")
).c_str()
);
}
return;
}
std::string arg1 = sep->arg[1];
std::string arg2 = sep->arg[2];
int ab_arg = 2;
bool current_check = false;
uint16 spell_type = 0;
uint32 type_value = 0;
// String/Int type checks
if (sep->IsNumber(1)) {
spell_type = atoi(sep->arg[1]);
if (!EQ::ValueWithin(spell_type, BotSpellTypes::START, BotSpellTypes::END)) {
c->Message(Chat::Yellow, "You must choose a valid spell type. Spell types range from %i to %i", BotSpellTypes::START, BotSpellTypes::END);
return;
}
}
else {
if (Bot::GetSpellTypeIDByShortName(arg1) != UINT16_MAX) {
spell_type = Bot::GetSpellTypeIDByShortName(arg1);
}
else {
c->Message(
Chat::Yellow,
fmt::format(
"Incorrect argument, use {} for information regarding this command.",
Saylink::Silent(
fmt::format("{} help", sep->arg[0])
)
).c_str()
);
return;
}
}
if (sep->IsNumber(2)) {
type_value = atoi(sep->arg[2]);
++ab_arg;
if (type_value < 0 || type_value > 100) {
c->Message(Chat::Yellow, "You must enter a value between 0-100 (0%% to 100%% of mana).");
return;
}
}
else if (!arg2.compare("current")) {
++ab_arg;
current_check = true;
}
else {
c->Message(
Chat::Yellow,
fmt::format(
"Incorrect argument, use {} for information regarding this command.",
Saylink::Silent(
fmt::format("{} help", sep->arg[0])
)
).c_str()
);
return;
}
const int ab_mask = ActionableBots::ABM_Type1;
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::vector<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.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end());
Bot* first_found = nullptr;
int success_count = 0;
for (auto my_bot : sbl) {
if (my_bot->BotPassiveCheck()) {
continue;
}
if (!first_found) {
first_found = my_bot;
}
if (current_check) {
c->Message(
Chat::Green,
fmt::format(
"{} says, 'My [{}] maximum mana is currently [{}%%].'",
my_bot->GetCleanName(),
Bot::GetSpellTypeNameByID(spell_type),
my_bot->GetSpellTypeMaxManaLimit(spell_type)
).c_str()
);
}
else {
my_bot->SetSpellTypeMaxManaLimit(spell_type, type_value);
++success_count;
}
}
if (!current_check) {
if (success_count == 1 && first_found) {
c->Message(
Chat::Green,
fmt::format(
"{} says, 'My [{}] maximum mana was set to [{}%%].'",
first_found->GetCleanName(),
Bot::GetSpellTypeNameByID(spell_type),
first_found->GetSpellTypeMaxManaLimit(spell_type)
).c_str()
);
}
else {
c->Message(
Chat::Green,
fmt::format(
"{} of your bots set their [{}] maximum mana to [{}%%].",
success_count,
Bot::GetSpellTypeNameByID(spell_type),
type_value
).c_str()
);
}
}
}
+278
View File
@@ -0,0 +1,278 @@
#include "../bot_command.h"
void bot_command_spell_max_thresholds(Client* c, const Seperator* sep) {
if (helper_command_alias_fail(c, "bot_command_spell_max_thresholds", sep->arg[0], "spellmaxthresholds")) {
c->Message(Chat::White, "note: Controls at what target HP % the bot will start casting different spell types.");
return;
}
if (helper_is_help_or_usage(sep->arg[1])) {
BotCommandHelpParams p;
p.description = { "Controls at what target HP % the bot will start casting different spell types." };
p.notes =
{
"- Targeting yourself for this command will allow you to control your own settings for how bots cast on you",
"- All pet types are based off the pet's owner's setting",
"- Any remaining types use the owner's setting when a pet is the target",
"- All Heals, Cures, Buffs (DS and resists included) are based off the target's setting, not the caster",
"- e.g., BotA is healing BotB using BotB's settings",
};
p.example_format =
{
fmt::format("{} [Type Shortname] [value] [actionable, default: target]", sep->arg[0]),
fmt::format("{} [Type ID] [value] [actionable, default: target]", sep->arg[0])
};
p.examples_one =
{
"To set all bots to start snaring at 99%:",
fmt::format(
"{} {} 99 spawned",
sep->arg[0],
Bot::GetSpellTypeShortNameByID(BotSpellTypes::Snare)
),
fmt::format(
"{} {} 99 spawned",
sep->arg[0],
BotSpellTypes::Snare
)
};
p.examples_two =
{
"To set bot Enchbot to start casting Debuffs at 99%:",
fmt::format(
"{} {} 99 byname Enchbot",
sep->arg[0],
Bot::GetSpellTypeShortNameByID(BotSpellTypes::Debuff)
),
fmt::format(
"{} {} 99 byname Enchbot",
sep->arg[0],
BotSpellTypes::Debuff
)
};
p.examples_three =
{
"To check the current Nuke max threshold on all bots:",
fmt::format(
"{} {} current spawned",
sep->arg[0],
Bot::GetSpellTypeShortNameByID(BotSpellTypes::Nuke)
),
fmt::format(
"{} {} current spawned",
sep->arg[0],
BotSpellTypes::Nuke
)
};
p.actionables = { "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" };
std::string popup_text = c->SendBotCommandHelpWindow(p);
popup_text = DialogueWindow::Table(popup_text);
c->SendPopupToClient(sep->arg[0], popup_text.c_str());
c->SendSpellTypePrompts();
c->SendSpellTypePrompts(false, true);
if (RuleB(Bots, SendClassRaceOnHelp)) {
c->Message(
Chat::Yellow,
fmt::format(
"Use {} for information about race/class IDs.",
Saylink::Silent("^classracelist")
).c_str()
);
}
return;
}
std::string arg1 = sep->arg[1];
std::string arg2 = sep->arg[2];
int ab_arg = 2;
bool current_check = false;
uint16 spell_type = 0;
uint32 type_value = 0;
Mob* target = c->GetTarget();
bool clientSetting = (target && target == c);
if (sep->IsNumber(1)) {
spell_type = atoi(sep->arg[1]);
if (
(clientSetting && !IsClientBotSpellType(spell_type)) ||
(!clientSetting && !EQ::ValueWithin(spell_type, BotSpellTypes::START, BotSpellTypes::END))
) {
c->Message(
Chat::Yellow,
fmt::format(
"{}. Use {} for information regarding this command.",
(!clientSetting ? "You must choose a valid spell type" : "You must choose a valid client spell type"),
Saylink::Silent(
fmt::format("{} help", sep->arg[0])
)
).c_str()
);
if (clientSetting) {
c->SendSpellTypePrompts(false, true);
}
return;
}
}
else {
if (Bot::GetSpellTypeIDByShortName(arg1) != UINT16_MAX) {
spell_type = Bot::GetSpellTypeIDByShortName(arg1);
if (clientSetting && !IsClientBotSpellType(spell_type)) {
c->Message(Chat::Yellow, "Invalid spell type for clients.");
c->SendSpellTypePrompts(false, true);
}
}
else {
c->Message(
Chat::Yellow,
fmt::format(
"Incorrect argument, use {} for information regarding this command.",
Saylink::Silent(
fmt::format("{} help", sep->arg[0])
)
).c_str()
);
return;
}
}
if (sep->IsNumber(2)) {
type_value = atoi(sep->arg[2]);
++ab_arg;
if (type_value < 0 || type_value > 100) {
c->Message(Chat::Yellow, "You must enter a value between 0-100 (0%% to 100%% of health).");
return;
}
}
else if (!arg2.compare("current")) {
++ab_arg;
current_check = true;
}
else {
c->Message(
Chat::Yellow,
fmt::format(
"Incorrect argument, use {} for information regarding this command.",
Saylink::Silent(
fmt::format("{} help", sep->arg[0])
)
).c_str()
);
return;
}
const int ab_mask = ActionableBots::ABM_Type1;
std::string actionable_arg = sep->arg[ab_arg];
if (actionable_arg.empty()) {
actionable_arg = "target";
}
if (!clientSetting) {
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::vector<Bot*> sbl;
if (ActionableBots::PopulateSBL(c, actionable_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.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end());
Bot* first_found = nullptr;
int success_count = 0;
for (auto my_bot : sbl) {
if (my_bot->BotPassiveCheck()) {
continue;
}
if (!first_found) {
first_found = my_bot;
}
if (current_check) {
c->Message(
Chat::Green,
fmt::format(
"{} says, 'My [{}] maximum threshold is currently [{}%%].'",
my_bot->GetCleanName(),
Bot::GetSpellTypeNameByID(spell_type),
my_bot->GetSpellTypeMaxThreshold(spell_type)
).c_str()
);
}
else {
my_bot->SetSpellTypeMaxThreshold(spell_type, type_value);
++success_count;
}
}
if (!current_check) {
if (success_count == 1 && first_found) {
c->Message(
Chat::Green,
fmt::format(
"{} says, 'My [{}] maximum threshold was set to [{}%%].'",
first_found->GetCleanName(),
Bot::GetSpellTypeNameByID(spell_type),
first_found->GetSpellTypeMaxThreshold(spell_type)
).c_str()
);
}
else {
c->Message(
Chat::Green,
fmt::format(
"{} of your bots set their [{}] maximum threshold to [{}%%].",
success_count,
Bot::GetSpellTypeNameByID(spell_type),
type_value
).c_str()
);
}
}
}
else {
if (current_check) {
c->Message(
Chat::Green,
fmt::format(
"Your [{}] maximum threshold is currently [{}%%].",
Bot::GetSpellTypeNameByID(spell_type),
c->GetSpellTypeMaxThreshold(spell_type)
).c_str()
);
}
else {
c->SetSpellTypeMaxThreshold(spell_type, type_value);
c->Message(
Chat::Green,
fmt::format(
"Your [{}] maximum threshold was set to [{}%%].",
Bot::GetSpellTypeNameByID(spell_type),
c->GetSpellTypeMaxThreshold(spell_type)
).c_str()
);
}
}
}
+211
View File
@@ -0,0 +1,211 @@
#include "../bot_command.h"
void bot_command_spell_min_hp_pct(Client* c, const Seperator* sep)
{
if (helper_command_alias_fail(c, "bot_command_spell_min_hp_pct", sep->arg[0], "spellminhppct")) {
c->Message(Chat::White, "note: Controls at what health percentage a bot will stop casting different spell types.");
return;
}
if (helper_is_help_or_usage(sep->arg[1])) {
BotCommandHelpParams p;
p.description = { "Controls at what health percentage a bot will stop casting different spell types." };
p.example_format =
{
fmt::format("{} [Type Shortname] [value] [actionable]", sep->arg[0]),
fmt::format("{} [Type ID] [value] [actionable]", sep->arg[0])
};
p.examples_one =
{
"To set all bots to stop snaring when their health is below 10% HP:",
fmt::format(
"{} {} 10 spawned",
sep->arg[0],
Bot::GetSpellTypeShortNameByID(BotSpellTypes::Snare)
),
fmt::format(
"{} {} 10 spawned",
sep->arg[0],
BotSpellTypes::Snare
)
};
p.examples_two =
{
"To set BotA to stop casting fast heals at 30% HP:",
fmt::format(
"{} {} 30 byname BotA",
sep->arg[0],
Bot::GetSpellTypeShortNameByID(BotSpellTypes::FastHeals)
),
fmt::format(
"{} {} 30 byname BotA",
sep->arg[0],
BotSpellTypes::FastHeals
)
};
p.examples_three =
{
"To check the current Stun min HP percent on all bots:",
fmt::format(
"{} {} current spawned",
sep->arg[0],
Bot::GetSpellTypeShortNameByID(BotSpellTypes::Stun)
),
fmt::format(
"{} {} current spawned",
sep->arg[0],
BotSpellTypes::Stun
)
};
p.actionables = { "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" };
std::string popup_text = c->SendBotCommandHelpWindow(p);
popup_text = DialogueWindow::Table(popup_text);
c->SendPopupToClient(sep->arg[0], popup_text.c_str());
c->SendSpellTypePrompts();
if (RuleB(Bots, SendClassRaceOnHelp)) {
c->Message(
Chat::Yellow,
fmt::format(
"Use {} for information about race/class IDs.",
Saylink::Silent("^classracelist")
).c_str()
);
}
return;
}
std::string arg1 = sep->arg[1];
std::string arg2 = sep->arg[2];
int ab_arg = 2;
bool current_check = false;
uint16 spell_type = 0;
uint32 type_value = 0;
// String/Int type checks
if (sep->IsNumber(1)) {
spell_type = atoi(sep->arg[1]);
if (!EQ::ValueWithin(spell_type, BotSpellTypes::START, BotSpellTypes::END)) {
c->Message(Chat::Yellow, "You must choose a valid spell type. Spell types range from %i to %i", BotSpellTypes::START, BotSpellTypes::END);
return;
}
}
else {
if (Bot::GetSpellTypeIDByShortName(arg1) != UINT16_MAX) {
spell_type = Bot::GetSpellTypeIDByShortName(arg1);
}
else {
c->Message(
Chat::Yellow,
fmt::format(
"Incorrect argument, use {} for information regarding this command.",
Saylink::Silent(
fmt::format("{} help", sep->arg[0])
)
).c_str()
);
return;
}
}
if (sep->IsNumber(2)) {
type_value = atoi(sep->arg[2]);
++ab_arg;
if (type_value < 0 || type_value > 100) {
c->Message(Chat::Yellow, "You must enter a value between 0-100 (0%% to 100%% of mana).");
return;
}
}
else if (!arg2.compare("current")) {
++ab_arg;
current_check = true;
}
else {
c->Message(
Chat::Yellow,
fmt::format(
"Incorrect argument, use {} for information regarding this command.",
Saylink::Silent(
fmt::format("{} help", sep->arg[0])
)
).c_str()
);
return;
}
const int ab_mask = ActionableBots::ABM_Type1;
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::vector<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.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end());
Bot* first_found = nullptr;
int success_count = 0;
for (auto my_bot : sbl) {
if (my_bot->BotPassiveCheck()) {
continue;
}
if (!first_found) {
first_found = my_bot;
}
if (current_check) {
c->Message(
Chat::Green,
fmt::format(
"{} says, 'My [{}] minimum HP is currently [{}%%].'",
my_bot->GetCleanName(),
Bot::GetSpellTypeNameByID(spell_type),
my_bot->GetSpellTypeMinHPLimit(spell_type)
).c_str()
);
}
else {
my_bot->SetSpellTypeMinHPLimit(spell_type, type_value);
++success_count;
}
}
if (!current_check) {
if (success_count == 1 && first_found) {
c->Message(
Chat::Green,
fmt::format(
"{} says, 'My [{}] minimum HP was set to [{}%%].'",
first_found->GetCleanName(),
Bot::GetSpellTypeNameByID(spell_type),
first_found->GetSpellTypeMinHPLimit(spell_type)
).c_str()
);
}
else {
c->Message(
Chat::Green,
fmt::format(
"{} of your bots set their [{}] minimum HP to [{}%%].",
success_count,
Bot::GetSpellTypeNameByID(spell_type),
type_value
).c_str()
);
}
}
}
+211
View File
@@ -0,0 +1,211 @@
#include "../bot_command.h"
void bot_command_spell_min_mana_pct(Client* c, const Seperator* sep)
{
if (helper_command_alias_fail(c, "bot_command_spell_min_mana_pct", sep->arg[0], "spellminmanapct")) {
c->Message(Chat::White, "note: Controls at what mana percentage a bot will stop casting different spell types.");
return;
}
if (helper_is_help_or_usage(sep->arg[1])) {
BotCommandHelpParams p;
p.description = { "Controls at what mana percentage a bot will stop casting different spell types." };
p.example_format =
{
fmt::format("{} [Type Shortname] [value] [actionable]", sep->arg[0]),
fmt::format("{} [Type ID] [value] [actionable]", sep->arg[0])
};
p.examples_one =
{
"To set all bots to stop snaring when their mana is below 10% HP:",
fmt::format(
"{} {} 10 spawned",
sep->arg[0],
Bot::GetSpellTypeShortNameByID(BotSpellTypes::Snare)
),
fmt::format(
"{} {} 10 spawned",
sep->arg[0],
BotSpellTypes::Snare
)
};
p.examples_two =
{
"To set BotA to stop casting fast heals at 30% mana:",
fmt::format(
"{} {} 30 byname BotA",
sep->arg[0],
Bot::GetSpellTypeShortNameByID(BotSpellTypes::FastHeals)
),
fmt::format(
"{} {} 30 byname BotA",
sep->arg[0],
BotSpellTypes::FastHeals
)
};
p.examples_three =
{
"To check the current Stun min mana percent on all bots:",
fmt::format(
"{} {} current spawned",
sep->arg[0],
Bot::GetSpellTypeShortNameByID(BotSpellTypes::Stun)
),
fmt::format(
"{} {} current spawned",
sep->arg[0],
BotSpellTypes::Stun
)
};
p.actionables = { "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" };
std::string popup_text = c->SendBotCommandHelpWindow(p);
popup_text = DialogueWindow::Table(popup_text);
c->SendPopupToClient(sep->arg[0], popup_text.c_str());
c->SendSpellTypePrompts();
if (RuleB(Bots, SendClassRaceOnHelp)) {
c->Message(
Chat::Yellow,
fmt::format(
"Use {} for information about race/class IDs.",
Saylink::Silent("^classracelist")
).c_str()
);
}
return;
}
std::string arg1 = sep->arg[1];
std::string arg2 = sep->arg[2];
int ab_arg = 2;
bool current_check = false;
uint16 spell_type = 0;
uint32 type_value = 0;
// String/Int type checks
if (sep->IsNumber(1)) {
spell_type = atoi(sep->arg[1]);
if (!EQ::ValueWithin(spell_type, BotSpellTypes::START, BotSpellTypes::END)) {
c->Message(Chat::Yellow, "You must choose a valid spell type. Spell types range from %i to %i", BotSpellTypes::START, BotSpellTypes::END);
return;
}
}
else {
if (Bot::GetSpellTypeIDByShortName(arg1) != UINT16_MAX) {
spell_type = Bot::GetSpellTypeIDByShortName(arg1);
}
else {
c->Message(
Chat::Yellow,
fmt::format(
"Incorrect argument, use {} for information regarding this command.",
Saylink::Silent(
fmt::format("{} help", sep->arg[0])
)
).c_str()
);
return;
}
}
if (sep->IsNumber(2)) {
type_value = atoi(sep->arg[2]);
++ab_arg;
if (type_value < 0 || type_value > 100) {
c->Message(Chat::Yellow, "You must enter a value between 0-100 (0%% to 100%% of mana).");
return;
}
}
else if (!arg2.compare("current")) {
++ab_arg;
current_check = true;
}
else {
c->Message(
Chat::Yellow,
fmt::format(
"Incorrect argument, use {} for information regarding this command.",
Saylink::Silent(
fmt::format("{} help", sep->arg[0])
)
).c_str()
);
return;
}
const int ab_mask = ActionableBots::ABM_Type1;
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::vector<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.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end());
Bot* first_found = nullptr;
int success_count = 0;
for (auto my_bot : sbl) {
if (my_bot->BotPassiveCheck()) {
continue;
}
if (!first_found) {
first_found = my_bot;
}
if (current_check) {
c->Message(
Chat::Green,
fmt::format(
"{} says, 'My [{}] minimum mana is currently [{}%%].'",
my_bot->GetCleanName(),
Bot::GetSpellTypeNameByID(spell_type),
my_bot->GetSpellTypeMinManaLimit(spell_type)
).c_str()
);
}
else {
my_bot->SetSpellTypeMinManaLimit(spell_type, type_value);
++success_count;
}
}
if (!current_check) {
if (success_count == 1 && first_found) {
c->Message(
Chat::Green,
fmt::format(
"{} says, 'My [{}] minimum mana was set to [{}%%].'",
first_found->GetCleanName(),
Bot::GetSpellTypeNameByID(spell_type),
first_found->GetSpellTypeMinManaLimit(spell_type)
).c_str()
);
}
else {
c->Message(
Chat::Green,
fmt::format(
"{} of your bots set their [{}] minimum mana to [{}%%].",
success_count,
Bot::GetSpellTypeNameByID(spell_type),
type_value
).c_str()
);
}
}
}
+280
View File
@@ -0,0 +1,280 @@
#include "../bot_command.h"
void bot_command_spell_min_thresholds(Client* c, const Seperator* sep) {
if (helper_command_alias_fail(c, "bot_command_spell_min_thresholds", sep->arg[0], "spellminthresholds")) {
c->Message(Chat::White, "note: Controls at what target HP % the bot will stop casting different spell types.");
return;
}
if (helper_is_help_or_usage(sep->arg[1])) {
BotCommandHelpParams p;
p.description = { "Controls at what target HP % the bot will stop casting different spell types." };
p.notes =
{
"- Targeting yourself for this command will allow you to control your own settings for how bots cast on you",
"- All pet types are based off the pet's owner's setting",
"- Any remaining types use the owner's setting when a pet is the target",
"- All Heals, Cures, Buffs (DS and resists included) are based off the target's setting, not the caster",
"- e.g., BotA is healing BotB using BotB's settings",
};
p.example_format =
{
fmt::format("{} [Type Shortname] [value] [actionable, default: target]", sep->arg[0]),
fmt::format("{} [Type ID] [value] [actionable, default: target]", sep->arg[0])
};
p.examples_one =
{
"To set all bots to stop debuffing at 10%:",
fmt::format(
"{} {} 10 spawned",
sep->arg[0],
Bot::GetSpellTypeShortNameByID(BotSpellTypes::Debuff)
),
fmt::format(
"{} {} 10 spawned",
sep->arg[0],
BotSpellTypes::Debuff
)
};
p.examples_two =
{
"To set all Druids to stop casting DoTs at 15%:",
fmt::format(
"{} {} 15 byclass {}",
sep->arg[0],
Bot::GetSpellTypeShortNameByID(BotSpellTypes::DOT),
Class::Druid
),
fmt::format(
"{} {} 15 byclass {}",
sep->arg[0],
BotSpellTypes::DOT,
Class::Druid
)
};
p.examples_three =
{
"To check the current Fast Heal min threshold on all bots:",
fmt::format(
"{} {} current spawned",
sep->arg[0],
Bot::GetSpellTypeShortNameByID(BotSpellTypes::FastHeals)
),
fmt::format(
"{} {} current spawned",
sep->arg[0],
BotSpellTypes::FastHeals
)
};
p.actionables = { "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" };
std::string popup_text = c->SendBotCommandHelpWindow(p);
popup_text = DialogueWindow::Table(popup_text);
c->SendPopupToClient(sep->arg[0], popup_text.c_str());
c->SendSpellTypePrompts();
c->SendSpellTypePrompts(false, true);
if (RuleB(Bots, SendClassRaceOnHelp)) {
c->Message(
Chat::Yellow,
fmt::format(
"Use {} for information about race/class IDs.",
Saylink::Silent("^classracelist")
).c_str()
);
}
return;
}
std::string arg1 = sep->arg[1];
std::string arg2 = sep->arg[2];
int ab_arg = 2;
bool current_check = false;
uint16 spell_type = 0;
uint32 type_value = 0;
Mob* target = c->GetTarget();
bool clientSetting = (target && target == c);
if (sep->IsNumber(1)) {
spell_type = atoi(sep->arg[1]);
if (
(clientSetting && !IsClientBotSpellType(spell_type)) ||
(!clientSetting && !EQ::ValueWithin(spell_type, BotSpellTypes::START, BotSpellTypes::END))
) {
c->Message(
Chat::Yellow,
fmt::format(
"{}. Use {} for information regarding this command.",
(!clientSetting ? "You must choose a valid spell type" : "You must choose a valid client spell type"),
Saylink::Silent(
fmt::format("{} help", sep->arg[0])
)
).c_str()
);
if (clientSetting) {
c->SendSpellTypePrompts(false, true);
}
return;
}
}
else {
if (Bot::GetSpellTypeIDByShortName(arg1) != UINT16_MAX) {
spell_type = Bot::GetSpellTypeIDByShortName(arg1);
if (clientSetting && !IsClientBotSpellType(spell_type)) {
c->Message(Chat::Yellow, "Invalid spell type for clients.");
c->SendSpellTypePrompts(false, true);
}
}
else {
c->Message(
Chat::Yellow,
fmt::format(
"Incorrect argument, use {} for information regarding this command.",
Saylink::Silent(
fmt::format("{} help", sep->arg[0])
)
).c_str()
);
return;
}
}
if (sep->IsNumber(2)) {
type_value = atoi(sep->arg[2]);
++ab_arg;
if (type_value < 0 || type_value > 100) {
c->Message(Chat::Yellow, "You must enter a value between 0-100 (0%% to 100%% of health).");
return;
}
}
else if (!arg2.compare("current")) {
++ab_arg;
current_check = true;
}
else {
c->Message(
Chat::Yellow,
fmt::format(
"Incorrect argument, use {} for information regarding this command.",
Saylink::Silent(
fmt::format("{} help", sep->arg[0])
)
).c_str()
);
return;
}
const int ab_mask = ActionableBots::ABM_Type1;
std::string actionable_arg = sep->arg[ab_arg];
if (actionable_arg.empty()) {
actionable_arg = "target";
}
if (!clientSetting) {
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::vector<Bot*> sbl;
if (ActionableBots::PopulateSBL(c, actionable_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.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end());
Bot* first_found = nullptr;
int success_count = 0;
for (auto my_bot : sbl) {
if (my_bot->BotPassiveCheck()) {
continue;
}
if (!first_found) {
first_found = my_bot;
}
if (current_check) {
c->Message(
Chat::Green,
fmt::format(
"{} says, 'My [{}] minimum threshold is currently [{}%%].'",
my_bot->GetCleanName(),
Bot::GetSpellTypeNameByID(spell_type),
my_bot->GetSpellTypeMinThreshold(spell_type)
).c_str()
);
}
else {
my_bot->SetSpellTypeMinThreshold(spell_type, type_value);
++success_count;
}
}
if (!current_check) {
if (success_count == 1 && first_found) {
c->Message(
Chat::Green,
fmt::format(
"{} says, 'My [{}] minimum threshold was set to [{}%%].'",
first_found->GetCleanName(),
Bot::GetSpellTypeNameByID(spell_type),
first_found->GetSpellTypeMinThreshold(spell_type)
).c_str()
);
}
else {
c->Message(
Chat::Green,
fmt::format(
"{} of your bots set their [{}] minimum threshold to [{}%%].",
success_count,
Bot::GetSpellTypeNameByID(spell_type),
type_value
).c_str()
);
}
}
}
else {
if (current_check) {
c->Message(
Chat::Green,
fmt::format(
"Your [{}] minimum threshold is currently [{}%%].",
Bot::GetSpellTypeNameByID(spell_type),
c->GetSpellTypeMinThreshold(spell_type)
).c_str()
);
}
else {
c->SetSpellTypeMinThreshold(spell_type, type_value);
c->Message(
Chat::Green,
fmt::format(
"Your [{}] minimum threshold was set to [{}%%].",
Bot::GetSpellTypeNameByID(spell_type),
c->GetSpellTypeMinThreshold(spell_type)
).c_str()
);
}
}
}
+248
View File
@@ -0,0 +1,248 @@
#include "../bot_command.h"
void bot_command_spell_pursue_priority(Client* c, const Seperator* sep)
{
if (helper_command_alias_fail(c, "bot_command_spell_pursue_priority", sep->arg[0], "spellpursuepriority")) {
c->Message(Chat::White, "note: Sets the order of spell casts when the mob is fleeing in combat by spell type.");
return;
}
if (helper_is_help_or_usage(sep->arg[1])) {
BotCommandHelpParams p;
p.description = { "Sets the order of spell casts when the mob is fleeing in combat by spell type." };
p.notes =
{
"- Setting a spell type to 0 will prevent that type from being cast.",
"- If 2 or more are set to the same priority they will sort by spell type ID."
};
p.example_format =
{
fmt::format("{} [Type Shortname] [value] [actionable]", sep->arg[0]),
fmt::format("{} [Type ID] [value] [actionable]", sep->arg[0])
};
p.examples_one =
{
"To set all bots to cast nukes first:",
fmt::format(
"{} {} 1 spawned",
sep->arg[0],
Bot::GetSpellTypeShortNameByID(BotSpellTypes::Nuke)
),
fmt::format(
"{} {} 1 spawned",
sep->arg[0],
BotSpellTypes::FastHeals
)
};
p.examples_two =
{
"To set all Shaman to not cast cures:",
fmt::format(
"{} {} 0 byclass {}",
sep->arg[0],
Bot::GetSpellTypeShortNameByID(BotSpellTypes::Cure),
Class::Shaman
),
fmt::format(
"{} {} 0 byclass {}",
sep->arg[0],
BotSpellTypes::Cure,
Class::Shaman
)
};
p.examples_three =
{
"To check the current pursue priority of buffs on all bots:",
fmt::format(
"{} {} current spawned",
sep->arg[0],
Bot::GetSpellTypeShortNameByID(BotSpellTypes::Buff)
),
fmt::format(
"{} {} current spawned",
sep->arg[0],
BotSpellTypes::Buff
)
};
p.actionables = { "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" };
std::string popup_text = c->SendBotCommandHelpWindow(p);
popup_text = DialogueWindow::Table(popup_text);
c->SendPopupToClient(sep->arg[0], popup_text.c_str());
c->SendSpellTypePrompts();
if (RuleB(Bots, SendClassRaceOnHelp)) {
c->Message(
Chat::Yellow,
fmt::format(
"Use {} for information about race/class IDs.",
Saylink::Silent("^classracelist")
).c_str()
);
}
return;
}
std::string arg1 = sep->arg[1];
std::string arg2 = sep->arg[2];
int ab_arg = 2;
bool current_check = false;
bool list_check = false;
uint16 spell_type = 0;
uint32 type_value = 0;
// String/Int type checks
if (sep->IsNumber(1)) {
spell_type = atoi(sep->arg[1]);
if (!EQ::ValueWithin(spell_type, BotSpellTypes::START, BotSpellTypes::END)) {
c->Message(Chat::Yellow, "You must choose a valid spell type. Spell types range from %i to %i", BotSpellTypes::START, BotSpellTypes::END);
return;
}
}
else {
if (Bot::GetSpellTypeIDByShortName(arg1) != UINT16_MAX) {
spell_type = Bot::GetSpellTypeIDByShortName(arg1);
}
else if (!arg1.compare("list")) {
++ab_arg;
list_check = true;
}
else {
c->Message(
Chat::Yellow,
fmt::format(
"Incorrect argument, use {} for information regarding this command.",
Saylink::Silent(
fmt::format("{} help", sep->arg[0])
)
).c_str()
);
return;
}
}
if (sep->IsNumber(2)) {
type_value = atoi(sep->arg[2]);
++ab_arg;
if (EQ::ValueWithin(type_value, BotSpellTypes::START, BotSpellTypes::END)) {
c->Message(Chat::Yellow, "You must enter a value between {} and {}.", BotSpellTypes::START, BotSpellTypes::END);
return;
}
}
else if (!arg2.compare("current")) {
++ab_arg;
current_check = true;
}
else if (!list_check) {
c->Message(
Chat::Yellow,
fmt::format(
"Incorrect argument, use {} for information regarding this command.",
Saylink::Silent(
fmt::format("{} help", sep->arg[0])
)
).c_str()
);
return;
}
const int ab_mask = ActionableBots::ABM_Type1;
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::vector<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.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end());
Bot* first_found = nullptr;
int success_count = 0;
for (auto my_bot : sbl) {
if (my_bot->BotPassiveCheck()) {
continue;
}
if (!first_found) {
first_found = my_bot;
}
if (current_check) {
c->Message(
Chat::Green,
fmt::format(
"{} says, 'My [{}] pursue cast priority is currently [{}].'",
my_bot->GetCleanName(),
Bot::GetSpellTypeNameByID(spell_type),
my_bot->GetSpellTypePriority(spell_type, BotPriorityCategories::Pursue)
).c_str()
);
}
else if (list_check) {
auto cast_order = my_bot->GetSpellTypesPrioritized(BotPriorityCategories::Pursue);
for (auto& current_cast : cast_order) {
c->Message(
Chat::Green,
fmt::format(
"{} says, 'My [{}] pursue cast priority for is currently [{}].'",
my_bot->GetCleanName(),
Bot::GetSpellTypeNameByID(current_cast.spellType),
(current_cast.priority == 0 ? "disabled (0)" : std::to_string(current_cast.priority))
).c_str()
);
}
c->Message(
Chat::Green,
fmt::format(
"{} says, 'Anything not listed is currently disabled (0).'",
my_bot->GetCleanName()
).c_str()
);
return;
}
else {
my_bot->SetSpellTypePriority(spell_type, BotPriorityCategories::Pursue, type_value);
++success_count;
}
}
if (!current_check) {
if (success_count == 1 && first_found) {
c->Message(
Chat::Green,
fmt::format(
"{} says, 'My [{}] pursue cast priority was set to [{}].'",
first_found->GetCleanName(),
Bot::GetSpellTypeNameByID(spell_type),
first_found->GetSpellTypePriority(spell_type, BotPriorityCategories::Pursue)
).c_str()
);
}
else {
c->Message(
Chat::Green,
fmt::format(
"{} of your bots set their [{}] pursue cast priority to [{}].",
success_count,
Bot::GetSpellTypeNameByID(spell_type),
type_value
).c_str()
);
}
}
}
+214
View File
@@ -0,0 +1,214 @@
#include "../bot_command.h"
void bot_command_spell_resist_limits(Client* c, const Seperator* sep) {
if (helper_command_alias_fail(c, "bot_command_spell_resist_limits", sep->arg[0], "spellresistlimits")) {
c->Message(Chat::White, "note: Sets the limit of a target's resists to where a bot won't attempt to cast due to resist chances.");
return;
}
if (helper_is_help_or_usage(sep->arg[1])) {
BotCommandHelpParams p;
p.description = { "Sets the limit of a target's resists to where a bot won't attempt to cast due to resist chances." };
p.example_format =
{
fmt::format("{} [Type Shortname] [value] [actionable]", sep->arg[0]),
fmt::format("{} [Type ID] [value] [actionable]", sep->arg[0])
};
p.examples_one =
{
"To set all bots' slow resist limit to 250:",
fmt::format(
"{} {} 250 spawned",
sep->arg[0],
Bot::GetSpellTypeShortNameByID(BotSpellTypes::Slow)
),
fmt::format(
"{} {} 250 spawned",
sep->arg[0],
BotSpellTypes::Slow
)
};
p.examples_two =
{
"To set Magicians to limit their resist to 175 for nukes:",
fmt::format(
"{} {} 175 byclass {}",
sep->arg[0],
Bot::GetSpellTypeShortNameByID(BotSpellTypes::Nuke),
Class::Magician
),
fmt::format(
"{} {} 175 byclass {}",
sep->arg[0],
BotSpellTypes::Nuke,
Class::Magician
)
};
p.examples_three =
{
"To check the current debuff resist limit on all bots:",
fmt::format(
"{} {} current spawned",
sep->arg[0],
Bot::GetSpellTypeShortNameByID(BotSpellTypes::Debuff)
),
fmt::format(
"{} {} current spawned",
sep->arg[0],
BotSpellTypes::Debuff
)
};
p.actionables = { "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" };
std::string popup_text = c->SendBotCommandHelpWindow(p);
popup_text = DialogueWindow::Table(popup_text);
c->SendPopupToClient(sep->arg[0], popup_text.c_str());
c->SendSpellTypePrompts();
if (RuleB(Bots, SendClassRaceOnHelp)) {
c->Message(
Chat::Yellow,
fmt::format(
"Use {} for information about race/class IDs.",
Saylink::Silent("^classracelist")
).c_str()
);
}
return;
}
std::string arg1 = sep->arg[1];
std::string arg2 = sep->arg[2];
int ab_arg = 2;
bool current_check = false;
uint16 spell_type = 0;
uint32 type_value = 0;
// String/Int type checks
if (sep->IsNumber(1)) {
spell_type = atoi(sep->arg[1]);
if (!EQ::ValueWithin(spell_type, BotSpellTypes::START, BotSpellTypes::END)) {
c->Message(Chat::Yellow, "You must choose a valid spell type. Spell types range from %i to %i", BotSpellTypes::START, BotSpellTypes::END);
return;
}
}
else {
if (Bot::GetSpellTypeIDByShortName(arg1) != UINT16_MAX) {
spell_type = Bot::GetSpellTypeIDByShortName(arg1);
}
else {
c->Message(
Chat::Yellow,
fmt::format(
"Incorrect argument, use {} for information regarding this command.",
Saylink::Silent(
fmt::format("{} help", sep->arg[0])
)
).c_str()
);
return;
}
}
if (sep->IsNumber(2)) {
type_value = atoi(sep->arg[2]);
++ab_arg;
if (type_value < 0 || type_value > 1000) {
c->Message(Chat::Yellow, "You must enter a value between 1-1000.");
return;
}
}
else if (!arg2.compare("current")) {
++ab_arg;
current_check = true;
}
else {
c->Message(
Chat::Yellow,
fmt::format(
"Incorrect argument, use {} for information regarding this command.",
Saylink::Silent(
fmt::format("{} help", sep->arg[0])
)
).c_str()
);
return;
}
const int ab_mask = ActionableBots::ABM_Type1;
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::vector<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.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end());
Bot* first_found = nullptr;
int success_count = 0;
for (auto my_bot : sbl) {
if (my_bot->BotPassiveCheck()) {
continue;
}
if (!first_found) {
first_found = my_bot;
}
if (current_check) {
c->Message(
Chat::Green,
fmt::format(
"{} says, 'My [{}] resist limit is currently [{}].'",
my_bot->GetCleanName(),
Bot::GetSpellTypeNameByID(spell_type),
my_bot->GetSpellTypeResistLimit(spell_type)
).c_str()
);
}
else {
my_bot->SetSpellTypeResistLimit(spell_type, type_value);
++success_count;
}
}
if (!current_check) {
if (success_count == 1 && first_found) {
c->Message(
Chat::Green,
fmt::format(
"{} says, 'My [{}] resist limit was set to [{}].'",
first_found->GetCleanName(),
Bot::GetSpellTypeNameByID(spell_type),
first_found->GetSpellTypeResistLimit(spell_type)
).c_str()
);
}
else {
c->Message(
Chat::Green,
fmt::format(
"{} of your bots set their [{}] resist limit to [{}].",
success_count,
Bot::GetSpellTypeNameByID(spell_type),
type_value
).c_str()
);
}
}
}
+214
View File
@@ -0,0 +1,214 @@
#include "../bot_command.h"
void bot_command_spell_target_count(Client* c, const Seperator* sep)
{
if (helper_command_alias_fail(c, "bot_command_spell_target_count", sep->arg[0], "spelltargetcount")) {
c->Message(Chat::White, "note: Decides how many eligible targets are required for an AE or group spell to cast by spell type.");
return;
}
if (helper_is_help_or_usage(sep->arg[1])) {
BotCommandHelpParams p;
p.description = { "Decides how many eligible targets are required for an AE or group spell to cast by spell type." };
p.example_format =
{
fmt::format("{} [Type Shortname] [value] [actionable]", sep->arg[0]),
fmt::format("{} [Type ID] [value] [actionable]", sep->arg[0])
};
p.examples_one =
{
"To set all bots to AEMez with 5 or more targets:",
fmt::format(
"{} {} 5 spawned",
sep->arg[0],
Bot::GetSpellTypeShortNameByID(BotSpellTypes::AEMez)
),
fmt::format(
"{} {} 5 spawned",
sep->arg[0],
BotSpellTypes::AEMez
)
};
p.examples_two =
{
"To set Wizards to require 5 targets for AENukes:",
fmt::format(
"{} {} byclass {}",
sep->arg[0],
Bot::GetSpellTypeShortNameByID(BotSpellTypes::AENukes),
Class::Wizard
),
fmt::format(
"{} {} byclass {}",
sep->arg[0],
BotSpellTypes::AENukes,
Class::Wizard
)
};
p.examples_three =
{
"To check the current AESlow count on all bots:",
fmt::format(
"{} {} current spawned",
sep->arg[0],
Bot::GetSpellTypeShortNameByID(BotSpellTypes::AESlow)
),
fmt::format(
"{} {} current spawned",
sep->arg[0],
BotSpellTypes::AESlow
)
};
p.actionables = { "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" };
std::string popup_text = c->SendBotCommandHelpWindow(p);
popup_text = DialogueWindow::Table(popup_text);
c->SendPopupToClient(sep->arg[0], popup_text.c_str());
c->SendSpellTypePrompts();
if (RuleB(Bots, SendClassRaceOnHelp)) {
c->Message(
Chat::Yellow,
fmt::format(
"Use {} for information about race/class IDs.",
Saylink::Silent("^classracelist")
).c_str()
);
}
return;
}
std::string arg1 = sep->arg[1];
std::string arg2 = sep->arg[2];
int ab_arg = 2;
bool current_check = false;
uint16 spell_type = 0;
uint32 type_value = 0;
// String/Int type checks
if (sep->IsNumber(1)) {
spell_type = atoi(sep->arg[1]);
if (!EQ::ValueWithin(spell_type, BotSpellTypes::START, BotSpellTypes::END)) {
c->Message(Chat::Yellow, "You must choose a valid spell type. Spell types range from %i to %i", BotSpellTypes::START, BotSpellTypes::END);
return;
}
}
else {
if (Bot::GetSpellTypeIDByShortName(arg1) != UINT16_MAX) {
spell_type = Bot::GetSpellTypeIDByShortName(arg1);
}
else {
c->Message(
Chat::Yellow,
fmt::format(
"Incorrect argument, use {} for information regarding this command.",
Saylink::Silent(
fmt::format("{} help", sep->arg[0])
)
).c_str()
);
return;
}
}
if (sep->IsNumber(2)) {
type_value = atoi(sep->arg[2]);
++ab_arg;
if (type_value < 1 || type_value > 100) {
c->Message(Chat::Yellow, "You must enter a value between 1-100.");
return;
}
}
else if (!arg2.compare("current")) {
++ab_arg;
current_check = true;
}
else {
c->Message(
Chat::Yellow,
fmt::format(
"Incorrect argument, use {} for information regarding this command.",
Saylink::Silent(
fmt::format("{} help", sep->arg[0])
)
).c_str()
);
return;
}
const int ab_mask = ActionableBots::ABM_Type1;
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::vector<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.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end());
Bot* first_found = nullptr;
int success_count = 0;
for (auto my_bot : sbl) {
if (my_bot->BotPassiveCheck()) {
continue;
}
if (!first_found) {
first_found = my_bot;
}
if (current_check) {
c->Message(
Chat::Green,
fmt::format(
"{} says, 'My [{}] target count is currently [{}].'",
my_bot->GetCleanName(),
Bot::GetSpellTypeNameByID(spell_type),
my_bot->GetSpellTypeAEOrGroupTargetCount(spell_type)
).c_str()
);
}
else {
my_bot->SetSpellTypeAEOrGroupTargetCount(spell_type, type_value);
++success_count;
}
}
if (!current_check) {
if (success_count == 1 && first_found) {
c->Message(
Chat::Green,
fmt::format(
"{} says, 'My [{}] target count was set to [{}].'",
first_found->GetCleanName(),
Bot::GetSpellTypeNameByID(spell_type),
first_found->GetSpellTypeAEOrGroupTargetCount(spell_type)
).c_str()
);
}
else {
c->Message(
Chat::Green,
fmt::format(
"{} of your bots set their [{}] target count to [{}].",
success_count,
Bot::GetSpellTypeNameByID(spell_type),
type_value
).c_str()
);
}
}
}
+9
View File
@@ -0,0 +1,9 @@
void bot_command_spelltype_ids(Client* c, const Seperator* sep)
{
SendSpellTypeWindow(c, sep);
}
void bot_command_spelltype_names(Client* c, const Seperator* sep)
{
SendSpellTypeWindow(c, sep);
}
-47
View File
@@ -1,47 +0,0 @@
#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);
}
+3 -3
View File
@@ -6,17 +6,17 @@ void bot_command_suspend(Client *c, const Seperator *sep)
return;
}
if (helper_is_help_or_usage(sep->arg[1])) {
c->Message(Chat::White, "usage: %s ([actionable: <any>] ([actionable_name]))", sep->arg[0]);
c->Message(Chat::White, "usage: %s ([actionable: target | byname | ownergroup | ownerraid | targetgroup | namesgroup | healrotationmembers | healrotationtargets | mmr | byclass | byrace | spawned] ([actionable_name]))", sep->arg[0]);
return;
}
const int ab_mask = ActionableBots::ABM_NoFilter;
std::list<Bot*> sbl;
std::vector<Bot*> sbl;
if (ActionableBots::PopulateSBL(c, sep->arg[1], sbl, ab_mask, sep->arg[2]) == ActionableBots::ABT_None) {
return;
}
sbl.remove(nullptr);
sbl.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end());
for (auto bot_iter : sbl) {
bot_iter->SetPauseAI(true);
}
+135 -64
View File
@@ -1,118 +1,189 @@
#include "../bot_command.h"
void bot_command_taunt(Client *c, const Seperator *sep)
void bot_command_taunt(Client* c, const Seperator* sep)
{
if (helper_command_alias_fail(c, "bot_command_taunt", sep->arg[0], "taunt"))
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]);
if (helper_command_alias_fail(c, "bot_command_taunt", sep->arg[0], "taunt")) {
c->Message(Chat::White, "note: TAllows you to turn on/off the taunting state of your bots and/or their pets.");
return;
}
if (helper_is_help_or_usage(sep->arg[1])) {
BotCommandHelpParams p;
p.description = { "Allows you to turn on/off the taunting state of your bots and/or their pets." };
p.example_format = { fmt::format("{} [on / off / pet] [optional: pet] [actionable, default: target]", sep->arg[0]) };
p.examples_one =
{
"To turn off taunt on all bots:",
fmt::format(
"{} off spawned",
sep->arg[0]
)
};
p.examples_two =
{
"To turn on taunt on all bots' pets:",
fmt::format(
"{} on pet spawned",
sep->arg[0]
)
};
p.examples_three =
{
"To turn on taunt for all ShadowKnights:",
fmt::format(
"{} on byclass {}",
sep->arg[0],
Class::ShadowKnight
)
};
p.actionables = { "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" };
std::string popup_text = c->SendBotCommandHelpWindow(p);
popup_text = DialogueWindow::Table(popup_text);
c->SendPopupToClient(sep->arg[0], popup_text.c_str());
if (RuleB(Bots, SendClassRaceOnHelp)) {
c->Message(
Chat::Yellow,
fmt::format(
"Use {} for information about race/class IDs.",
Saylink::Silent("^classracelist")
).c_str()
);
}
return;
}
const int ab_mask = ActionableBots::ABM_Type1;
std::string arg1 = sep->arg[1];
std::string arg2 = sep->arg[2];
bool taunt_state = false;
bool toggle_taunt = true;
bool pet_taunt = false;
bool valid_option = false;
int ab_arg = 1;
if (!arg1.compare("on")) {
taunt_state = true;
toggle_taunt = false;
ab_arg = 2;
valid_option = true;
++ab_arg;
}
else if (!arg1.compare("off")) {
toggle_taunt = false;
ab_arg = 2;
valid_option = true;
++ab_arg;
}
if (!arg2.compare("pet")) {
pet_taunt = true;
valid_option = true;
++ab_arg;
}
if (!valid_option) {
c->Message(
Chat::Yellow,
fmt::format(
"Incorrect argument, use {} for information regarding this command.",
Saylink::Silent(
fmt::format("{} help", sep->arg[0])
)
).c_str()
);
return;
}
const int ab_mask = ActionableBots::ABM_Type1;
std::string actionable_arg = sep->arg[ab_arg];
if (actionable_arg.empty()) {
actionable_arg = "target";
}
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) {
std::vector<Bot*> sbl;
if (ActionableBots::PopulateSBL(c, actionable_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;
}
sbl.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end());
int bot_taunting_count = 0;
int pet_taunting_count = 0;
if (!pet_taunt) {
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::RaidGroupSay(
bot_iter,
fmt::format(
"I am {} taunting.",
bot_iter->IsTaunting() ? "now" : "no longer"
).c_str()
);
}
++taunting_count;
++bot_taunting_count;
}
}
for (auto bot_iter : sbl) {
if (!bot_iter->HasPet()) {
continue;
}
if (pet_taunt) {
for (auto bot_iter : sbl) {
if (!bot_iter->HasPet()) {
continue;
}
if (!bot_iter->GetPet()->GetSkill(EQ::skills::SkillTaunt)) {
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::RaidGroupSay(
bot_iter,
fmt::format(
"My Pet is {} taunting.",
bot_iter->GetPet()->CastToNPC()->IsTaunting() ? "now" : "no longer"
).c_str()
);
}
++taunting_count;
++pet_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()
);
}
if (bot_taunting_count || pet_taunting_count) {
c->Message(
Chat::Green,
fmt::format(
"{} of your {} are {} taunting.",
(bot_taunting_count ? bot_taunting_count : pet_taunting_count),
(bot_taunting_count ? "bots" : "bots' pets"),
taunt_state ? "now" : "no longer"
).c_str()
);
}
else {
c->Message(Chat::White, "None of your bots are capable of taunting");
c->Message(
Chat::Yellow,
fmt::format(
"None of your {} are capable of taunting.",
!pet_taunt ? "bots" : "bots' pets"
).c_str()
);
}
}
-119
View File
@@ -1,119 +0,0 @@
#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);
}
+9 -4
View File
@@ -2,10 +2,12 @@
void bot_command_timer(Client* c, const Seperator* sep)
{
if (helper_command_alias_fail(c, "bot_command_timer", sep->arg[0], "timer"))
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, "usage: %s [clear | has | set] [disc | item | spell] [timer ID | item ID | spell ID | all] [optional ms for set] ([actionable: target | byname | ownergroup | ownerraid | targetgroup | namesgroup | healrotationtargets | mmr | byclass | byrace | spawned] ([actionable_name])).", 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;
@@ -88,15 +90,18 @@ void bot_command_timer(Client* c, const Seperator* sep)
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;
std::vector<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);
sbl.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end());
for (auto my_bot : sbl) {
bool found = false;
+2 -2
View File
@@ -13,7 +13,7 @@ void bot_command_track(Client *c, const Seperator *sep)
std::string tracking_scope = sep->arg[1];
std::list<Bot*> sbl;
std::vector<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]);
@@ -67,6 +67,6 @@ void bot_command_track(Client *c, const Seperator *sep)
}
my_bot->InterruptSpell();
Bot::BotGroupSay(my_bot, tracking_msg.c_str());
Bot::RaidGroupSay(my_bot, tracking_msg.c_str());
entity_list.ShowSpawnWindow(c, (c->GetLevel() * base_distance), track_named);
}
-38
View File
@@ -1,38 +0,0 @@
#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);
}