mirror of
https://github.com/EQEmu/Server.git
synced 2025-12-11 12:41:30 +00:00
[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 0690783a9d1e99005d6bee0824597ea920e26df9 --------- Co-authored-by: Akkadius <akkadius1@gmail.com>
This commit is contained in:
parent
4fda3c045e
commit
f466964db8
@ -89,6 +89,7 @@ SET(common_sources
|
||||
skills.cpp
|
||||
skill_caps.cpp
|
||||
spdat.cpp
|
||||
spdat_bot.cpp
|
||||
strings.cpp
|
||||
struct_strategy.cpp
|
||||
textures.cpp
|
||||
|
||||
@ -131,6 +131,8 @@ static std::map<uint8, std::string> class_names = {
|
||||
#define ARMOR_TYPE_LAST ARMOR_TYPE_PLATE
|
||||
#define ARMOR_TYPE_COUNT 5
|
||||
|
||||
#define BOT_CLASS_BASE_ID_PREFIX 3000
|
||||
|
||||
|
||||
const char* GetClassIDName(uint8 class_id, uint8 level = 0);
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -406,6 +406,7 @@ namespace DatabaseSchema {
|
||||
static std::vector<std::string> GetBotTables()
|
||||
{
|
||||
return {
|
||||
"bot_blocked_buffs",
|
||||
"bot_buffs",
|
||||
"bot_command_settings",
|
||||
"bot_create_combinations",
|
||||
@ -419,6 +420,7 @@ namespace DatabaseSchema {
|
||||
"bot_pet_buffs",
|
||||
"bot_pet_inventories",
|
||||
"bot_pets",
|
||||
"bot_settings",
|
||||
"bot_spell_casting_chances",
|
||||
"bot_spell_settings",
|
||||
"bot_spells_entries",
|
||||
|
||||
@ -145,6 +145,9 @@ namespace Logs {
|
||||
EvolveItem,
|
||||
PositionUpdate,
|
||||
KSM,
|
||||
BotSettings,
|
||||
BotSpellChecks,
|
||||
BotSpellTypeChecks,
|
||||
MaxCategoryID /* Don't Remove this */
|
||||
};
|
||||
|
||||
@ -248,7 +251,10 @@ namespace Logs {
|
||||
"XTargets",
|
||||
"EvolveItem",
|
||||
"PositionUpdate",
|
||||
"KSM" // Kernel Samepage Merging
|
||||
"KSM", // Kernel Samepage Merging
|
||||
"Bot Settings",
|
||||
"Bot Spell Checks",
|
||||
"Bot Spell Type Checks"
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -874,6 +874,36 @@
|
||||
OutF(LogSys, Logs::Detail, Logs::KSM, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
|
||||
} while (0)
|
||||
|
||||
#define LogBotSettings(message, ...) do {\
|
||||
if (LogSys.IsLogEnabled(Logs::General, Logs::BotSettings))\
|
||||
OutF(LogSys, Logs::General, Logs::BotSettings, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
|
||||
} while (0)
|
||||
|
||||
#define LogBotSettingsDetail(message, ...) do {\
|
||||
if (LogSys.IsLogEnabled(Logs::Detail, Logs::BotSettings))\
|
||||
OutF(LogSys, Logs::Detail, Logs::BotSettings, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
|
||||
} while (0)
|
||||
|
||||
#define LogBotSpellChecks(message, ...) do {\
|
||||
if (LogSys.IsLogEnabled(Logs::General, Logs::BotSpellChecks))\
|
||||
OutF(LogSys, Logs::General, Logs::BotSpellChecks, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
|
||||
} while (0)
|
||||
|
||||
#define LogBotSpellChecksDetail(message, ...) do {\
|
||||
if (LogSys.IsLogEnabled(Logs::Detail, Logs::BotSpellChecks))\
|
||||
OutF(LogSys, Logs::Detail, Logs::BotSpellChecks, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
|
||||
} while (0)
|
||||
|
||||
#define LogBotSpellTypeChecks(message, ...) do {\
|
||||
if (LogSys.IsLogEnabled(Logs::General, Logs::BotSpellTypeChecks))\
|
||||
OutF(LogSys, Logs::General, Logs::BotSpellTypeChecks, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
|
||||
} while (0)
|
||||
|
||||
#define LogBotSpellTypeChecksDetail(message, ...) do {\
|
||||
if (LogSys.IsLogEnabled(Logs::Detail, Logs::BotSpellTypeChecks))\
|
||||
OutF(LogSys, Logs::Detail, Logs::BotSpellTypeChecks, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
|
||||
} while (0)
|
||||
|
||||
#define Log(debug_level, log_category, message, ...) do {\
|
||||
if (LogSys.IsLogEnabled(debug_level, log_category))\
|
||||
LogSys.Out(debug_level, log_category, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
|
||||
|
||||
416
common/repositories/base/base_bot_blocked_buffs_repository.h
Normal file
416
common/repositories/base/base_bot_blocked_buffs_repository.h
Normal file
@ -0,0 +1,416 @@
|
||||
/**
|
||||
* DO NOT MODIFY THIS FILE
|
||||
*
|
||||
* This repository was automatically generated and is NOT to be modified directly.
|
||||
* Any repository modifications are meant to be made to the repository extending the base.
|
||||
* Any modifications to base repositories are to be made by the generator only
|
||||
*
|
||||
* @generator ./utils/scripts/generators/repository-generator.pl
|
||||
* @docs https://docs.eqemu.io/developer/repositories
|
||||
*/
|
||||
|
||||
#ifndef EQEMU_BASE_BOT_BLOCKED_BUFFS_REPOSITORY_H
|
||||
#define EQEMU_BASE_BOT_BLOCKED_BUFFS_REPOSITORY_H
|
||||
|
||||
#include "../../database.h"
|
||||
#include "../../strings.h"
|
||||
#include <ctime>
|
||||
|
||||
class BaseBotBlockedBuffsRepository {
|
||||
public:
|
||||
struct BotBlockedBuffs {
|
||||
uint32_t bot_id;
|
||||
uint32_t spell_id;
|
||||
uint8_t blocked;
|
||||
uint8_t blocked_pet;
|
||||
};
|
||||
|
||||
static std::string PrimaryKey()
|
||||
{
|
||||
return std::string("bot_id");
|
||||
}
|
||||
|
||||
static std::vector<std::string> Columns()
|
||||
{
|
||||
return {
|
||||
"bot_id",
|
||||
"spell_id",
|
||||
"blocked",
|
||||
"blocked_pet",
|
||||
};
|
||||
}
|
||||
|
||||
static std::vector<std::string> SelectColumns()
|
||||
{
|
||||
return {
|
||||
"bot_id",
|
||||
"spell_id",
|
||||
"blocked",
|
||||
"blocked_pet",
|
||||
};
|
||||
}
|
||||
|
||||
static std::string ColumnsRaw()
|
||||
{
|
||||
return std::string(Strings::Implode(", ", Columns()));
|
||||
}
|
||||
|
||||
static std::string SelectColumnsRaw()
|
||||
{
|
||||
return std::string(Strings::Implode(", ", SelectColumns()));
|
||||
}
|
||||
|
||||
static std::string TableName()
|
||||
{
|
||||
return std::string("bot_blocked_buffs");
|
||||
}
|
||||
|
||||
static std::string BaseSelect()
|
||||
{
|
||||
return fmt::format(
|
||||
"SELECT {} FROM {}",
|
||||
SelectColumnsRaw(),
|
||||
TableName()
|
||||
);
|
||||
}
|
||||
|
||||
static std::string BaseInsert()
|
||||
{
|
||||
return fmt::format(
|
||||
"INSERT INTO {} ({}) ",
|
||||
TableName(),
|
||||
ColumnsRaw()
|
||||
);
|
||||
}
|
||||
|
||||
static BotBlockedBuffs NewEntity()
|
||||
{
|
||||
BotBlockedBuffs e{};
|
||||
|
||||
e.bot_id = 0;
|
||||
e.spell_id = 0;
|
||||
e.blocked = 0;
|
||||
e.blocked_pet = 0;
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
static BotBlockedBuffs GetBotBlockedBuffs(
|
||||
const std::vector<BotBlockedBuffs> &bot_blocked_buffss,
|
||||
int bot_blocked_buffs_id
|
||||
)
|
||||
{
|
||||
for (auto &bot_blocked_buffs : bot_blocked_buffss) {
|
||||
if (bot_blocked_buffs.bot_id == bot_blocked_buffs_id) {
|
||||
return bot_blocked_buffs;
|
||||
}
|
||||
}
|
||||
|
||||
return NewEntity();
|
||||
}
|
||||
|
||||
static BotBlockedBuffs FindOne(
|
||||
Database& db,
|
||||
int bot_blocked_buffs_id
|
||||
)
|
||||
{
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"{} WHERE {} = {} LIMIT 1",
|
||||
BaseSelect(),
|
||||
PrimaryKey(),
|
||||
bot_blocked_buffs_id
|
||||
)
|
||||
);
|
||||
|
||||
auto row = results.begin();
|
||||
if (results.RowCount() == 1) {
|
||||
BotBlockedBuffs e{};
|
||||
|
||||
e.bot_id = row[0] ? static_cast<uint32_t>(atoi(row[0])) : 0;
|
||||
e.spell_id = row[1] ? static_cast<uint32_t>(atoi(row[1])) : 0;
|
||||
e.blocked = row[2] ? static_cast<uint8_t>(atoi(row[2])) : 0;
|
||||
e.blocked_pet = row[3] ? static_cast<uint8_t>(atoi(row[3])) : 0;
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
return NewEntity();
|
||||
}
|
||||
|
||||
static int DeleteOne(
|
||||
Database& db,
|
||||
int bot_blocked_buffs_id
|
||||
)
|
||||
{
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"DELETE FROM {} WHERE {} = {}",
|
||||
TableName(),
|
||||
PrimaryKey(),
|
||||
bot_blocked_buffs_id
|
||||
)
|
||||
);
|
||||
|
||||
return (results.Success() ? results.RowsAffected() : 0);
|
||||
}
|
||||
|
||||
static int UpdateOne(
|
||||
Database& db,
|
||||
const BotBlockedBuffs &e
|
||||
)
|
||||
{
|
||||
std::vector<std::string> v;
|
||||
|
||||
auto columns = Columns();
|
||||
|
||||
v.push_back(columns[0] + " = " + std::to_string(e.bot_id));
|
||||
v.push_back(columns[1] + " = " + std::to_string(e.spell_id));
|
||||
v.push_back(columns[2] + " = " + std::to_string(e.blocked));
|
||||
v.push_back(columns[3] + " = " + std::to_string(e.blocked_pet));
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"UPDATE {} SET {} WHERE {} = {}",
|
||||
TableName(),
|
||||
Strings::Implode(", ", v),
|
||||
PrimaryKey(),
|
||||
e.bot_id
|
||||
)
|
||||
);
|
||||
|
||||
return (results.Success() ? results.RowsAffected() : 0);
|
||||
}
|
||||
|
||||
static BotBlockedBuffs InsertOne(
|
||||
Database& db,
|
||||
BotBlockedBuffs e
|
||||
)
|
||||
{
|
||||
std::vector<std::string> v;
|
||||
|
||||
v.push_back(std::to_string(e.bot_id));
|
||||
v.push_back(std::to_string(e.spell_id));
|
||||
v.push_back(std::to_string(e.blocked));
|
||||
v.push_back(std::to_string(e.blocked_pet));
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"{} VALUES ({})",
|
||||
BaseInsert(),
|
||||
Strings::Implode(",", v)
|
||||
)
|
||||
);
|
||||
|
||||
if (results.Success()) {
|
||||
e.bot_id = results.LastInsertedID();
|
||||
return e;
|
||||
}
|
||||
|
||||
e = NewEntity();
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
static int InsertMany(
|
||||
Database& db,
|
||||
const std::vector<BotBlockedBuffs> &entries
|
||||
)
|
||||
{
|
||||
std::vector<std::string> insert_chunks;
|
||||
|
||||
for (auto &e: entries) {
|
||||
std::vector<std::string> v;
|
||||
|
||||
v.push_back(std::to_string(e.bot_id));
|
||||
v.push_back(std::to_string(e.spell_id));
|
||||
v.push_back(std::to_string(e.blocked));
|
||||
v.push_back(std::to_string(e.blocked_pet));
|
||||
|
||||
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
||||
}
|
||||
|
||||
std::vector<std::string> v;
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"{} VALUES {}",
|
||||
BaseInsert(),
|
||||
Strings::Implode(",", insert_chunks)
|
||||
)
|
||||
);
|
||||
|
||||
return (results.Success() ? results.RowsAffected() : 0);
|
||||
}
|
||||
|
||||
static std::vector<BotBlockedBuffs> All(Database& db)
|
||||
{
|
||||
std::vector<BotBlockedBuffs> all_entries;
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"{}",
|
||||
BaseSelect()
|
||||
)
|
||||
);
|
||||
|
||||
all_entries.reserve(results.RowCount());
|
||||
|
||||
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||
BotBlockedBuffs e{};
|
||||
|
||||
e.bot_id = row[0] ? static_cast<uint32_t>(atoi(row[0])) : 0;
|
||||
e.spell_id = row[1] ? static_cast<uint32_t>(atoi(row[1])) : 0;
|
||||
e.blocked = row[2] ? static_cast<uint8_t>(atoi(row[2])) : 0;
|
||||
e.blocked_pet = row[3] ? static_cast<uint8_t>(atoi(row[3])) : 0;
|
||||
|
||||
all_entries.push_back(e);
|
||||
}
|
||||
|
||||
return all_entries;
|
||||
}
|
||||
|
||||
static std::vector<BotBlockedBuffs> GetWhere(Database& db, const std::string &where_filter)
|
||||
{
|
||||
std::vector<BotBlockedBuffs> all_entries;
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"{} WHERE {}",
|
||||
BaseSelect(),
|
||||
where_filter
|
||||
)
|
||||
);
|
||||
|
||||
all_entries.reserve(results.RowCount());
|
||||
|
||||
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||
BotBlockedBuffs e{};
|
||||
|
||||
e.bot_id = row[0] ? static_cast<uint32_t>(atoi(row[0])) : 0;
|
||||
e.spell_id = row[1] ? static_cast<uint32_t>(atoi(row[1])) : 0;
|
||||
e.blocked = row[2] ? static_cast<uint8_t>(atoi(row[2])) : 0;
|
||||
e.blocked_pet = row[3] ? static_cast<uint8_t>(atoi(row[3])) : 0;
|
||||
|
||||
all_entries.push_back(e);
|
||||
}
|
||||
|
||||
return all_entries;
|
||||
}
|
||||
|
||||
static int DeleteWhere(Database& db, const std::string &where_filter)
|
||||
{
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"DELETE FROM {} WHERE {}",
|
||||
TableName(),
|
||||
where_filter
|
||||
)
|
||||
);
|
||||
|
||||
return (results.Success() ? results.RowsAffected() : 0);
|
||||
}
|
||||
|
||||
static int Truncate(Database& db)
|
||||
{
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"TRUNCATE TABLE {}",
|
||||
TableName()
|
||||
)
|
||||
);
|
||||
|
||||
return (results.Success() ? results.RowsAffected() : 0);
|
||||
}
|
||||
|
||||
static int64 GetMaxId(Database& db)
|
||||
{
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"SELECT COALESCE(MAX({}), 0) FROM {}",
|
||||
PrimaryKey(),
|
||||
TableName()
|
||||
)
|
||||
);
|
||||
|
||||
return (results.Success() && results.begin()[0] ? strtoll(results.begin()[0], nullptr, 10) : 0);
|
||||
}
|
||||
|
||||
static int64 Count(Database& db, const std::string &where_filter = "")
|
||||
{
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"SELECT COUNT(*) FROM {} {}",
|
||||
TableName(),
|
||||
(where_filter.empty() ? "" : "WHERE " + where_filter)
|
||||
)
|
||||
);
|
||||
|
||||
return (results.Success() && results.begin()[0] ? strtoll(results.begin()[0], nullptr, 10) : 0);
|
||||
}
|
||||
|
||||
static std::string BaseReplace()
|
||||
{
|
||||
return fmt::format(
|
||||
"REPLACE INTO {} ({}) ",
|
||||
TableName(),
|
||||
ColumnsRaw()
|
||||
);
|
||||
}
|
||||
|
||||
static int ReplaceOne(
|
||||
Database& db,
|
||||
const BotBlockedBuffs &e
|
||||
)
|
||||
{
|
||||
std::vector<std::string> v;
|
||||
|
||||
v.push_back(std::to_string(e.bot_id));
|
||||
v.push_back(std::to_string(e.spell_id));
|
||||
v.push_back(std::to_string(e.blocked));
|
||||
v.push_back(std::to_string(e.blocked_pet));
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"{} VALUES ({})",
|
||||
BaseReplace(),
|
||||
Strings::Implode(",", v)
|
||||
)
|
||||
);
|
||||
|
||||
return (results.Success() ? results.RowsAffected() : 0);
|
||||
}
|
||||
|
||||
static int ReplaceMany(
|
||||
Database& db,
|
||||
const std::vector<BotBlockedBuffs> &entries
|
||||
)
|
||||
{
|
||||
std::vector<std::string> insert_chunks;
|
||||
|
||||
for (auto &e: entries) {
|
||||
std::vector<std::string> v;
|
||||
|
||||
v.push_back(std::to_string(e.bot_id));
|
||||
v.push_back(std::to_string(e.spell_id));
|
||||
v.push_back(std::to_string(e.blocked));
|
||||
v.push_back(std::to_string(e.blocked_pet));
|
||||
|
||||
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
||||
}
|
||||
|
||||
std::vector<std::string> v;
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"{} VALUES {}",
|
||||
BaseReplace(),
|
||||
Strings::Implode(",", insert_chunks)
|
||||
)
|
||||
);
|
||||
|
||||
return (results.Success() ? results.RowsAffected() : 0);
|
||||
}
|
||||
};
|
||||
|
||||
#endif //EQEMU_BASE_BOT_BLOCKED_BUFFS_REPOSITORY_H
|
||||
@ -64,13 +64,6 @@ public:
|
||||
int16_t poison;
|
||||
int16_t disease;
|
||||
int16_t corruption;
|
||||
uint32_t show_helm;
|
||||
uint32_t follow_distance;
|
||||
uint8_t stop_melee_level;
|
||||
int32_t expansion_bitmask;
|
||||
uint8_t enforce_spell_settings;
|
||||
uint8_t archery_setting;
|
||||
uint32_t caster_range;
|
||||
};
|
||||
|
||||
static std::string PrimaryKey()
|
||||
@ -126,13 +119,6 @@ public:
|
||||
"poison",
|
||||
"disease",
|
||||
"corruption",
|
||||
"show_helm",
|
||||
"follow_distance",
|
||||
"stop_melee_level",
|
||||
"expansion_bitmask",
|
||||
"enforce_spell_settings",
|
||||
"archery_setting",
|
||||
"caster_range",
|
||||
};
|
||||
}
|
||||
|
||||
@ -184,13 +170,6 @@ public:
|
||||
"poison",
|
||||
"disease",
|
||||
"corruption",
|
||||
"show_helm",
|
||||
"follow_distance",
|
||||
"stop_melee_level",
|
||||
"expansion_bitmask",
|
||||
"enforce_spell_settings",
|
||||
"archery_setting",
|
||||
"caster_range",
|
||||
};
|
||||
}
|
||||
|
||||
@ -276,13 +255,7 @@ public:
|
||||
e.poison = 0;
|
||||
e.disease = 0;
|
||||
e.corruption = 0;
|
||||
e.show_helm = 0;
|
||||
e.follow_distance = 200;
|
||||
e.stop_melee_level = 255;
|
||||
e.expansion_bitmask = -1;
|
||||
e.enforce_spell_settings = 0;
|
||||
e.archery_setting = 0;
|
||||
e.caster_range = 300;
|
||||
|
||||
|
||||
return e;
|
||||
}
|
||||
@ -364,13 +337,6 @@ public:
|
||||
e.poison = row[42] ? static_cast<int16_t>(atoi(row[42])) : 0;
|
||||
e.disease = row[43] ? static_cast<int16_t>(atoi(row[43])) : 0;
|
||||
e.corruption = row[44] ? static_cast<int16_t>(atoi(row[44])) : 0;
|
||||
e.show_helm = row[45] ? static_cast<uint32_t>(strtoul(row[45], nullptr, 10)) : 0;
|
||||
e.follow_distance = row[46] ? static_cast<uint32_t>(strtoul(row[46], nullptr, 10)) : 200;
|
||||
e.stop_melee_level = row[47] ? static_cast<uint8_t>(strtoul(row[47], nullptr, 10)) : 255;
|
||||
e.expansion_bitmask = row[48] ? static_cast<int32_t>(atoi(row[48])) : -1;
|
||||
e.enforce_spell_settings = row[49] ? static_cast<uint8_t>(strtoul(row[49], nullptr, 10)) : 0;
|
||||
e.archery_setting = row[50] ? static_cast<uint8_t>(strtoul(row[50], nullptr, 10)) : 0;
|
||||
e.caster_range = row[51] ? static_cast<uint32_t>(strtoul(row[51], nullptr, 10)) : 300;
|
||||
|
||||
return e;
|
||||
}
|
||||
@ -383,14 +349,25 @@ public:
|
||||
int bot_data_id
|
||||
)
|
||||
{
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
std::string query;
|
||||
|
||||
if (RuleB(Bots, BotSoftDeletes)) {
|
||||
query = fmt::format(
|
||||
"UPDATE {} SET name = SUBSTRING(CONCAT(name, '-deleted-', UNIX_TIMESTAMP()), 1, 64) WHERE {} = {}",
|
||||
TableName(),
|
||||
PrimaryKey(),
|
||||
bot_data_id
|
||||
);
|
||||
}
|
||||
else {
|
||||
query = fmt::format(
|
||||
"DELETE FROM {} WHERE {} = {}",
|
||||
TableName(),
|
||||
PrimaryKey(),
|
||||
bot_data_id
|
||||
)
|
||||
);
|
||||
);
|
||||
}
|
||||
auto results = db.QueryDatabase(query);
|
||||
|
||||
return (results.Success() ? results.RowsAffected() : 0);
|
||||
}
|
||||
@ -448,13 +425,6 @@ public:
|
||||
v.push_back(columns[42] + " = " + std::to_string(e.poison));
|
||||
v.push_back(columns[43] + " = " + std::to_string(e.disease));
|
||||
v.push_back(columns[44] + " = " + std::to_string(e.corruption));
|
||||
v.push_back(columns[45] + " = " + std::to_string(e.show_helm));
|
||||
v.push_back(columns[46] + " = " + std::to_string(e.follow_distance));
|
||||
v.push_back(columns[47] + " = " + std::to_string(e.stop_melee_level));
|
||||
v.push_back(columns[48] + " = " + std::to_string(e.expansion_bitmask));
|
||||
v.push_back(columns[49] + " = " + std::to_string(e.enforce_spell_settings));
|
||||
v.push_back(columns[50] + " = " + std::to_string(e.archery_setting));
|
||||
v.push_back(columns[51] + " = " + std::to_string(e.caster_range));
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
@ -521,13 +491,6 @@ public:
|
||||
v.push_back(std::to_string(e.poison));
|
||||
v.push_back(std::to_string(e.disease));
|
||||
v.push_back(std::to_string(e.corruption));
|
||||
v.push_back(std::to_string(e.show_helm));
|
||||
v.push_back(std::to_string(e.follow_distance));
|
||||
v.push_back(std::to_string(e.stop_melee_level));
|
||||
v.push_back(std::to_string(e.expansion_bitmask));
|
||||
v.push_back(std::to_string(e.enforce_spell_settings));
|
||||
v.push_back(std::to_string(e.archery_setting));
|
||||
v.push_back(std::to_string(e.caster_range));
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
@ -602,13 +565,6 @@ public:
|
||||
v.push_back(std::to_string(e.poison));
|
||||
v.push_back(std::to_string(e.disease));
|
||||
v.push_back(std::to_string(e.corruption));
|
||||
v.push_back(std::to_string(e.show_helm));
|
||||
v.push_back(std::to_string(e.follow_distance));
|
||||
v.push_back(std::to_string(e.stop_melee_level));
|
||||
v.push_back(std::to_string(e.expansion_bitmask));
|
||||
v.push_back(std::to_string(e.enforce_spell_settings));
|
||||
v.push_back(std::to_string(e.archery_setting));
|
||||
v.push_back(std::to_string(e.caster_range));
|
||||
|
||||
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
||||
}
|
||||
@ -687,13 +643,6 @@ public:
|
||||
e.poison = row[42] ? static_cast<int16_t>(atoi(row[42])) : 0;
|
||||
e.disease = row[43] ? static_cast<int16_t>(atoi(row[43])) : 0;
|
||||
e.corruption = row[44] ? static_cast<int16_t>(atoi(row[44])) : 0;
|
||||
e.show_helm = row[45] ? static_cast<uint32_t>(strtoul(row[45], nullptr, 10)) : 0;
|
||||
e.follow_distance = row[46] ? static_cast<uint32_t>(strtoul(row[46], nullptr, 10)) : 200;
|
||||
e.stop_melee_level = row[47] ? static_cast<uint8_t>(strtoul(row[47], nullptr, 10)) : 255;
|
||||
e.expansion_bitmask = row[48] ? static_cast<int32_t>(atoi(row[48])) : -1;
|
||||
e.enforce_spell_settings = row[49] ? static_cast<uint8_t>(strtoul(row[49], nullptr, 10)) : 0;
|
||||
e.archery_setting = row[50] ? static_cast<uint8_t>(strtoul(row[50], nullptr, 10)) : 0;
|
||||
e.caster_range = row[51] ? static_cast<uint32_t>(strtoul(row[51], nullptr, 10)) : 300;
|
||||
|
||||
all_entries.push_back(e);
|
||||
}
|
||||
@ -763,13 +712,6 @@ public:
|
||||
e.poison = row[42] ? static_cast<int16_t>(atoi(row[42])) : 0;
|
||||
e.disease = row[43] ? static_cast<int16_t>(atoi(row[43])) : 0;
|
||||
e.corruption = row[44] ? static_cast<int16_t>(atoi(row[44])) : 0;
|
||||
e.show_helm = row[45] ? static_cast<uint32_t>(strtoul(row[45], nullptr, 10)) : 0;
|
||||
e.follow_distance = row[46] ? static_cast<uint32_t>(strtoul(row[46], nullptr, 10)) : 200;
|
||||
e.stop_melee_level = row[47] ? static_cast<uint8_t>(strtoul(row[47], nullptr, 10)) : 255;
|
||||
e.expansion_bitmask = row[48] ? static_cast<int32_t>(atoi(row[48])) : -1;
|
||||
e.enforce_spell_settings = row[49] ? static_cast<uint8_t>(strtoul(row[49], nullptr, 10)) : 0;
|
||||
e.archery_setting = row[50] ? static_cast<uint8_t>(strtoul(row[50], nullptr, 10)) : 0;
|
||||
e.caster_range = row[51] ? static_cast<uint32_t>(strtoul(row[51], nullptr, 10)) : 300;
|
||||
|
||||
all_entries.push_back(e);
|
||||
}
|
||||
@ -889,13 +831,6 @@ public:
|
||||
v.push_back(std::to_string(e.poison));
|
||||
v.push_back(std::to_string(e.disease));
|
||||
v.push_back(std::to_string(e.corruption));
|
||||
v.push_back(std::to_string(e.show_helm));
|
||||
v.push_back(std::to_string(e.follow_distance));
|
||||
v.push_back(std::to_string(e.stop_melee_level));
|
||||
v.push_back(std::to_string(e.expansion_bitmask));
|
||||
v.push_back(std::to_string(e.enforce_spell_settings));
|
||||
v.push_back(std::to_string(e.archery_setting));
|
||||
v.push_back(std::to_string(e.caster_range));
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
@ -963,13 +898,6 @@ public:
|
||||
v.push_back(std::to_string(e.poison));
|
||||
v.push_back(std::to_string(e.disease));
|
||||
v.push_back(std::to_string(e.corruption));
|
||||
v.push_back(std::to_string(e.show_helm));
|
||||
v.push_back(std::to_string(e.follow_distance));
|
||||
v.push_back(std::to_string(e.stop_melee_level));
|
||||
v.push_back(std::to_string(e.expansion_bitmask));
|
||||
v.push_back(std::to_string(e.enforce_spell_settings));
|
||||
v.push_back(std::to_string(e.archery_setting));
|
||||
v.push_back(std::to_string(e.caster_range));
|
||||
|
||||
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
||||
}
|
||||
|
||||
464
common/repositories/base/base_bot_settings_repository.h
Normal file
464
common/repositories/base/base_bot_settings_repository.h
Normal file
@ -0,0 +1,464 @@
|
||||
/**
|
||||
* DO NOT MODIFY THIS FILE
|
||||
*
|
||||
* This repository was automatically generated and is NOT to be modified directly.
|
||||
* Any repository modifications are meant to be made to the repository extending the base.
|
||||
* Any modifications to base repositories are to be made by the generator only
|
||||
*
|
||||
* @generator ./utils/scripts/generators/repository-generator.pl
|
||||
* @docs https://docs.eqemu.io/developer/repositories
|
||||
*/
|
||||
|
||||
#ifndef EQEMU_BASE_BOT_SETTINGS_REPOSITORY_H
|
||||
#define EQEMU_BASE_BOT_SETTINGS_REPOSITORY_H
|
||||
|
||||
#include "../../database.h"
|
||||
#include "../../strings.h"
|
||||
#include <ctime>
|
||||
|
||||
class BaseBotSettingsRepository {
|
||||
public:
|
||||
struct BotSettings {
|
||||
uint32_t character_id;
|
||||
uint32_t bot_id;
|
||||
uint8_t stance;
|
||||
uint16_t setting_id;
|
||||
uint8_t setting_type;
|
||||
int32_t value;
|
||||
std::string category_name;
|
||||
std::string setting_name;
|
||||
};
|
||||
|
||||
static std::string PrimaryKey()
|
||||
{
|
||||
return std::string("character_id");
|
||||
}
|
||||
|
||||
static std::vector<std::string> Columns()
|
||||
{
|
||||
return {
|
||||
"character_id",
|
||||
"bot_id",
|
||||
"stance",
|
||||
"setting_id",
|
||||
"setting_type",
|
||||
"value",
|
||||
"category_name",
|
||||
"setting_name",
|
||||
};
|
||||
}
|
||||
|
||||
static std::vector<std::string> SelectColumns()
|
||||
{
|
||||
return {
|
||||
"character_id",
|
||||
"bot_id",
|
||||
"stance",
|
||||
"setting_id",
|
||||
"setting_type",
|
||||
"value",
|
||||
"category_name",
|
||||
"setting_name",
|
||||
};
|
||||
}
|
||||
|
||||
static std::string ColumnsRaw()
|
||||
{
|
||||
return std::string(Strings::Implode(", ", Columns()));
|
||||
}
|
||||
|
||||
static std::string SelectColumnsRaw()
|
||||
{
|
||||
return std::string(Strings::Implode(", ", SelectColumns()));
|
||||
}
|
||||
|
||||
static std::string TableName()
|
||||
{
|
||||
return std::string("bot_settings");
|
||||
}
|
||||
|
||||
static std::string BaseSelect()
|
||||
{
|
||||
return fmt::format(
|
||||
"SELECT {} FROM {}",
|
||||
SelectColumnsRaw(),
|
||||
TableName()
|
||||
);
|
||||
}
|
||||
|
||||
static std::string BaseInsert()
|
||||
{
|
||||
return fmt::format(
|
||||
"INSERT INTO {} ({}) ",
|
||||
TableName(),
|
||||
ColumnsRaw()
|
||||
);
|
||||
}
|
||||
|
||||
static BotSettings NewEntity()
|
||||
{
|
||||
BotSettings e{};
|
||||
|
||||
e.character_id = 0;
|
||||
e.bot_id = 0;
|
||||
e.stance = 0;
|
||||
e.setting_id = 0;
|
||||
e.setting_type = 0;
|
||||
e.value = 0;
|
||||
e.category_name = "";
|
||||
e.setting_name = "";
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
static BotSettings GetBotSettings(
|
||||
const std::vector<BotSettings> &bot_settingss,
|
||||
int bot_settings_id
|
||||
)
|
||||
{
|
||||
for (auto &bot_settings : bot_settingss) {
|
||||
if (bot_settings.character_id == bot_settings_id) {
|
||||
return bot_settings;
|
||||
}
|
||||
}
|
||||
|
||||
return NewEntity();
|
||||
}
|
||||
|
||||
static BotSettings FindOne(
|
||||
Database& db,
|
||||
int bot_settings_id
|
||||
)
|
||||
{
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"{} WHERE {} = {} LIMIT 1",
|
||||
BaseSelect(),
|
||||
PrimaryKey(),
|
||||
bot_settings_id
|
||||
)
|
||||
);
|
||||
|
||||
auto row = results.begin();
|
||||
if (results.RowCount() == 1) {
|
||||
BotSettings e{};
|
||||
|
||||
e.character_id = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0;
|
||||
e.bot_id = row[1] ? static_cast<uint32_t>(strtoul(row[1], nullptr, 10)) : 0;
|
||||
e.stance = row[2] ? static_cast<uint8_t>(strtoul(row[2], nullptr, 10)) : 0;
|
||||
e.setting_id = row[3] ? static_cast<uint16_t>(strtoul(row[3], nullptr, 10)) : 0;
|
||||
e.setting_type = row[4] ? static_cast<uint8_t>(strtoul(row[4], nullptr, 10)) : 0;
|
||||
e.value = row[5] ? static_cast<int32_t>(atoi(row[5])) : 0;
|
||||
e.category_name = row[6] ? row[6] : "";
|
||||
e.setting_name = row[7] ? row[7] : "";
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
return NewEntity();
|
||||
}
|
||||
|
||||
static int DeleteOne(
|
||||
Database& db,
|
||||
int bot_settings_id
|
||||
)
|
||||
{
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"DELETE FROM {} WHERE {} = {}",
|
||||
TableName(),
|
||||
PrimaryKey(),
|
||||
bot_settings_id
|
||||
)
|
||||
);
|
||||
|
||||
return (results.Success() ? results.RowsAffected() : 0);
|
||||
}
|
||||
|
||||
static int UpdateOne(
|
||||
Database& db,
|
||||
const BotSettings &e
|
||||
)
|
||||
{
|
||||
std::vector<std::string> v;
|
||||
|
||||
auto columns = Columns();
|
||||
|
||||
v.push_back(columns[0] + " = " + std::to_string(e.character_id));
|
||||
v.push_back(columns[1] + " = " + std::to_string(e.bot_id));
|
||||
v.push_back(columns[2] + " = " + std::to_string(e.stance));
|
||||
v.push_back(columns[3] + " = " + std::to_string(e.setting_id));
|
||||
v.push_back(columns[4] + " = " + std::to_string(e.setting_type));
|
||||
v.push_back(columns[5] + " = " + std::to_string(e.value));
|
||||
v.push_back(columns[6] + " = '" + Strings::Escape(e.category_name) + "'");
|
||||
v.push_back(columns[7] + " = '" + Strings::Escape(e.setting_name) + "'");
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"UPDATE {} SET {} WHERE {} = {}",
|
||||
TableName(),
|
||||
Strings::Implode(", ", v),
|
||||
PrimaryKey(),
|
||||
e.character_id
|
||||
)
|
||||
);
|
||||
|
||||
return (results.Success() ? results.RowsAffected() : 0);
|
||||
}
|
||||
|
||||
static BotSettings InsertOne(
|
||||
Database& db,
|
||||
BotSettings e
|
||||
)
|
||||
{
|
||||
std::vector<std::string> v;
|
||||
|
||||
v.push_back(std::to_string(e.character_id));
|
||||
v.push_back(std::to_string(e.bot_id));
|
||||
v.push_back(std::to_string(e.stance));
|
||||
v.push_back(std::to_string(e.setting_id));
|
||||
v.push_back(std::to_string(e.setting_type));
|
||||
v.push_back(std::to_string(e.value));
|
||||
v.push_back("'" + Strings::Escape(e.category_name) + "'");
|
||||
v.push_back("'" + Strings::Escape(e.setting_name) + "'");
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"{} VALUES ({})",
|
||||
BaseInsert(),
|
||||
Strings::Implode(",", v)
|
||||
)
|
||||
);
|
||||
|
||||
if (results.Success()) {
|
||||
e.character_id = results.LastInsertedID();
|
||||
return e;
|
||||
}
|
||||
|
||||
e = NewEntity();
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
static int InsertMany(
|
||||
Database& db,
|
||||
const std::vector<BotSettings> &entries
|
||||
)
|
||||
{
|
||||
std::vector<std::string> insert_chunks;
|
||||
|
||||
for (auto &e: entries) {
|
||||
std::vector<std::string> v;
|
||||
|
||||
v.push_back(std::to_string(e.character_id));
|
||||
v.push_back(std::to_string(e.bot_id));
|
||||
v.push_back(std::to_string(e.stance));
|
||||
v.push_back(std::to_string(e.setting_id));
|
||||
v.push_back(std::to_string(e.setting_type));
|
||||
v.push_back(std::to_string(e.value));
|
||||
v.push_back("'" + Strings::Escape(e.category_name) + "'");
|
||||
v.push_back("'" + Strings::Escape(e.setting_name) + "'");
|
||||
|
||||
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
||||
}
|
||||
|
||||
std::vector<std::string> v;
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"{} VALUES {}",
|
||||
BaseInsert(),
|
||||
Strings::Implode(",", insert_chunks)
|
||||
)
|
||||
);
|
||||
|
||||
return (results.Success() ? results.RowsAffected() : 0);
|
||||
}
|
||||
|
||||
static std::vector<BotSettings> All(Database& db)
|
||||
{
|
||||
std::vector<BotSettings> all_entries;
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"{}",
|
||||
BaseSelect()
|
||||
)
|
||||
);
|
||||
|
||||
all_entries.reserve(results.RowCount());
|
||||
|
||||
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||
BotSettings e{};
|
||||
|
||||
e.character_id = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0;
|
||||
e.bot_id = row[1] ? static_cast<uint32_t>(strtoul(row[1], nullptr, 10)) : 0;
|
||||
e.stance = row[2] ? static_cast<uint8_t>(strtoul(row[2], nullptr, 10)) : 0;
|
||||
e.setting_id = row[3] ? static_cast<uint16_t>(strtoul(row[3], nullptr, 10)) : 0;
|
||||
e.setting_type = row[4] ? static_cast<uint8_t>(strtoul(row[4], nullptr, 10)) : 0;
|
||||
e.value = row[5] ? static_cast<int32_t>(atoi(row[5])) : 0;
|
||||
e.category_name = row[6] ? row[6] : "";
|
||||
e.setting_name = row[7] ? row[7] : "";
|
||||
|
||||
all_entries.push_back(e);
|
||||
}
|
||||
|
||||
return all_entries;
|
||||
}
|
||||
|
||||
static std::vector<BotSettings> GetWhere(Database& db, const std::string &where_filter)
|
||||
{
|
||||
std::vector<BotSettings> all_entries;
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"{} WHERE {}",
|
||||
BaseSelect(),
|
||||
where_filter
|
||||
)
|
||||
);
|
||||
|
||||
all_entries.reserve(results.RowCount());
|
||||
|
||||
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||
BotSettings e{};
|
||||
|
||||
e.character_id = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0;
|
||||
e.bot_id = row[1] ? static_cast<uint32_t>(strtoul(row[1], nullptr, 10)) : 0;
|
||||
e.stance = row[2] ? static_cast<uint8_t>(strtoul(row[2], nullptr, 10)) : 0;
|
||||
e.setting_id = row[3] ? static_cast<uint16_t>(strtoul(row[3], nullptr, 10)) : 0;
|
||||
e.setting_type = row[4] ? static_cast<uint8_t>(strtoul(row[4], nullptr, 10)) : 0;
|
||||
e.value = row[5] ? static_cast<int32_t>(atoi(row[5])) : 0;
|
||||
e.category_name = row[6] ? row[6] : "";
|
||||
e.setting_name = row[7] ? row[7] : "";
|
||||
|
||||
all_entries.push_back(e);
|
||||
}
|
||||
|
||||
return all_entries;
|
||||
}
|
||||
|
||||
static int DeleteWhere(Database& db, const std::string &where_filter)
|
||||
{
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"DELETE FROM {} WHERE {}",
|
||||
TableName(),
|
||||
where_filter
|
||||
)
|
||||
);
|
||||
|
||||
return (results.Success() ? results.RowsAffected() : 0);
|
||||
}
|
||||
|
||||
static int Truncate(Database& db)
|
||||
{
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"TRUNCATE TABLE {}",
|
||||
TableName()
|
||||
)
|
||||
);
|
||||
|
||||
return (results.Success() ? results.RowsAffected() : 0);
|
||||
}
|
||||
|
||||
static int64 GetMaxId(Database& db)
|
||||
{
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"SELECT COALESCE(MAX({}), 0) FROM {}",
|
||||
PrimaryKey(),
|
||||
TableName()
|
||||
)
|
||||
);
|
||||
|
||||
return (results.Success() && results.begin()[0] ? strtoll(results.begin()[0], nullptr, 10) : 0);
|
||||
}
|
||||
|
||||
static int64 Count(Database& db, const std::string &where_filter = "")
|
||||
{
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"SELECT COUNT(*) FROM {} {}",
|
||||
TableName(),
|
||||
(where_filter.empty() ? "" : "WHERE " + where_filter)
|
||||
)
|
||||
);
|
||||
|
||||
return (results.Success() && results.begin()[0] ? strtoll(results.begin()[0], nullptr, 10) : 0);
|
||||
}
|
||||
|
||||
static std::string BaseReplace()
|
||||
{
|
||||
return fmt::format(
|
||||
"REPLACE INTO {} ({}) ",
|
||||
TableName(),
|
||||
ColumnsRaw()
|
||||
);
|
||||
}
|
||||
|
||||
static int ReplaceOne(
|
||||
Database& db,
|
||||
const BotSettings &e
|
||||
)
|
||||
{
|
||||
std::vector<std::string> v;
|
||||
|
||||
v.push_back(std::to_string(e.character_id));
|
||||
v.push_back(std::to_string(e.bot_id));
|
||||
v.push_back(std::to_string(e.stance));
|
||||
v.push_back(std::to_string(e.setting_id));
|
||||
v.push_back(std::to_string(e.setting_type));
|
||||
v.push_back(std::to_string(e.value));
|
||||
v.push_back("'" + Strings::Escape(e.category_name) + "'");
|
||||
v.push_back("'" + Strings::Escape(e.setting_name) + "'");
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"{} VALUES ({})",
|
||||
BaseReplace(),
|
||||
Strings::Implode(",", v)
|
||||
)
|
||||
);
|
||||
|
||||
return (results.Success() ? results.RowsAffected() : 0);
|
||||
}
|
||||
|
||||
static int ReplaceMany(
|
||||
Database& db,
|
||||
const std::vector<BotSettings> &entries
|
||||
)
|
||||
{
|
||||
std::vector<std::string> insert_chunks;
|
||||
|
||||
for (auto &e: entries) {
|
||||
std::vector<std::string> v;
|
||||
|
||||
v.push_back(std::to_string(e.character_id));
|
||||
v.push_back(std::to_string(e.bot_id));
|
||||
v.push_back(std::to_string(e.stance));
|
||||
v.push_back(std::to_string(e.setting_id));
|
||||
v.push_back(std::to_string(e.setting_type));
|
||||
v.push_back(std::to_string(e.value));
|
||||
v.push_back("'" + Strings::Escape(e.category_name) + "'");
|
||||
v.push_back("'" + Strings::Escape(e.setting_name) + "'");
|
||||
|
||||
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
||||
}
|
||||
|
||||
std::vector<std::string> v;
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"{} VALUES {}",
|
||||
BaseReplace(),
|
||||
Strings::Implode(",", insert_chunks)
|
||||
)
|
||||
);
|
||||
|
||||
return (results.Success() ? results.RowsAffected() : 0);
|
||||
}
|
||||
};
|
||||
|
||||
#endif //EQEMU_BASE_BOT_SETTINGS_REPOSITORY_H
|
||||
50
common/repositories/bot_blocked_buffs_repository.h
Normal file
50
common/repositories/bot_blocked_buffs_repository.h
Normal file
@ -0,0 +1,50 @@
|
||||
#ifndef EQEMU_BOT_BLOCKED_BUFFS_REPOSITORY_H
|
||||
#define EQEMU_BOT_BLOCKED_BUFFS_REPOSITORY_H
|
||||
|
||||
#include "../database.h"
|
||||
#include "../strings.h"
|
||||
#include "base/base_bot_blocked_buffs_repository.h"
|
||||
|
||||
class BotBlockedBuffsRepository: public BaseBotBlockedBuffsRepository {
|
||||
public:
|
||||
|
||||
/**
|
||||
* This file was auto generated and can be modified and extended upon
|
||||
*
|
||||
* Base repository methods are automatically
|
||||
* generated in the "base" version of this repository. The base repository
|
||||
* is immutable and to be left untouched, while methods in this class
|
||||
* are used as extension methods for more specific persistence-layer
|
||||
* accessors or mutators.
|
||||
*
|
||||
* Base Methods (Subject to be expanded upon in time)
|
||||
*
|
||||
* Note: Not all tables are designed appropriately to fit functionality with all base methods
|
||||
*
|
||||
* InsertOne
|
||||
* UpdateOne
|
||||
* DeleteOne
|
||||
* FindOne
|
||||
* GetWhere(std::string where_filter)
|
||||
* DeleteWhere(std::string where_filter)
|
||||
* InsertMany
|
||||
* All
|
||||
*
|
||||
* Example custom methods in a repository
|
||||
*
|
||||
* BotBlockedBuffsRepository::GetByZoneAndVersion(int zone_id, int zone_version)
|
||||
* BotBlockedBuffsRepository::GetWhereNeverExpires()
|
||||
* BotBlockedBuffsRepository::GetWhereXAndY()
|
||||
* BotBlockedBuffsRepository::DeleteWhereXAndY()
|
||||
*
|
||||
* Most of the above could be covered by base methods, but if you as a developer
|
||||
* find yourself re-using logic for other parts of the code, its best to just make a
|
||||
* method that can be re-used easily elsewhere especially if it can use a base repository
|
||||
* method and encapsulate filters there
|
||||
*/
|
||||
|
||||
// Custom extended repository methods here
|
||||
|
||||
};
|
||||
|
||||
#endif //EQEMU_BOT_BLOCKED_BUFFS_REPOSITORY_H
|
||||
@ -44,46 +44,6 @@ public:
|
||||
*/
|
||||
|
||||
// Custom extended repository methods here
|
||||
static bool SaveAllHelmAppearances(Database& db, const uint32 owner_id, const bool show_flag)
|
||||
{
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"UPDATE `{}` SET `show_helm` = {} WHERE `owner_id` = {}",
|
||||
TableName(),
|
||||
show_flag ? 1 : 0,
|
||||
owner_id
|
||||
)
|
||||
);
|
||||
|
||||
return results.Success();
|
||||
}
|
||||
|
||||
static bool ToggleAllHelmAppearances(Database& db, const uint32 owner_id)
|
||||
{
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"UPDATE `{}` SET `show_helm` = (`show_helm` XOR '1') WHERE `owner_id` = {}",
|
||||
TableName(),
|
||||
owner_id
|
||||
)
|
||||
);
|
||||
|
||||
return results.Success();
|
||||
}
|
||||
|
||||
static bool SaveAllFollowDistances(Database& db, const uint32 owner_id, const uint32 follow_distance)
|
||||
{
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"UPDATE `{}` SET `follow_distance` = {} WHERE `owner_id` = {}",
|
||||
TableName(),
|
||||
follow_distance,
|
||||
owner_id
|
||||
)
|
||||
);
|
||||
|
||||
return results.Success();
|
||||
}
|
||||
};
|
||||
|
||||
#endif //EQEMU_BOT_DATA_REPOSITORY_H
|
||||
|
||||
50
common/repositories/bot_settings_repository.h
Normal file
50
common/repositories/bot_settings_repository.h
Normal file
@ -0,0 +1,50 @@
|
||||
#ifndef EQEMU_BOT_SETTINGS_REPOSITORY_H
|
||||
#define EQEMU_BOT_SETTINGS_REPOSITORY_H
|
||||
|
||||
#include "../database.h"
|
||||
#include "../strings.h"
|
||||
#include "base/base_bot_settings_repository.h"
|
||||
|
||||
class BotSettingsRepository: public BaseBotSettingsRepository {
|
||||
public:
|
||||
|
||||
/**
|
||||
* This file was auto generated and can be modified and extended upon
|
||||
*
|
||||
* Base repository methods are automatically
|
||||
* generated in the "base" version of this repository. The base repository
|
||||
* is immutable and to be left untouched, while methods in this class
|
||||
* are used as extension methods for more specific persistence-layer
|
||||
* accessors or mutators.
|
||||
*
|
||||
* Base Methods (Subject to be expanded upon in time)
|
||||
*
|
||||
* Note: Not all tables are designed appropriately to fit functionality with all base methods
|
||||
*
|
||||
* InsertOne
|
||||
* UpdateOne
|
||||
* DeleteOne
|
||||
* FindOne
|
||||
* GetWhere(std::string where_filter)
|
||||
* DeleteWhere(std::string where_filter)
|
||||
* InsertMany
|
||||
* All
|
||||
*
|
||||
* Example custom methods in a repository
|
||||
*
|
||||
* BotSettingsRepository::GetByZoneAndVersion(int zone_id, int zone_version)
|
||||
* BotSettingsRepository::GetWhereNeverExpires()
|
||||
* BotSettingsRepository::GetWhereXAndY()
|
||||
* BotSettingsRepository::DeleteWhereXAndY()
|
||||
*
|
||||
* Most of the above could be covered by base methods, but if you as a developer
|
||||
* find yourself re-using logic for other parts of the code, its best to just make a
|
||||
* method that can be re-used easily elsewhere especially if it can use a base repository
|
||||
* method and encapsulate filters there
|
||||
*/
|
||||
|
||||
// Custom extended repository methods here
|
||||
|
||||
};
|
||||
|
||||
#endif //EQEMU_BOT_SETTINGS_REPOSITORY_H
|
||||
@ -382,6 +382,9 @@ RULE_BOOL(Map, MobZVisualDebug, false, "Displays spell effects determining wheth
|
||||
RULE_BOOL(Map, MobPathingVisualDebug, false, "Displays nodes in pathing points in realtime to help with visual debugging")
|
||||
RULE_REAL(Map, FixPathingZMaxDeltaSendTo, 20, "At runtime in SendTo: maximum change in Z to allow the BestZ code to apply")
|
||||
RULE_INT(Map, FindBestZHeightAdjust, 1, "Adds this to the current Z before seeking the best Z position")
|
||||
RULE_BOOL(Map, CheckForLoSCheat, false, "Runs predefined zone checks to check for LoS cheating through doors and such.")
|
||||
RULE_BOOL(Map, EnableLoSCheatExemptions, false, "Enables exemptions for the LoS Cheat check.")
|
||||
RULE_REAL(Map, RangeCheckForLoSCheat, 20.0, "Default 20.0. Range to check if one is within range of a door.")
|
||||
RULE_CATEGORY_END()
|
||||
|
||||
RULE_CATEGORY(Pathing)
|
||||
@ -754,13 +757,13 @@ RULE_INT(Bots, CommandSpellRank, 1, "Filters bot command spells by rank. 1, 2 an
|
||||
RULE_INT(Bots, CreationLimit, 150, "Number of bots that each account can create")
|
||||
RULE_BOOL(Bots, FinishBuffing, false, "Allow for buffs to complete even if the bot caster is out of mana. Only affects buffing out of combat")
|
||||
RULE_BOOL(Bots, GroupBuffing, false, "Bots will cast single target buffs as group buffs, default is false for single. Does not make single target buffs work for MGB")
|
||||
RULE_BOOL(Bots, RaidBuffing, false, "Bots will cast single target buffs as raid buffs, default is false for single. Does not make single target buffs work for MGB")
|
||||
RULE_INT(Bots, HealRotationMaxMembers, 24, "Maximum number of heal rotation members")
|
||||
RULE_INT(Bots, HealRotationMaxTargets, 12, "Maximum number of heal rotation targets")
|
||||
RULE_REAL(Bots, ManaRegen, 2.0, "Adjust mana regen. Acts as a final multiplier, stacks with Rule Character:ManaRegenMultiplier.")
|
||||
RULE_BOOL(Bots, PreferNoManaCommandSpells, true, "Give sorting priority to newer no-mana spells (i.e., 'Bind Affinity')")
|
||||
RULE_BOOL(Bots, QuestableSpawnLimit, false, "Optional quest method to manage bot spawn limits using the quest_globals name bot_spawn_limit, see: /bazaar/Aediles_Thrall.pl")
|
||||
RULE_INT(Bots, SpawnLimit, 71, "Number of bots a character can have spawned at one time, You + 71 bots is a 12 group pseudo-raid")
|
||||
RULE_BOOL(Bots, BotGroupXP, false, "Determines whether client gets experience for bots outside their group")
|
||||
RULE_BOOL(Bots, BotLevelsWithOwner, false, "Auto-updates spawned bots as owner levels/de-levels (false is original behavior)")
|
||||
RULE_INT(Bots, BotCharacterLevel, 0, "If level is greater that value player can spawn bots if BotCharacterLevelEnabled is true")
|
||||
RULE_INT(Bots, CasterStopMeleeLevel, 13, "Level at which caster bots stop melee attacks")
|
||||
@ -783,6 +786,119 @@ RULE_BOOL(Bots, CanClickMageEpicV1, true, "Whether or not bots are allowed to cl
|
||||
RULE_BOOL(Bots, BotsIgnoreLevelBasedHasteCaps, false, "Ignores hard coded level based haste caps.")
|
||||
RULE_INT(Bots, BotsHasteCap, 100, "Haste cap for non-v3(over haste) haste")
|
||||
RULE_INT(Bots, BotsHastev3Cap, 25, "Haste cap for v3(over haste) haste")
|
||||
RULE_BOOL(Bots, CrossRaidBuffingAndHealing, true, "If True, bots will be able to cast on all raid members rather than just their raid group members. Default true.")
|
||||
RULE_BOOL(Bots, CanCastIllusionsOnPets, false, "If True, bots will be able to cast spells that have an illusion effect on pets. Default false.")
|
||||
RULE_BOOL(Bots, CanCastPetOnlyOnOthersPets, false, "If True, bots will be able to cast pet only spells on other's pets. Default false.")
|
||||
RULE_BOOL(Bots, RequirePetAffinity, true, "If True, bots will be need to have the Pet Affinity AA to allow their pets to be hit with group spells.")
|
||||
RULE_INT(Bots, SpellResistLimit, 150, "150 Default. This is the resist cap where bots will refuse to cast spells on enemies due to a high resist chance.")
|
||||
RULE_INT(Bots, StunCastChanceIfCasting, 50, "50 Default. Chance for non-Paladins to cast a stun spell if the target is casting.")
|
||||
RULE_INT(Bots, StunCastChanceNormal, 15, "15 Default. Chance for non-Paladins to cast a stun spell on the target.")
|
||||
RULE_INT(Bots, StunCastChancePaladins, 75, "75 Default. Chance for Paladins to cast a stun spell if the target is casting.")
|
||||
RULE_INT(Bots, PercentChanceToCastAEs, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.")
|
||||
RULE_INT(Bots, PercentChanceToCastNuke, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.")
|
||||
RULE_INT(Bots, PercentChanceToCastGroupHeal, 90, "The chance for a bot to attempt to cast the given spell type in combat. Default 90%.")
|
||||
RULE_INT(Bots, PercentChanceToCastHeal, 90, "The chance for a bot to attempt to cast the given spell type in combat. Default 90%.")
|
||||
RULE_INT(Bots, PercentChanceToCastRoot, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.")
|
||||
RULE_INT(Bots, PercentChanceToCastBuff, 90, "The chance for a bot to attempt to cast the given spell type in combat. Default 90%.")
|
||||
RULE_INT(Bots, PercentChanceToCastEscape, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.")
|
||||
RULE_INT(Bots, PercentChanceToCastLifetap, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.")
|
||||
RULE_INT(Bots, PercentChanceToCastSnare, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.")
|
||||
RULE_INT(Bots, PercentChanceToCastDOT, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.")
|
||||
RULE_INT(Bots, PercentChanceToCastDispel, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.")
|
||||
RULE_INT(Bots, PercentChanceToCastInCombatBuff, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.")
|
||||
RULE_INT(Bots, PercentChanceToCastHateLine, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.")
|
||||
RULE_INT(Bots, PercentChanceToCastMez, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.")
|
||||
RULE_INT(Bots, PercentChanceToCastSlow, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.")
|
||||
RULE_INT(Bots, PercentChanceToCastDebuff, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.")
|
||||
RULE_INT(Bots, PercentChanceToCastCure, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.")
|
||||
RULE_INT(Bots, PercentChanceToCastGroupCure, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.")
|
||||
RULE_INT(Bots, PercentChanceToCastHateRedux, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.")
|
||||
RULE_INT(Bots, PercentChanceToCastFear, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.")
|
||||
RULE_INT(Bots, PercentChanceToCastOtherType, 90, "The chance for a bot to attempt to cast the remaining spell types in combat. Default 0-%.")
|
||||
RULE_INT(Bots, MinDelayBetweenInCombatCastAttempts, 500, "The minimum delay in milliseconds between cast attempts while in-combat. This is rolled between the min and max. Default 500ms.")
|
||||
RULE_INT(Bots, MaxDelayBetweenInCombatCastAttempts, 2000, "The maximum delay in milliseconds between cast attempts while in-combat. This is rolled between the min and max. Default 2500ms.")
|
||||
RULE_INT(Bots, MinDelayBetweenOutCombatCastAttempts, 1000, "The minimum delay in milliseconds between cast attempts while out-of-combat. This is rolled between the min and max. Default 1000ms.")
|
||||
RULE_INT(Bots, MaxDelayBetweenOutCombatCastAttempts, 2500, "The maximum delay in milliseconds between cast attempts while out-of-combat. This is rolled between the min and max. Default 2500ms.")
|
||||
RULE_INT(Bots, MezChance, 35, "35 Default. Chance for a bot to attempt to Mez a target after validating it is eligible.")
|
||||
RULE_INT(Bots, AEMezChance, 35, "35 Default. Chance for a bot to attempt to AE Mez targets after validating they are eligible.")
|
||||
RULE_INT(Bots, MezSuccessDelay, 3500, "3500 (3.5 sec) Default. Delay between successful Mez attempts.")
|
||||
RULE_INT(Bots, AEMezSuccessDelay, 5000, "5000 (5 sec) Default. Delay between successful AEMez attempts.")
|
||||
RULE_INT(Bots, MezFailDelay, 2000, "2000 (2 sec) Default. Delay between failed Mez attempts.")
|
||||
RULE_INT(Bots, MezAEFailDelay, 4000, "4000 (4 sec) Default. Delay between failed AEMez attempts.")
|
||||
RULE_INT(Bots, MinGroupHealTargets, 3, "Minimum number of targets in valid range that are required for a group heal to cast. Default 3.")
|
||||
RULE_INT(Bots, MinGroupCureTargets, 3, "Minimum number of targets in valid range that are required for a cure heal to cast. Default 3.")
|
||||
RULE_INT(Bots, MinTargetsForAESpell, 3, "Minimum number of targets in valid range that are required for an AE spell to cast. Default 3.")
|
||||
RULE_INT(Bots, MinTargetsForGroupSpell, 3, "Minimum number of targets in valid range that are required for an group spell to cast. Default 3.")
|
||||
RULE_BOOL(Bots, AllowBuffingHealingFamiliars, false, "Determines if bots are allowed to buff and heal familiars. Default false.")
|
||||
RULE_BOOL(Bots, RunSpellTypeChecksOnSpawn, false, "This will run a serious of checks on spell types and output errors to LogBotSpellTypeChecks")
|
||||
RULE_BOOL(Bots, UseParentSpellTypeForChecks, true, "This will check only the parent instead of AE/Group/Pet types (ex: AENukes/AERains/PBAENukes fall under Nukes or PetBuffs fall under buffs) when RunSpellTypeChecksOnSpawn fires")
|
||||
RULE_BOOL(Bots, AllowForcedCastsBySpellID, true, "If enabled, players can use ^cast spellid # to cast a specific spell by ID that is in their spell list")
|
||||
RULE_BOOL(Bots, AllowCastAAs, true, "If enabled, players can use ^cast aa to cast a clickable AA")
|
||||
RULE_BOOL(Bots, AllowMagicianEpicPet, false, "If enabled, magician bots can summon their epic pets following the rules AllowMagicianEpicPetLevel")
|
||||
RULE_INT(Bots, AllowMagicianEpicPetLevel, 50, "If AllowMagicianEpicPet is enabled, bots can start using their epic pets at this level")
|
||||
RULE_INT(Bots, RequiredMagicianEpicPetItemID, 28034, "If AllowMagicianEpicPet is enabled and this is set, bots will be required to have this item ID equipped to cast their epic. Takes in to account AllowMagicianEpicPetLevel as well. Set to 0 to disable requirement")
|
||||
RULE_STRING(Bots, EpicPetSpellName, "", "'teleport_zone' in the spell to be cast for epic pets. This must be in their spell list to cast.")
|
||||
RULE_INT(Bots, ReclaimEnergySpellID, 331, "Spell ID for reclaim energy when using ^petsettype. Default 331")
|
||||
RULE_BOOL(Bots, UseSpellPulling, true, "If enabled bots will use a spell to pull when within range. Uses PullSpellID.")
|
||||
RULE_INT(Bots, PullSpellID, 5225, "Default 5225 - Throw Stone. Spell that will be cast to pull by bots")
|
||||
RULE_BOOL(Bots, AllowRangedPulling, true, "If enabled bots will pull with their ranged items if set to ranged.")
|
||||
RULE_BOOL(Bots, AllowAISpellPulling, true, "If enabled bots will rely on their detrimental AI to pull when within range.")
|
||||
RULE_BOOL(Bots, AllowBotEquipAnyClassGear, false, "Allows Bots to wear Equipment even if their class is not valid")
|
||||
RULE_BOOL(Bots, BotArcheryConsumesAmmo, true, "Set to false to disable Archery Ammo Consumption")
|
||||
RULE_BOOL(Bots, BotThrowingConsumesAmmo, true, "Set to false to disable Throwing Ammo Consumption")
|
||||
RULE_INT(Bots, StackSizeMin, 20, "20 Default. -1 to disable and use default max stack size. Minimum stack size to give a bot (Arrows/Throwing).")
|
||||
RULE_INT(Bots, HasOrMayGetAggroThreshold, 90, "90 Default. Percent threshold of total hate where bots will stop casting spells that generate hate if they are set to try to not pull aggro via spells.")
|
||||
RULE_BOOL(Bots, UseFlatNormalMeleeRange, false, "False Default. If true, bots melee distance will be a flat distance set by Bots:NormalMeleeRangeDistance.")
|
||||
RULE_REAL(Bots, NormalMeleeRangeDistance, 0.75, "If UseFlatNormalMeleeRange is enabled, multiplier of the max melee range at which a bot will stand in melee combat. 0.75 Recommended, max melee for all abilities to land.")
|
||||
RULE_REAL(Bots, PercentMinMeleeDistance, 0.75, "Multiplier of the their melee range - Minimum distance from target a bot will stand while in melee combat before trying to adjust. 0.60 Recommended.")
|
||||
RULE_REAL(Bots, MaxDistanceForMelee, 20, "Maximum distance bots will stand for melee. Default 20 to allow all special attacks to land.")
|
||||
RULE_REAL(Bots, TauntNormalMeleeRangeDistance, 0.50, "Multiplier of the max melee range at which a taunting bot will stand in melee combat. 0.50 Recommended, closer than others .")
|
||||
RULE_REAL(Bots, PercentTauntMinMeleeDistance, 0.40, "Multiplier of their melee range - Minimum distance from target a taunting bot will stand while in melee combat before trying to adjust. 0.25 Recommended.")
|
||||
RULE_REAL(Bots, PercentMaxMeleeRangeDistance, 0.95, "Multiplier of the max melee range at which a bot will stand in melee combat. 0.95 Recommended, max melee while disabling special attacks/taunt.")
|
||||
RULE_REAL(Bots, PercentMinMaxMeleeRangeDistance, 0.75, "Multiplier of the closest max melee range at which a bot will stand in melee combat before trying to adjust. 0.75 Recommended, max melee while disabling special attacks/taunt.")
|
||||
RULE_BOOL(Bots, TauntingBotsFollowTopHate, true, "True Default. If true, bots that are taunting will attempt to stick with whoever currently is top hate.")
|
||||
RULE_INT(Bots, DistanceTauntingBotsStickMainHate, 10, "If TauntingBotsFollowTopHate is enabled, this is the distance bots will try to stick to whoever currently is Top Hate.")
|
||||
RULE_BOOL(Bots, DisableSpecialAbilitiesAtMaxMelee, true, "True Default. If true, when bots are at max melee distance, special abilities including taunt will be disabled.")
|
||||
RULE_INT(Bots, MinJitterTimer, 500, "Minimum ms between bot movement jitter checks.")
|
||||
RULE_INT(Bots, MaxJitterTimer, 2500, "Maximum ms between bot movement jitter checks. Set to 0 to disable timer checks.")
|
||||
RULE_BOOL(Bots, PreventBotCampOnFD, true, "True Default. If true, players will not be able to camp bots while feign death.")
|
||||
RULE_BOOL(Bots, PreventBotSpawnOnFD, true, "True Default. If true, players will not be able to spawn bots while feign death.")
|
||||
RULE_BOOL(Bots, PreventBotSpawnOnEngaged, true, "True Default. If true, players will not be able to spawn bots while you, your group or raid are engaged.")
|
||||
RULE_BOOL(Bots, PreventBotCampOnEngaged, true, "True Default. If true, players will not be able to camp bots while you, your group or raid are engaged.")
|
||||
RULE_BOOL(Bots, CopySettingsOwnBotsOnly, true, "Determines whether a bot you are copying settings from must be a bot you own or not, default true.")
|
||||
RULE_BOOL(Bots, AllowCopySettingsAnon, false, "If player's are allowed to copy settings of bots owned by anonymous players.")
|
||||
RULE_BOOL(Bots, AllowCharmedPetBuffs, true, "Whether or not bots are allowed to cast buff charmed pets, default true.")
|
||||
RULE_BOOL(Bots, AllowCharmedPetHeals, true, "Whether or not bots are allowed to cast heal charmed pets, default true.")
|
||||
RULE_BOOL(Bots, AllowCharmedPetCures, true, "Whether or not bots are allowed to cast cure charmed pets, default true.")
|
||||
RULE_BOOL(Bots, ShowResistMessagesToOwner, true, "Default True. If enabled, when a bot's spell is resisted it will send a spell failure to their owner.")
|
||||
RULE_BOOL(Bots, BotBuffLevelRestrictions, true, "Buffs will not land on low level bots like live players")
|
||||
RULE_BOOL(Bots, BotsUseLiveBlockedMessage, true, "Setting whether detailed spell block messages should be used for bots as players do on the live servers")
|
||||
RULE_BOOL(Bots, BotSoftDeletes, true, "When bots are deleted, they are only soft deleted")
|
||||
RULE_INT(Bots, MinStatusToBypassSpawnLimit, 100, "Minimum status to bypass spawn limit. Default 100.")
|
||||
RULE_INT(Bots, MinStatusBypassSpawnLimit, 120, "Spawn limit with status bypass. Default 120.")
|
||||
RULE_INT(Bots, MinStatusToBypassCreateLimit, 100, "Minimum status to bypass create limit. Default 100.")
|
||||
RULE_INT(Bots, MinStatusBypassCreateLimit, 120, "Create limit with status bypass. Default 120.")
|
||||
RULE_BOOL(Bots, EnableBotTGB, true, "If enabled bots will cast group buffs as TGB.")
|
||||
RULE_BOOL(Bots, DoResponseAnimations, true, "If enabled bots will do animations to certain responses or commands.")
|
||||
RULE_INT(Bots, DefaultFollowDistance, 20, "Default 20. Distance a bot will follow behind.")
|
||||
RULE_INT(Bots, MaxFollowDistance, 300, "Default 300. Max distance a bot can be set to follow behind.")
|
||||
RULE_INT(Bots, MaxDistanceRanged, 300, "Default 300. Max distance a bot can be set to ranged.")
|
||||
RULE_BOOL(Bots, AllowAIMez, true, "If enabled bots will automatically mez/AE mez eligible targets.")
|
||||
RULE_BOOL(Bots, AllowCommandedCharm, true, "If enabled bots can be commanded to charm NPCs.")
|
||||
RULE_BOOL(Bots, AllowCommandedMez, true, "If enabled bots can be commanded to mez NPCs.")
|
||||
RULE_BOOL(Bots, AllowCommandedResurrect, true, "If enabled bots can be commanded to resurrect players.")
|
||||
RULE_BOOL(Bots, AllowCommandedSummonCorpse, true, "If enabled bots can be commanded to summon other's corpses.")
|
||||
RULE_BOOL(Bots, AllowCommandedLull, true, "If enabled bots can be commanded to lull targets.")
|
||||
RULE_INT(Bots, CampTimer, 25, "Number of seconds after /camp has begun before bots camp out.")
|
||||
RULE_BOOL(Bots, SendClassRaceOnHelp, true, "If enabled a reminder of how to check class/race IDs will be sent when using compatible commands.")
|
||||
RULE_BOOL(Bots, AllowCrossGroupRaidAssist, true, "If enabled bots will autodefend group or raid members set as main assist.")
|
||||
RULE_BOOL(Bots, AllowBotBlockedBuffs, true, "If enabled, you can create blocked buffs for each bot and for their pets.")
|
||||
RULE_STRING(Bots, ZonesWithSpawnLimits, "", "Comma-delimited list of zones where different bot spawn limits apply. This is the max a zone allows.")
|
||||
RULE_STRING(Bots, ZoneSpawnLimits, "", "Comma-delimited list of spawn limits for zones.")
|
||||
RULE_STRING(Bots, ZonesWithForcedSpawnLimits, "", "Comma-delimited list of zones where bot spawn limits are forced. This will take priority over any other type of spawn limits.")
|
||||
RULE_STRING(Bots, ZoneForcedSpawnLimits, "", "Comma-delimited list of forced spawn limits for zones.")
|
||||
RULE_INT(Bots, AICastSpellTypeDelay, 100, "Delay in milliseconds between AI cast attempts for each spell type. Default 100ms")
|
||||
RULE_INT(Bots, AICastSpellTypeHeldDelay, 2500, "Delay in milliseconds between AI cast attempts for each spell type that is held or disabled. Default 2500ms (2.5s)")
|
||||
RULE_CATEGORY_END()
|
||||
|
||||
RULE_CATEGORY(Chat)
|
||||
@ -1009,6 +1125,7 @@ RULE_CATEGORY_END()
|
||||
RULE_CATEGORY(Command)
|
||||
RULE_BOOL(Command, DyeCommandRequiresDyes, false, "Enable this to require a Prismatic Dye (32557) each time someone uses #dye.")
|
||||
RULE_BOOL(Command, HideMeCommandDisablesTells, true, "Disable this to allow tells to be received when using #hideme.")
|
||||
RULE_INT(Command, MaxHelpLineLength, 53, "Maximum length of a line before splitting it in to new lines for DiaWind. Default 53.")
|
||||
RULE_CATEGORY_END()
|
||||
|
||||
RULE_CATEGORY(Doors)
|
||||
|
||||
663
common/spdat.cpp
663
common/spdat.cpp
@ -125,9 +125,14 @@ bool IsMesmerizeSpell(uint16 spell_id)
|
||||
return IsEffectInSpell(spell_id, SE_Mez);
|
||||
}
|
||||
|
||||
bool SpellBreaksMez(uint16 spell_id)
|
||||
{
|
||||
return (IsValidSpell(spell_id) && IsDetrimentalSpell(spell_id) && IsAnyDamageSpell(spell_id));
|
||||
}
|
||||
|
||||
bool IsStunSpell(uint16 spell_id)
|
||||
{
|
||||
return IsEffectInSpell(spell_id, SE_Stun);
|
||||
return (IsValidSpell(spell_id) && IsEffectInSpell(spell_id, SE_Stun) || IsEffectInSpell(spell_id, SE_SpinTarget));
|
||||
}
|
||||
|
||||
bool IsSummonSpell(uint16 spell_id)
|
||||
@ -154,6 +159,10 @@ bool IsSummonSpell(uint16 spell_id)
|
||||
|
||||
bool IsDamageSpell(uint16 spell_id)
|
||||
{
|
||||
if (!IsValidSpell(spell_id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (IsLifetapSpell(spell_id)) {
|
||||
return false;
|
||||
}
|
||||
@ -162,6 +171,32 @@ bool IsDamageSpell(uint16 spell_id)
|
||||
|
||||
for (int i = 0; i < EFFECT_COUNT; i++) {
|
||||
const auto effect_id = spell.effect_id[i];
|
||||
if (
|
||||
spell.base_value[i] < 0 &&
|
||||
(effect_id == SE_CurrentHPOnce || effect_id == SE_CurrentHP)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsAnyDamageSpell(uint16 spell_id)
|
||||
{
|
||||
if (!IsValidSpell(spell_id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (IsLifetapSpell(spell_id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto& spell = spells[spell_id];
|
||||
|
||||
for (int i = 0; i < EFFECT_COUNT; i++) {
|
||||
const auto effect_id = spell.effect_id[i];
|
||||
|
||||
if (
|
||||
spell.base_value[i] < 0 &&
|
||||
(
|
||||
@ -179,6 +214,35 @@ bool IsDamageSpell(uint16 spell_id)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsDamageOverTimeSpell(uint16 spell_id)
|
||||
{
|
||||
if (!IsValidSpell(spell_id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (IsLifetapSpell(spell_id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto& spell = spells[spell_id];
|
||||
|
||||
if (spell.good_effect || !spell.buff_duration_formula) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < EFFECT_COUNT; i++) {
|
||||
const auto effect_id = spell.effect_id[i];
|
||||
if (
|
||||
spell.base_value[i] < 0 &&
|
||||
effect_id == SE_CurrentHP &&
|
||||
spell.buff_duration > 1
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsFearSpell(uint16 spell_id)
|
||||
{
|
||||
@ -409,7 +473,8 @@ bool IsSummonPetSpell(uint16 spell_id)
|
||||
return (
|
||||
IsEffectInSpell(spell_id, SE_SummonPet) ||
|
||||
IsEffectInSpell(spell_id, SE_SummonBSTPet) ||
|
||||
IsEffectInSpell(spell_id, SE_Familiar)
|
||||
IsEffectInSpell(spell_id, SE_Familiar) ||
|
||||
IsEffectInSpell(spell_id, SE_NecPet)
|
||||
);
|
||||
}
|
||||
|
||||
@ -560,12 +625,11 @@ bool IsPBAENukeSpell(uint16 spell_id)
|
||||
|
||||
if (
|
||||
IsPureNukeSpell(spell_id) &&
|
||||
spell.aoe_range > 0 &&
|
||||
spell.target_type == ST_AECaster
|
||||
!IsTargetRequiredForSpell(spell_id)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -588,6 +652,120 @@ bool IsAERainNukeSpell(uint16 spell_id)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsAnyNukeOrStunSpell(uint16 spell_id) {
|
||||
if (!IsValidSpell(spell_id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (IsSelfConversionSpell(spell_id) || IsEscapeSpell(spell_id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (
|
||||
IsPBAENukeSpell(spell_id) ||
|
||||
IsAERainNukeSpell(spell_id) ||
|
||||
IsPureNukeSpell(spell_id) ||
|
||||
IsStunSpell(spell_id) ||
|
||||
(IsDamageSpell(spell_id) && !IsDamageOverTimeSpell(spell_id))
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsAnyAESpell(uint16 spell_id) {
|
||||
return (
|
||||
IsValidSpell(spell_id) &&
|
||||
(
|
||||
IsAEDurationSpell(spell_id) ||
|
||||
IsAESpell(spell_id) ||
|
||||
IsAERainNukeSpell(spell_id) ||
|
||||
IsAERainSpell(spell_id) ||
|
||||
IsPBAESpell(spell_id) ||
|
||||
IsPBAENukeSpell(spell_id)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
bool IsAESpell(uint16 spell_id)
|
||||
{
|
||||
if (!IsValidSpell(spell_id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (spells[spell_id].target_type) {
|
||||
case ST_TargetOptional:
|
||||
case ST_GroupTeleport :
|
||||
case ST_Target:
|
||||
case ST_Self:
|
||||
case ST_Animal:
|
||||
case ST_Undead:
|
||||
case ST_Summoned:
|
||||
case ST_Tap:
|
||||
case ST_Pet:
|
||||
case ST_Corpse:
|
||||
case ST_Plant:
|
||||
case ST_Giant:
|
||||
case ST_Dragon:
|
||||
case ST_LDoNChest_Cursed:
|
||||
case ST_Muramite:
|
||||
case ST_SummonedPet:
|
||||
case ST_GroupNoPets:
|
||||
case ST_Group:
|
||||
case ST_GroupClientAndPet:
|
||||
case ST_TargetsTarget:
|
||||
case ST_PetMaster:
|
||||
return false;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (
|
||||
spells[spell_id].aoe_range > 0
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsPBAESpell(uint16 spell_id)
|
||||
{
|
||||
if (!IsValidSpell(spell_id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto& spell = spells[spell_id];
|
||||
|
||||
if (
|
||||
spell.aoe_range > 0 &&
|
||||
spell.target_type == ST_AECaster
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsAERainSpell(uint16 spell_id)
|
||||
{
|
||||
if (!IsValidSpell(spell_id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto& spell = spells[spell_id];
|
||||
|
||||
if (
|
||||
spell.aoe_range > 0 &&
|
||||
spell.aoe_duration > 1000
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsPartialResistableSpell(uint16 spell_id)
|
||||
{
|
||||
if (!IsValidSpell(spell_id)) {
|
||||
@ -644,7 +822,9 @@ bool IsGroupSpell(uint16 spell_id)
|
||||
return (
|
||||
spell.target_type == ST_AEBard ||
|
||||
spell.target_type == ST_Group ||
|
||||
spell.target_type == ST_GroupTeleport
|
||||
spell.target_type == ST_GroupTeleport ||
|
||||
spell.target_type == ST_GroupNoPets ||
|
||||
spell.target_type == ST_GroupClientAndPet
|
||||
);
|
||||
}
|
||||
|
||||
@ -1265,45 +1445,45 @@ bool IsCompleteHealSpell(uint16 spell_id)
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
bool IsFastHealSpell(uint16 spell_id)
|
||||
{
|
||||
spell_id = (
|
||||
IsEffectInSpell(spell_id, SE_CurrentHP) ?
|
||||
spell_id :
|
||||
GetSpellTriggerSpellID(spell_id, SE_CurrentHP)
|
||||
);
|
||||
bool IsFastHealSpell(uint16 spell_id) {
|
||||
spell_id = (
|
||||
IsEffectInSpell(spell_id, SE_CurrentHP) ?
|
||||
spell_id :
|
||||
GetSpellTriggerSpellID(spell_id, SE_CurrentHP)
|
||||
);
|
||||
|
||||
if (!spell_id) {
|
||||
spell_id = (
|
||||
IsEffectInSpell(spell_id, SE_CurrentHPOnce) ?
|
||||
spell_id :
|
||||
GetSpellTriggerSpellID(spell_id, SE_CurrentHPOnce)
|
||||
);
|
||||
}
|
||||
if (!spell_id) {
|
||||
spell_id = (
|
||||
IsEffectInSpell(spell_id, SE_CurrentHPOnce) ?
|
||||
spell_id :
|
||||
GetSpellTriggerSpellID(spell_id, SE_CurrentHPOnce)
|
||||
);
|
||||
}
|
||||
|
||||
if (spell_id) {
|
||||
if (
|
||||
spells[spell_id].cast_time <= MAX_FAST_HEAL_CASTING_TIME &&
|
||||
spells[spell_id].good_effect &&
|
||||
!IsGroupSpell(spell_id)
|
||||
) {
|
||||
for (int i = 0; i < EFFECT_COUNT; i++) {
|
||||
if (
|
||||
spells[spell_id].base_value[i] > 0 &&
|
||||
(
|
||||
spells[spell_id].effect_id[i] == SE_CurrentHP ||
|
||||
spells[spell_id].effect_id[i] == SE_CurrentHPOnce
|
||||
)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (spell_id && IsValidSpell(spell_id)) {
|
||||
if (
|
||||
spells[spell_id].cast_time <= MAX_FAST_HEAL_CASTING_TIME &&
|
||||
spells[spell_id].good_effect &&
|
||||
!IsGroupSpell(spell_id)
|
||||
) {
|
||||
for (int i = 0; i < EFFECT_COUNT; i++) {
|
||||
if (
|
||||
spells[spell_id].base_value[i] > 0 &&
|
||||
(
|
||||
spells[spell_id].effect_id[i] == SE_CurrentHP ||
|
||||
spells[spell_id].effect_id[i] == SE_CurrentHPOnce
|
||||
)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsVeryFastHealSpell(uint16 spell_id)
|
||||
@ -1386,6 +1566,47 @@ bool IsRegularSingleTargetHealSpell(uint16 spell_id)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsRegularPetHealSpell(uint16 spell_id)
|
||||
{
|
||||
spell_id = (
|
||||
IsEffectInSpell(spell_id, SE_CurrentHP) ?
|
||||
spell_id :
|
||||
GetSpellTriggerSpellID(spell_id, SE_CurrentHP)
|
||||
);
|
||||
|
||||
if (!spell_id) {
|
||||
spell_id = (
|
||||
IsEffectInSpell(spell_id, SE_CurrentHPOnce) ?
|
||||
spell_id :
|
||||
GetSpellTriggerSpellID(spell_id, SE_CurrentHPOnce)
|
||||
);
|
||||
}
|
||||
|
||||
if (spell_id && IsValidSpell(spell_id)) {
|
||||
if (
|
||||
(spells[spell_id].target_type == ST_Pet || spells[spell_id].target_type == ST_Undead) &&
|
||||
!IsCompleteHealSpell(spell_id) &&
|
||||
!IsHealOverTimeSpell(spell_id) &&
|
||||
!IsGroupSpell(spell_id)
|
||||
) {
|
||||
for (int i = 0; i < EFFECT_COUNT; i++) {
|
||||
if (
|
||||
spells[spell_id].base_value[i] > 0 &&
|
||||
spells[spell_id].buff_duration == 0 &&
|
||||
(
|
||||
spells[spell_id].effect_id[i] == SE_CurrentHP ||
|
||||
spells[spell_id].effect_id[i] == SE_CurrentHPOnce
|
||||
)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsRegularGroupHealSpell(uint16 spell_id)
|
||||
{
|
||||
spell_id = (
|
||||
@ -1426,11 +1647,60 @@ bool IsRegularGroupHealSpell(uint16 spell_id)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsGroupCompleteHealSpell(uint16 spell_id)
|
||||
{
|
||||
bool IsGroupCompleteHealSpell(uint16 spell_id) {
|
||||
if (
|
||||
IsValidSpell(spell_id) &&
|
||||
(
|
||||
spell_id == SPELL_COMPLETE_HEAL ||
|
||||
IsEffectInSpell(spell_id, SE_CompleteHeal) ||
|
||||
IsPercentalHealSpell(spell_id) ||
|
||||
GetSpellTriggerSpellID(spell_id, SE_CompleteHeal)
|
||||
) &&
|
||||
IsGroupSpell(spell_id)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsGroupHealOverTimeSpell(uint16 spell_id) {
|
||||
if (
|
||||
IsValidSpell(spell_id) &&
|
||||
(
|
||||
IsEffectInSpell(spell_id, SE_HealOverTime) ||
|
||||
GetSpellTriggerSpellID(spell_id, SE_HealOverTime)
|
||||
) &&
|
||||
IsGroupSpell(spell_id)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsAnyHealSpell(uint16 spell_id) {
|
||||
if (!IsValidSpell(spell_id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (spell_id == SPELL_NATURES_RECOVERY) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//spell_id != SPELL_ADRENALINE_SWELL &&
|
||||
//spell_id != SPELL_ADRENALINE_SWELL_RK2 &&
|
||||
//spell_id != SPELL_ADRENALINE_SWELL_RK3 &&
|
||||
if (
|
||||
IsGroupSpell(spell_id) &&
|
||||
IsCompleteHealSpell(spell_id)
|
||||
IsHealOverTimeSpell(spell_id) ||
|
||||
IsGroupHealOverTimeSpell(spell_id) ||
|
||||
IsFastHealSpell(spell_id) ||
|
||||
IsVeryFastHealSpell(spell_id) ||
|
||||
IsRegularSingleTargetHealSpell(spell_id) ||
|
||||
IsRegularGroupHealSpell(spell_id) ||
|
||||
IsCompleteHealSpell(spell_id) ||
|
||||
IsGroupCompleteHealSpell(spell_id) ||
|
||||
IsRegularPetHealSpell(spell_id)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
@ -1438,22 +1708,64 @@ bool IsGroupCompleteHealSpell(uint16 spell_id)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsGroupHealOverTimeSpell(uint16 spell_id)
|
||||
{
|
||||
bool IsAnyBuffSpell(uint16 spell_id) {
|
||||
if (!IsValidSpell(spell_id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (
|
||||
IsGroupSpell(spell_id) &&
|
||||
IsHealOverTimeSpell(spell_id) &&
|
||||
spells[spell_id].buff_duration < 10
|
||||
spell_id == SPELL_NATURES_RECOVERY ||
|
||||
IsBuffSpell(spell_id) &&
|
||||
IsBeneficialSpell(spell_id) &&
|
||||
!IsBardSong(spell_id) &&
|
||||
!IsEscapeSpell(spell_id) &&
|
||||
(!IsSummonPetSpell(spell_id) && !IsEffectInSpell(spell_id, SE_TemporaryPets))
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
bool IsDispelSpell(uint16 spell_id) {
|
||||
if (!IsValidSpell(spell_id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (
|
||||
IsEffectInSpell(spell_id, SE_CancelMagic) ||
|
||||
IsEffectInSpell(spell_id, SE_DispelBeneficial) ||
|
||||
IsEffectInSpell(spell_id, SE_DispelBeneficial)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsEscapeSpell(uint16 spell_id) {
|
||||
if (!IsValidSpell(spell_id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (
|
||||
IsInvulnerabilitySpell(spell_id) ||
|
||||
IsEffectInSpell(spell_id, SE_FeignDeath) ||
|
||||
IsEffectInSpell(spell_id, SE_DeathSave) ||
|
||||
IsEffectInSpell(spell_id, SE_Destroy) ||
|
||||
(
|
||||
IsEffectInSpell(spell_id, SE_WipeHateList) &&
|
||||
spells[spell_id].base_value[GetSpellEffectIndex(spell_id, SE_WipeHateList)] > 0
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
bool IsDebuffSpell(uint16 spell_id)
|
||||
{
|
||||
if (
|
||||
if (!IsValidSpell(spell_id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return !(
|
||||
IsBeneficialSpell(spell_id) ||
|
||||
IsHealthSpell(spell_id) ||
|
||||
IsStunSpell(spell_id) ||
|
||||
@ -1464,17 +1776,39 @@ bool IsDebuffSpell(uint16 spell_id)
|
||||
IsEffectInSpell(spell_id, SE_CancelMagic) ||
|
||||
IsEffectInSpell(spell_id, SE_MovementSpeed) ||
|
||||
IsFearSpell(spell_id) ||
|
||||
IsEffectInSpell(spell_id, SE_InstantHate)
|
||||
) {
|
||||
IsEffectInSpell(spell_id, SE_InstantHate) ||
|
||||
IsEffectInSpell(spell_id, SE_TossUp)
|
||||
);
|
||||
}
|
||||
|
||||
bool IsHateReduxSpell(uint16 spell_id) {
|
||||
if (!IsValidSpell(spell_id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
return (
|
||||
(
|
||||
IsEffectInSpell(spell_id, SE_InstantHate) &&
|
||||
spells[spell_id].base_value[GetSpellEffectIndex(spell_id, SE_InstantHate)] < 0
|
||||
) ||
|
||||
(
|
||||
IsEffectInSpell(spell_id, SE_Hate) &&
|
||||
spells[spell_id].base_value[GetSpellEffectIndex(spell_id, SE_Hate)] < 0
|
||||
) ||
|
||||
(
|
||||
IsEffectInSpell(spell_id, SE_ReduceHate) &&
|
||||
spells[spell_id].base_value[GetSpellEffectIndex(spell_id, SE_ReduceHate)] < 0
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
bool IsResistDebuffSpell(uint16 spell_id)
|
||||
{
|
||||
if (
|
||||
if (!IsValidSpell(spell_id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (
|
||||
!IsBeneficialSpell(spell_id) &&
|
||||
(
|
||||
IsEffectInSpell(spell_id, SE_ResistFire) ||
|
||||
@ -1485,42 +1819,34 @@ bool IsResistDebuffSpell(uint16 spell_id)
|
||||
IsEffectInSpell(spell_id, SE_ResistAll) ||
|
||||
IsEffectInSpell(spell_id, SE_ResistCorruption)
|
||||
)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
);
|
||||
}
|
||||
|
||||
bool IsSelfConversionSpell(uint16 spell_id)
|
||||
{
|
||||
if (
|
||||
if (!IsValidSpell(spell_id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (
|
||||
GetSpellTargetType(spell_id) == ST_Self &&
|
||||
IsEffectInSpell(spell_id, SE_CurrentMana) &&
|
||||
IsEffectInSpell(spell_id, SE_CurrentHP) &&
|
||||
spells[spell_id].base_value[GetSpellEffectIndex(spell_id, SE_CurrentMana)] > 0 &&
|
||||
spells[spell_id].base_value[GetSpellEffectIndex(spell_id, SE_CurrentHP)] < 0
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
);
|
||||
}
|
||||
|
||||
// returns true for both detrimental and beneficial buffs
|
||||
bool IsBuffSpell(uint16 spell_id)
|
||||
{
|
||||
if (
|
||||
return (
|
||||
IsValidSpell(spell_id) &&
|
||||
(
|
||||
spells[spell_id].buff_duration ||
|
||||
spells[spell_id].buff_duration_formula
|
||||
)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
);
|
||||
}
|
||||
|
||||
bool IsPersistDeathSpell(uint16 spell_id)
|
||||
@ -2383,7 +2709,7 @@ bool AegolismStackingIsSymbolSpell(uint16 spell_id) {
|
||||
|
||||
if ((i < 2 && spells[spell_id].effect_id[i] != SE_CHA) ||
|
||||
i > 3 && spells[spell_id].effect_id[i] != SE_Blank) {
|
||||
return 0;;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (i == 2 && spells[spell_id].effect_id[i] == SE_TotalHP) {
|
||||
@ -2430,3 +2756,196 @@ bool AegolismStackingIsArmorClassSpell(uint16 spell_id) {
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int8 SpellEffectsCount(uint16 spell_id) {
|
||||
if (!IsValidSpell(spell_id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int8 x = 0;
|
||||
|
||||
for (int i = 0; i < EFFECT_COUNT; i++) {
|
||||
if (!IsBlankSpellEffect(spell_id, i)) {
|
||||
++x;
|
||||
}
|
||||
}
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
bool IsLichSpell(uint16 spell_id)
|
||||
{
|
||||
if (!IsValidSpell(spell_id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (
|
||||
GetSpellTargetType(spell_id) == ST_Self &&
|
||||
IsEffectInSpell(spell_id, SE_CurrentMana) &&
|
||||
IsEffectInSpell(spell_id, SE_CurrentHP) &&
|
||||
spells[spell_id].base_value[GetSpellEffectIndex(spell_id, SE_CurrentMana)] > 0 &&
|
||||
spells[spell_id].base_value[GetSpellEffectIndex(spell_id, SE_CurrentHP)] < 0 &&
|
||||
spells[spell_id].buff_duration > 0
|
||||
);
|
||||
}
|
||||
|
||||
bool IsValidSpellAndLoS(uint32 spell_id, bool has_los) {
|
||||
if (!IsValidSpell(spell_id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!has_los && IsTargetRequiredForSpell(spell_id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IsInstantHealSpell(uint32 spell_id) {
|
||||
if (!IsValidSpell(spell_id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (
|
||||
IsRegularSingleTargetHealSpell(spell_id) ||
|
||||
IsRegularGroupHealSpell(spell_id) ||
|
||||
IsRegularPetHealSpell(spell_id) ||
|
||||
IsRegularGroupHealSpell(spell_id) ||
|
||||
spell_id == SPELL_COMPLETE_HEAL
|
||||
);
|
||||
}
|
||||
|
||||
bool IsResurrectSpell(uint16 spell_id)
|
||||
{
|
||||
if (!IsValidSpell(spell_id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return IsEffectInSpell(spell_id, SE_Revive);
|
||||
}
|
||||
|
||||
bool IsResistanceBuffSpell(uint16 spell_id) {
|
||||
if (!IsValidSpell(spell_id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto& spell = spells[spell_id];
|
||||
|
||||
for (int i = 0; i < EFFECT_COUNT; i++) {
|
||||
if (IsBlankSpellEffect(spell_id, i)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (
|
||||
spell.effect_id[i] == SE_ResistFire ||
|
||||
spell.effect_id[i] == SE_ResistCold ||
|
||||
spell.effect_id[i] == SE_ResistPoison ||
|
||||
spell.effect_id[i] == SE_ResistDisease ||
|
||||
spell.effect_id[i] == SE_ResistMagic ||
|
||||
spell.effect_id[i] == SE_ResistCorruption ||
|
||||
spell.effect_id[i] == SE_ResistAll
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsResistanceOnlySpell(uint16 spell_id) {
|
||||
if (!IsValidSpell(spell_id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto& spell = spells[spell_id];
|
||||
|
||||
for (int i = 0; i < EFFECT_COUNT; i++) {
|
||||
if (IsBlankSpellEffect(spell_id, i)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (
|
||||
spell.effect_id[i] == SE_ResistFire ||
|
||||
spell.effect_id[i] == SE_ResistCold ||
|
||||
spell.effect_id[i] == SE_ResistPoison ||
|
||||
spell.effect_id[i] == SE_ResistDisease ||
|
||||
spell.effect_id[i] == SE_ResistMagic ||
|
||||
spell.effect_id[i] == SE_ResistCorruption ||
|
||||
spell.effect_id[i] == SE_ResistAll
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IsDamageShieldOnlySpell(uint16 spell_id) {
|
||||
if (!IsValidSpell(spell_id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto& spell = spells[spell_id];
|
||||
|
||||
for (int i = 0; i < EFFECT_COUNT; i++) {
|
||||
if (IsBlankSpellEffect(spell_id, i)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (
|
||||
spell.effect_id[i] != SE_DamageShield
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IsDamageShieldAndResistSpell(uint16 spell_id) {
|
||||
if (!IsValidSpell(spell_id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto& spell = spells[spell_id];
|
||||
|
||||
for (int i = 0; i < EFFECT_COUNT; i++) {
|
||||
if (IsBlankSpellEffect(spell_id, i)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (
|
||||
spell.effect_id[i] != SE_DamageShield &&
|
||||
spell.effect_id[i] != SE_ResistFire &&
|
||||
spell.effect_id[i] != SE_ResistCold &&
|
||||
spell.effect_id[i] != SE_ResistPoison &&
|
||||
spell.effect_id[i] != SE_ResistDisease &&
|
||||
spell.effect_id[i] != SE_ResistMagic &&
|
||||
spell.effect_id[i] != SE_ResistCorruption &&
|
||||
spell.effect_id[i] != SE_ResistAll
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IsHateSpell(uint16 spell_id) {
|
||||
if (!IsValidSpell(spell_id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (
|
||||
(
|
||||
IsEffectInSpell(spell_id, SE_Hate) &&
|
||||
spells[spell_id].base_value[GetSpellEffectIndex(spell_id, SE_Hate)] > 0
|
||||
) ||
|
||||
(
|
||||
IsEffectInSpell(spell_id, SE_InstantHate) &&
|
||||
spells[spell_id].base_value[GetSpellEffectIndex(spell_id, SE_InstantHate)] > 0
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
335
common/spdat.h
335
common/spdat.h
@ -213,6 +213,10 @@
|
||||
#define SPELL_BLOODTHIRST 8476
|
||||
#define SPELL_AMPLIFICATION 2603
|
||||
#define SPELL_DIVINE_REZ 2738
|
||||
#define SPELL_NATURES_RECOVERY 2520
|
||||
#define SPELL_ADRENALINE_SWELL 14445
|
||||
#define SPELL_ADRENALINE_SWELL_RK2 14446
|
||||
#define SPELL_ADRENALINE_SWELL_RK3 14447
|
||||
|
||||
// discipline IDs.
|
||||
#define DISC_UNHOLY_AURA 4520
|
||||
@ -623,38 +627,294 @@ enum ProcType
|
||||
|
||||
enum SpellTypes : uint32
|
||||
{
|
||||
SpellType_Nuke = (1 << 0),
|
||||
SpellType_Heal = (1 << 1),
|
||||
SpellType_Root = (1 << 2),
|
||||
SpellType_Buff = (1 << 3),
|
||||
SpellType_Escape = (1 << 4),
|
||||
SpellType_Pet = (1 << 5),
|
||||
SpellType_Lifetap = (1 << 6),
|
||||
SpellType_Snare = (1 << 7),
|
||||
SpellType_DOT = (1 << 8),
|
||||
SpellType_Dispel = (1 << 9),
|
||||
SpellType_InCombatBuff = (1 << 10),
|
||||
SpellType_Mez = (1 << 11),
|
||||
SpellType_Charm = (1 << 12),
|
||||
SpellType_Slow = (1 << 13),
|
||||
SpellType_Debuff = (1 << 14),
|
||||
SpellType_Cure = (1 << 15),
|
||||
SpellType_Resurrect = (1 << 16),
|
||||
SpellType_HateRedux = (1 << 17),
|
||||
SpellType_InCombatBuffSong = (1 << 18), // bard in-combat group/ae buffs
|
||||
SpellType_OutOfCombatBuffSong = (1 << 19), // bard out-of-combat group/ae buffs
|
||||
SpellType_PreCombatBuff = (1 << 20),
|
||||
SpellType_PreCombatBuffSong = (1 << 21)
|
||||
SpellType_Nuke = (1 << 0),
|
||||
SpellType_Heal = (1 << 1),
|
||||
SpellType_Root = (1 << 2),
|
||||
SpellType_Buff = (1 << 3),
|
||||
SpellType_Escape = (1 << 4),
|
||||
SpellType_Pet = (1 << 5),
|
||||
SpellType_Lifetap = (1 << 6),
|
||||
SpellType_Snare = (1 << 7),
|
||||
SpellType_DOT = (1 << 8),
|
||||
SpellType_Dispel = (1 << 9),
|
||||
SpellType_InCombatBuff = (1 << 10),
|
||||
SpellType_Mez = (1 << 11),
|
||||
SpellType_Charm = (1 << 12),
|
||||
SpellType_Slow = (1 << 13),
|
||||
SpellType_Debuff = (1 << 14),
|
||||
SpellType_Cure = (1 << 15),
|
||||
SpellType_Resurrect = (1 << 16),
|
||||
SpellType_HateRedux = (1 << 17),
|
||||
SpellType_InCombatBuffSong = (1 << 18), // bard in-combat group/ae buffs
|
||||
SpellType_OutOfCombatBuffSong = (1 << 19), // bard out-of-combat group/ae buffs
|
||||
SpellType_PreCombatBuff = (1 << 20),
|
||||
SpellType_PreCombatBuffSong = (1 << 21)
|
||||
};
|
||||
|
||||
const uint32 SPELL_TYPE_MIN = (SpellType_Nuke << 1) - 1;
|
||||
const uint32 SPELL_TYPE_MAX = (SpellType_PreCombatBuffSong << 1) - 1;
|
||||
const uint32 SPELL_TYPE_ANY = 0xFFFFFFFF;
|
||||
namespace BotSpellTypes
|
||||
{
|
||||
constexpr uint16 Nuke = 0;
|
||||
constexpr uint16 RegularHeal = 1;
|
||||
constexpr uint16 Root = 2;
|
||||
constexpr uint16 Buff = 3;
|
||||
constexpr uint16 Escape = 4;
|
||||
constexpr uint16 Pet = 5;
|
||||
constexpr uint16 Lifetap = 6;
|
||||
constexpr uint16 Snare = 7;
|
||||
constexpr uint16 DOT = 8;
|
||||
constexpr uint16 Dispel = 9;
|
||||
constexpr uint16 InCombatBuff = 10;
|
||||
constexpr uint16 Mez = 11;
|
||||
constexpr uint16 Charm = 12;
|
||||
constexpr uint16 Slow = 13;
|
||||
constexpr uint16 Debuff = 14;
|
||||
constexpr uint16 Cure = 15;
|
||||
constexpr uint16 Resurrect = 16;
|
||||
constexpr uint16 HateRedux = 17;
|
||||
constexpr uint16 InCombatBuffSong = 18;
|
||||
constexpr uint16 OutOfCombatBuffSong = 19;
|
||||
constexpr uint16 PreCombatBuff = 20;
|
||||
constexpr uint16 PreCombatBuffSong = 21;
|
||||
constexpr uint16 Fear = 22;
|
||||
constexpr uint16 Stun = 23;
|
||||
constexpr uint16 HateLine = 24;
|
||||
constexpr uint16 GroupCures = 25;
|
||||
constexpr uint16 CompleteHeal = 26;
|
||||
constexpr uint16 FastHeals = 27;
|
||||
constexpr uint16 VeryFastHeals = 28;
|
||||
constexpr uint16 GroupHeals = 29;
|
||||
constexpr uint16 GroupCompleteHeals = 30;
|
||||
constexpr uint16 GroupHoTHeals = 31;
|
||||
constexpr uint16 HoTHeals = 32;
|
||||
constexpr uint16 AENukes = 33;
|
||||
constexpr uint16 AERains = 34;
|
||||
constexpr uint16 AEMez = 35;
|
||||
constexpr uint16 AEStun = 36;
|
||||
constexpr uint16 AEDebuff = 37;
|
||||
constexpr uint16 AESlow = 38;
|
||||
constexpr uint16 AESnare = 39;
|
||||
constexpr uint16 AEFear = 40;
|
||||
constexpr uint16 AEDispel = 41;
|
||||
constexpr uint16 AERoot = 42;
|
||||
constexpr uint16 AEDoT = 43;
|
||||
constexpr uint16 AELifetap = 44;
|
||||
constexpr uint16 AEHateLine = 45;
|
||||
constexpr uint16 PBAENuke = 46;
|
||||
constexpr uint16 PetBuffs = 47;
|
||||
constexpr uint16 PetRegularHeals = 48;
|
||||
constexpr uint16 PetCompleteHeals = 49;
|
||||
constexpr uint16 PetFastHeals = 50;
|
||||
constexpr uint16 PetVeryFastHeals = 51;
|
||||
constexpr uint16 PetHoTHeals = 52;
|
||||
constexpr uint16 PetCures = 53;
|
||||
constexpr uint16 DamageShields = 54;
|
||||
constexpr uint16 ResistBuffs = 55;
|
||||
constexpr uint16 PetDamageShields = 56;
|
||||
constexpr uint16 PetResistBuffs = 57;
|
||||
|
||||
// Command Spell Types
|
||||
constexpr uint16 Teleport = 100; // this is handled by ^depart so uses other logic
|
||||
constexpr uint16 Lull = 101;
|
||||
constexpr uint16 Succor = 102;
|
||||
constexpr uint16 BindAffinity = 103;
|
||||
constexpr uint16 Identify = 104;
|
||||
constexpr uint16 Levitate = 105;
|
||||
constexpr uint16 Rune = 106;
|
||||
constexpr uint16 WaterBreathing = 107;
|
||||
constexpr uint16 Size = 108;
|
||||
constexpr uint16 Invisibility = 109;
|
||||
constexpr uint16 MovementSpeed = 110;
|
||||
constexpr uint16 SendHome = 111;
|
||||
constexpr uint16 SummonCorpse = 112;
|
||||
constexpr uint16 AELull = 113;
|
||||
|
||||
// Discipline Types
|
||||
constexpr uint16 Discipline = 200;
|
||||
constexpr uint16 DiscAggressive = 201;
|
||||
constexpr uint16 DiscDefensive = 202;
|
||||
constexpr uint16 DiscUtility = 203;
|
||||
|
||||
constexpr uint16 START = BotSpellTypes::Nuke; // Do not remove or change this
|
||||
constexpr uint16 END = BotSpellTypes::PetResistBuffs; // Do not remove this, increment as needed
|
||||
constexpr uint16 COMMANDED_START = BotSpellTypes::Lull; // Do not remove or change this
|
||||
constexpr uint16 COMMANDED_END = BotSpellTypes::AELull; // Do not remove this, increment as needed
|
||||
constexpr uint16 DISCIPLINE_START = BotSpellTypes::Discipline; // Do not remove or change this
|
||||
constexpr uint16 DISCIPLINE_END = BotSpellTypes::DiscUtility; // Do not remove this, increment as needed
|
||||
}
|
||||
|
||||
static std::map<uint16, std::string> spellType_names = {
|
||||
{ BotSpellTypes::Nuke, "Nuke" },
|
||||
{ BotSpellTypes::RegularHeal, "Regular Heal" },
|
||||
{ BotSpellTypes::Root, "Root" },
|
||||
{ BotSpellTypes::Buff, "Buff" },
|
||||
{ BotSpellTypes::Escape, "Escape" },
|
||||
{ BotSpellTypes::Pet, "Pet" },
|
||||
{ BotSpellTypes::Lifetap, "Lifetap" },
|
||||
{ BotSpellTypes::Snare, "Snare" },
|
||||
{ BotSpellTypes::DOT, "DoT" },
|
||||
{ BotSpellTypes::Dispel, "Dispel" },
|
||||
{ BotSpellTypes::InCombatBuff, "In-Combat Buff" },
|
||||
{ BotSpellTypes::Mez, "Mez" },
|
||||
{ BotSpellTypes::Charm, "Charm" },
|
||||
{ BotSpellTypes::Slow, "Slow" },
|
||||
{ BotSpellTypes::Debuff, "Debuff" },
|
||||
{ BotSpellTypes::Cure, "Cure" },
|
||||
{ BotSpellTypes::GroupCures, "Group Cure" },
|
||||
{ BotSpellTypes::PetCures, "Pet Cure" },
|
||||
{ BotSpellTypes::Resurrect, "Resurrect" },
|
||||
{ BotSpellTypes::HateRedux, "Hate Reduction" },
|
||||
{ BotSpellTypes::InCombatBuffSong, "In-Combat Buff Song" },
|
||||
{ BotSpellTypes::OutOfCombatBuffSong, "Out-of-Combat Buff Song" },
|
||||
{ BotSpellTypes::PreCombatBuff, "Pre-Combat Buff" },
|
||||
{ BotSpellTypes::PreCombatBuffSong, "Pre-Combat Buff Song" },
|
||||
{ BotSpellTypes::Fear, "Fear" },
|
||||
{ BotSpellTypes::Stun, "Stun" },
|
||||
{ BotSpellTypes::CompleteHeal, "Complete Heal" },
|
||||
{ BotSpellTypes::FastHeals, "Fast Heal" },
|
||||
{ BotSpellTypes::VeryFastHeals, "Very Fast Heal" },
|
||||
{ BotSpellTypes::GroupHeals, "Group Heal" },
|
||||
{ BotSpellTypes::GroupCompleteHeals, "Group Complete Heal" },
|
||||
{ BotSpellTypes::GroupHoTHeals, "Group HoT Heal" },
|
||||
{ BotSpellTypes::HoTHeals, "HoT Heal" },
|
||||
{ BotSpellTypes::AENukes, "AE Nuke" },
|
||||
{ BotSpellTypes::AERains, "AE Rain" },
|
||||
{ BotSpellTypes::AEMez, "AE Mez" },
|
||||
{ BotSpellTypes::AEStun, "AE Stun" },
|
||||
{ BotSpellTypes::AEDebuff, "AE Debuff" },
|
||||
{ BotSpellTypes::AESlow, "AE Slow" },
|
||||
{ BotSpellTypes::AESnare, "AE Snare" },
|
||||
{ BotSpellTypes::AEFear, "AE Fear" },
|
||||
{ BotSpellTypes::AEDispel, "AE Dispel" },
|
||||
{ BotSpellTypes::AERoot, "AE Root" },
|
||||
{ BotSpellTypes::AEDoT, "AE DoT" },
|
||||
{ BotSpellTypes::AELifetap, "AE Lifetap" },
|
||||
{ BotSpellTypes::PBAENuke, "PBAE Nuke" },
|
||||
{ BotSpellTypes::PetBuffs, "Pet Buff" },
|
||||
{ BotSpellTypes::PetRegularHeals, "Pet Regular Heal" },
|
||||
{ BotSpellTypes::PetCompleteHeals, "Pet Complete Heal" },
|
||||
{ BotSpellTypes::PetFastHeals, "Pet Fast Heal" },
|
||||
{ BotSpellTypes::PetVeryFastHeals, "Pet Very Fast Heal" },
|
||||
{ BotSpellTypes::PetHoTHeals, "Pet HoT Heal" },
|
||||
{ BotSpellTypes::DamageShields, "Damage Shield" },
|
||||
{ BotSpellTypes::ResistBuffs, "Resist Buff" },
|
||||
{ BotSpellTypes::PetDamageShields, "Pet Damage Shield" },
|
||||
{ BotSpellTypes::PetResistBuffs, "Pet Resist Buff" },
|
||||
{ BotSpellTypes::HateLine, "Hate Line" },
|
||||
{ BotSpellTypes::AEHateLine, "AE Hate Line" },
|
||||
{ BotSpellTypes::Lull, "Lull" },
|
||||
{ BotSpellTypes::Teleport, "Teleport" },
|
||||
{ BotSpellTypes::Succor, "Succor" },
|
||||
{ BotSpellTypes::BindAffinity, "Bind Affinity" },
|
||||
{ BotSpellTypes::Identify, "Identify" },
|
||||
{ BotSpellTypes::Levitate, "Levitate" },
|
||||
{ BotSpellTypes::Rune, "Rune" },
|
||||
{ BotSpellTypes::WaterBreathing, "Water Breathing" },
|
||||
{ BotSpellTypes::Size, "Size" },
|
||||
{ BotSpellTypes::Invisibility, "Invisibility" },
|
||||
{ BotSpellTypes::MovementSpeed, "Movement Speed" },
|
||||
{ BotSpellTypes::SendHome, "Send Home" },
|
||||
{ BotSpellTypes::SummonCorpse, "Summon Corpse" },
|
||||
{ BotSpellTypes::AELull, "AE Lull" }
|
||||
};
|
||||
|
||||
static std::map<uint16, std::string> spellType_shortNames = {
|
||||
{ BotSpellTypes::Nuke, "nukes" },
|
||||
{ BotSpellTypes::RegularHeal, "regularheals" },
|
||||
{ BotSpellTypes::Root, "roots" },
|
||||
{ BotSpellTypes::Buff, "buffs" },
|
||||
{ BotSpellTypes::Escape, "escapes" },
|
||||
{ BotSpellTypes::Pet, "pets" },
|
||||
{ BotSpellTypes::Lifetap, "lifetaps" },
|
||||
{ BotSpellTypes::Snare, "snares" },
|
||||
{ BotSpellTypes::DOT, "dots" },
|
||||
{ BotSpellTypes::Dispel, "dispels" },
|
||||
{ BotSpellTypes::InCombatBuff, "incombatbuffs" },
|
||||
{ BotSpellTypes::Mez, "mez" },
|
||||
{ BotSpellTypes::Charm, "charms" },
|
||||
{ BotSpellTypes::Slow, "slows" },
|
||||
{ BotSpellTypes::Debuff, "debuffs" },
|
||||
{ BotSpellTypes::Cure, "cures" },
|
||||
{ BotSpellTypes::GroupCures, "groupcures" },
|
||||
{ BotSpellTypes::PetCures, "petcures" },
|
||||
{ BotSpellTypes::Resurrect, "resurrects" },
|
||||
{ BotSpellTypes::HateRedux, "hateredux" },
|
||||
{ BotSpellTypes::InCombatBuffSong, "incombatbuffsongs" },
|
||||
{ BotSpellTypes::OutOfCombatBuffSong, "outofcombatbuffsongs" },
|
||||
{ BotSpellTypes::PreCombatBuff, "precombatbuffs" },
|
||||
{ BotSpellTypes::PreCombatBuffSong, "precombatbuffsongs" },
|
||||
{ BotSpellTypes::Fear, "fears" },
|
||||
{ BotSpellTypes::Stun, "stuns" },
|
||||
{ BotSpellTypes::CompleteHeal, "completeheals" },
|
||||
{ BotSpellTypes::FastHeals, "fastheals" },
|
||||
{ BotSpellTypes::VeryFastHeals, "veryfastheals" },
|
||||
{ BotSpellTypes::GroupHeals, "groupheals" },
|
||||
{ BotSpellTypes::GroupCompleteHeals, "groupcompleteheals" },
|
||||
{ BotSpellTypes::GroupHoTHeals, "grouphotheals" },
|
||||
{ BotSpellTypes::HoTHeals, "hotheals" },
|
||||
{ BotSpellTypes::AENukes, "aenukes" },
|
||||
{ BotSpellTypes::AERains, "aerains" },
|
||||
{ BotSpellTypes::AEMez, "aemez" },
|
||||
{ BotSpellTypes::AEStun, "aestuns" },
|
||||
{ BotSpellTypes::AEDebuff, "aedebuffs" },
|
||||
{ BotSpellTypes::AESlow, "aeslows" },
|
||||
{ BotSpellTypes::AESnare, "aesnares" },
|
||||
{ BotSpellTypes::AEFear, "aefears" },
|
||||
{ BotSpellTypes::AEDispel, "aedispels" },
|
||||
{ BotSpellTypes::AERoot, "aeroots" },
|
||||
{ BotSpellTypes::AEDoT, "aedots" },
|
||||
{ BotSpellTypes::AELifetap, "aelifetaps" },
|
||||
{ BotSpellTypes::PBAENuke, "pbaenukes" },
|
||||
{ BotSpellTypes::PetBuffs, "petbuffs" },
|
||||
{ BotSpellTypes::PetRegularHeals, "petregularheals" },
|
||||
{ BotSpellTypes::PetCompleteHeals, "petcompleteheals" },
|
||||
{ BotSpellTypes::PetFastHeals, "petfastheals" },
|
||||
{ BotSpellTypes::PetVeryFastHeals, "petveryfastheals" },
|
||||
{ BotSpellTypes::PetHoTHeals, "pethotheals" },
|
||||
{ BotSpellTypes::DamageShields, "damageshields" },
|
||||
{ BotSpellTypes::ResistBuffs, "resistbuffs" },
|
||||
{ BotSpellTypes::PetDamageShields, "petdamageshields" },
|
||||
{ BotSpellTypes::PetResistBuffs, "petresistbuffs" },
|
||||
{ BotSpellTypes::HateLine, "hatelines" },
|
||||
{ BotSpellTypes::AEHateLine, "aehatelines" },
|
||||
{ BotSpellTypes::Lull, "lull" },
|
||||
{ BotSpellTypes::Teleport, "teleport" },
|
||||
{ BotSpellTypes::Succor, "succor" },
|
||||
{ BotSpellTypes::BindAffinity, "bindaffinity" },
|
||||
{ BotSpellTypes::Identify, "identify" },
|
||||
{ BotSpellTypes::Levitate, "levitate" },
|
||||
{ BotSpellTypes::Rune, "rune" },
|
||||
{ BotSpellTypes::WaterBreathing, "waterbreathing" },
|
||||
{ BotSpellTypes::Size, "size" },
|
||||
{ BotSpellTypes::Invisibility, "invisibility" },
|
||||
{ BotSpellTypes::MovementSpeed, "movementspeed" },
|
||||
{ BotSpellTypes::SendHome, "sendhome" },
|
||||
{ BotSpellTypes::SummonCorpse, "summoncorpse" },
|
||||
{ BotSpellTypes::AELull, "aelull" }
|
||||
};
|
||||
|
||||
const uint32 SPELL_TYPES_DETRIMENTAL = (SpellType_Nuke | SpellType_Root | SpellType_Lifetap | SpellType_Snare | SpellType_DOT | SpellType_Dispel | SpellType_Mez | SpellType_Charm | SpellType_Debuff | SpellType_Slow);
|
||||
const uint32 SPELL_TYPES_BENEFICIAL = (SpellType_Heal | SpellType_Buff | SpellType_Escape | SpellType_Pet | SpellType_InCombatBuff | SpellType_Cure | SpellType_HateRedux | SpellType_InCombatBuffSong | SpellType_OutOfCombatBuffSong | SpellType_PreCombatBuff | SpellType_PreCombatBuffSong);
|
||||
const uint32 SPELL_TYPES_INNATE = (SpellType_Nuke | SpellType_Lifetap | SpellType_DOT | SpellType_Dispel | SpellType_Mez | SpellType_Slow | SpellType_Debuff | SpellType_Charm | SpellType_Root);
|
||||
|
||||
// Bot related functions
|
||||
bool IsBotSpellTypeDetrimental (uint16 spell_type);
|
||||
bool IsBotSpellTypeBeneficial (uint16 spell_type);
|
||||
bool IsBotSpellTypeOtherBeneficial(uint16 spell_type);
|
||||
bool IsBotSpellTypeInnate (uint16 spell_type);
|
||||
bool IsAEBotSpellType(uint16 spell_type);
|
||||
bool IsGroupBotSpellType(uint16 spell_type);
|
||||
bool IsGroupTargetOnlyBotSpellType(uint16 spell_type);
|
||||
bool IsPetBotSpellType(uint16 spell_type);
|
||||
bool IsClientBotSpellType(uint16 spell_type);
|
||||
bool IsHealBotSpellType(uint16 spell_type);
|
||||
bool BotSpellTypeRequiresLoS(uint16 spell_type);
|
||||
bool BotSpellTypeRequiresTarget(uint16 spell_type);
|
||||
bool BotSpellTypeRequiresAEChecks(uint16 spell_type);
|
||||
bool IsCommandedBotSpellType(uint16 spell_type);
|
||||
bool IsPullingBotSpellType(uint16 spell_type);
|
||||
uint16 GetCorrectBotSpellType(uint16 spell_type, uint16 spell_id);
|
||||
uint16 GetPetBotSpellType(uint16 spell_type);
|
||||
|
||||
// These should not be used to determine spell category..
|
||||
// They are a graphical affects (effects?) index only
|
||||
// TODO: import sai list
|
||||
@ -1503,6 +1763,7 @@ bool IsTargetableAESpell(uint16 spell_id);
|
||||
bool IsSacrificeSpell(uint16 spell_id);
|
||||
bool IsLifetapSpell(uint16 spell_id);
|
||||
bool IsMesmerizeSpell(uint16 spell_id);
|
||||
bool SpellBreaksMez(uint16 spell_id);
|
||||
bool IsStunSpell(uint16 spell_id);
|
||||
bool IsSlowSpell(uint16 spell_id);
|
||||
bool IsHasteSpell(uint16 spell_id);
|
||||
@ -1536,6 +1797,11 @@ bool IsPureNukeSpell(uint16 spell_id);
|
||||
bool IsAENukeSpell(uint16 spell_id);
|
||||
bool IsPBAENukeSpell(uint16 spell_id);
|
||||
bool IsAERainNukeSpell(uint16 spell_id);
|
||||
bool IsAnyNukeOrStunSpell(uint16 spell_id);
|
||||
bool IsAnyAESpell(uint16 spell_id);
|
||||
bool IsAESpell(uint16 spell_id);
|
||||
bool IsPBAESpell(uint16 spell_id);
|
||||
bool IsAERainSpell(uint16 spell_id);
|
||||
bool IsPartialResistableSpell(uint16 spell_id);
|
||||
bool IsResistableSpell(uint16 spell_id);
|
||||
bool IsGroupSpell(uint16 spell_id);
|
||||
@ -1545,8 +1811,11 @@ bool IsEffectInSpell(uint16 spell_id, int effect_id);
|
||||
uint16 GetSpellTriggerSpellID(uint16 spell_id, int effect_id);
|
||||
bool IsBlankSpellEffect(uint16 spell_id, int effect_index);
|
||||
bool IsValidSpell(uint32 spell_id);
|
||||
bool IsValidSpellAndLoS(uint32 spell_id, bool has_los = true);
|
||||
bool IsSummonSpell(uint16 spell_id);
|
||||
bool IsDamageSpell(uint16 spell_id);
|
||||
bool IsAnyDamageSpell(uint16 spell_id);
|
||||
bool IsDamageOverTimeSpell(uint16 spell_i);
|
||||
bool IsFearSpell(uint16 spell_id);
|
||||
bool IsCureSpell(uint16 spell_id);
|
||||
bool IsHarmTouchSpell(uint16 spell_id);
|
||||
@ -1585,10 +1854,16 @@ bool IsCompleteHealSpell(uint16 spell_id);
|
||||
bool IsFastHealSpell(uint16 spell_id);
|
||||
bool IsVeryFastHealSpell(uint16 spell_id);
|
||||
bool IsRegularSingleTargetHealSpell(uint16 spell_id);
|
||||
bool IsRegularPetHealSpell(uint16 spell_id);
|
||||
bool IsRegularGroupHealSpell(uint16 spell_id);
|
||||
bool IsGroupCompleteHealSpell(uint16 spell_id);
|
||||
bool IsGroupHealOverTimeSpell(uint16 spell_id);
|
||||
bool IsAnyHealSpell(uint16 spell_id);
|
||||
bool IsAnyBuffSpell(uint16 spell_id);
|
||||
bool IsDispelSpell(uint16 spell_id);
|
||||
bool IsEscapeSpell(uint16 spell_id);
|
||||
bool IsDebuffSpell(uint16 spell_id);
|
||||
bool IsHateReduxSpell(uint16 spell_id);
|
||||
bool IsResistDebuffSpell(uint16 spell_id);
|
||||
bool IsSelfConversionSpell(uint16 spell_id);
|
||||
bool IsBuffSpell(uint16 spell_id);
|
||||
@ -1628,5 +1903,15 @@ bool IsCastRestrictedSpell(uint16 spell_id);
|
||||
bool IsAegolismSpell(uint16 spell_id);
|
||||
bool AegolismStackingIsSymbolSpell(uint16 spell_id);
|
||||
bool AegolismStackingIsArmorClassSpell(uint16 spell_id);
|
||||
int8 SpellEffectsCount(uint16 spell_id);
|
||||
bool IsLichSpell(uint16 spell_id);
|
||||
bool IsInstantHealSpell(uint32 spell_id);
|
||||
bool IsResurrectSpell(uint16 spell_id);
|
||||
bool RequiresStackCheck(uint16 spell_type);
|
||||
bool IsResistanceBuffSpell(uint16 spell_id);
|
||||
bool IsResistanceOnlySpell(uint16 spell_id);
|
||||
bool IsDamageShieldOnlySpell(uint16 spell_id);
|
||||
bool IsDamageShieldAndResistSpell(uint16 spell_id);
|
||||
bool IsHateSpell(uint16 spell_id);
|
||||
|
||||
#endif
|
||||
|
||||
710
common/spdat_bot.cpp
Normal file
710
common/spdat_bot.cpp
Normal file
@ -0,0 +1,710 @@
|
||||
#include "spdat.h"
|
||||
|
||||
bool IsBotSpellTypeDetrimental(uint16 spell_type) {
|
||||
switch (spell_type) {
|
||||
case BotSpellTypes::Nuke:
|
||||
case BotSpellTypes::Root:
|
||||
case BotSpellTypes::Lifetap:
|
||||
case BotSpellTypes::Snare:
|
||||
case BotSpellTypes::DOT:
|
||||
case BotSpellTypes::Dispel:
|
||||
case BotSpellTypes::Mez:
|
||||
case BotSpellTypes::Charm:
|
||||
case BotSpellTypes::Slow:
|
||||
case BotSpellTypes::Debuff:
|
||||
case BotSpellTypes::HateRedux:
|
||||
case BotSpellTypes::Fear:
|
||||
case BotSpellTypes::Stun:
|
||||
case BotSpellTypes::AENukes:
|
||||
case BotSpellTypes::AERains:
|
||||
case BotSpellTypes::AEMez:
|
||||
case BotSpellTypes::AEStun:
|
||||
case BotSpellTypes::AEDebuff:
|
||||
case BotSpellTypes::AESlow:
|
||||
case BotSpellTypes::AESnare:
|
||||
case BotSpellTypes::AEFear:
|
||||
case BotSpellTypes::AEDispel:
|
||||
case BotSpellTypes::AERoot:
|
||||
case BotSpellTypes::AEDoT:
|
||||
case BotSpellTypes::AELifetap:
|
||||
case BotSpellTypes::PBAENuke:
|
||||
case BotSpellTypes::Lull:
|
||||
case BotSpellTypes::AELull:
|
||||
case BotSpellTypes::HateLine:
|
||||
case BotSpellTypes::AEHateLine:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsBotSpellTypeBeneficial(uint16 spell_type) {
|
||||
switch (spell_type) {
|
||||
case BotSpellTypes::RegularHeal:
|
||||
case BotSpellTypes::CompleteHeal:
|
||||
case BotSpellTypes::GroupCompleteHeals:
|
||||
case BotSpellTypes::FastHeals:
|
||||
case BotSpellTypes::VeryFastHeals:
|
||||
case BotSpellTypes::GroupHeals:
|
||||
case BotSpellTypes::GroupHoTHeals:
|
||||
case BotSpellTypes::HoTHeals:
|
||||
case BotSpellTypes::PetRegularHeals:
|
||||
case BotSpellTypes::PetCompleteHeals:
|
||||
case BotSpellTypes::PetFastHeals:
|
||||
case BotSpellTypes::PetVeryFastHeals:
|
||||
case BotSpellTypes::PetHoTHeals:
|
||||
case BotSpellTypes::Buff:
|
||||
case BotSpellTypes::Cure:
|
||||
case BotSpellTypes::GroupCures:
|
||||
case BotSpellTypes::PetCures:
|
||||
case BotSpellTypes::DamageShields:
|
||||
case BotSpellTypes::InCombatBuffSong:
|
||||
case BotSpellTypes::OutOfCombatBuffSong:
|
||||
case BotSpellTypes::Pet:
|
||||
case BotSpellTypes::PetBuffs:
|
||||
case BotSpellTypes::PreCombatBuff:
|
||||
case BotSpellTypes::PreCombatBuffSong:
|
||||
case BotSpellTypes::PetDamageShields:
|
||||
case BotSpellTypes::PetResistBuffs:
|
||||
case BotSpellTypes::ResistBuffs:
|
||||
case BotSpellTypes::Resurrect:
|
||||
case BotSpellTypes::Teleport:
|
||||
case BotSpellTypes::Succor:
|
||||
case BotSpellTypes::BindAffinity:
|
||||
case BotSpellTypes::Identify:
|
||||
case BotSpellTypes::Levitate:
|
||||
case BotSpellTypes::Rune:
|
||||
case BotSpellTypes::WaterBreathing:
|
||||
case BotSpellTypes::Size:
|
||||
case BotSpellTypes::Invisibility:
|
||||
case BotSpellTypes::MovementSpeed:
|
||||
case BotSpellTypes::SendHome:
|
||||
case BotSpellTypes::SummonCorpse:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsBotSpellTypeOtherBeneficial(uint16 spell_type) {
|
||||
switch (spell_type) {
|
||||
case BotSpellTypes::RegularHeal:
|
||||
case BotSpellTypes::CompleteHeal:
|
||||
case BotSpellTypes::GroupCompleteHeals:
|
||||
case BotSpellTypes::FastHeals:
|
||||
case BotSpellTypes::VeryFastHeals:
|
||||
case BotSpellTypes::GroupHeals:
|
||||
case BotSpellTypes::GroupHoTHeals:
|
||||
case BotSpellTypes::HoTHeals:
|
||||
case BotSpellTypes::PetRegularHeals:
|
||||
case BotSpellTypes::PetCompleteHeals:
|
||||
case BotSpellTypes::PetFastHeals:
|
||||
case BotSpellTypes::PetVeryFastHeals:
|
||||
case BotSpellTypes::PetHoTHeals:
|
||||
case BotSpellTypes::Buff:
|
||||
case BotSpellTypes::Cure:
|
||||
case BotSpellTypes::GroupCures:
|
||||
case BotSpellTypes::PetCures:
|
||||
case BotSpellTypes::DamageShields:
|
||||
case BotSpellTypes::PetDamageShields:
|
||||
case BotSpellTypes::PetBuffs:
|
||||
case BotSpellTypes::ResistBuffs:
|
||||
case BotSpellTypes::PetResistBuffs:
|
||||
case BotSpellTypes::Teleport:
|
||||
case BotSpellTypes::Succor:
|
||||
case BotSpellTypes::BindAffinity:
|
||||
case BotSpellTypes::Identify:
|
||||
case BotSpellTypes::Levitate:
|
||||
case BotSpellTypes::Rune:
|
||||
case BotSpellTypes::WaterBreathing:
|
||||
case BotSpellTypes::Size:
|
||||
case BotSpellTypes::Invisibility:
|
||||
case BotSpellTypes::MovementSpeed:
|
||||
case BotSpellTypes::SendHome:
|
||||
case BotSpellTypes::SummonCorpse:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsBotSpellTypeInnate(uint16 spell_type) {
|
||||
switch (spell_type) {
|
||||
case BotSpellTypes::AENukes:
|
||||
case BotSpellTypes::AERains:
|
||||
case BotSpellTypes::PBAENuke:
|
||||
case BotSpellTypes::Nuke:
|
||||
case BotSpellTypes::AEDispel:
|
||||
case BotSpellTypes::Dispel:
|
||||
case BotSpellTypes::AERoot:
|
||||
case BotSpellTypes::Root:
|
||||
case BotSpellTypes::AESlow:
|
||||
case BotSpellTypes::Slow:
|
||||
case BotSpellTypes::Charm:
|
||||
case BotSpellTypes::AEDebuff:
|
||||
case BotSpellTypes::Debuff:
|
||||
case BotSpellTypes::AEDoT:
|
||||
case BotSpellTypes::DOT:
|
||||
case BotSpellTypes::AELifetap:
|
||||
case BotSpellTypes::Lifetap:
|
||||
case BotSpellTypes::AEStun:
|
||||
case BotSpellTypes::Stun:
|
||||
case BotSpellTypes::AEMez:
|
||||
case BotSpellTypes::Mez:
|
||||
case BotSpellTypes::Lull:
|
||||
case BotSpellTypes::AELull:
|
||||
case BotSpellTypes::HateLine:
|
||||
case BotSpellTypes::AEHateLine:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsAEBotSpellType(uint16 spell_type) {
|
||||
switch (spell_type) {
|
||||
case BotSpellTypes::AEDebuff:
|
||||
case BotSpellTypes::AEFear:
|
||||
case BotSpellTypes::AEMez:
|
||||
case BotSpellTypes::AENukes:
|
||||
case BotSpellTypes::AERains:
|
||||
case BotSpellTypes::AESlow:
|
||||
case BotSpellTypes::AESnare:
|
||||
case BotSpellTypes::AEStun:
|
||||
case BotSpellTypes::AEDispel:
|
||||
case BotSpellTypes::AEDoT:
|
||||
case BotSpellTypes::PBAENuke:
|
||||
case BotSpellTypes::AELifetap:
|
||||
case BotSpellTypes::AERoot:
|
||||
case BotSpellTypes::AEHateLine:
|
||||
case BotSpellTypes::AELull:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsGroupBotSpellType(uint16 spell_type) {
|
||||
switch (spell_type) {
|
||||
case BotSpellTypes::GroupCures:
|
||||
case BotSpellTypes::GroupCompleteHeals:
|
||||
case BotSpellTypes::GroupHeals:
|
||||
case BotSpellTypes::GroupHoTHeals:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsGroupTargetOnlyBotSpellType(uint16 spell_type) {
|
||||
switch (spell_type) {
|
||||
case BotSpellTypes::GroupCures:
|
||||
case BotSpellTypes::GroupCompleteHeals:
|
||||
case BotSpellTypes::GroupHeals:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsPetBotSpellType(uint16 spell_type) {
|
||||
switch (spell_type) {
|
||||
case BotSpellTypes::PetBuffs:
|
||||
case BotSpellTypes::PetRegularHeals:
|
||||
case BotSpellTypes::PetCompleteHeals:
|
||||
case BotSpellTypes::PetFastHeals:
|
||||
case BotSpellTypes::PetVeryFastHeals:
|
||||
case BotSpellTypes::PetHoTHeals:
|
||||
case BotSpellTypes::PetDamageShields:
|
||||
case BotSpellTypes::PetResistBuffs:
|
||||
case BotSpellTypes::PetCures:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsClientBotSpellType(uint16 spell_type) {
|
||||
switch (spell_type) {
|
||||
case BotSpellTypes::RegularHeal:
|
||||
case BotSpellTypes::CompleteHeal:
|
||||
case BotSpellTypes::GroupCompleteHeals:
|
||||
case BotSpellTypes::FastHeals:
|
||||
case BotSpellTypes::VeryFastHeals:
|
||||
case BotSpellTypes::GroupHeals:
|
||||
case BotSpellTypes::GroupHoTHeals:
|
||||
case BotSpellTypes::HoTHeals:
|
||||
case BotSpellTypes::PetRegularHeals:
|
||||
case BotSpellTypes::PetCompleteHeals:
|
||||
case BotSpellTypes::PetFastHeals:
|
||||
case BotSpellTypes::PetVeryFastHeals:
|
||||
case BotSpellTypes::PetHoTHeals:
|
||||
case BotSpellTypes::Buff:
|
||||
case BotSpellTypes::Cure:
|
||||
case BotSpellTypes::GroupCures:
|
||||
case BotSpellTypes::PetCures:
|
||||
case BotSpellTypes::DamageShields:
|
||||
case BotSpellTypes::PetDamageShields:
|
||||
case BotSpellTypes::PetBuffs:
|
||||
case BotSpellTypes::ResistBuffs:
|
||||
case BotSpellTypes::PetResistBuffs:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsHealBotSpellType(uint16 spell_type) {
|
||||
switch (spell_type) {
|
||||
case BotSpellTypes::VeryFastHeals:
|
||||
case BotSpellTypes::FastHeals:
|
||||
case BotSpellTypes::RegularHeal:
|
||||
case BotSpellTypes::GroupHeals:
|
||||
case BotSpellTypes::CompleteHeal:
|
||||
case BotSpellTypes::GroupCompleteHeals:
|
||||
case BotSpellTypes::HoTHeals:
|
||||
case BotSpellTypes::GroupHoTHeals:
|
||||
case BotSpellTypes::PetRegularHeals:
|
||||
case BotSpellTypes::PetCompleteHeals:
|
||||
case BotSpellTypes::PetFastHeals:
|
||||
case BotSpellTypes::PetVeryFastHeals:
|
||||
case BotSpellTypes::PetHoTHeals:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool BotSpellTypeRequiresLoS(uint16 spell_type) {
|
||||
if (IsAEBotSpellType(spell_type)) { // These gather their own targets later
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (spell_type) {
|
||||
case BotSpellTypes::RegularHeal:
|
||||
case BotSpellTypes::GroupCompleteHeals:
|
||||
case BotSpellTypes::CompleteHeal:
|
||||
case BotSpellTypes::FastHeals:
|
||||
case BotSpellTypes::VeryFastHeals:
|
||||
case BotSpellTypes::GroupHeals:
|
||||
case BotSpellTypes::GroupHoTHeals:
|
||||
case BotSpellTypes::HoTHeals:
|
||||
case BotSpellTypes::PetRegularHeals:
|
||||
case BotSpellTypes::PetCompleteHeals:
|
||||
case BotSpellTypes::PetFastHeals:
|
||||
case BotSpellTypes::PetVeryFastHeals:
|
||||
case BotSpellTypes::PetHoTHeals:
|
||||
case BotSpellTypes::InCombatBuff:
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BotSpellTypeRequiresTarget(uint16 spell_type) {
|
||||
switch (spell_type) {
|
||||
case BotSpellTypes::Pet:
|
||||
case BotSpellTypes::Succor:
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BotSpellTypeRequiresAEChecks(uint16 spell_type) {
|
||||
switch (spell_type) {
|
||||
case BotSpellTypes::AEMez:
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RequiresStackCheck(uint16 spell_type) {
|
||||
switch (spell_type) {
|
||||
case BotSpellTypes::VeryFastHeals:
|
||||
case BotSpellTypes::PetVeryFastHeals:
|
||||
case BotSpellTypes::FastHeals:
|
||||
case BotSpellTypes::PetFastHeals:
|
||||
case BotSpellTypes::RegularHeal:
|
||||
case BotSpellTypes::PetRegularHeals:
|
||||
case BotSpellTypes::CompleteHeal:
|
||||
case BotSpellTypes::PetCompleteHeals:
|
||||
case BotSpellTypes::GroupCompleteHeals:
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IsCommandedBotSpellType(uint16 spell_type) {
|
||||
switch (spell_type) {
|
||||
case BotSpellTypes::Charm:
|
||||
case BotSpellTypes::AEFear:
|
||||
case BotSpellTypes::Fear:
|
||||
case BotSpellTypes::Resurrect:
|
||||
case BotSpellTypes::AELull:
|
||||
case BotSpellTypes::Lull:
|
||||
case BotSpellTypes::Teleport:
|
||||
case BotSpellTypes::Succor:
|
||||
case BotSpellTypes::BindAffinity:
|
||||
case BotSpellTypes::Identify:
|
||||
case BotSpellTypes::Levitate:
|
||||
case BotSpellTypes::Rune:
|
||||
case BotSpellTypes::WaterBreathing:
|
||||
case BotSpellTypes::Size:
|
||||
case BotSpellTypes::Invisibility:
|
||||
case BotSpellTypes::MovementSpeed:
|
||||
case BotSpellTypes::SendHome:
|
||||
case BotSpellTypes::SummonCorpse:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsPullingBotSpellType(uint16 spell_type) {
|
||||
switch (spell_type) {
|
||||
case BotSpellTypes::Nuke:
|
||||
case BotSpellTypes::Lifetap:
|
||||
case BotSpellTypes::Snare:
|
||||
case BotSpellTypes::DOT:
|
||||
case BotSpellTypes::Dispel:
|
||||
case BotSpellTypes::Slow:
|
||||
case BotSpellTypes::Debuff:
|
||||
case BotSpellTypes::Stun:
|
||||
case BotSpellTypes::HateLine:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
uint16 GetCorrectBotSpellType(uint16 spell_type, uint16 spell_id) {
|
||||
if (!IsValidSpell(spell_id)) {
|
||||
return UINT16_MAX;
|
||||
}
|
||||
|
||||
uint16 correct_type = UINT16_MAX;
|
||||
SPDat_Spell_Struct spell = spells[spell_id];
|
||||
std::string teleport_zone = spell.teleport_zone;
|
||||
|
||||
if (IsCharmSpell(spell_id)) {
|
||||
correct_type = BotSpellTypes::Charm;
|
||||
}
|
||||
else if (IsFearSpell(spell_id)) {
|
||||
correct_type = BotSpellTypes::Fear;
|
||||
}
|
||||
else if (IsEffectInSpell(spell_id, SE_Revive)) {
|
||||
correct_type = BotSpellTypes::Resurrect;
|
||||
}
|
||||
else if (IsHarmonySpell(spell_id)) {
|
||||
correct_type = BotSpellTypes::Lull;
|
||||
}
|
||||
else if (
|
||||
teleport_zone.compare("") &&
|
||||
!IsEffectInSpell(spell_id, SE_GateToHomeCity) &&
|
||||
IsBeneficialSpell(spell_id) &&
|
||||
(IsEffectInSpell(spell_id, SE_Teleport) || IsEffectInSpell(spell_id, SE_Translocate))
|
||||
) {
|
||||
correct_type = BotSpellTypes::Teleport;
|
||||
}
|
||||
else if (
|
||||
IsBeneficialSpell(spell_id) &&
|
||||
IsEffectInSpell(spell_id, SE_Succor)
|
||||
) {
|
||||
correct_type = BotSpellTypes::Succor;
|
||||
}
|
||||
else if (IsEffectInSpell(spell_id, SE_BindAffinity)) {
|
||||
correct_type = BotSpellTypes::BindAffinity;
|
||||
}
|
||||
else if (IsEffectInSpell(spell_id, SE_Identify)) {
|
||||
correct_type = BotSpellTypes::Identify;
|
||||
}
|
||||
else if (
|
||||
spell_type == BotSpellTypes::Levitate &&
|
||||
IsBeneficialSpell(spell_id) &&
|
||||
IsEffectInSpell(spell_id, SE_Levitate)
|
||||
) {
|
||||
correct_type = BotSpellTypes::Levitate;
|
||||
}
|
||||
else if (
|
||||
spell_type == BotSpellTypes::Rune &&
|
||||
IsBeneficialSpell(spell_id) &&
|
||||
(IsEffectInSpell(spell_id, SE_AbsorbMagicAtt) || IsEffectInSpell(spell_id, SE_Rune))
|
||||
) {
|
||||
correct_type = BotSpellTypes::Rune;
|
||||
}
|
||||
else if (
|
||||
spell_type == BotSpellTypes::WaterBreathing &&
|
||||
IsBeneficialSpell(spell_id) &&
|
||||
IsEffectInSpell(spell_id, SE_WaterBreathing)
|
||||
) {
|
||||
correct_type = BotSpellTypes::WaterBreathing;
|
||||
}
|
||||
else if (
|
||||
spell_type == BotSpellTypes::Size &&
|
||||
IsBeneficialSpell(spell_id) &&
|
||||
(IsEffectInSpell(spell_id, SE_ModelSize) || IsEffectInSpell(spell_id, SE_ChangeHeight))
|
||||
) {
|
||||
correct_type = BotSpellTypes::Size;
|
||||
}
|
||||
else if (
|
||||
spell_type == BotSpellTypes::Invisibility &&
|
||||
IsBeneficialSpell(spell_id) &&
|
||||
(IsEffectInSpell(spell_id, SE_SeeInvis) || IsInvisibleSpell(spell_id))
|
||||
) {
|
||||
correct_type = BotSpellTypes::Invisibility;
|
||||
}
|
||||
else if (
|
||||
spell_type == BotSpellTypes::MovementSpeed &&
|
||||
IsBeneficialSpell(spell_id) &&
|
||||
IsEffectInSpell(spell_id, SE_MovementSpeed)
|
||||
) {
|
||||
correct_type = BotSpellTypes::MovementSpeed;
|
||||
}
|
||||
else if (
|
||||
!teleport_zone.compare("") &&
|
||||
IsBeneficialSpell(spell_id) &&
|
||||
(IsEffectInSpell(spell_id, SE_Translocate) || IsEffectInSpell(spell_id, SE_GateToHomeCity))
|
||||
) {
|
||||
correct_type = BotSpellTypes::SendHome;
|
||||
}
|
||||
else if (IsEffectInSpell(spell_id, SE_SummonCorpse)) {
|
||||
correct_type = BotSpellTypes::SummonCorpse;
|
||||
}
|
||||
|
||||
if (correct_type == UINT16_MAX) {
|
||||
if (
|
||||
IsSummonPetSpell(spell_id) ||
|
||||
IsEffectInSpell(spell_id, SE_TemporaryPets)
|
||||
) {
|
||||
correct_type = BotSpellTypes::Pet;
|
||||
}
|
||||
else if (IsMesmerizeSpell(spell_id)) {
|
||||
correct_type = BotSpellTypes::Mez;
|
||||
}
|
||||
else if (IsEscapeSpell(spell_id)) {
|
||||
correct_type = BotSpellTypes::Escape;
|
||||
}
|
||||
else if (
|
||||
IsDetrimentalSpell(spell_id) &&
|
||||
IsEffectInSpell(spell_id, SE_Root)
|
||||
) {
|
||||
if (IsAnyAESpell(spell_id)) {
|
||||
correct_type = BotSpellTypes::AERoot;
|
||||
}
|
||||
else {
|
||||
correct_type = BotSpellTypes::Root;
|
||||
}
|
||||
}
|
||||
else if (
|
||||
IsDetrimentalSpell(spell_id) &&
|
||||
IsLifetapSpell(spell_id)
|
||||
) {
|
||||
correct_type = BotSpellTypes::Lifetap;
|
||||
}
|
||||
else if (
|
||||
IsDetrimentalSpell(spell_id) &&
|
||||
IsEffectInSpell(spell_id, SE_MovementSpeed)
|
||||
) {
|
||||
correct_type = BotSpellTypes::Snare;
|
||||
}
|
||||
else if (
|
||||
IsDetrimentalSpell(spell_id) &&
|
||||
(IsStackableDOT(spell_id) || IsDamageOverTimeSpell(spell_id))
|
||||
) {
|
||||
correct_type = BotSpellTypes::DOT;
|
||||
}
|
||||
else if (IsDispelSpell(spell_id)) {
|
||||
correct_type = BotSpellTypes::Dispel;
|
||||
}
|
||||
else if (
|
||||
IsDetrimentalSpell(spell_id) &&
|
||||
IsSlowSpell(spell_id)
|
||||
) {
|
||||
correct_type = BotSpellTypes::Slow;
|
||||
}
|
||||
else if (
|
||||
IsDebuffSpell(spell_id) &&
|
||||
!IsHateReduxSpell(spell_id) &&
|
||||
!IsHateSpell(spell_id)
|
||||
) {
|
||||
correct_type = BotSpellTypes::Debuff;
|
||||
}
|
||||
else if (IsHateReduxSpell(spell_id)) {
|
||||
correct_type = BotSpellTypes::HateRedux;
|
||||
}
|
||||
else if (
|
||||
IsDetrimentalSpell(spell_id) &&
|
||||
IsHateSpell(spell_id)
|
||||
) {
|
||||
correct_type = BotSpellTypes::HateLine;
|
||||
}
|
||||
else if (
|
||||
IsBuffSpell(spell_id) &&
|
||||
IsBeneficialSpell(spell_id) &&
|
||||
IsBardSong(spell_id)
|
||||
) {
|
||||
if (
|
||||
spell_type == BotSpellTypes::InCombatBuffSong ||
|
||||
spell_type == BotSpellTypes::OutOfCombatBuffSong ||
|
||||
spell_type == BotSpellTypes::PreCombatBuffSong
|
||||
) {
|
||||
correct_type = spell_type;
|
||||
}
|
||||
else {
|
||||
correct_type = BotSpellTypes::OutOfCombatBuffSong;
|
||||
}
|
||||
}
|
||||
else if (
|
||||
!IsBardSong(spell_id) &&
|
||||
(
|
||||
(IsSelfConversionSpell(spell_id) && spell.buff_duration < 1) ||
|
||||
(spell_type == BotSpellTypes::InCombatBuff && IsAnyBuffSpell(spell_id))
|
||||
)
|
||||
) {
|
||||
correct_type = BotSpellTypes::InCombatBuff;
|
||||
}
|
||||
else if (
|
||||
spell_type == BotSpellTypes::PreCombatBuff &&
|
||||
IsAnyBuffSpell(spell_id) &&
|
||||
!IsBardSong(spell_id)
|
||||
) {
|
||||
correct_type = BotSpellTypes::PreCombatBuff;
|
||||
}
|
||||
else if (
|
||||
(IsCureSpell(spell_id) && spell_type == BotSpellTypes::Cure) ||
|
||||
(IsCureSpell(spell_id) && !IsAnyHealSpell(spell_id))
|
||||
) {
|
||||
correct_type = BotSpellTypes::Cure;
|
||||
}
|
||||
else if (IsAnyNukeOrStunSpell(spell_id)) {
|
||||
if (IsAnyAESpell(spell_id)) {
|
||||
if (IsAERainSpell(spell_id)) {
|
||||
correct_type = BotSpellTypes::AERains;
|
||||
}
|
||||
else if (IsPBAENukeSpell(spell_id)) {
|
||||
correct_type = BotSpellTypes::PBAENuke;
|
||||
}
|
||||
else if (IsStunSpell(spell_id)) {
|
||||
correct_type = BotSpellTypes::AEStun;
|
||||
}
|
||||
else {
|
||||
correct_type = BotSpellTypes::AENukes;
|
||||
}
|
||||
}
|
||||
else if (IsStunSpell(spell_id)) {
|
||||
correct_type = BotSpellTypes::Stun;
|
||||
}
|
||||
else {
|
||||
correct_type = BotSpellTypes::Nuke;
|
||||
}
|
||||
}
|
||||
else if (IsAnyHealSpell(spell_id)) {
|
||||
if (IsGroupSpell(spell_id)) {
|
||||
if (IsGroupCompleteHealSpell(spell_id)) {
|
||||
correct_type = BotSpellTypes::GroupCompleteHeals;
|
||||
}
|
||||
else if (IsGroupHealOverTimeSpell(spell_id)) {
|
||||
correct_type = BotSpellTypes::GroupHoTHeals;
|
||||
}
|
||||
else if (IsRegularGroupHealSpell(spell_id)) {
|
||||
correct_type = BotSpellTypes::GroupHeals;
|
||||
}
|
||||
|
||||
return correct_type;
|
||||
}
|
||||
|
||||
if (IsVeryFastHealSpell(spell_id)) {
|
||||
correct_type = BotSpellTypes::VeryFastHeals;
|
||||
}
|
||||
else if (IsFastHealSpell(spell_id)) {
|
||||
correct_type = BotSpellTypes::FastHeals;
|
||||
}
|
||||
else if (IsCompleteHealSpell(spell_id)) {
|
||||
correct_type = BotSpellTypes::CompleteHeal;
|
||||
}
|
||||
else if (IsHealOverTimeSpell(spell_id)) {
|
||||
correct_type = BotSpellTypes::HoTHeals;
|
||||
}
|
||||
else if (IsRegularSingleTargetHealSpell(spell_id)) {
|
||||
correct_type = BotSpellTypes::RegularHeal;
|
||||
}
|
||||
else if (IsRegularPetHealSpell(spell_id)) {
|
||||
correct_type = BotSpellTypes::RegularHeal;
|
||||
}
|
||||
}
|
||||
else if (IsAnyBuffSpell(spell_id)) {
|
||||
correct_type = BotSpellTypes::Buff;
|
||||
|
||||
if (IsResistanceOnlySpell(spell_id)) {
|
||||
correct_type = BotSpellTypes::ResistBuffs;
|
||||
}
|
||||
else if (IsDamageShieldOnlySpell(spell_id)) {
|
||||
correct_type = BotSpellTypes::DamageShields;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return correct_type;
|
||||
}
|
||||
|
||||
uint16 GetPetBotSpellType(uint16 spell_type) {
|
||||
switch (spell_type) {
|
||||
case BotSpellTypes::Buff:
|
||||
return BotSpellTypes::PetBuffs;
|
||||
case BotSpellTypes::RegularHeal:
|
||||
return BotSpellTypes::PetRegularHeals;
|
||||
case BotSpellTypes::CompleteHeal:
|
||||
return BotSpellTypes::PetCompleteHeals;
|
||||
case BotSpellTypes::FastHeals:
|
||||
return BotSpellTypes::PetFastHeals;
|
||||
case BotSpellTypes::VeryFastHeals:
|
||||
return BotSpellTypes::PetVeryFastHeals;
|
||||
case BotSpellTypes::HoTHeals:
|
||||
return BotSpellTypes::PetHoTHeals;
|
||||
case BotSpellTypes::Cure:
|
||||
return BotSpellTypes::PetCures;
|
||||
case BotSpellTypes::DamageShields:
|
||||
return BotSpellTypes::PetDamageShields;
|
||||
case BotSpellTypes::ResistBuffs:
|
||||
return BotSpellTypes::PetResistBuffs;
|
||||
default:
|
||||
return spell_type;
|
||||
}
|
||||
|
||||
return spell_type;
|
||||
}
|
||||
@ -43,7 +43,7 @@
|
||||
*/
|
||||
|
||||
#define CURRENT_BINARY_DATABASE_VERSION 9296
|
||||
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9045
|
||||
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9054
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@ -743,12 +743,30 @@ bool Mob::IsAttackAllowed(Mob *target, bool isSpellAttack)
|
||||
}
|
||||
|
||||
// can't damage own pet (applies to everthing)
|
||||
Mob *target_owner = target->GetOwner();
|
||||
Mob *our_owner = GetOwner();
|
||||
if(target_owner && target_owner == this)
|
||||
return false;
|
||||
else if(our_owner && our_owner == target)
|
||||
Mob* target_owner = target->GetOwner();
|
||||
Mob* our_owner = GetOwner();
|
||||
|
||||
// Self-owner check
|
||||
if (target_owner == this || our_owner == target) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Bot-specific logic
|
||||
if (IsBot()) {
|
||||
Mob* target_ultimate_owner = target->IsBot() ? target->CastToBot()->GetBotOwner() : target->GetUltimateOwner();
|
||||
Mob* our_ultimate_owner = CastToBot()->GetBotOwner();
|
||||
|
||||
if (target_ultimate_owner) {
|
||||
if (target_ultimate_owner == our_ultimate_owner || target_ultimate_owner->IsOfClientBot()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Bots should not attack their ultimate owner
|
||||
if (our_ultimate_owner == target) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// invalidate for swarm pets for later on if their owner is a corpse
|
||||
if (IsNPC() && CastToNPC()->GetSwarmInfo() && our_owner &&
|
||||
@ -1278,6 +1296,39 @@ bool Mob::CheckLosFN(glm::vec3 posWatcher, float sizeWatcher, glm::vec3 posTarge
|
||||
return zone->zonemap->CheckLoS(posWatcher, posTarget);
|
||||
}
|
||||
|
||||
bool Mob::CheckPositioningLosFN(Mob* other, float x, float y, float z) {
|
||||
if (!zone->zonemap) {
|
||||
//not sure what the best return is on error
|
||||
//should make this a database variable, but im lazy today
|
||||
#ifdef LOS_DEFAULT_CAN_SEE
|
||||
return(true);
|
||||
#else
|
||||
return(false);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (!other) {
|
||||
return(true);
|
||||
}
|
||||
glm::vec3 myloc;
|
||||
glm::vec3 oloc;
|
||||
|
||||
#define LOS_DEFAULT_HEIGHT 6.0f
|
||||
|
||||
oloc.x = other->GetX();
|
||||
oloc.y = other->GetY();
|
||||
oloc.z = other->GetZ() + (other->GetSize() == 0.0 ? LOS_DEFAULT_HEIGHT : other->GetSize()) / 2 * SEE_POSITION;
|
||||
|
||||
myloc.x = x;
|
||||
myloc.y = y;
|
||||
myloc.z = z + (GetSize() == 0.0 ? LOS_DEFAULT_HEIGHT : GetSize()) / 2 * HEAD_POSITION;
|
||||
|
||||
#if LOSDEBUG>=5
|
||||
LogDebug("LOS from ([{}], [{}], [{}]) to ([{}], [{}], [{}]) sizes: ([{}], [{}])", myloc.x, myloc.y, myloc.z, oloc.x, oloc.y, oloc.z, GetSize(), mobSize);
|
||||
#endif
|
||||
return zone->zonemap->CheckLoS(myloc, oloc);
|
||||
}
|
||||
|
||||
//offensive spell aggro
|
||||
int32 Mob::CheckAggroAmount(uint16 spell_id, Mob *target, bool is_proc)
|
||||
{
|
||||
@ -1658,4 +1709,3 @@ void Mob::RogueEvade(Mob *other)
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@ -1246,7 +1246,12 @@ int64 Mob::GetWeaponDamage(Mob *against, const EQ::ItemInstance *weapon_item, in
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!weapon_item->IsClassEquipable(GetClass())) {
|
||||
if (!weapon_item->IsClassEquipable(GetClass()) &&
|
||||
(
|
||||
!IsBot() ||
|
||||
(IsBot() && !RuleB(Bots, AllowBotEquipAnyClassGear))
|
||||
)
|
||||
) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -2393,7 +2398,7 @@ bool NPC::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, bool
|
||||
|
||||
LogCombat("Final damage against [{}]: [{}]", other->GetName(), my_hit.damage_done);
|
||||
|
||||
if (other->IsClient() && IsPet() && GetOwner()->IsClient()) {
|
||||
if (other->IsClient() && IsPet() && GetOwner()->IsOfClientBot()) {
|
||||
//pets do half damage to clients in pvp
|
||||
my_hit.damage_done /= 2;
|
||||
if (my_hit.damage_done < 1) {
|
||||
@ -2612,35 +2617,28 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
|
||||
}
|
||||
|
||||
if (give_exp && give_exp->HasOwner()) {
|
||||
bool owner_in_group = false;
|
||||
auto owner = give_exp->GetOwner();
|
||||
|
||||
if (
|
||||
(
|
||||
give_exp->HasGroup() &&
|
||||
give_exp->GetGroup()->IsGroupMember(give_exp->GetUltimateOwner())
|
||||
) ||
|
||||
(
|
||||
give_exp->IsPet() &&
|
||||
(
|
||||
give_exp->GetOwner()->IsClient() ||
|
||||
(
|
||||
give_exp->GetOwner()->HasGroup() &&
|
||||
give_exp->GetOwner()->GetGroup()->IsGroupMember(give_exp->GetOwner()->GetUltimateOwner())
|
||||
)
|
||||
)
|
||||
)
|
||||
) {
|
||||
owner_in_group = true;
|
||||
if (owner) {
|
||||
Mob* ulimate_owner = give_exp->GetUltimateOwner();
|
||||
bool pet_owner_is_client = give_exp->IsPet() && owner->IsClient();
|
||||
bool pet_owner_is_bot = give_exp->IsPet() && owner->IsBot();
|
||||
bool owner_is_client = owner->IsClient();
|
||||
|
||||
bool is_in_same_group_or_raid = (
|
||||
pet_owner_is_client ||
|
||||
(pet_owner_is_bot && owner->IsInGroupOrRaid(ulimate_owner)) ||
|
||||
(owner_is_client && give_exp->IsInGroupOrRaid(ulimate_owner))
|
||||
);
|
||||
|
||||
give_exp = (is_in_same_group_or_raid ? give_exp->GetUltimateOwner() : nullptr);
|
||||
}
|
||||
|
||||
give_exp = give_exp->GetUltimateOwner();
|
||||
|
||||
if (!RuleB(Bots, BotGroupXP) && !owner_in_group) {
|
||||
else {
|
||||
give_exp = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (give_exp && give_exp->IsTempPet() && give_exp->IsPetOwnerClient()) {
|
||||
if (give_exp && give_exp->IsTempPet() && give_exp->IsPetOwnerOfClientBot()) {
|
||||
if (give_exp->IsNPC() && give_exp->CastToNPC()->GetSwarmOwner()) {
|
||||
Mob* temp_owner = entity_list.GetMobID(give_exp->CastToNPC()->GetSwarmOwner());
|
||||
if (temp_owner) {
|
||||
@ -2810,7 +2808,7 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
|
||||
const uint32 con_level = give_exp->GetLevelCon(GetLevel());
|
||||
|
||||
if (con_level != ConsiderColor::Gray) {
|
||||
if (!GetOwner() || (GetOwner() && !GetOwner()->IsClient())) {
|
||||
if (!GetOwner() || (GetOwner() && !GetOwner()->IsOfClientBot())) {
|
||||
give_exp_client->AddEXP(ExpSource::Kill, final_exp, con_level, false, this);
|
||||
|
||||
if (
|
||||
@ -6435,8 +6433,10 @@ void Mob::CommonOutgoingHitSuccess(Mob* defender, DamageHitInfo &hit, ExtraAttac
|
||||
}
|
||||
else {
|
||||
int ass = TryAssassinate(defender, hit.skill);
|
||||
if (ass > 0)
|
||||
|
||||
if (ass > 0) {
|
||||
hit.damage_done = ass;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (hit.skill == EQ::skills::SkillFrenzy && GetClass() == Class::Berserker && GetLevel() > 50) {
|
||||
@ -6481,7 +6481,7 @@ void Mob::CommonOutgoingHitSuccess(Mob* defender, DamageHitInfo &hit, ExtraAttac
|
||||
int mod = GetSpecialAbilityParam(SpecialAbility::Rampage, 2);
|
||||
if (mod > 0)
|
||||
spec_mod = mod;
|
||||
if ((IsPet() || IsTempPet()) && IsPetOwnerClient()) {
|
||||
if ((IsPet() || IsTempPet()) && IsPetOwnerOfClientBot()) {
|
||||
//SE_PC_Pet_Rampage SPA 464 on pet, damage modifier
|
||||
int spell_mod = spellbonuses.PC_Pet_Rampage[SBIndex::PET_RAMPAGE_DMG_MOD] + itembonuses.PC_Pet_Rampage[SBIndex::PET_RAMPAGE_DMG_MOD] + aabonuses.PC_Pet_Rampage[SBIndex::PET_RAMPAGE_DMG_MOD];
|
||||
if (spell_mod > spec_mod)
|
||||
@ -6492,7 +6492,7 @@ void Mob::CommonOutgoingHitSuccess(Mob* defender, DamageHitInfo &hit, ExtraAttac
|
||||
int mod = GetSpecialAbilityParam(SpecialAbility::AreaRampage, 2);
|
||||
if (mod > 0)
|
||||
spec_mod = mod;
|
||||
if ((IsPet() || IsTempPet()) && IsPetOwnerClient()) {
|
||||
if ((IsPet() || IsTempPet()) && IsPetOwnerOfClientBot()) {
|
||||
//SE_PC_Pet_AE_Rampage SPA 465 on pet, damage modifier
|
||||
int spell_mod = spellbonuses.PC_Pet_AE_Rampage[SBIndex::PET_RAMPAGE_DMG_MOD] + itembonuses.PC_Pet_AE_Rampage[SBIndex::PET_RAMPAGE_DMG_MOD] + aabonuses.PC_Pet_AE_Rampage[SBIndex::PET_RAMPAGE_DMG_MOD];
|
||||
if (spell_mod > spec_mod)
|
||||
@ -6924,7 +6924,7 @@ void Mob::DoMainHandAttackRounds(Mob *target, ExtraAttackOptions *opts, bool ram
|
||||
Attack(target, EQ::invslot::slotPrimary, false, false, false, opts);
|
||||
if (CanThisClassDoubleAttack() && CheckDoubleAttack()) {
|
||||
Attack(target, EQ::invslot::slotPrimary, false, false, false, opts);
|
||||
if ((IsPet() || IsTempPet()) && IsPetOwnerClient()) {
|
||||
if ((IsPet() || IsTempPet()) && IsPetOwnerOfClientBot()) {
|
||||
int chance = spellbonuses.PC_Pet_Flurry + itembonuses.PC_Pet_Flurry + aabonuses.PC_Pet_Flurry;
|
||||
if (chance && zone->random.Roll(chance)) {
|
||||
Flurry(nullptr);
|
||||
@ -6985,7 +6985,7 @@ void Mob::DoOffHandAttackRounds(Mob *target, ExtraAttackOptions *opts, bool ramp
|
||||
if (CanThisClassDoubleAttack() && GetLevel() > 35 && CheckDoubleAttack() && !rampage) {
|
||||
Attack(target, EQ::invslot::slotSecondary, false, false, false, opts);
|
||||
|
||||
if ((IsPet() || IsTempPet()) && IsPetOwnerClient()) {
|
||||
if ((IsPet() || IsTempPet()) && IsPetOwnerOfClientBot()) {
|
||||
int chance = spellbonuses.PC_Pet_Flurry + itembonuses.PC_Pet_Flurry + aabonuses.PC_Pet_Flurry;
|
||||
if (chance && zone->random.Roll(chance)) {
|
||||
Flurry(nullptr);
|
||||
|
||||
@ -81,7 +81,7 @@ void Aura::ProcessOnAllFriendlies(Mob *owner)
|
||||
if (!mob) {
|
||||
continue;
|
||||
}
|
||||
if (mob->IsClient() || mob->IsPetOwnerClient() || mob->IsMerc() || mob->IsBot()) {
|
||||
if (mob->IsOfClientBotMerc() || mob->IsPetOwnerOfClientBot()) {
|
||||
auto it = casted_on.find(mob->GetID());
|
||||
|
||||
if (it != casted_on.end()) { // we are already on the list, let's check for removal
|
||||
@ -131,7 +131,7 @@ void Aura::ProcessOnAllGroupMembers(Mob *owner)
|
||||
std::set<int> delayed_remove;
|
||||
bool is_buff = IsBuffSpell(spell_id); // non-buff spells don't cast on enter
|
||||
|
||||
if (owner->IsRaidGrouped() && owner->IsClient()) { // currently raids are just client, but safety check
|
||||
if (owner->IsRaidGrouped() && owner->IsOfClientBot()) { // currently raids are just client, but safety check
|
||||
auto raid = owner->GetRaid();
|
||||
if (raid == nullptr) { // well shit
|
||||
owner->RemoveAura(GetID(), false, true);
|
||||
@ -198,17 +198,17 @@ void Aura::ProcessOnAllGroupMembers(Mob *owner)
|
||||
auto it = casted_on.find(mob->GetID());
|
||||
if (it != casted_on.end()) {
|
||||
// verify still good!
|
||||
if (mob->IsClient()) {
|
||||
if (mob->IsOfClientBot()) {
|
||||
if (!verify_raid_client(mob->CastToClient())) {
|
||||
delayed_remove.insert(mob->GetID());
|
||||
}
|
||||
}
|
||||
else if (mob->IsPet() && mob->IsPetOwnerClient() && mob->GetOwner()) {
|
||||
else if (mob->IsPet() && mob->IsPetOwnerOfClientBot() && mob->GetOwner()) {
|
||||
if (!verify_raid_client_pet(mob)) {
|
||||
delayed_remove.insert(mob->GetID());
|
||||
}
|
||||
}
|
||||
else if (mob->IsNPC() && mob->IsPetOwnerClient()) {
|
||||
else if (mob->IsNPC() && mob->IsPetOwnerOfClientBot()) {
|
||||
auto npc = mob->CastToNPC();
|
||||
if (!verify_raid_client_swarm(npc)) {
|
||||
delayed_remove.insert(mob->GetID());
|
||||
@ -216,19 +216,19 @@ void Aura::ProcessOnAllGroupMembers(Mob *owner)
|
||||
}
|
||||
}
|
||||
else { // we're not on it!
|
||||
if (mob->IsClient() && verify_raid_client(mob->CastToClient())) {
|
||||
if (mob->IsOfClientBot() && verify_raid_client(mob->CastToClient())) {
|
||||
casted_on.insert(mob->GetID());
|
||||
if (is_buff) {
|
||||
SpellFinished(spell_id, mob);
|
||||
}
|
||||
}
|
||||
else if (mob->IsPet() && mob->IsPetOwnerClient() && mob->GetOwner() && verify_raid_client_pet(mob)) {
|
||||
else if (mob->IsPet() && mob->IsPetOwnerOfClientBot() && mob->GetOwner() && verify_raid_client_pet(mob)) {
|
||||
casted_on.insert(mob->GetID());
|
||||
if (is_buff) {
|
||||
SpellFinished(spell_id, mob);
|
||||
}
|
||||
}
|
||||
else if (mob->IsNPC() && mob->IsPetOwnerClient()) {
|
||||
else if (mob->IsNPC() && mob->IsPetOwnerOfClientBot()) {
|
||||
auto npc = mob->CastToNPC();
|
||||
if (verify_raid_client_swarm(npc)) {
|
||||
casted_on.insert(mob->GetID());
|
||||
@ -376,7 +376,7 @@ void Aura::ProcessOnGroupMembersPets(Mob *owner)
|
||||
auto group_member = owner->GetOwnerOrSelf();
|
||||
|
||||
if (group_member->IsRaidGrouped() &&
|
||||
group_member->IsClient()) { // currently raids are just client, but safety check
|
||||
group_member->IsOfClientBot()) { // currently raids are just client, but safety check
|
||||
auto raid = group_member->GetRaid();
|
||||
if (raid == nullptr) { // well shit
|
||||
owner->RemoveAura(GetID(), false, true);
|
||||
@ -428,12 +428,12 @@ void Aura::ProcessOnGroupMembersPets(Mob *owner)
|
||||
auto it = casted_on.find(mob->GetID());
|
||||
if (it != casted_on.end()) {
|
||||
// verify still good!
|
||||
if (mob->IsPet() && mob->IsPetOwnerClient() && mob->GetOwner()) {
|
||||
if (mob->IsPet() && mob->IsPetOwnerOfClientBot() && mob->GetOwner()) {
|
||||
if (!verify_raid_client_pet(mob)) {
|
||||
delayed_remove.insert(mob->GetID());
|
||||
}
|
||||
}
|
||||
else if (mob->IsNPC() && mob->IsPetOwnerClient()) {
|
||||
else if (mob->IsNPC() && mob->IsPetOwnerOfClientBot()) {
|
||||
auto npc = mob->CastToNPC();
|
||||
if (!verify_raid_client_swarm(npc)) {
|
||||
delayed_remove.insert(mob->GetID());
|
||||
@ -441,16 +441,16 @@ void Aura::ProcessOnGroupMembersPets(Mob *owner)
|
||||
}
|
||||
}
|
||||
else { // we're not on it!
|
||||
if (mob->IsClient()) {
|
||||
if (mob->IsOfClientBot()) {
|
||||
continue; // never hit client
|
||||
}
|
||||
else if (mob->IsPet() && mob->IsPetOwnerClient() && mob->GetOwner() && verify_raid_client_pet(mob)) {
|
||||
else if (mob->IsPet() && mob->IsPetOwnerOfClientBot() && mob->GetOwner() && verify_raid_client_pet(mob)) {
|
||||
casted_on.insert(mob->GetID());
|
||||
if (is_buff) {
|
||||
SpellFinished(spell_id, mob);
|
||||
}
|
||||
}
|
||||
else if (mob->IsNPC() && mob->IsPetOwnerClient()) {
|
||||
else if (mob->IsNPC() && mob->IsPetOwnerOfClientBot()) {
|
||||
auto npc = mob->CastToNPC();
|
||||
if (verify_raid_client_swarm(npc)) {
|
||||
casted_on.insert(mob->GetID());
|
||||
@ -499,7 +499,7 @@ void Aura::ProcessOnGroupMembersPets(Mob *owner)
|
||||
}
|
||||
}
|
||||
else { // not on, check if we should be!
|
||||
if (mob->IsClient()) {
|
||||
if (mob->IsOfClientBot()) {
|
||||
continue;
|
||||
}
|
||||
else if (mob->IsPet() && verify_group_pet(mob)) {
|
||||
@ -690,7 +690,7 @@ void Aura::ProcessSpawns()
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!e.second->IsClient()) {
|
||||
if (!e.second->IsOfClientBot()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -801,7 +801,7 @@ bool Aura::ShouldISpawnFor(Client *c)
|
||||
return true;
|
||||
}
|
||||
|
||||
if (owner->IsRaidGrouped() && owner->IsClient()) {
|
||||
if (owner->IsRaidGrouped() && owner->IsOfClientBot()) {
|
||||
auto raid = owner->GetRaid();
|
||||
if (raid == nullptr) {
|
||||
return false;
|
||||
|
||||
7727
zone/bot.cpp
7727
zone/bot.cpp
File diff suppressed because it is too large
Load Diff
562
zone/bot.h
562
zone/bot.h
@ -37,18 +37,24 @@
|
||||
|
||||
#include <sstream>
|
||||
|
||||
constexpr uint32 BOT_FOLLOW_DISTANCE_DEFAULT = 184; // as DSq value (~13.565 units)
|
||||
constexpr uint32 BOT_FOLLOW_DISTANCE_DEFAULT_MAX = 2500; // as DSq value (50 units)
|
||||
|
||||
constexpr uint32 BOT_KEEP_ALIVE_INTERVAL = 5000; // 5 seconds
|
||||
|
||||
constexpr uint32 BOT_COMBAT_JITTER_INTERVAL_MIN = 1500; // 1.5 seconds
|
||||
constexpr uint32 BOT_COMBAT_JITTER_INTERVAL_MAX = 3000; // 3 seconds
|
||||
|
||||
constexpr uint32 MAG_EPIC_1_0 = 28034;
|
||||
|
||||
extern WorldServer worldserver;
|
||||
|
||||
constexpr int BotAISpellRange = 100; // TODO: Write a method that calcs what the bot's spell range is based on spell, equipment, AA, whatever and replace this
|
||||
constexpr int NegativeItemReuse = -1; // Unlinked timer for items
|
||||
|
||||
constexpr uint8 SumWater = 1;
|
||||
constexpr uint8 SumFire = 2;
|
||||
constexpr uint8 SumAir = 3;
|
||||
constexpr uint8 SumEarth = 4;
|
||||
constexpr uint8 MonsterSum = 5;
|
||||
constexpr uint8 SumMageMultiElement = 6;
|
||||
|
||||
// nHSND negative Healer/Slower/Nuker/Doter
|
||||
// pH positive Healer
|
||||
// pS positive Slower
|
||||
@ -87,6 +93,141 @@ enum BotCastingChanceConditional : uint8
|
||||
cntHSND = 16
|
||||
};
|
||||
|
||||
namespace BotSettingCategories { // Update GetBotSpellCategoryName as needed
|
||||
constexpr uint8 BaseSetting = 0;
|
||||
constexpr uint8 SpellHold = 1;
|
||||
constexpr uint8 SpellDelay = 2;
|
||||
constexpr uint8 SpellMinThreshold = 3;
|
||||
constexpr uint8 SpellMaxThreshold = 4;
|
||||
constexpr uint8 SpellTypeResistLimit = 5;
|
||||
constexpr uint8 SpellTypeAggroCheck = 6;
|
||||
constexpr uint8 SpellTypeMinManaPct = 7;
|
||||
constexpr uint8 SpellTypeMaxManaPct = 8;
|
||||
constexpr uint8 SpellTypeMinHPPct = 9;
|
||||
constexpr uint8 SpellTypeMaxHPPct = 10;
|
||||
constexpr uint8 SpellTypeIdlePriority = 11;
|
||||
constexpr uint8 SpellTypeEngagedPriority = 12;
|
||||
constexpr uint8 SpellTypePursuePriority = 13;
|
||||
constexpr uint8 SpellTypeAEOrGroupTargetCount = 14;
|
||||
constexpr uint8 SpellTypeAnnounceCast = 15;
|
||||
|
||||
constexpr uint16 START = BotSettingCategories::BaseSetting;
|
||||
constexpr uint16 START_NO_BASE = BotSettingCategories::SpellHold;
|
||||
constexpr uint16 START_CLIENT = BotSettingCategories::SpellDelay;
|
||||
constexpr uint16 END_CLIENT = BotSettingCategories::SpellMaxThreshold;
|
||||
constexpr uint16 END = BotSettingCategories::SpellTypeAnnounceCast;
|
||||
};
|
||||
|
||||
static std::map<uint8, std::string> botSpellCategory_names = {
|
||||
{ BotSettingCategories::BaseSetting, "BaseSetting" },
|
||||
{ BotSettingCategories::SpellHold, "SpellHolds" },
|
||||
{ BotSettingCategories::SpellDelay, "SpellDelays" },
|
||||
{ BotSettingCategories::SpellMinThreshold, "SpellMinThresholds" },
|
||||
{ BotSettingCategories::SpellMaxThreshold, "SpellMaxThresholds" },
|
||||
{ BotSettingCategories::SpellTypeResistLimit, "SpellResistLimit" },
|
||||
{ BotSettingCategories::SpellTypeAggroCheck, "SpellAggroChecks" },
|
||||
{ BotSettingCategories::SpellTypeMinManaPct, "SpellMinManaPct" },
|
||||
{ BotSettingCategories::SpellTypeMaxManaPct, "SpellMaxManaPct" },
|
||||
{ BotSettingCategories::SpellTypeMinHPPct, "SpellMinHPPct" },
|
||||
{ BotSettingCategories::SpellTypeMaxHPPct, "SpellMaxHPPct" },
|
||||
{ BotSettingCategories::SpellTypeIdlePriority, "SpellIdlePriority" },
|
||||
{ BotSettingCategories::SpellTypeEngagedPriority, "SpellEngagedPriority" },
|
||||
{ BotSettingCategories::SpellTypePursuePriority, "SpellPursuePriority" },
|
||||
{ BotSettingCategories::SpellTypeAEOrGroupTargetCount, "SpellTargetCounts" },
|
||||
{ BotSettingCategories::SpellTypeAnnounceCast, "SpellAnnounceCasts" }
|
||||
};
|
||||
|
||||
namespace BotPriorityCategories { // Update GetBotSpellCategoryName as needed
|
||||
constexpr uint8 Idle = 0;
|
||||
constexpr uint8 Engaged = 1;
|
||||
constexpr uint8 Pursue = 2;
|
||||
|
||||
constexpr uint16 START = BotPriorityCategories::Idle;
|
||||
constexpr uint16 END = BotPriorityCategories::Pursue; // Increment as needed
|
||||
};
|
||||
|
||||
namespace BotBaseSettings {
|
||||
constexpr uint16 ExpansionBitmask = 0;
|
||||
constexpr uint16 ShowHelm = 1;
|
||||
constexpr uint16 FollowDistance = 2;
|
||||
constexpr uint16 StopMeleeLevel = 3;
|
||||
constexpr uint16 EnforceSpellSettings = 4;
|
||||
constexpr uint16 RangedSetting = 5;
|
||||
constexpr uint16 PetSetTypeSetting = 6;
|
||||
constexpr uint16 BehindMob = 7;
|
||||
constexpr uint16 DistanceRanged = 8;
|
||||
constexpr uint16 IllusionBlock = 9;
|
||||
constexpr uint16 MaxMeleeRange = 10;
|
||||
constexpr uint16 MedInCombat = 11;
|
||||
constexpr uint16 SitHPPct = 12;
|
||||
constexpr uint16 SitManaPct = 13;
|
||||
|
||||
constexpr uint16 START_ALL = ExpansionBitmask;
|
||||
constexpr uint16 START = BotBaseSettings::ShowHelm; // Everything above this cannot be copied, changed or viewed by players
|
||||
constexpr uint16 END = BotBaseSettings::SitManaPct; // Increment as needed
|
||||
};
|
||||
|
||||
static std::map<uint16, std::string> botBaseSettings_names = {
|
||||
{ BotBaseSettings::ExpansionBitmask, "ExpansionBitmask" },
|
||||
{ BotBaseSettings::ShowHelm, "ShowHelm" },
|
||||
{ BotBaseSettings::FollowDistance, "FollowDistance" },
|
||||
{ BotBaseSettings::StopMeleeLevel, "StopMeleeLevel" },
|
||||
{ BotBaseSettings::EnforceSpellSettings, "EnforceSpellSettings" },
|
||||
{ BotBaseSettings::RangedSetting, "RangedSetting" },
|
||||
{ BotBaseSettings::PetSetTypeSetting, "PetSetTypeSetting" },
|
||||
{ BotBaseSettings::BehindMob, "BehindMob" },
|
||||
{ BotBaseSettings::DistanceRanged, "DistanceRanged" },
|
||||
{ BotBaseSettings::IllusionBlock, "IllusionBlock" },
|
||||
{ BotBaseSettings::MaxMeleeRange, "MaxMeleeRange" },
|
||||
{ BotBaseSettings::MedInCombat, "MedInCombat" },
|
||||
{ BotBaseSettings::SitHPPct, "SitHPPct" },
|
||||
{ BotBaseSettings::SitManaPct, "SitManaPct" }
|
||||
};
|
||||
|
||||
namespace CommandedSubTypes {
|
||||
constexpr uint16 SingleTarget = 1;
|
||||
constexpr uint16 GroupTarget = 2;
|
||||
constexpr uint16 AETarget = 3;
|
||||
constexpr uint16 SeeInvis = 4;
|
||||
constexpr uint16 Invis = 5;
|
||||
constexpr uint16 InvisUndead = 6;
|
||||
constexpr uint16 InvisAnimals = 7;
|
||||
constexpr uint16 Shrink = 8;
|
||||
constexpr uint16 Grow = 9;
|
||||
constexpr uint16 Selo = 10;
|
||||
|
||||
constexpr uint16 START = CommandedSubTypes::SingleTarget;
|
||||
constexpr uint16 END = CommandedSubTypes::Selo;
|
||||
};
|
||||
|
||||
static std::map<uint16, std::string> botSubType_names = {
|
||||
{ CommandedSubTypes::SingleTarget, "SingleTarget" },
|
||||
{ CommandedSubTypes::GroupTarget, "GroupTarget" },
|
||||
{ CommandedSubTypes::AETarget, "AETarget" },
|
||||
{ CommandedSubTypes::SeeInvis, "SeeInvis" },
|
||||
{ CommandedSubTypes::Invis, "Invis" },
|
||||
{ CommandedSubTypes::InvisUndead, "InvisUndead" },
|
||||
{ CommandedSubTypes::InvisAnimals, "InvisAnimals" },
|
||||
{ CommandedSubTypes::Shrink, "Shrink" },
|
||||
{ CommandedSubTypes::Grow, "Grow" },
|
||||
{ CommandedSubTypes::Selo, "Selo" }
|
||||
};
|
||||
|
||||
struct CombatRangeInput {
|
||||
Mob* target;
|
||||
float target_distance;
|
||||
bool behind_mob;
|
||||
uint8 stop_melee_level;
|
||||
const EQ::ItemInstance* p_item;
|
||||
const EQ::ItemInstance* s_item;
|
||||
};
|
||||
|
||||
struct CombatRangeOutput {
|
||||
bool at_combat_range = false;
|
||||
float melee_distance_min = 0.0f;
|
||||
float melee_distance = 0.0f;
|
||||
float melee_distance_max = 0.0f;
|
||||
};
|
||||
|
||||
class Bot : public NPC {
|
||||
friend class Mob;
|
||||
@ -128,7 +269,7 @@ public:
|
||||
|
||||
// Class Constructors
|
||||
Bot(NPCType *npcTypeData, Client* botOwner);
|
||||
Bot(uint32 botID, uint32 botOwnerCharacterID, uint32 botSpellsID, double totalPlayTime, uint32 lastZoneId, NPCType *npcTypeData, int32 expansion_bitmask);
|
||||
Bot(uint32 botID, uint32 botOwnerCharacterID, uint32 botSpellsID, double totalPlayTime, uint32 lastZoneId, NPCType *npcTypeData);
|
||||
|
||||
//abstract virtual override function implementations requird by base abstract class
|
||||
bool Death(Mob* killer_mob, int64 damage, uint16 spell_id, EQ::skills::SkillType attack_skill, KilledByTypes killed_by = KilledByTypes::Killed_NPC, bool is_buff_tic = false) override;
|
||||
@ -157,13 +298,14 @@ public:
|
||||
void SetLevel(uint8 in_level, bool command = false) override;
|
||||
void FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho) override;
|
||||
bool Process() override;
|
||||
void FinishTrade(Client* client, BotTradeType trade_type);
|
||||
void FinishTrade(Client* client, BotTradeType trade_type, int16 chosen_slot = INVALID_INDEX);
|
||||
bool Save() override;
|
||||
void Depop();
|
||||
void CalcBotStats(bool showtext = true);
|
||||
uint16 BotGetSpells(int spellslot) { return AIBot_spells[spellslot].spellid; }
|
||||
uint32 BotGetSpellType(int spellslot) { return AIBot_spells[spellslot].type; }
|
||||
uint16 BotGetSpellPriority(int spellslot) { return AIBot_spells[spellslot].priority; }
|
||||
std::vector<BotSpells_wIndex> BotGetSpellsByType(uint16 spell_type);
|
||||
float GetProcChances(float ProcBonus, uint16 hand) override;
|
||||
int GetHandToHandDamage(void) override;
|
||||
bool TryFinishingBlow(Mob *defender, int64 &damage) override;
|
||||
@ -177,9 +319,6 @@ public:
|
||||
inline uint16 MaxSkill(EQ::skills::SkillType skillid) { return MaxSkill(skillid, GetClass(), GetLevel()); }
|
||||
int GetBaseSkillDamage(EQ::skills::SkillType skill, Mob *target = nullptr) override;
|
||||
void DoSpecialAttackDamage(Mob *who, EQ::skills::SkillType skill, int32 max_damage, int32 min_damage = 1, int32 hate_override = -1, int ReuseTime = 10, bool HitChance = false);
|
||||
void TryBackstab(Mob *other,int ReuseTime = 10) override;
|
||||
void RogueBackstab(Mob* other, bool min_damage = false, int ReuseTime = 10) override;
|
||||
void RogueAssassinate(Mob* other) override;
|
||||
void DoClassAttacks(Mob *target, bool IsRiposte=false);
|
||||
void CalcBonuses() override;
|
||||
|
||||
@ -199,8 +338,8 @@ public:
|
||||
void Camp(bool save_to_database = true);
|
||||
void SetTarget(Mob* mob) override;
|
||||
void Zone();
|
||||
bool IsArcheryRange(Mob* target);
|
||||
void ChangeBotArcherWeapons(bool isArcher);
|
||||
bool IsAtRange(Mob* target);
|
||||
void ChangeBotRangedWeapons(bool is_ranged);
|
||||
void Sit();
|
||||
void Stand();
|
||||
bool IsSitting() const override;
|
||||
@ -216,6 +355,7 @@ public:
|
||||
void SetHoldFlag(bool flag = true) { m_hold_flag = flag; }
|
||||
bool GetAttackFlag() const { return m_attack_flag; }
|
||||
void SetAttackFlag(bool flag = true) { m_attack_flag = flag; }
|
||||
bool GetCombatRoundForAlerts() const { return m_combat_round_alert_flag; }
|
||||
bool GetAttackingFlag() const { return m_attacking_flag; }
|
||||
bool GetPullFlag() const { return m_pull_flag; }
|
||||
void SetPullFlag(bool flag = true) { m_pull_flag = flag; }
|
||||
@ -224,11 +364,10 @@ public:
|
||||
bool GetIsUsingItemClick() { return is_using_item_click; }
|
||||
void SetIsUsingItemClick(bool flag = true) { is_using_item_click = flag; }
|
||||
bool UseDiscipline(uint32 spell_id, uint32 target);
|
||||
uint8 GetNumberNeedingHealedInGroup(uint8 hpr, bool includePets, Raid* raid);
|
||||
uint8 GetNumberNeedingHealedInRaidGroup(uint8& need_healed, uint8 hpr, bool includePets, Raid* raid);
|
||||
uint8 GetNumberNeedingHealedInGroup(Mob* tar, uint16 spell_type, uint16 spell_id, float range);
|
||||
bool GetNeedsCured(Mob *tar);
|
||||
bool GetNeedsHateRedux(Mob *tar);
|
||||
bool HasOrMayGetAggro();
|
||||
bool HasOrMayGetAggro(bool SitAggro, uint32 spell_id = 0);
|
||||
void SetDefaultBotStance();
|
||||
void SetSurname(std::string_view bot_surname);
|
||||
void SetTitle(std::string_view bot_title);
|
||||
@ -327,19 +466,26 @@ public:
|
||||
void AI_Bot_Event_SpellCastFinished(bool iCastSucceeded, uint16 slot);
|
||||
|
||||
// AI Methods
|
||||
bool AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes);
|
||||
bool AICastSpell(Mob* tar, uint8 chance, uint16 spell_type, uint16 sub_target_type = UINT16_MAX, uint16 sub_type = UINT16_MAX);
|
||||
bool AttemptAICastSpell(uint16 spell_type, Mob* tar = nullptr);
|
||||
bool AttemptAACastSpell(Mob* tar, uint16 spell_id, AA::Rank* rank);
|
||||
bool AttemptForcedCastSpell(Mob* tar, uint16 spell_id, bool is_disc = false);
|
||||
bool AttemptCloseBeneficialSpells(uint16 spell_type);
|
||||
bool AI_EngagedCastCheck() override;
|
||||
bool AI_PursueCastCheck() override;
|
||||
bool AI_IdleCastCheck() override;
|
||||
bool AIHealRotation(Mob* tar, bool useFastHeals);
|
||||
bool GetPauseAI() const { return _pauseAI; }
|
||||
void SetPauseAI(bool pause_flag) { _pauseAI = pause_flag; }
|
||||
uint8 GetStopMeleeLevel() const { return _stopMeleeLevel; }
|
||||
void SetStopMeleeLevel(uint8 level);
|
||||
bool IsCommandedSpell() const { return _commandedSpell; }
|
||||
void SetCommandedSpell(bool value) { _commandedSpell = value; }
|
||||
bool IsPullingSpell() const { return _pullingSpell; }
|
||||
void SetPullingSpell(bool value) { _pullingSpell = value; }
|
||||
|
||||
void SetGuardMode();
|
||||
void SetHoldMode();
|
||||
uint32 GetBotCasterRange() const { return m_bot_caster_range; }
|
||||
bool IsValidSpellRange(uint16 spell_id, Mob const* tar);
|
||||
|
||||
bool IsValidSpellRange(uint16 spell_id, Mob* tar);
|
||||
|
||||
// Bot AI Methods
|
||||
void AI_Bot_Init();
|
||||
@ -377,6 +523,169 @@ public:
|
||||
inline bool Attack(Mob* other, int Hand = EQ::invslot::slotPrimary, bool FromRiposte = false, bool IsStrikethrough = false,
|
||||
bool IsFromSpell = false, ExtraAttackOptions *opts = nullptr) override
|
||||
{ return Mob::Attack(other, Hand, FromRiposte, IsStrikethrough, IsFromSpell, opts); }
|
||||
void DoAttackRounds(Mob* target, int hand);
|
||||
|
||||
bool BotPassiveCheck();
|
||||
Raid* GetStoredRaid() { return _storedRaid; }
|
||||
void SetStoredRaid(Raid* stored_raid) { _storedRaid = stored_raid; }
|
||||
bool GetVerifiedRaid() { return _verifiedRaid; }
|
||||
void SetVerifiedRaid(bool status) { _verifiedRaid = status; }
|
||||
uint16 GetTempSpellType() { return _tempSpellType; }
|
||||
void SetTempSpellType(uint16 spell_type) { _tempSpellType = spell_type; }
|
||||
bool IsMobEngagedByAnyone(Mob* tar);
|
||||
void SetBotTimers(std::vector<BotTimer> timers) { bot_timers = timers; }
|
||||
|
||||
// Targeting
|
||||
std::vector<Mob*> GatherSpellTargets(bool entireRaid = false, Mob* target = nullptr, bool no_clients = false, bool no_bots = false);
|
||||
bool HasValidAETarget(Bot* caster, uint16 spell_id, uint16 spell_type, Mob* tar);
|
||||
void SetHasLoS(bool has_los) { _hasLoS = has_los; }
|
||||
bool HasLoS() const { return _hasLoS; }
|
||||
bool IsValidMezTarget(Mob* owner, Mob* npc, uint16 spell_id);
|
||||
|
||||
// Cast checks
|
||||
bool PrecastChecks(Mob* tar, uint16 spell_type);
|
||||
bool CastChecks(uint16 spell_id, Mob* tar, uint16 spell_type, bool prechecks = false, bool ae_check = false);
|
||||
bool IsImmuneToBotSpell(uint16 spell_id, Mob* caster);
|
||||
bool CanCastSpellType(uint16 spell_type, uint16 spell_id, Mob* tar);
|
||||
bool BotHasEnoughMana(uint16 spell_id);
|
||||
bool IsTargetAlreadyReceivingSpell(Mob* tar, uint16 spell_id);
|
||||
bool DoResistCheck(Mob* target, uint16 spell_id, int32 resist_limit);
|
||||
bool DoResistCheckBySpellType(Mob* tar, uint16 spell_id, uint16 spell_type);
|
||||
bool IsValidTargetType(uint16 spell_id, int target_type, uint8 body_type);
|
||||
|
||||
// Spell checks
|
||||
static bool IsValidBotSpellType(uint16 spell_type);
|
||||
uint16 GetPetBotSpellType(uint16 spell_type);
|
||||
|
||||
// Movement checks
|
||||
bool PlotBotPositionAroundTarget(Mob* target, float& x_dest, float& y_dest, float& z_dest, float min_distance, float max_distance, bool behind_only = false, bool front_only = false, bool bypass_los = false);
|
||||
std::vector<Mob*> GetSpellTargetList(bool entire_raid = false);
|
||||
void SetSpellTargetList(std::vector<Mob*> spell_target_list) { _spell_target_list = spell_target_list; }
|
||||
std::vector<Mob*> GetGroupSpellTargetList() { return _group_spell_target_list; }
|
||||
void SetGroupSpellTargetList(std::vector<Mob*> spell_target_list) { _group_spell_target_list = spell_target_list; }
|
||||
std::vector<Mob*> GetBuffTargets(Mob* spellTarget);
|
||||
|
||||
// Bot settings
|
||||
void LoadDefaultBotSettings();
|
||||
int GetDefaultSetting(uint16 setting_category, uint16 setting_type, uint8 stance = Stance::Balanced);
|
||||
int GetDefaultBotBaseSetting(uint16 bot_setting, uint8 stance = Stance::Balanced);
|
||||
bool GetDefaultSpellTypeHold(uint16 spell_type, uint8 stance = Stance::Balanced);
|
||||
uint16 GetDefaultSpellTypePriority(uint16 spell_type, uint8 priority_type, uint8 bot_class, uint8 stance = Stance::Balanced);
|
||||
uint16 GetDefaultSpellTypeIdlePriority(uint16 spell_type, uint8 bot_class, uint8 stance = Stance::Balanced);
|
||||
uint16 GetDefaultSpellTypeEngagedPriority(uint16 spell_type, uint8 bot_class, uint8 stance = Stance::Balanced);
|
||||
uint16 GetDefaultSpellTypePursuePriority(uint16 spell_type, uint8 bot_class, uint8 stance = Stance::Balanced);
|
||||
uint16 GetDefaultSpellTypeResistLimit(uint16 spell_type, uint8 stance = Stance::Balanced);
|
||||
bool GetDefaultSpellTypeAggroCheck(uint16 spell_type, uint8 stance = Stance::Balanced);
|
||||
uint8 GetDefaultSpellTypeMinManaLimit(uint16 spell_type, uint8 stance = Stance::Balanced);
|
||||
uint8 GetDefaultSpellTypeMaxManaLimit(uint16 spell_type, uint8 stance = Stance::Balanced);
|
||||
uint8 GetDefaultSpellTypeMinHPLimit(uint16 spell_type, uint8 stance = Stance::Balanced);
|
||||
uint8 GetDefaultSpellTypeMaxHPLimit(uint16 spell_type, uint8 stance = Stance::Balanced);
|
||||
uint16 GetDefaultSpellTypeAnnounceCast(uint16 spell_type, uint8 stance = Stance::Balanced);
|
||||
uint16 GetDefaultSpellTypeAEOrGroupTargetCount(uint16 spell_type, uint8 stance = Stance::Balanced);
|
||||
|
||||
static bool IsValidBotBaseSetting(uint16 setting_type);
|
||||
static std::string GetBotSettingCategoryName(uint16 setting_type);
|
||||
uint16 GetBaseSettingIDByShortName(std::string setting_string);
|
||||
int GetBotBaseSetting(uint16 bot_setting);
|
||||
void SetBotBaseSetting(uint16 bot_setting, int setting_value);
|
||||
int GetSetting(uint16 setting_category, uint16 setting_type);
|
||||
void SetBotSetting(uint8 setting_type, uint16 bot_setting, int setting_value);
|
||||
void CopySettings(Bot* to, uint8 setting_type, uint16 spell_type = UINT16_MAX);
|
||||
void CopyBotSpellSettings(Bot* to);
|
||||
void ResetBotSpellSettings();
|
||||
|
||||
void CopyBotBlockedBuffs(Bot* to);
|
||||
void CopyBotBlockedPetBuffs(Bot* to);
|
||||
void CleanBotBlockedBuffs();
|
||||
void ClearBotBlockedBuffs() { bot_blocked_buffs.clear(); }
|
||||
bool IsBlockedBuff(int32 spell_id) override;
|
||||
bool IsBlockedPetBuff(int32 spell_id) override;
|
||||
void SetBotBlockedBuff(uint16 spell_id, bool block);
|
||||
void SetBotBlockedPetBuff(uint16 spell_id, bool block);
|
||||
std::vector<BotBlockedBuffs> GetBotBlockedBuffs() { return bot_blocked_buffs; }
|
||||
void SetBotBlockedBuffs(std::vector<BotBlockedBuffs> blocked_buffs) { bot_blocked_buffs = blocked_buffs; }
|
||||
|
||||
void SetBotSpellRecastTimer(uint16 spell_type, Mob* spelltar, bool pre_cast = false);
|
||||
uint16 GetSpellTypePriority(uint16 spell_type, uint8 priority_type);
|
||||
void SetSpellTypePriority(uint16 spell_type, uint8 priority_type, uint16 priority);
|
||||
inline uint16 GetSpellTypeResistLimit(uint16 spell_type) const { return m_bot_spell_settings[spell_type].resist_limit; }
|
||||
inline void SetSpellTypeResistLimit(uint16 spell_type, uint16 value) { m_bot_spell_settings[spell_type].resist_limit = value; }
|
||||
inline bool GetSpellTypeAggroCheck(uint16 spell_type) const { return m_bot_spell_settings[spell_type].aggro_check; }
|
||||
inline void SetSpellTypeAggroCheck(uint16 spell_type, bool value) { m_bot_spell_settings[spell_type].aggro_check = value; }
|
||||
uint8 GetHPRatioForSpellType(uint16 spell_type, Mob* tar);
|
||||
inline uint8 GetSpellTypeMinManaLimit(uint16 spell_type) const { return m_bot_spell_settings[spell_type].min_mana_pct; }
|
||||
inline uint8 GetSpellTypeMaxManaLimit(uint16 spell_type) const { return m_bot_spell_settings[spell_type].max_mana_pct; }
|
||||
inline void SetSpellTypeMinManaLimit(uint16 spell_type, uint8 value) { m_bot_spell_settings[spell_type].min_mana_pct = value; }
|
||||
inline void SetSpellTypeMaxManaLimit(uint16 spell_type, uint8 value) { m_bot_spell_settings[spell_type].max_mana_pct = value; }
|
||||
inline uint8 GetSpellTypeMinHPLimit(uint16 spell_type) const { return m_bot_spell_settings[spell_type].min_hp_pct; }
|
||||
inline uint8 GetSpellTypeMaxHPLimit(uint16 spell_type) const { return m_bot_spell_settings[spell_type].max_hp_pct; }
|
||||
inline void SetSpellTypeMinHPLimit(uint16 spell_type, uint8 value) { m_bot_spell_settings[spell_type].min_hp_pct = value; }
|
||||
inline void SetSpellTypeMaxHPLimit(uint16 spell_type, uint8 value) { m_bot_spell_settings[spell_type].max_hp_pct = value; }
|
||||
inline uint16 GetSpellTypeAEOrGroupTargetCount(uint16 spell_type) const { return m_bot_spell_settings[spell_type].ae_or_group_target_count; }
|
||||
inline void SetSpellTypeAEOrGroupTargetCount(uint16 spell_type, uint16 value) { m_bot_spell_settings[spell_type].ae_or_group_target_count = value; }
|
||||
inline uint16 GetSpellTypeAnnounceCast(uint16 spell_type) const { return m_bot_spell_settings[spell_type].announce_cast; }
|
||||
inline void SetSpellTypeAnnounceCast(uint16 spell_type, uint16 value) { m_bot_spell_settings[spell_type].announce_cast = value; }
|
||||
inline bool GetSpellTypeHold(uint16 spell_type) const { return m_bot_spell_settings[spell_type].hold; }
|
||||
inline void SetSpellTypeHold(uint16 spell_type, bool value) { m_bot_spell_settings[spell_type].hold = value; }
|
||||
inline uint16 GetSpellTypeDelay(uint16 spell_type) const { return m_bot_spell_settings[spell_type].delay; }
|
||||
inline void SetSpellTypeDelay(uint16 spell_type, uint16 delay_value) { m_bot_spell_settings[spell_type].delay = delay_value; }
|
||||
inline uint8 GetSpellTypeMinThreshold(uint16 spell_type) const { return m_bot_spell_settings[spell_type].min_threshold; }
|
||||
inline void SetSpellTypeMinThreshold(uint16 spell_type, uint8 threshold_value) { m_bot_spell_settings[spell_type].min_threshold = threshold_value; }
|
||||
inline uint8 GetSpellTypeMaxThreshold(uint16 spell_type) const { return m_bot_spell_settings[spell_type].max_threshold; }
|
||||
inline void SetSpellTypeMaxThreshold(uint16 spell_type, uint8 threshold_value) { m_bot_spell_settings[spell_type].max_threshold = threshold_value; }
|
||||
inline bool SpellTypeRecastCheck(uint16 spellType) { return !m_bot_spell_settings[spellType].recast_timer.GetRemainingTime(); }
|
||||
void SetSpellTypeRecastTimer(uint16 spell_type, uint32 recast_time) { m_bot_spell_settings[spell_type].recast_timer.Start(recast_time); }
|
||||
inline bool SpellTypeAIDelayCheck(uint16 spellType) { return !m_bot_spell_settings[spellType].ai_delay.GetRemainingTime(); }
|
||||
void SetSpellTypeAITimer(uint16 spell_type, uint32 recast_time) { m_bot_spell_settings[spell_type].ai_delay.Start(recast_time); }
|
||||
bool GetUltimateSpellTypeHold(uint16 spell_type, Mob* tar);
|
||||
uint16 GetDefaultSpellTypeDelay(uint16 spell_type, uint8 stance = Stance::Balanced);
|
||||
uint8 GetDefaultSpellTypeMinThreshold(uint16 spell_type, uint8 stance = Stance::Balanced);
|
||||
uint8 GetDefaultSpellTypeMaxThreshold(uint16 spell_type, uint8 stance = Stance::Balanced);
|
||||
uint16 GetUltimateSpellTypeDelay(uint16 spell_type, Mob* tar);
|
||||
bool GetUltimateSpellTypeDelayCheck(uint16 spell_type, Mob* tar);
|
||||
uint8 GetUltimateSpellTypeMinThreshold(uint16 spell_type, Mob* tar);
|
||||
uint8 GetUltimateSpellTypeMaxThreshold(uint16 spell_type, Mob* tar);
|
||||
void SetIllusionBlock(bool value) { _illusionBlock = value; }
|
||||
bool GetIllusionBlock() const override { return _illusionBlock; }
|
||||
bool GetShowHelm() const { return _showHelm; }
|
||||
void SetShowHelm(bool show_helm) { _showHelm = show_helm; }
|
||||
bool GetBehindMob() const { return _behindMobStatus; }
|
||||
void SetBehindMob(bool value) { _behindMobStatus = value; }
|
||||
bool GetMaxMeleeRange() const { return _maxMeleeRangeStatus; }
|
||||
void SetMaxMeleeRange(bool value) { _maxMeleeRangeStatus = value; }
|
||||
uint8 GetStopMeleeLevel() const { return _stopMeleeLevel; }
|
||||
void SetStopMeleeLevel(uint8 level) { _stopMeleeLevel = level; }
|
||||
uint32 GetBotDistanceRanged() const { return _distanceRanged; }
|
||||
void SetBotDistanceRanged(uint32 distance) { _distanceRanged = distance; }
|
||||
bool GetMedInCombat() const { return _medInCombat; }
|
||||
void SetMedInCombat(bool value) { _medInCombat = value; }
|
||||
uint8 GetSitHPPct() const { return _SitHPPct; }
|
||||
void SetSitHPPct(uint8 value) { _SitHPPct = value; }
|
||||
uint8 GetSitManaPct() const { return _SitManaPct; }
|
||||
void SetSitManaPct(uint8 value) { _SitManaPct = value; }
|
||||
|
||||
// Spell lists
|
||||
void CheckBotSpells();
|
||||
void MapSpellTypeLevels();
|
||||
const std::map<int32_t, std::map<int32_t, BotSpellTypesByClass>>& GetCommandedSpellTypesMinLevels() { return commanded_spells_min_level; }
|
||||
std::list<BotSpellTypeOrder> GetSpellTypesPrioritized(uint8 priority_type);
|
||||
uint16 GetParentSpellType(uint16 spell_type);
|
||||
bool IsValidSpellTypeBySpellID(uint16 spell_type, uint16 spell_id);
|
||||
inline uint16 GetCastedSpellType() const { return _castedSpellType; }
|
||||
void SetCastedSpellType(uint16 spell_type);
|
||||
bool IsValidSpellTypeSubType(uint16 spell_type, uint16 sub_type, uint16 spell_id);
|
||||
static bool IsValidBotSpellCategory(uint8 setting_type);
|
||||
static std::string GetBotSpellCategoryName(uint8 setting_type);
|
||||
static uint16 GetBotSpellCategoryIDByShortName(std::string setting_string);
|
||||
void AssignBotSpellsToTypes(std::vector<BotSpells>& AIBot_spells, std::unordered_map<uint16, std::vector<BotSpells_wIndex>>& AIBot_spells_by_type);
|
||||
uint16 GetSpellByAA(int id, AA::Rank*& rank);
|
||||
|
||||
// Spell Type
|
||||
static uint16 GetSpellTypeIDByShortName(std::string spellType_string);
|
||||
static std::string GetSpellTypeNameByID(uint16 spell_type);
|
||||
static std::string GetSpellTypeShortNameByID(uint16 spell_type);
|
||||
bool IsValidSubType(uint16 sub_type);
|
||||
static std::string GetSubTypeNameByID(uint16 sub_type);
|
||||
|
||||
[[nodiscard]] int GetMaxBuffSlots() const final { return EQ::spells::LONG_BUFFS; }
|
||||
[[nodiscard]] int GetMaxSongSlots() const final { return EQ::spells::SHORT_BUFFS; }
|
||||
@ -423,33 +732,36 @@ public:
|
||||
ProcessBotGroupAdd(Group* group, Raid* raid, Client* client = nullptr, bool new_raid = false, bool initial = false);
|
||||
|
||||
|
||||
static std::list<BotSpell> GetBotSpellsForSpellEffect(Bot* botCaster, int spellEffect);
|
||||
static std::list<BotSpell> GetBotSpellsForSpellEffectAndTargetType(Bot* botCaster, int spellEffect, SpellTargetType targetType);
|
||||
static std::list<BotSpell> GetBotSpellsBySpellType(Bot* botCaster, uint32 spellType);
|
||||
static std::list<BotSpell_wPriority> GetPrioritizedBotSpellsBySpellType(Bot* botCaster, uint32 spellType);
|
||||
static std::list<BotSpell> GetBotSpellsForSpellEffect(Bot* caster, uint16 spell_type, int spell_effect);
|
||||
static std::list<BotSpell> GetBotSpellsForSpellEffectAndTargetType(Bot* caster, uint16 spell_type, int spell_effect, SpellTargetType target_type);
|
||||
static std::list<BotSpell> GetBotSpellsBySpellType(Bot* caster, uint16 spell_type);
|
||||
static std::vector<BotSpell_wPriority> GetPrioritizedBotSpellsBySpellType(Bot* caster, uint16 spell_type, Mob* tar, bool AE = false, uint16 sub_target_type = UINT16_MAX, uint16 sub_type = UINT16_MAX);
|
||||
|
||||
static BotSpell GetFirstBotSpellBySpellType(Bot* botCaster, uint32 spellType);
|
||||
static BotSpell GetBestBotSpellForFastHeal(Bot* botCaster);
|
||||
static BotSpell GetBestBotSpellForHealOverTime(Bot* botCaster);
|
||||
static BotSpell GetBestBotSpellForPercentageHeal(Bot* botCaster);
|
||||
static BotSpell GetBestBotSpellForRegularSingleTargetHeal(Bot* botCaster);
|
||||
static BotSpell GetFirstBotSpellForSingleTargetHeal(Bot* botCaster);
|
||||
static BotSpell GetBestBotSpellForGroupHealOverTime(Bot* botCaster);
|
||||
static BotSpell GetBestBotSpellForGroupCompleteHeal(Bot* botCaster);
|
||||
static BotSpell GetBestBotSpellForGroupHeal(Bot* botCaster);
|
||||
static BotSpell GetBestBotSpellForMagicBasedSlow(Bot* botCaster);
|
||||
static BotSpell GetBestBotSpellForDiseaseBasedSlow(Bot* botCaster);
|
||||
static BotSpell GetFirstBotSpellBySpellType(Bot* caster, uint16 spell_type);
|
||||
BotSpell GetSpellByHealType(uint16 spell_type, Mob* tar);
|
||||
static BotSpell GetBestBotSpellForVeryFastHeal(Bot* caster, Mob* tar, uint16 spell_type = BotSpellTypes::RegularHeal);
|
||||
static BotSpell GetBestBotSpellForFastHeal(Bot* caster, Mob* tar, uint16 spell_type = BotSpellTypes::RegularHeal);
|
||||
static BotSpell GetBestBotSpellForHealOverTime(Bot* caster, Mob* tar, uint16 spell_type = BotSpellTypes::RegularHeal);
|
||||
static BotSpell GetBestBotSpellForPercentageHeal(Bot* caster, Mob* tar, uint16 spell_type = BotSpellTypes::RegularHeal);
|
||||
static BotSpell GetBestBotSpellForRegularSingleTargetHeal(Bot* caster, Mob* tar, uint16 spell_type = BotSpellTypes::RegularHeal);
|
||||
static BotSpell GetFirstBotSpellForSingleTargetHeal(Bot* caster, Mob* tar, uint16 spell_type = BotSpellTypes::RegularHeal);
|
||||
static BotSpell GetBestBotSpellForGroupHealOverTime(Bot* caster, Mob* tar, uint16 spell_type = BotSpellTypes::RegularHeal);
|
||||
static BotSpell GetBestBotSpellForGroupCompleteHeal(Bot* caster, Mob* tar, uint16 spell_type = BotSpellTypes::RegularHeal);
|
||||
static BotSpell GetBestBotSpellForGroupHeal(Bot* caster, Mob* tar, uint16 spell_type = BotSpellTypes::RegularHeal);
|
||||
|
||||
static Mob* GetFirstIncomingMobToMez(Bot* botCaster, BotSpell botSpell);
|
||||
static BotSpell GetBestBotSpellForMez(Bot* botCaster);
|
||||
static BotSpell GetBestBotMagicianPetSpell(Bot* botCaster);
|
||||
static std::string GetBotMagicianPetType(Bot* botCaster);
|
||||
static BotSpell GetBestBotSpellForNukeByTargetType(Bot* botCaster, SpellTargetType targetType);
|
||||
static BotSpell GetBestBotSpellForStunByTargetType(Bot* botCaster, SpellTargetType targetType);
|
||||
static BotSpell GetBestBotWizardNukeSpellByTargetResists(Bot* botCaster, Mob* target);
|
||||
static BotSpell GetDebuffBotSpell(Bot* botCaster, Mob* target);
|
||||
static BotSpell GetBestBotSpellForCure(Bot* botCaster, Mob* target);
|
||||
static BotSpell GetBestBotSpellForResistDebuff(Bot* botCaster, Mob* target);
|
||||
static Mob* GetFirstIncomingMobToMez(Bot* caster, int16 spell_id, uint16 spell_type, bool AE);
|
||||
static BotSpell GetBestBotSpellForMez(Bot* caster, uint16 spell_type = BotSpellTypes::Mez);
|
||||
static BotSpell GetBestBotMagicianPetSpell(Bot* caster, uint16 spell_type = BotSpellTypes::Pet);
|
||||
static std::string GetBotMagicianPetType(Bot* caster);
|
||||
static BotSpell GetBestBotSpellForNukeByTargetType(Bot* caster, SpellTargetType target_type, uint16 spell_type, bool AE = false, Mob* tar = nullptr);
|
||||
static BotSpell GetBestBotSpellForStunByTargetType(Bot* caster, SpellTargetType target_type, uint16 spell_type, bool AE = false, Mob* tar = nullptr);
|
||||
static BotSpell GetBestBotWizardNukeSpellByTargetResists(Bot* caster, Mob* target, uint16 spell_type);
|
||||
static BotSpell GetDebuffBotSpell(Bot* caster, Mob* target, uint16 spell_type);
|
||||
static BotSpell GetBestBotSpellForCure(Bot* caster, Mob* target, uint16 spell_type);
|
||||
static BotSpell GetBestBotSpellForResistDebuff(Bot* caster, Mob* target, uint16 spell_type);
|
||||
static BotSpell GetBestBotSpellForNukeByBodyType(Bot* caster, uint8 body_type, uint16 spell_type, bool AE = false, Mob* tar = nullptr);
|
||||
static BotSpell GetBestBotSpellForRez(Bot* caster, Mob* target, uint16 spell_type);
|
||||
static BotSpell GetBestBotSpellForCharm(Bot* caster, Mob* target, uint16 spell_type);
|
||||
|
||||
static NPCType *CreateDefaultNPCTypeStructForBot(
|
||||
const std::string& botName,
|
||||
@ -465,19 +777,18 @@ public:
|
||||
// Static Bot Group Methods
|
||||
static bool AddBotToGroup(Bot* bot, Group* group);
|
||||
static bool RemoveBotFromGroup(Bot* bot, Group* group);
|
||||
static void BotGroupSay(Mob *speaker, const char *msg, ...);
|
||||
static void RaidGroupSay(Mob *speaker, const char *msg, ...);
|
||||
|
||||
// "GET" Class Methods
|
||||
uint32 GetBotID() const { return _botID; }
|
||||
uint32 GetBotOwnerCharacterID() const { return _botOwnerCharacterID; }
|
||||
uint32 GetBotSpellID() const { return npc_spells_id; }
|
||||
Mob* GetBotOwner() { return this->_botOwner; }
|
||||
uint32 GetBotArcheryRange();
|
||||
uint32 GetBotRangedValue();
|
||||
EQ::ItemInstance* GetBotItem(uint16 slot_id);
|
||||
bool GetSpawnStatus() { return _spawnStatus; }
|
||||
uint8 GetPetChooserID() { return _petChooserID; }
|
||||
bool IsPetChooser() { return _petChooser; }
|
||||
bool IsBotArcher() { return m_bot_archery_setting; }
|
||||
bool IsBotRanged() { return _botRangedSetting; }
|
||||
bool IsBotCharmer() { return _botCharmer; }
|
||||
bool IsBot() const override { return true; }
|
||||
bool IsOfClientBot() const override { return true; }
|
||||
@ -485,9 +796,7 @@ public:
|
||||
|
||||
bool GetRangerAutoWeaponSelect() { return _rangerAutoWeaponSelect; }
|
||||
uint8 GetBotStance() { return _botStance; }
|
||||
uint8 GetChanceToCastBySpellType(uint32 spellType);
|
||||
bool GetBotEnforceSpellSetting() { return m_enforce_spell_settings; }
|
||||
float GetBotCasterMaxRange(float melee_distance_max);
|
||||
uint8 GetChanceToCastBySpellType(uint16 spell_type);
|
||||
bool IsGroupHealer() const { return m_CastingRoles.GroupHealer; }
|
||||
bool IsGroupSlower() const { return m_CastingRoles.GroupSlower; }
|
||||
bool IsGroupNuker() const { return m_CastingRoles.GroupNuker; }
|
||||
@ -527,8 +836,6 @@ public:
|
||||
|
||||
std::shared_ptr<HealRotation>* MemberOfHealRotation() { return &m_member_of_heal_rotation; }
|
||||
|
||||
bool GetAltOutOfCombatBehavior() const { return _altoutofcombatbehavior;}
|
||||
bool GetShowHelm() const { return _showhelm; }
|
||||
inline int32 GetSTR() const override { return STR; }
|
||||
inline int32 GetSTA() const override { return STA; }
|
||||
inline int32 GetDEX() const override { return DEX; }
|
||||
@ -584,7 +891,8 @@ public:
|
||||
inline const InspectMessage_Struct& GetInspectMessage() const { return _botInspectMessage; }
|
||||
|
||||
// "Quest API" Methods
|
||||
bool HasBotSpellEntry(uint16 spellid);
|
||||
bool HasBotSpellEntry(uint16 spell_id);
|
||||
bool CanUseBotSpell(uint16 spell_id);
|
||||
void ApplySpell(int spell_id, int duration = 0, int level = -1, ApplySpellType apply_type = ApplySpellType::Solo, bool allow_pets = false, bool is_raid_group_only = true);
|
||||
void BreakInvis();
|
||||
void Escape();
|
||||
@ -600,13 +908,11 @@ public:
|
||||
void SetBotSpellID(uint32 newSpellID);
|
||||
void SetSpawnStatus(bool spawnStatus) { _spawnStatus = spawnStatus; }
|
||||
void SetPetChooserID(uint8 id) { _petChooserID = id; }
|
||||
void SetBotArcherySetting(bool bot_archer_setting, bool save = false);
|
||||
void SetBotRangedSetting(bool value) { _botRangedSetting = value; }
|
||||
void SetBotCharmer(bool c) { _botCharmer = c; }
|
||||
void SetPetChooser(bool p) { _petChooser = p; }
|
||||
void SetBotOwner(Mob* botOwner) { this->_botOwner = botOwner; }
|
||||
void SetRangerAutoWeaponSelect(bool enable) { GetClass() == Class::Ranger ? _rangerAutoWeaponSelect = enable : _rangerAutoWeaponSelect = false; }
|
||||
void SetBotStance(uint8 stance_id) { _botStance = Stance::IsValid(stance_id) ? stance_id : Stance::Passive; }
|
||||
void SetBotCasterRange(uint32 bot_caster_range) { m_bot_caster_range = bot_caster_range; }
|
||||
uint32 GetSpellRecastTimer(uint16 spell_id = 0);
|
||||
bool CheckSpellRecastTimer(uint16 spell_id = 0);
|
||||
uint32 GetSpellRecastRemainingTime(uint16 spell_id = 0);
|
||||
@ -624,8 +930,6 @@ public:
|
||||
void ClearSpellRecastTimer(uint16 spell_id = 0);
|
||||
uint32 GetItemReuseRemainingTime(uint32 item_id = 0);
|
||||
void ClearExpiredTimers();
|
||||
void SetAltOutOfCombatBehavior(bool behavior_flag) { _altoutofcombatbehavior = behavior_flag;}
|
||||
void SetShowHelm(bool showhelm) { _showhelm = showhelm; }
|
||||
void SetBeardColor(uint8 value) { beardcolor = value; }
|
||||
void SetBeard(uint8 value) { beard = value; }
|
||||
void SetEyeColor1(uint8 value) { eyecolor1 = value; }
|
||||
@ -639,7 +943,7 @@ public:
|
||||
bool DyeArmor(int16 slot_id, uint32 rgb, bool all_flag = false, bool save_flag = true);
|
||||
|
||||
int GetExpansionBitmask();
|
||||
void SetExpansionBitmask(int expansion_bitmask, bool save = true);
|
||||
void SetExpansionBitmask(int expansionBitmask);
|
||||
|
||||
void ListBotSpells(uint8 min_level);
|
||||
|
||||
@ -651,15 +955,12 @@ public:
|
||||
void ListBotSpellSettings();
|
||||
void LoadBotSpellSettings();
|
||||
bool UpdateBotSpellSetting(uint16 spell_id, BotSpellSetting* bs);
|
||||
void SetBotEnforceSpellSetting(bool enforcespellsettings, bool save = false);
|
||||
bool GetBotEnforceSpellSetting() const { return m_enforce_spell_settings; }
|
||||
void SetBotEnforceSpellSetting(bool enforceSpellSettings);
|
||||
bool GetBotEnforceSpellSetting() { return _enforceSpellSettings; }
|
||||
|
||||
// Class Destructors
|
||||
~Bot() override;
|
||||
|
||||
// Publicized protected functions
|
||||
void BotRangedAttack(Mob* other);
|
||||
|
||||
// Publicized private functions
|
||||
static NPCType *FillNPCTypeStruct(
|
||||
uint32 botSpellsID,
|
||||
@ -722,8 +1023,7 @@ public:
|
||||
|
||||
// New accessors for BotDatabase access
|
||||
bool DeleteBot();
|
||||
std::vector<BotTimer_Struct> GetBotTimers() { return bot_timers; }
|
||||
void SetBotTimers(std::vector<BotTimer_Struct> timers) { bot_timers = timers; }
|
||||
std::vector<BotTimer> GetBotTimers() { return bot_timers; }
|
||||
uint32 GetLastZoneID() const { return _lastZoneId; }
|
||||
int32 GetBaseAC() const { return _baseAC; }
|
||||
int32 GetBaseATK() const { return _baseATK; }
|
||||
@ -750,24 +1050,11 @@ public:
|
||||
|
||||
static uint8 spell_casting_chances[SPELL_TYPE_COUNT][Class::PLAYER_CLASS_COUNT][Stance::AEBurn][cntHSND];
|
||||
|
||||
bool BotCastMez(Mob* tar, uint8 botLevel, bool checked_los, BotSpell& botSpell, Raid* raid);
|
||||
bool BotCastHeal(Mob* tar, uint8 botLevel, uint8 botClass, BotSpell& botSpell, Raid* raid);
|
||||
bool BotCastRoot(Mob* tar, uint8 botLevel, uint32 iSpellTypes, BotSpell& botSpell, const bool& checked_los);
|
||||
bool BotCastBuff(Mob* tar, uint8 botLevel, uint8 botClass);
|
||||
bool BotCastEscape(Mob*& tar, uint8 botClass, BotSpell& botSpell, uint32 iSpellTypes);
|
||||
bool BotCastNuke(Mob* tar, uint8 botLevel, uint8 botClass, BotSpell& botSpell, const bool& checked_los);
|
||||
bool BotCastDispel(Mob* tar, BotSpell& botSpell, uint32 iSpellTypes, const bool& checked_los);
|
||||
bool BotCastPet(Mob* tar, uint8 botClass, BotSpell& botSpell);
|
||||
bool BotCastCombatBuff(Mob* tar, uint8 botLevel, uint8 botClass);
|
||||
bool BotCastLifetap(Mob* tar, uint8 botLevel, BotSpell& botSpell, const bool& checked_los, uint32 iSpellTypes);
|
||||
bool BotCastSnare(Mob* tar, uint8 botLevel, BotSpell& botSpell, const bool& checked_los, uint32 iSpellTypes);
|
||||
bool BotCastDOT(Mob* tar, uint8 botLevel, const BotSpell& botSpell, const bool& checked_los);
|
||||
bool BotCastSlow(Mob* tar, uint8 botLevel, uint8 botClass, BotSpell& botSpell, const bool& checked_los, Raid* raid);
|
||||
bool BotCastDebuff(Mob* tar, uint8 botLevel, BotSpell& botSpell, bool checked_los);
|
||||
bool BotCastCure(Mob* tar, uint8 botClass, BotSpell& botSpell, Raid* raid);
|
||||
bool BotCastHateReduction(Mob* tar, uint8 botLevel, const BotSpell& botSpell);
|
||||
bool BotCastCombatSong(Mob* tar, uint8 botLevel);
|
||||
bool BotCastSong(Mob* tar, uint8 botLevel);
|
||||
bool BotCastMez(Mob* tar, uint8 bot_class, BotSpell& bot_spell, uint16 spell_type);
|
||||
bool BotCastHeal(Mob* tar, uint8 bot_class, BotSpell& bot_spell, uint16 spell_type);
|
||||
bool BotCastNuke(Mob* tar, uint8 bot_class, BotSpell& bot_spell, uint16 spell_type);
|
||||
bool BotCastPet(Mob* tar, uint8 bot_class, BotSpell& bot_spell, uint16 spell_type);
|
||||
bool BotCastCure(Mob* tar, uint8 bot_class, BotSpell& bot_spell, uint16 spell_type);
|
||||
|
||||
bool CheckIfIncapacitated();
|
||||
bool IsAIProcessValid(const Client* bot_owner, const Group* bot_group, const Raid* raid);
|
||||
@ -785,51 +1072,52 @@ public:
|
||||
Mob* tar,
|
||||
float tar_distance
|
||||
);
|
||||
bool TargetValidation(Mob* other);
|
||||
|
||||
bool PullingFlagChecks(Client* bot_owner);
|
||||
bool ReturningFlagChecks(Client* bot_owner, float fm_distance);
|
||||
bool ReturningFlagChecks(Client* bot_owner, Mob* leash_owner, float fm_distance);
|
||||
void BotPullerProcess(Client* bot_owner, Raid* raid);
|
||||
|
||||
|
||||
// Movement Methods
|
||||
void CalcMeleeDistances(
|
||||
const Mob* tar,
|
||||
const EQ::ItemInstance* const& p_item,
|
||||
const EQ::ItemInstance* const& s_item,
|
||||
bool behind_mob,
|
||||
bool backstab_weapon,
|
||||
float& melee_distance_max,
|
||||
float& melee_distance
|
||||
) const;
|
||||
|
||||
// Combat Checks
|
||||
CombatRangeOutput EvaluateCombatRange(const CombatRangeInput& input);
|
||||
|
||||
void SetBerserkState();
|
||||
bool CheckIfCasting(float fm_distance);
|
||||
void HealRotationChecks();
|
||||
void CheckCombatRange(Mob* tar, float tar_distance, bool& atCombatRange, const EQ::ItemInstance*& p_item, const EQ::ItemInstance*& s_item);
|
||||
|
||||
bool GetCombatJitterFlag() { return m_combat_jitter_flag; }
|
||||
void SetCombatJitterFlag(bool flag = true) { m_combat_jitter_flag = flag; }
|
||||
bool GetCombatOutOfRangeJitterFlag() { return m_combat_out_of_range_jitter_flag; }
|
||||
void SetCombatOutOfRangeJitterFlag(bool flag = true) { m_combat_out_of_range_jitter_flag = flag; }
|
||||
void SetCombatJitter();
|
||||
void SetCombatOutOfRangeJitter();
|
||||
void DoCombatPositioning(Mob* tar, glm::vec3 Goal, bool stop_melee_level, float tar_distance, float melee_distance_min, float melee_distance, float melee_distance_max, bool behind_mob, bool front_mob);
|
||||
void DoFaceCheckWithJitter(Mob* tar);
|
||||
void DoFaceCheckNoJitter(Mob* tar);
|
||||
void RunToGoalWithJitter(glm::vec3 Goal);
|
||||
bool RequiresLoSForPositioning();
|
||||
bool HasRequiredLoSForPositioning(Mob* tar);
|
||||
|
||||
// Try Combat Methods
|
||||
bool TryEvade(Mob* tar);
|
||||
bool TryFacingTarget(Mob* tar);
|
||||
bool TryRangedAttack(Mob* tar);
|
||||
bool TryClassAttacks(Mob* tar);
|
||||
bool TryPrimaryWeaponAttacks(Mob* tar, const EQ::ItemInstance* p_item);
|
||||
bool TrySecondaryWeaponAttacks(Mob* tar, const EQ::ItemInstance* s_item);
|
||||
bool TryPursueTarget(float leash_distance, glm::vec3& Goal);
|
||||
bool TryMeditate();
|
||||
bool TryAutoDefend(Client* bot_owner, float leash_distance);
|
||||
bool TryIdleChecks(float fm_distance);
|
||||
bool TryNonCombatMovementChecks(Client* bot_owner, const Mob* follow_mob, glm::vec3& Goal);
|
||||
bool TryBardMovementCasts();
|
||||
void SetRangerCombatWeapon(bool atArcheryRange);
|
||||
bool BotRangedAttack(Mob* other, bool can_double_attack = false);
|
||||
bool CheckDoubleRangedAttack();
|
||||
|
||||
// Public "Refactor" Methods
|
||||
static bool CheckSpawnConditions(Client* c);
|
||||
static bool CheckCampSpawnConditions(Client* c);
|
||||
|
||||
protected:
|
||||
void BotMeditate(bool isSitting);
|
||||
bool CheckBotDoubleAttack(bool Triple = false);
|
||||
void PerformTradeWithClient(int16 begin_slot_id, int16 end_slot_id, Client* client);
|
||||
void BotMeditate(bool is_sitting);
|
||||
bool CheckBotDoubleAttack(bool triple_attack = false);
|
||||
bool CheckTripleAttack();
|
||||
void PerformTradeWithClient(int16 begin_slot_id, int16 end_slot_id, Client* client, int16 chosen_slot = INVALID_INDEX);
|
||||
bool AIDoSpellCast(int32 i, Mob* tar, int32 mana_cost, uint32* oDontDoAgainBefore = nullptr) override;
|
||||
|
||||
BotCastingRoles& GetCastingRoles() { return m_CastingRoles; }
|
||||
@ -839,9 +1127,14 @@ protected:
|
||||
void SetGroupDoter(bool flag = true) { m_CastingRoles.GroupDoter = flag; }
|
||||
std::deque<int> bot_signal_q;
|
||||
|
||||
std::vector<BotSpells_Struct> AIBot_spells;
|
||||
std::vector<BotSpells_Struct> AIBot_spells_enforced;
|
||||
std::vector<BotTimer_Struct> bot_timers;
|
||||
std::vector<BotSpells> AIBot_spells;
|
||||
std::vector<BotSpells> AIBot_spells_enforced;
|
||||
std::unordered_map<uint16, std::vector<BotSpells_wIndex>> AIBot_spells_by_type;
|
||||
|
||||
std::map<int32_t, std::map<int32_t, BotSpellTypesByClass>> commanded_spells_min_level;
|
||||
|
||||
std::vector<BotTimer> bot_timers;
|
||||
std::vector<BotBlockedBuffs> bot_blocked_buffs;
|
||||
|
||||
private:
|
||||
// Class Members
|
||||
@ -849,9 +1142,7 @@ private:
|
||||
uint32 _botOwnerCharacterID;
|
||||
bool _spawnStatus;
|
||||
Mob* _botOwner;
|
||||
bool m_bot_archery_setting;
|
||||
bool _botCharmer;
|
||||
bool _petChooser;
|
||||
uint8 _petChooserID;
|
||||
bool berserk;
|
||||
EQ::InventoryProfile m_inv;
|
||||
@ -870,25 +1161,32 @@ private:
|
||||
unsigned int RestRegenMana;
|
||||
unsigned int RestRegenEndurance;
|
||||
Timer rest_timer;
|
||||
Timer ping_timer;
|
||||
Timer m_ping_timer;
|
||||
int32 base_end;
|
||||
int32 cur_end;
|
||||
int32 max_end;
|
||||
int32 end_regen;
|
||||
|
||||
Timer m_evade_timer; // can be moved to pTimers at some point
|
||||
Timer m_rogue_evade_timer; // Rogue evade timer
|
||||
Timer m_monk_evade_timer; // Monk evade FD timer
|
||||
Timer m_auto_defend_timer;
|
||||
Timer auto_save_timer;
|
||||
Timer m_auto_save_timer;
|
||||
|
||||
Timer m_combat_jitter_timer;
|
||||
bool m_combat_jitter_flag;
|
||||
bool m_combat_out_of_range_jitter_flag;
|
||||
|
||||
bool m_dirtyautohaters;
|
||||
bool m_guard_flag;
|
||||
bool m_hold_flag;
|
||||
bool m_attack_flag;
|
||||
bool m_combat_round_alert_flag;
|
||||
bool m_attacking_flag;
|
||||
bool m_pull_flag;
|
||||
bool m_pulling_flag;
|
||||
bool m_returning_flag;
|
||||
bool is_using_item_click;
|
||||
uint32 m_bot_caster_range;
|
||||
|
||||
BotCastingRoles m_CastingRoles;
|
||||
|
||||
std::map<uint16, BotSpellSetting> bot_spell_settings;
|
||||
@ -896,12 +1194,31 @@ private:
|
||||
std::shared_ptr<HealRotation> m_member_of_heal_rotation;
|
||||
|
||||
InspectMessage_Struct _botInspectMessage;
|
||||
bool _altoutofcombatbehavior;
|
||||
bool _showhelm;
|
||||
bool _pauseAI;
|
||||
|
||||
int _expansionBitmask;
|
||||
bool _enforceSpellSettings;
|
||||
bool _showHelm;
|
||||
bool _botRangedSetting;
|
||||
uint8 _stopMeleeLevel;
|
||||
int m_expansion_bitmask;
|
||||
bool m_enforce_spell_settings;
|
||||
uint32 _distanceRanged;
|
||||
bool _behindMobStatus;
|
||||
bool _maxMeleeRangeStatus;
|
||||
bool _medInCombat;
|
||||
uint8 _SitHPPct;
|
||||
uint8 _SitManaPct;
|
||||
uint16 _castedSpellType;
|
||||
bool _hasLoS;
|
||||
bool _commandedSpell;
|
||||
bool _pullingSpell;
|
||||
|
||||
bool _illusionBlock;
|
||||
std::vector<BotSpellSettings> m_bot_spell_settings;
|
||||
std::vector<Mob*> _spell_target_list;
|
||||
std::vector<Mob*> _group_spell_target_list;
|
||||
Raid* _storedRaid;
|
||||
bool _verifiedRaid;
|
||||
uint16 _tempSpellType;
|
||||
|
||||
// Private "base stats" Members
|
||||
int32 _baseMR;
|
||||
@ -930,6 +1247,7 @@ private:
|
||||
int32 GenerateBaseManaPoints();
|
||||
void GenerateSpecialAttacks();
|
||||
void SetBotID(uint32 botID);
|
||||
void SetCombatRoundForAlerts(bool flag = true) { m_combat_round_alert_flag = flag; }
|
||||
void SetAttackingFlag(bool flag = true) { m_attacking_flag = flag; }
|
||||
void SetPullingFlag(bool flag = true) { m_pulling_flag = flag; }
|
||||
void SetReturningFlag(bool flag = true) { m_returning_flag = flag; }
|
||||
@ -948,6 +1266,6 @@ private:
|
||||
int32 CalcItemATKCap() final;
|
||||
};
|
||||
|
||||
bool IsSpellInBotList(DBbotspells_Struct* spell_list, uint16 iSpellID);
|
||||
bool IsSpellInBotList(DBbotspells_Struct* spell_list, uint16 spell_id);
|
||||
|
||||
#endif // BOT_H
|
||||
|
||||
1663
zone/bot_command.cpp
1663
zone/bot_command.cpp
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -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;
|
||||
}
|
||||
|
||||
@ -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
|
||||
);
|
||||
}
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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
zone/bot_commands/behind_mob.cpp
Normal file
146
zone/bot_commands/behind_mob.cpp
Normal 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()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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
zone/bot_commands/blocked_buffs.cpp
Normal file
460
zone/bot_commands/blocked_buffs.cpp
Normal 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()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
47
zone/bot_commands/bot_settings.cpp
Normal file
47
zone/bot_commands/bot_settings.cpp
Normal 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
zone/bot_commands/cast.cpp
Normal file
625
zone/bot_commands/cast.cpp
Normal 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()
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -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()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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
zone/bot_commands/class_race_list.cpp
Normal file
113
zone/bot_commands/class_race_list.cpp
Normal 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;
|
||||
}
|
||||
@ -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
zone/bot_commands/copy_settings.cpp
Normal file
376
zone/bot_commands/copy_settings.cpp
Normal 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()
|
||||
);
|
||||
}
|
||||
@ -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
zone/bot_commands/default_settings.cpp
Normal file
516
zone/bot_commands/default_settings.cpp
Normal 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()
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
@ -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
zone/bot_commands/discipline.cpp
Normal file
273
zone/bot_commands/discipline.cpp
Normal 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
zone/bot_commands/distance_ranged.cpp
Normal file
157
zone/bot_commands/distance_ranged.cpp
Normal 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()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
@ -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()
|
||||
);
|
||||
}
|
||||
|
||||
@ -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.",
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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.",
|
||||
|
||||
@ -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
zone/bot_commands/illusion_block.cpp
Normal file
157
zone/bot_commands/illusion_block.cpp
Normal 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()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
@ -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
zone/bot_commands/max_melee_range.cpp
Normal file
155
zone/bot_commands/max_melee_range.cpp
Normal 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()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
@ -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()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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") : ("")));
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
@ -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
zone/bot_commands/set_assistee.cpp
Normal file
42
zone/bot_commands/set_assistee.cpp
Normal 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
zone/bot_commands/sit_hp_percent.cpp
Normal file
156
zone/bot_commands/sit_hp_percent.cpp
Normal 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
zone/bot_commands/sit_in_combat.cpp
Normal file
156
zone/bot_commands/sit_in_combat.cpp
Normal 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
zone/bot_commands/sit_mana_percent.cpp
Normal file
156
zone/bot_commands/sit_mana_percent.cpp
Normal 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()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
@ -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
zone/bot_commands/spell_aggro_checks.cpp
Normal file
222
zone/bot_commands/spell_aggro_checks.cpp
Normal 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
zone/bot_commands/spell_announce_cast.cpp
Normal file
214
zone/bot_commands/spell_announce_cast.cpp
Normal 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
zone/bot_commands/spell_delays.cpp
Normal file
282
zone/bot_commands/spell_delays.cpp
Normal 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()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
248
zone/bot_commands/spell_engaged_priority.cpp
Normal file
248
zone/bot_commands/spell_engaged_priority.cpp
Normal file
@ -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
zone/bot_commands/spell_holds.cpp
Normal file
193
zone/bot_commands/spell_holds.cpp
Normal 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
zone/bot_commands/spell_idle_priority.cpp
Normal file
248
zone/bot_commands/spell_idle_priority.cpp
Normal 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
zone/bot_commands/spell_max_hp_pct.cpp
Normal file
211
zone/bot_commands/spell_max_hp_pct.cpp
Normal 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
zone/bot_commands/spell_max_mana_pct.cpp
Normal file
211
zone/bot_commands/spell_max_mana_pct.cpp
Normal 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
zone/bot_commands/spell_max_thresholds.cpp
Normal file
278
zone/bot_commands/spell_max_thresholds.cpp
Normal 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
zone/bot_commands/spell_min_hp_pct.cpp
Normal file
211
zone/bot_commands/spell_min_hp_pct.cpp
Normal 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
zone/bot_commands/spell_min_mana_pct.cpp
Normal file
211
zone/bot_commands/spell_min_mana_pct.cpp
Normal 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
zone/bot_commands/spell_min_thresholds.cpp
Normal file
280
zone/bot_commands/spell_min_thresholds.cpp
Normal 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
zone/bot_commands/spell_pursue_priority.cpp
Normal file
248
zone/bot_commands/spell_pursue_priority.cpp
Normal 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
zone/bot_commands/spell_resist_limits.cpp
Normal file
214
zone/bot_commands/spell_resist_limits.cpp
Normal 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
zone/bot_commands/spell_target_count.cpp
Normal file
214
zone/bot_commands/spell_target_count.cpp
Normal 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
zone/bot_commands/spelltypes.cpp
Normal file
9
zone/bot_commands/spelltypes.cpp
Normal 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);
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
@ -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;
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user