mirror of
https://github.com/EQEmu/Server.git
synced 2026-05-16 22:58:34 +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 0690783a9d
---------
Co-authored-by: Akkadius <akkadius1@gmail.com>
This commit is contained in:
+56
-6
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
+31
-31
@@ -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);
|
||||
|
||||
+17
-17
@@ -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;
|
||||
|
||||
+5932
-1795
File diff suppressed because it is too large
Load Diff
+440
-122
@@ -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
|
||||
|
||||
+233
-1430
File diff suppressed because it is too large
Load Diff
+124
-703
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(),
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -0,0 +1,460 @@
|
||||
#include "../bot_command.h"
|
||||
|
||||
void bot_command_blocked_buffs(Client* c, const Seperator* sep)
|
||||
{
|
||||
if (!RuleB(Bots, AllowBotBlockedBuffs)) {
|
||||
c->Message(Chat::Yellow, "This command is disabled.");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (helper_command_alias_fail(c, "bot_command_blocked_buffs", sep->arg[0], "blockedbuffs")) {
|
||||
c->Message(Chat::White, "note: Allows you to set, view and wipe blocked buffs for the selected bots.");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (helper_is_help_or_usage(sep->arg[1])) {
|
||||
BotCommandHelpParams p;
|
||||
|
||||
p.description = { "Allows you to set, view and wipe blocked buffs for the selected bots." };
|
||||
p.notes = { "- You can 'set' spells to be blocked, 'remove' spells from the blocked list, 'list' the current blocked spells or 'wipe' the entire list." };
|
||||
p.example_format = { fmt::format("{} [add [ID] | remove [ID] | list | wipe] [actionable, default: target]", sep->arg[0]) };
|
||||
p.examples_one =
|
||||
{
|
||||
"To add Courage(Spell ID #202) to the targeted bot's blocked list:",
|
||||
fmt::format("{} add 202", sep->arg[0])
|
||||
};
|
||||
p.examples_two =
|
||||
{
|
||||
"To view the targeted bot's blocked buff list:",
|
||||
fmt::format("{} list", sep->arg[0])
|
||||
};
|
||||
p.examples_three =
|
||||
{
|
||||
"To wipe all Warriors bots' blocked buff list:",
|
||||
fmt::format( "{} wipe byclass {}", sep->arg[0], Class::Warrior)
|
||||
};
|
||||
p.actionables = { "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" };
|
||||
|
||||
std::string popup_text = c->SendBotCommandHelpWindow(p);
|
||||
popup_text = DialogueWindow::Table(popup_text);
|
||||
|
||||
c->SendPopupToClient(sep->arg[0], popup_text.c_str());
|
||||
|
||||
c->Message(
|
||||
Chat::Yellow,
|
||||
fmt::format(
|
||||
"You can also control bot buffs ({}).",
|
||||
Saylink::Silent("^blockedbuffs help", "^blockedbuffs")
|
||||
).c_str()
|
||||
);
|
||||
|
||||
if (RuleB(Bots, SendClassRaceOnHelp)) {
|
||||
c->Message(
|
||||
Chat::Yellow,
|
||||
fmt::format(
|
||||
"Use {} for information about race/class IDs.",
|
||||
Saylink::Silent("^classracelist")
|
||||
).c_str()
|
||||
);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
std::string arg1 = sep->arg[1];
|
||||
std::string arg2 = sep->arg[2];
|
||||
int ab_arg = 2;
|
||||
bool add = false;
|
||||
bool remove = false;
|
||||
bool list = false;
|
||||
bool wipe = false;
|
||||
uint16 spell_id;
|
||||
|
||||
if (!arg1.compare("add")) {
|
||||
if (!sep->IsNumber(2) || !IsValidSpell(atoi(sep->arg[2])) || !IsBeneficialSpell(atoi(sep->arg[2]))) {
|
||||
c->Message(Chat::Yellow, "You must enter a valid spell ID.");
|
||||
return;
|
||||
}
|
||||
|
||||
add = true;
|
||||
spell_id = atoi(sep->arg[2]);
|
||||
++ab_arg;
|
||||
}
|
||||
else if (!arg1.compare("remove")) {
|
||||
if (!sep->IsNumber(2) || !IsValidSpell(atoi(sep->arg[2]))) {
|
||||
c->Message(Chat::Yellow, "You must enter a valid spell ID.");
|
||||
return;
|
||||
}
|
||||
|
||||
remove = true;
|
||||
spell_id = atoi(sep->arg[2]);
|
||||
++ab_arg;
|
||||
}
|
||||
else if (!arg1.compare("list")) {
|
||||
list = true;
|
||||
}
|
||||
else if (!arg1.compare("wipe")) {
|
||||
wipe = true;
|
||||
}
|
||||
else {
|
||||
c->Message(
|
||||
Chat::Yellow,
|
||||
fmt::format(
|
||||
"Incorrect argument, use {} for information regarding this command.",
|
||||
Saylink::Silent(
|
||||
fmt::format("{} help", sep->arg[0])
|
||||
)
|
||||
).c_str()
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const int ab_mask = ActionableBots::ABM_Type1;
|
||||
std::string actionable_arg = sep->arg[ab_arg];
|
||||
|
||||
if (actionable_arg.empty()) {
|
||||
actionable_arg = "target";
|
||||
}
|
||||
|
||||
std::string class_race_arg = sep->arg[ab_arg];
|
||||
bool class_race_check = false;
|
||||
|
||||
if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) {
|
||||
class_race_check = true;
|
||||
}
|
||||
|
||||
std::vector<Bot*> sbl;
|
||||
|
||||
if (ActionableBots::PopulateSBL(c, actionable_arg, sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) {
|
||||
return;
|
||||
}
|
||||
|
||||
sbl.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end());
|
||||
|
||||
bool is_success = false;
|
||||
uint16 success_count = 0;
|
||||
Bot* first_found = nullptr;
|
||||
|
||||
for (auto bot_iter : sbl) {
|
||||
if (!bot_iter->IsInGroupOrRaid(c)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!first_found) {
|
||||
first_found = bot_iter;
|
||||
}
|
||||
|
||||
if (add) {
|
||||
bot_iter->SetBotBlockedBuff(spell_id, true);
|
||||
}
|
||||
else if (remove) {
|
||||
bot_iter->SetBotBlockedBuff(spell_id, false);
|
||||
}
|
||||
else if (list) {
|
||||
std::vector<BotBlockedBuffs> blocked_buffs = bot_iter->GetBotBlockedBuffs();
|
||||
bool found = false;
|
||||
|
||||
if (!blocked_buffs.empty()) {
|
||||
for (auto& blocked_buff : blocked_buffs) {
|
||||
if (blocked_buff.blocked == 1 && IsValidSpell(blocked_buff.spell_id)) {
|
||||
found = true;
|
||||
c->Message(
|
||||
Chat::Yellow,
|
||||
fmt::format(
|
||||
"{} says, '{} [#{}] is currently blocked. [{}]'",
|
||||
bot_iter->GetCleanName(),
|
||||
spells[blocked_buff.spell_id].name,
|
||||
blocked_buff.spell_id,
|
||||
Saylink::Silent(fmt::format("^blockedbuffs remove {}", blocked_buff.spell_id),"Remove")
|
||||
).c_str()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
c->Message(
|
||||
Chat::Yellow,
|
||||
fmt::format(
|
||||
"{} says, 'I am not currently blocking any buffs.'",
|
||||
bot_iter->GetCleanName()
|
||||
).c_str()
|
||||
);
|
||||
}
|
||||
}
|
||||
else if (wipe) {
|
||||
bot_iter->ClearBotBlockedBuffs();
|
||||
|
||||
c->Message(
|
||||
Chat::Yellow,
|
||||
fmt::format(
|
||||
"{} says, I have wiped my blocked buffs list.'",
|
||||
bot_iter->GetCleanName()
|
||||
).c_str()
|
||||
);
|
||||
}
|
||||
|
||||
is_success = true;
|
||||
++success_count;
|
||||
}
|
||||
|
||||
if (!is_success) {
|
||||
c->Message(Chat::Yellow, "No bots were selected.");
|
||||
}
|
||||
else {
|
||||
if (add || remove) {
|
||||
c->Message(
|
||||
Chat::Yellow,
|
||||
fmt::format(
|
||||
"{} {} {} blocking {} [#{}]",
|
||||
((success_count == 1 && first_found) ? first_found->GetCleanName() : (fmt::format("{}", success_count).c_str())),
|
||||
((success_count == 1 && first_found) ? "is" : "of your bots"),
|
||||
(add ? "now" : "no longer"),
|
||||
spells[spell_id].name,
|
||||
spell_id
|
||||
).c_str()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void bot_command_blocked_pet_buffs(Client* c, const Seperator* sep)
|
||||
{
|
||||
if (!RuleB(Bots, AllowBotBlockedBuffs)) {
|
||||
c->Message(Chat::Yellow, "This command is disabled.");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (helper_command_alias_fail(c, "bot_command_blocked_pet_buffs", sep->arg[0], "blockedpetbuffs")) {
|
||||
c->Message(Chat::White, "note: Allows you to set, view and wipe blocked pet buffs for the selected bots.");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (helper_is_help_or_usage(sep->arg[1])) {
|
||||
BotCommandHelpParams p;
|
||||
|
||||
p.description = { "Allows you to set, view and wipe blocked pet buffs for the selected bots." };
|
||||
p.notes =
|
||||
{
|
||||
"- You can 'set' spells to be blocked, 'remove' spells from the blocked list, 'list' the current blocked spells or 'wipe' the entire list.",
|
||||
"- This controls whether or not any pet the selected bot(s) own will prevent certain beneficial buffs from landing on them."
|
||||
};
|
||||
p.example_format = { fmt::format("{} [add [ID] | remove [ID] | list | wipe] [actionable, default: target]", sep->arg[0]) };
|
||||
p.examples_one =
|
||||
{
|
||||
"To add Courage (Spell ID #202) to the targeted bot's blocked list:",
|
||||
fmt::format(
|
||||
"{} add 202",
|
||||
sep->arg[0],
|
||||
Bot::GetSpellTypeShortNameByID(BotSpellTypes::Nuke)
|
||||
)
|
||||
};
|
||||
p.examples_two =
|
||||
{
|
||||
"To view the targeted bot's blocked buff list:",
|
||||
fmt::format(
|
||||
"{} list",
|
||||
sep->arg[0]
|
||||
)
|
||||
};
|
||||
p.examples_three =
|
||||
{
|
||||
"To wipe all Warriors bots' blocked buff list:",
|
||||
fmt::format(
|
||||
"{} wipe byclass {}",
|
||||
sep->arg[0],
|
||||
Class::Warrior
|
||||
)
|
||||
};
|
||||
p.actionables = { "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" };
|
||||
|
||||
std::string popup_text = c->SendBotCommandHelpWindow(p);
|
||||
popup_text = DialogueWindow::Table(popup_text);
|
||||
|
||||
c->SendPopupToClient(sep->arg[0], popup_text.c_str());
|
||||
|
||||
c->Message(
|
||||
Chat::Yellow,
|
||||
fmt::format(
|
||||
"You can also control pet buffs ({}).",
|
||||
Saylink::Silent("^blockedpetbuffs help", "^blockedpetbuffs")
|
||||
).c_str()
|
||||
);
|
||||
|
||||
if (RuleB(Bots, SendClassRaceOnHelp)) {
|
||||
c->Message(
|
||||
Chat::Yellow,
|
||||
fmt::format(
|
||||
"Use {} for information about race/class IDs.",
|
||||
Saylink::Silent("^classracelist")
|
||||
).c_str()
|
||||
);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
std::string arg1 = sep->arg[1];
|
||||
std::string arg2 = sep->arg[2];
|
||||
int ab_arg = 2;
|
||||
bool add = false;
|
||||
bool remove = false;
|
||||
bool list = false;
|
||||
bool wipe = false;
|
||||
uint16 spell_id;
|
||||
|
||||
if (!arg1.compare("add")) {
|
||||
if (!sep->IsNumber(2) || !IsValidSpell(atoi(sep->arg[2])) || !IsBeneficialSpell(atoi(sep->arg[2]))) {
|
||||
c->Message(Chat::Yellow, "You must enter a valid spell ID.");
|
||||
return;
|
||||
}
|
||||
|
||||
add = true;
|
||||
spell_id = atoi(sep->arg[2]);
|
||||
++ab_arg;
|
||||
}
|
||||
else if (!arg1.compare("remove")) {
|
||||
if (!sep->IsNumber(2) || !IsValidSpell(atoi(sep->arg[2]))) {
|
||||
c->Message(Chat::Yellow, "You must enter a valid spell ID.");
|
||||
return;
|
||||
}
|
||||
|
||||
remove = true;
|
||||
spell_id = atoi(sep->arg[2]);
|
||||
++ab_arg;
|
||||
}
|
||||
else if (!arg1.compare("list")) {
|
||||
list = true;
|
||||
}
|
||||
else if (!arg1.compare("wipe")) {
|
||||
wipe = true;
|
||||
}
|
||||
else {
|
||||
c->Message(
|
||||
Chat::Yellow,
|
||||
fmt::format(
|
||||
"Incorrect argument, use {} for information regarding this command.",
|
||||
Saylink::Silent(
|
||||
fmt::format("{} help", sep->arg[0])
|
||||
)
|
||||
).c_str()
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const int ab_mask = ActionableBots::ABM_Type1;
|
||||
std::string actionable_arg = sep->arg[ab_arg];
|
||||
|
||||
if (actionable_arg.empty()) {
|
||||
actionable_arg = "target";
|
||||
}
|
||||
|
||||
std::string class_race_arg = sep->arg[ab_arg];
|
||||
bool class_race_check = false;
|
||||
|
||||
if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) {
|
||||
class_race_check = true;
|
||||
}
|
||||
|
||||
std::vector<Bot*> sbl;
|
||||
|
||||
if (ActionableBots::PopulateSBL(c, actionable_arg, sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) {
|
||||
return;
|
||||
}
|
||||
|
||||
sbl.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end());
|
||||
|
||||
bool is_success = false;
|
||||
uint16 success_count = 0;
|
||||
Bot* first_found = nullptr;
|
||||
|
||||
for (auto bot_iter : sbl) {
|
||||
if (!bot_iter->IsInGroupOrRaid(c)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!first_found) {
|
||||
first_found = bot_iter;
|
||||
}
|
||||
|
||||
if (add) {
|
||||
bot_iter->SetBotBlockedPetBuff(spell_id, true);
|
||||
}
|
||||
else if (remove) {
|
||||
bot_iter->SetBotBlockedPetBuff(spell_id, false);
|
||||
}
|
||||
else if (list) {
|
||||
std::vector<BotBlockedBuffs> blocked_buffs = bot_iter->GetBotBlockedBuffs();
|
||||
bool found = false;
|
||||
|
||||
if (!blocked_buffs.empty()) {
|
||||
for (auto& blocked_buff : blocked_buffs) {
|
||||
if (blocked_buff.blocked_pet == 1 && IsValidSpell(blocked_buff.spell_id)) {
|
||||
found = true;
|
||||
c->Message(
|
||||
Chat::Yellow,
|
||||
fmt::format(
|
||||
"{} says, '{} [#{}] is currently blocked for my pet. [{}]'",
|
||||
bot_iter->GetCleanName(),
|
||||
spells[blocked_buff.spell_id].name,
|
||||
blocked_buff.spell_id,
|
||||
Saylink::Silent(fmt::format("^blockedpetbuffs remove {}", blocked_buff.spell_id), "Remove")
|
||||
).c_str()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
c->Message(
|
||||
Chat::Yellow,
|
||||
fmt::format(
|
||||
"{} says, 'I am not currently blocking any pet buffs.'",
|
||||
bot_iter->GetCleanName()
|
||||
).c_str()
|
||||
);
|
||||
}
|
||||
}
|
||||
else if (wipe) {
|
||||
bot_iter->ClearBotBlockedBuffs();
|
||||
|
||||
c->Message(
|
||||
Chat::Yellow,
|
||||
fmt::format(
|
||||
"{} says, I have wiped my blocked buffs list.'",
|
||||
bot_iter->GetCleanName()
|
||||
).c_str()
|
||||
);
|
||||
}
|
||||
|
||||
is_success = true;
|
||||
++success_count;
|
||||
}
|
||||
|
||||
if (!is_success) {
|
||||
c->Message(Chat::Yellow, "No bots were selected.");
|
||||
}
|
||||
else {
|
||||
if (add || remove) {
|
||||
c->Message(
|
||||
Chat::Yellow,
|
||||
fmt::format(
|
||||
"{} {} {} blocking {} [#{}] on their pet.",
|
||||
((success_count == 1 && first_found) ? first_found->GetCleanName() : (fmt::format("{}", success_count).c_str())),
|
||||
((success_count == 1 && first_found) ? "is" : "of your bots"),
|
||||
(add ? "now" : "no longer"),
|
||||
spells[spell_id].name,
|
||||
spell_id
|
||||
).c_str()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
+855
-304
File diff suppressed because it is too large
Load Diff
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
+249
-36
@@ -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()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
+178
-55
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
+186
-70
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
+297
-46
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -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()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,282 @@
|
||||
#include "../bot_command.h"
|
||||
|
||||
void bot_command_spell_delays(Client* c, const Seperator* sep) {
|
||||
if (helper_command_alias_fail(c, "bot_command_spell_delays", sep->arg[0], "spelldelays")) {
|
||||
c->Message(Chat::White, "note: Controls how long a bot will wait between casts of different spell types.");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (helper_is_help_or_usage(sep->arg[1])) {
|
||||
BotCommandHelpParams p;
|
||||
|
||||
p.description = { "Controls how long a bot will wait between casts of different spell types." };
|
||||
p.notes =
|
||||
{
|
||||
"- Targeting yourself for this command will allow you to control your own settings for how bots cast on you",
|
||||
"- All pet types are based off the pet's owner's setting",
|
||||
"- Any remaining types use the owner's setting when a pet is the target",
|
||||
"- All Heals, Cures, Buffs (DS and resists included) are based off the target's setting, not the caster",
|
||||
"- e.g., BotA is healing BotB using BotB's settings"
|
||||
};
|
||||
p.example_format =
|
||||
{
|
||||
fmt::format("{} [Type Shortname] [value] [actionable, default: target]", sep->arg[0]),
|
||||
fmt::format("{} [Type ID] [value] [actionable, default: target]", sep->arg[0])
|
||||
};
|
||||
p.examples_one =
|
||||
{
|
||||
"To set all Necromancers to an 8s DoT delay:",
|
||||
fmt::format(
|
||||
"{} {} 8000 byclass {}",
|
||||
sep->arg[0],
|
||||
Bot::GetSpellTypeShortNameByID(BotSpellTypes::DOT),
|
||||
Class::Necromancer
|
||||
),
|
||||
fmt::format(
|
||||
"{} {} 8000 byclass {}",
|
||||
sep->arg[0],
|
||||
BotSpellTypes::DOT,
|
||||
Class::Necromancer
|
||||
)
|
||||
};
|
||||
p.examples_two =
|
||||
{
|
||||
"To set all Warriors to receive Fast Heals every 2.5s:",
|
||||
fmt::format(
|
||||
"{} {} 2500 byclass {}",
|
||||
sep->arg[0],
|
||||
Bot::GetSpellTypeShortNameByID(BotSpellTypes::FastHeals),
|
||||
Class::Warrior
|
||||
),
|
||||
fmt::format(
|
||||
"{} {} 2500 byclass {}",
|
||||
sep->arg[0],
|
||||
BotSpellTypes::FastHeals,
|
||||
Class::Warrior
|
||||
)
|
||||
};
|
||||
p.examples_three =
|
||||
{
|
||||
"To check the current Nuke delay on all bots:",
|
||||
fmt::format(
|
||||
"{} {} current spawned",
|
||||
sep->arg[0],
|
||||
Bot::GetSpellTypeShortNameByID(BotSpellTypes::Nuke)
|
||||
),
|
||||
fmt::format(
|
||||
"{} {} current spawned",
|
||||
sep->arg[0],
|
||||
BotSpellTypes::Nuke
|
||||
)
|
||||
};
|
||||
p.actionables = { "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" };
|
||||
|
||||
std::string popup_text = c->SendBotCommandHelpWindow(p);
|
||||
popup_text = DialogueWindow::Table(popup_text);
|
||||
|
||||
c->SendPopupToClient(sep->arg[0], popup_text.c_str());
|
||||
c->SendSpellTypePrompts();
|
||||
c->SendSpellTypePrompts(false, true);
|
||||
|
||||
if (RuleB(Bots, SendClassRaceOnHelp)) {
|
||||
c->Message(
|
||||
Chat::Yellow,
|
||||
fmt::format(
|
||||
"Use {} for information about race/class IDs.",
|
||||
Saylink::Silent("^classracelist")
|
||||
).c_str()
|
||||
);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
std::string arg1 = sep->arg[1];
|
||||
std::string arg2 = sep->arg[2];
|
||||
int ab_arg = 2;
|
||||
bool current_check = false;
|
||||
uint16 spell_type = 0;
|
||||
uint32 type_value = 0;
|
||||
Mob* target = c->GetTarget();
|
||||
bool clientSetting = (target && target == c);
|
||||
|
||||
if (sep->IsNumber(1)) {
|
||||
spell_type = atoi(sep->arg[1]);
|
||||
|
||||
if (
|
||||
(clientSetting && !IsClientBotSpellType(spell_type)) ||
|
||||
(!clientSetting && !EQ::ValueWithin(spell_type, BotSpellTypes::START, BotSpellTypes::END))
|
||||
) {
|
||||
c->Message(
|
||||
Chat::Yellow,
|
||||
fmt::format(
|
||||
"{}. Use {} for information regarding this command.",
|
||||
(!clientSetting ? "You must choose a valid spell type" : "You must choose a valid client spell type"),
|
||||
Saylink::Silent(
|
||||
fmt::format("{} help", sep->arg[0])
|
||||
)
|
||||
).c_str()
|
||||
);
|
||||
|
||||
if (clientSetting) {
|
||||
c->SendSpellTypePrompts(false, true);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (Bot::GetSpellTypeIDByShortName(arg1) != UINT16_MAX) {
|
||||
spell_type = Bot::GetSpellTypeIDByShortName(arg1);
|
||||
|
||||
if (clientSetting && !IsClientBotSpellType(spell_type)) {
|
||||
c->Message(Chat::Yellow, "Invalid spell type for clients.");
|
||||
c->SendSpellTypePrompts(false, true);
|
||||
}
|
||||
}
|
||||
else {
|
||||
c->Message(
|
||||
Chat::Yellow,
|
||||
fmt::format(
|
||||
"Incorrect argument, use {} for information regarding this command.",
|
||||
Saylink::Silent(
|
||||
fmt::format("{} help", sep->arg[0])
|
||||
)
|
||||
).c_str()
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (sep->IsNumber(2)) {
|
||||
type_value = atoi(sep->arg[2]);
|
||||
++ab_arg;
|
||||
if (type_value < 100 || type_value > 60000) {
|
||||
c->Message(Chat::Yellow, "You must enter a value between 100-60000 (100ms to 60s).");
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (!arg2.compare("current")) {
|
||||
++ab_arg;
|
||||
current_check = true;
|
||||
}
|
||||
else {
|
||||
c->Message(
|
||||
Chat::Yellow,
|
||||
fmt::format(
|
||||
"Incorrect argument, use {} for information regarding this command.",
|
||||
Saylink::Silent(
|
||||
fmt::format("{} help", sep->arg[0])
|
||||
)
|
||||
).c_str()
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const int ab_mask = ActionableBots::ABM_Type1;
|
||||
std::string actionable_arg = sep->arg[ab_arg];
|
||||
|
||||
if (actionable_arg.empty()) {
|
||||
actionable_arg = "target";
|
||||
}
|
||||
|
||||
if (!clientSetting) {
|
||||
|
||||
std::string class_race_arg = sep->arg[ab_arg];
|
||||
bool class_race_check = false;
|
||||
|
||||
if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) {
|
||||
class_race_check = true;
|
||||
}
|
||||
|
||||
std::vector<Bot*> sbl;
|
||||
|
||||
if (ActionableBots::PopulateSBL(c, actionable_arg, sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) {
|
||||
return;
|
||||
}
|
||||
|
||||
sbl.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end());
|
||||
|
||||
Bot* first_found = nullptr;
|
||||
int success_count = 0;
|
||||
|
||||
for (auto my_bot : sbl) {
|
||||
if (my_bot->BotPassiveCheck()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!first_found) {
|
||||
first_found = my_bot;
|
||||
}
|
||||
|
||||
if (current_check) {
|
||||
c->Message(
|
||||
Chat::Green,
|
||||
fmt::format(
|
||||
"{} says, 'My [{}] spell delay is currently [{}] seconds.'",
|
||||
my_bot->GetCleanName(),
|
||||
Bot::GetSpellTypeNameByID(spell_type),
|
||||
my_bot->GetSpellTypeDelay(spell_type) / 1000.00
|
||||
).c_str()
|
||||
);
|
||||
}
|
||||
else {
|
||||
my_bot->SetSpellTypeDelay(spell_type, type_value);
|
||||
++success_count;
|
||||
}
|
||||
}
|
||||
|
||||
if (!current_check) {
|
||||
if (success_count == 1 && first_found) {
|
||||
c->Message(
|
||||
Chat::Green,
|
||||
fmt::format(
|
||||
"{} says, 'My [{}] spell delay was set to [{}] seconds.'",
|
||||
first_found->GetCleanName(),
|
||||
Bot::GetSpellTypeNameByID(spell_type),
|
||||
first_found->GetSpellTypeDelay(spell_type) / 1000.00
|
||||
).c_str()
|
||||
);
|
||||
}
|
||||
else {
|
||||
c->Message(
|
||||
Chat::Green,
|
||||
fmt::format(
|
||||
"{} of your bots set their [{}] spell delay to [{}] seconds.",
|
||||
success_count,
|
||||
Bot::GetSpellTypeNameByID(spell_type),
|
||||
type_value / 1000.00
|
||||
).c_str()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (current_check) {
|
||||
c->Message(
|
||||
Chat::Green,
|
||||
fmt::format(
|
||||
"Your [{}] spell delay is currently [{}] seconds.",
|
||||
Bot::GetSpellTypeNameByID(spell_type),
|
||||
c->GetSpellTypeDelay(spell_type) / 1000.00
|
||||
).c_str()
|
||||
);
|
||||
}
|
||||
else {
|
||||
c->SetSpellTypeDelay(spell_type, type_value);
|
||||
|
||||
c->Message(
|
||||
Chat::Green,
|
||||
fmt::format(
|
||||
"Your [{}] spell delay was set to [{}] seconds.",
|
||||
Bot::GetSpellTypeNameByID(spell_type),
|
||||
c->GetSpellTypeDelay(spell_type) / 1000.00
|
||||
).c_str()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,248 @@
|
||||
#include "../bot_command.h"
|
||||
|
||||
void bot_command_spell_engaged_priority(Client* c, const Seperator* sep)
|
||||
{
|
||||
if (helper_command_alias_fail(c, "bot_command_spell_engaged_priority", sep->arg[0], "spellengagedpriority")) {
|
||||
c->Message(Chat::White, "note: Sets the order of spell casts when engaged in combat by spell type.");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (helper_is_help_or_usage(sep->arg[1])) {
|
||||
BotCommandHelpParams p;
|
||||
|
||||
p.description = { "Sets the order of spell casts when engaged in combat by spell type." };
|
||||
p.notes =
|
||||
{
|
||||
"- Setting a spell type to 0 will prevent that type from being cast.",
|
||||
"- If 2 or more are set to the same priority they will sort by spell type ID."
|
||||
};
|
||||
p.example_format =
|
||||
{
|
||||
fmt::format("{} [Type Shortname] [value] [actionable]", sep->arg[0]),
|
||||
fmt::format("{} [Type ID] [value] [actionable]", sep->arg[0])
|
||||
};
|
||||
p.examples_one =
|
||||
{
|
||||
"To set all Shaman to cast slows first:",
|
||||
fmt::format(
|
||||
"{} {} 1 byclass {}",
|
||||
sep->arg[0],
|
||||
Bot::GetSpellTypeShortNameByID(BotSpellTypes::Slow),
|
||||
Class::Shaman
|
||||
),
|
||||
fmt::format(
|
||||
"{} {} 1 byclass {}",
|
||||
sep->arg[0],
|
||||
BotSpellTypes::Slow,
|
||||
Class::Shaman
|
||||
)
|
||||
};
|
||||
p.examples_two =
|
||||
{
|
||||
"To set all bots to not cast snares:",
|
||||
fmt::format(
|
||||
"{} {} 0 spawned",
|
||||
sep->arg[0],
|
||||
Bot::GetSpellTypeShortNameByID(BotSpellTypes::Snare)
|
||||
),
|
||||
fmt::format(
|
||||
"{} {} 0 spawned",
|
||||
sep->arg[0],
|
||||
BotSpellTypes::Snare
|
||||
)
|
||||
};
|
||||
p.examples_three =
|
||||
{
|
||||
"To check the current engaged priority of dispels on all bots:",
|
||||
fmt::format(
|
||||
"{} {} current spawned",
|
||||
sep->arg[0],
|
||||
Bot::GetSpellTypeShortNameByID(BotSpellTypes::Dispel)
|
||||
),
|
||||
fmt::format(
|
||||
"{} {} current spawned",
|
||||
sep->arg[0],
|
||||
BotSpellTypes::Dispel
|
||||
)
|
||||
};
|
||||
p.actionables = { "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" };
|
||||
|
||||
std::string popup_text = c->SendBotCommandHelpWindow(p);
|
||||
popup_text = DialogueWindow::Table(popup_text);
|
||||
|
||||
c->SendPopupToClient(sep->arg[0], popup_text.c_str());
|
||||
c->SendSpellTypePrompts();
|
||||
|
||||
if (RuleB(Bots, SendClassRaceOnHelp)) {
|
||||
c->Message(
|
||||
Chat::Yellow,
|
||||
fmt::format(
|
||||
"Use {} for information about race/class IDs.",
|
||||
Saylink::Silent("^classracelist")
|
||||
).c_str()
|
||||
);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
std::string arg1 = sep->arg[1];
|
||||
std::string arg2 = sep->arg[2];
|
||||
int ab_arg = 2;
|
||||
bool current_check = false;
|
||||
bool list_check = false;
|
||||
uint16 spell_type = 0;
|
||||
uint32 type_value = 0;
|
||||
|
||||
// String/Int type checks
|
||||
if (sep->IsNumber(1)) {
|
||||
spell_type = atoi(sep->arg[1]);
|
||||
|
||||
if (!EQ::ValueWithin(spell_type, BotSpellTypes::START, BotSpellTypes::END)) {
|
||||
c->Message(Chat::Yellow, "You must choose a valid spell type. Spell types range from %i to %i", BotSpellTypes::START, BotSpellTypes::END);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (Bot::GetSpellTypeIDByShortName(arg1) != UINT16_MAX) {
|
||||
spell_type = Bot::GetSpellTypeIDByShortName(arg1);
|
||||
}
|
||||
else if (!arg1.compare("list")) {
|
||||
++ab_arg;
|
||||
list_check = true;
|
||||
}
|
||||
else {
|
||||
c->Message(
|
||||
Chat::Yellow,
|
||||
fmt::format(
|
||||
"Incorrect argument, use {} for information regarding this command.",
|
||||
Saylink::Silent(
|
||||
fmt::format("{} help", sep->arg[0])
|
||||
)
|
||||
).c_str()
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (sep->IsNumber(2)) {
|
||||
type_value = atoi(sep->arg[2]);
|
||||
++ab_arg;
|
||||
if (EQ::ValueWithin(type_value, BotSpellTypes::START, BotSpellTypes::END)) {
|
||||
c->Message(Chat::Yellow, "You must enter a value between {} and {}.", BotSpellTypes::START, BotSpellTypes::END);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (!arg2.compare("current")) {
|
||||
++ab_arg;
|
||||
current_check = true;
|
||||
}
|
||||
else if (!list_check) {
|
||||
c->Message(
|
||||
Chat::Yellow,
|
||||
fmt::format(
|
||||
"Incorrect argument, use {} for information regarding this command.",
|
||||
Saylink::Silent(
|
||||
fmt::format("{} help", sep->arg[0])
|
||||
)
|
||||
).c_str()
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const int ab_mask = ActionableBots::ABM_Type1;
|
||||
std::string class_race_arg = sep->arg[ab_arg];
|
||||
bool class_race_check = false;
|
||||
if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) {
|
||||
class_race_check = true;
|
||||
}
|
||||
|
||||
std::vector<Bot*> sbl;
|
||||
if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) {
|
||||
return;
|
||||
}
|
||||
|
||||
sbl.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end());
|
||||
|
||||
Bot* first_found = nullptr;
|
||||
int success_count = 0;
|
||||
for (auto my_bot : sbl) {
|
||||
if (my_bot->BotPassiveCheck()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!first_found) {
|
||||
first_found = my_bot;
|
||||
}
|
||||
|
||||
if (current_check) {
|
||||
c->Message(
|
||||
Chat::Green,
|
||||
fmt::format(
|
||||
"{} says, 'My [{}] engaged cast priority is currently [{}].'",
|
||||
my_bot->GetCleanName(),
|
||||
Bot::GetSpellTypeNameByID(spell_type),
|
||||
my_bot->GetSpellTypePriority(spell_type, BotPriorityCategories::Engaged)
|
||||
).c_str()
|
||||
);
|
||||
}
|
||||
else if (list_check) {
|
||||
auto cast_order = my_bot->GetSpellTypesPrioritized(BotPriorityCategories::Engaged);
|
||||
|
||||
for (auto& current_cast : cast_order) {
|
||||
c->Message(
|
||||
Chat::Green,
|
||||
fmt::format(
|
||||
"{} says, 'My [{}] engaged cast priority for is currently [{}].'",
|
||||
my_bot->GetCleanName(),
|
||||
Bot::GetSpellTypeNameByID(current_cast.spellType),
|
||||
(current_cast.priority == 0 ? "disabled (0)" : std::to_string(current_cast.priority))
|
||||
).c_str()
|
||||
);
|
||||
}
|
||||
|
||||
c->Message(
|
||||
Chat::Green,
|
||||
fmt::format(
|
||||
"{} says, 'Anything not listed is currently disabled (0).'",
|
||||
my_bot->GetCleanName()
|
||||
).c_str()
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
else {
|
||||
my_bot->SetSpellTypePriority(spell_type, BotPriorityCategories::Engaged, type_value);
|
||||
++success_count;
|
||||
}
|
||||
}
|
||||
if (!current_check) {
|
||||
if (success_count == 1 && first_found) {
|
||||
c->Message(
|
||||
Chat::Green,
|
||||
fmt::format(
|
||||
"{} says, 'My [{}] engaged cast priority was set to [{}].'",
|
||||
first_found->GetCleanName(),
|
||||
Bot::GetSpellTypeNameByID(spell_type),
|
||||
first_found->GetSpellTypePriority(spell_type, BotPriorityCategories::Engaged)
|
||||
).c_str()
|
||||
);
|
||||
}
|
||||
else {
|
||||
c->Message(
|
||||
Chat::Green,
|
||||
fmt::format(
|
||||
"{} of your bots set their [{}] engaged cast priority to [{}].",
|
||||
success_count,
|
||||
Bot::GetSpellTypeNameByID(spell_type),
|
||||
type_value
|
||||
).c_str()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
+135
-64
@@ -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);
|
||||
}
|
||||
+329
-170
@@ -22,6 +22,7 @@
|
||||
#include "../common/strings.h"
|
||||
#include "../common/eqemu_logsys.h"
|
||||
|
||||
#include "../common/repositories/bot_blocked_buffs_repository.h"
|
||||
#include "../common/repositories/bot_buffs_repository.h"
|
||||
#include "../common/repositories/bot_create_combinations_repository.h"
|
||||
#include "../common/repositories/bot_data_repository.h"
|
||||
@@ -35,6 +36,7 @@
|
||||
#include "../common/repositories/bot_pet_buffs_repository.h"
|
||||
#include "../common/repositories/bot_pet_inventories_repository.h"
|
||||
#include "../common/repositories/bot_spell_casting_chances_repository.h"
|
||||
#include "../common/repositories/bot_settings_repository.h"
|
||||
#include "../common/repositories/bot_stances_repository.h"
|
||||
#include "../common/repositories/bot_timers_repository.h"
|
||||
#include "../common/repositories/character_data_repository.h"
|
||||
@@ -184,6 +186,7 @@ bool BotDatabase::QueryNameAvailablity(const std::string& bot_name, bool& availa
|
||||
if (
|
||||
bot_name.empty() ||
|
||||
bot_name.size() > 60 ||
|
||||
!database.CheckNameFilter(bot_name) ||
|
||||
database.IsNameUsed(bot_name)
|
||||
) {
|
||||
return false;
|
||||
@@ -244,6 +247,8 @@ bool BotDatabase::LoadBotsList(const uint32 owner_id, std::list<BotsAvailableLis
|
||||
SELECT `account_id` FROM `character_data` WHERE `id` = {}
|
||||
)
|
||||
)
|
||||
AND
|
||||
`name` NOT LIKE '%-deleted-%'
|
||||
),
|
||||
owner_id
|
||||
)
|
||||
@@ -270,7 +275,7 @@ bool BotDatabase::LoadBotsList(const uint32 owner_id, std::list<BotsAvailableLis
|
||||
const auto& l = BotDataRepository::GetWhere(
|
||||
database,
|
||||
fmt::format(
|
||||
"`owner_id` = {}",
|
||||
"`owner_id` = {} AND `name` NOT LIKE '%-deleted-%'",
|
||||
owner_id
|
||||
)
|
||||
);
|
||||
@@ -317,7 +322,7 @@ bool BotDatabase::LoadBotID(const std::string& bot_name, uint32& bot_id, uint8&
|
||||
const auto& l = BotDataRepository::GetWhere(
|
||||
database,
|
||||
fmt::format(
|
||||
"`name` = '{}' LIMIT 1",
|
||||
"`name` = '{}' AND `name` NOT LIKE '%-deleted-%' LIMIT 1",
|
||||
Strings::Escape(bot_name)
|
||||
)
|
||||
);
|
||||
@@ -400,28 +405,13 @@ bool BotDatabase::LoadBot(const uint32 bot_id, Bot*& loaded_bot)
|
||||
e.spells_id,
|
||||
e.time_spawned,
|
||||
e.zone_id,
|
||||
t,
|
||||
e.expansion_bitmask
|
||||
t
|
||||
);
|
||||
|
||||
if (loaded_bot) {
|
||||
loaded_bot->SetSurname(e.last_name);
|
||||
loaded_bot->SetTitle(e.title);
|
||||
loaded_bot->SetSuffix(e.suffix);
|
||||
|
||||
loaded_bot->SetShowHelm(e.show_helm);
|
||||
|
||||
auto bfd = EQ::Clamp(e.follow_distance, static_cast<uint32>(1), BOT_FOLLOW_DISTANCE_DEFAULT_MAX);
|
||||
|
||||
loaded_bot->SetFollowDistance(bfd);
|
||||
|
||||
loaded_bot->SetStopMeleeLevel(e.stop_melee_level);
|
||||
|
||||
loaded_bot->SetBotEnforceSpellSetting(e.enforce_spell_settings);
|
||||
|
||||
loaded_bot->SetBotArcherySetting(e.archery_setting);
|
||||
|
||||
loaded_bot->SetBotCasterRange(e.caster_range);
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -476,13 +466,6 @@ bool BotDatabase::SaveNewBot(Bot* b, uint32& bot_id)
|
||||
e.poison = b->GetBasePR();
|
||||
e.disease = b->GetBaseDR();
|
||||
e.corruption = b->GetBaseCorrup();
|
||||
e.show_helm = b->GetShowHelm() ? 1 : 0;
|
||||
e.follow_distance = b->GetFollowDistance();
|
||||
e.stop_melee_level = b->GetStopMeleeLevel();
|
||||
e.expansion_bitmask = b->GetExpansionBitmask();
|
||||
e.enforce_spell_settings = b->GetBotEnforceSpellSetting();
|
||||
e.archery_setting = b->IsBotArcher() ? 1 : 0;
|
||||
e.caster_range = b->GetBotCasterRange();
|
||||
|
||||
e = BotDataRepository::InsertOne(database, e);
|
||||
|
||||
@@ -547,12 +530,6 @@ bool BotDatabase::SaveBot(Bot* b)
|
||||
e.poison = b->GetBasePR();
|
||||
e.disease = b->GetBaseDR();
|
||||
e.corruption = b->GetBaseCorrup();
|
||||
e.show_helm = b->GetShowHelm() ? 1 : 0;
|
||||
e.follow_distance = b->GetFollowDistance();
|
||||
e.stop_melee_level = b->GetStopMeleeLevel();
|
||||
e.expansion_bitmask = b->GetExpansionBitmask();
|
||||
e.enforce_spell_settings = b->GetBotEnforceSpellSetting();
|
||||
e.archery_setting = b->IsBotArcher() ? 1 : 0;
|
||||
|
||||
return BotDataRepository::UpdateOne(database, e);
|
||||
}
|
||||
@@ -823,9 +800,9 @@ bool BotDatabase::LoadTimers(Bot* b)
|
||||
)
|
||||
);
|
||||
|
||||
std::vector<BotTimer_Struct> v;
|
||||
std::vector<BotTimer> v;
|
||||
|
||||
BotTimer_Struct t{ };
|
||||
BotTimer t{ };
|
||||
|
||||
for (const auto& e : l) {
|
||||
if (e.timer_value < (Timer::GetCurrentTime() + e.recast_time)) {
|
||||
@@ -859,7 +836,7 @@ bool BotDatabase::SaveTimers(Bot* b)
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<BotTimer_Struct> v = b->GetBotTimers();
|
||||
std::vector<BotTimer> v = b->GetBotTimers();
|
||||
|
||||
if (v.empty()) {
|
||||
return true;
|
||||
@@ -868,7 +845,7 @@ bool BotDatabase::SaveTimers(Bot* b)
|
||||
std::vector<BotTimersRepository::BotTimers> l;
|
||||
|
||||
if (!v.empty()) {
|
||||
for (auto & bot_timer : v) {
|
||||
for (auto& bot_timer : v) {
|
||||
if (bot_timer.timer_value <= Timer::GetCurrentTime()) {
|
||||
continue;
|
||||
}
|
||||
@@ -1670,59 +1647,6 @@ bool BotDatabase::SaveAllArmorColors(const uint32 owner_id, const uint32 rgb_val
|
||||
return BotInventoriesRepository::SaveAllArmorColors(database, owner_id, rgb_value);
|
||||
}
|
||||
|
||||
bool BotDatabase::SaveHelmAppearance(const uint32 bot_id, const bool show_flag)
|
||||
{
|
||||
if (!bot_id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto e = BotDataRepository::FindOne(database, bot_id);
|
||||
|
||||
e.show_helm = show_flag ? 1 : 0;
|
||||
|
||||
return BotDataRepository::UpdateOne(database, e);
|
||||
}
|
||||
|
||||
bool BotDatabase::SaveAllHelmAppearances(const uint32 owner_id, const bool show_flag)
|
||||
{
|
||||
if (!owner_id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return BotDataRepository::SaveAllHelmAppearances(database, owner_id, show_flag);
|
||||
}
|
||||
|
||||
bool BotDatabase::ToggleAllHelmAppearances(const uint32 owner_id)
|
||||
{
|
||||
if (!owner_id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return BotDataRepository::ToggleAllHelmAppearances(database, owner_id);
|
||||
}
|
||||
|
||||
bool BotDatabase::SaveFollowDistance(const uint32 bot_id, const uint32 follow_distance)
|
||||
{
|
||||
if (!bot_id || !follow_distance) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto e = BotDataRepository::FindOne(database, bot_id);
|
||||
|
||||
e.follow_distance = follow_distance;
|
||||
|
||||
return BotDataRepository::UpdateOne(database, e);
|
||||
}
|
||||
|
||||
bool BotDatabase::SaveAllFollowDistances(const uint32 owner_id, const uint32 follow_distance)
|
||||
{
|
||||
if (!owner_id || !follow_distance) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return BotDataRepository::SaveAllFollowDistances(database, owner_id, follow_distance);
|
||||
}
|
||||
|
||||
bool BotDatabase::CreateCloneBot(const uint32 bot_id, const std::string& clone_name, uint32& clone_id)
|
||||
{
|
||||
if (!bot_id || clone_name.empty()) {
|
||||
@@ -1771,19 +1695,6 @@ bool BotDatabase::CreateCloneBotInventory(const uint32 bot_id, const uint32 clon
|
||||
return BotInventoriesRepository::InsertMany(database, l);
|
||||
}
|
||||
|
||||
bool BotDatabase::SaveStopMeleeLevel(const uint32 bot_id, const uint8 sml_value)
|
||||
{
|
||||
if (!bot_id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto e = BotDataRepository::FindOne(database, bot_id);
|
||||
|
||||
e.stop_melee_level = sml_value;
|
||||
|
||||
return BotDataRepository::UpdateOne(database, e);
|
||||
}
|
||||
|
||||
bool BotDatabase::LoadOwnerOptions(Client* c)
|
||||
{
|
||||
if (!c || !c->CharacterID()) {
|
||||
@@ -2224,74 +2135,6 @@ uint32 BotDatabase::GetRaceClassBitmask(uint32 bot_race)
|
||||
return e.race ? e.classes : 0;
|
||||
}
|
||||
|
||||
bool BotDatabase::SaveExpansionBitmask(const uint32 bot_id, const int expansion_bitmask)
|
||||
{
|
||||
if (!bot_id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto e = BotDataRepository::FindOne(database, bot_id);
|
||||
|
||||
if (!e.bot_id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
e.expansion_bitmask = expansion_bitmask;
|
||||
|
||||
return BotDataRepository::UpdateOne(database, e);
|
||||
}
|
||||
|
||||
bool BotDatabase::SaveEnforceSpellSetting(const uint32 bot_id, const bool enforce_spell_setting)
|
||||
{
|
||||
if (!bot_id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto e = BotDataRepository::FindOne(database, bot_id);
|
||||
|
||||
if (!e.bot_id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
e.enforce_spell_settings = enforce_spell_setting ? 1 : 0;
|
||||
|
||||
return BotDataRepository::UpdateOne(database, e);
|
||||
}
|
||||
|
||||
bool BotDatabase::SaveBotArcherSetting(const uint32 bot_id, const bool bot_archer_setting)
|
||||
{
|
||||
if (!bot_id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto e = BotDataRepository::FindOne(database, bot_id);
|
||||
|
||||
if (!e.bot_id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
e.archery_setting = bot_archer_setting ? 1 : 0;
|
||||
|
||||
return BotDataRepository::UpdateOne(database, e);
|
||||
}
|
||||
|
||||
bool BotDatabase::SaveBotCasterRange(const uint32 bot_id, const uint32 bot_caster_range_value)
|
||||
{
|
||||
if (!bot_id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto e = BotDataRepository::FindOne(database, bot_id);
|
||||
|
||||
if (!e.bot_id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
e.caster_range = bot_caster_range_value;
|
||||
|
||||
return BotDataRepository::UpdateOne(database, e);
|
||||
}
|
||||
|
||||
const uint8 BotDatabase::GetBotClassByID(const uint32 bot_id)
|
||||
{
|
||||
const auto& e = BotDataRepository::FindOne(database, bot_id);
|
||||
@@ -2322,7 +2165,7 @@ std::vector<uint32> BotDatabase::GetBotIDsByCharacterID(const uint32 character_i
|
||||
class_id
|
||||
) :
|
||||
""
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
@@ -2360,3 +2203,319 @@ const int BotDatabase::GetBotExtraHasteByID(const uint32 bot_id)
|
||||
|
||||
return e.bot_id ? e.extra_haste : 0;
|
||||
}
|
||||
|
||||
bool BotDatabase::LoadBotSettings(Mob* m)
|
||||
{
|
||||
if (!m) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!m->IsOfClientBot()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32 mob_id = (m->IsClient() ? m->CastToClient()->CharacterID() : m->CastToBot()->GetBotID());
|
||||
uint8 stance_id = (m->IsBot() ? m->CastToBot()->GetBotStance() : 0);
|
||||
|
||||
std::string query = "";
|
||||
|
||||
if (m->IsClient()) {
|
||||
query = fmt::format("`character_id` = {} AND `stance` = {}", mob_id, stance_id);
|
||||
}
|
||||
else {
|
||||
query = fmt::format("`bot_id` = {} AND `stance` = {}", mob_id, stance_id);
|
||||
}
|
||||
|
||||
if (stance_id == Stance::Passive) {
|
||||
LogBotSettings("{} is currently set to {} [#{}]. No saving or loading required.", m->GetCleanName(), Stance::GetName(Stance::Passive), Stance::Passive);
|
||||
return true;
|
||||
}
|
||||
|
||||
const auto& l = BotSettingsRepository::GetWhere(database, query);
|
||||
|
||||
if (l.empty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (const auto& e : l) {
|
||||
if (e.setting_type == BotSettingCategories::BaseSetting) {
|
||||
LogBotSettings("[{}] says, 'Loading {} [{}] - setting to [{}].",
|
||||
m->GetCleanName(),
|
||||
Bot::GetBotSettingCategoryName(e.setting_type),
|
||||
e.setting_type,
|
||||
e.value
|
||||
);
|
||||
}
|
||||
else {
|
||||
LogBotSettings("[{}] says, 'Loading {} [{}], {} [{}] - setting to [{}].",
|
||||
m->GetCleanName(),
|
||||
Bot::GetBotSpellCategoryName(e.setting_type),
|
||||
e.setting_type,
|
||||
Bot::GetSpellTypeNameByID(e.setting_id),
|
||||
e.setting_id,
|
||||
e.value
|
||||
);
|
||||
}
|
||||
|
||||
if (m->IsClient()) {
|
||||
m->CastToClient()->SetBotSetting(e.setting_type, e.setting_id, e.value);
|
||||
}
|
||||
else {
|
||||
m->CastToBot()->SetBotSetting(e.setting_type, e.setting_id, e.value);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BotDatabase::SaveBotSettings(Mob* m)
|
||||
{
|
||||
if (!m) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!m->IsOfClientBot()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32 bot_id = (m->IsBot() ? m->CastToBot()->GetBotID() : 0);
|
||||
uint32 character_id = (m->IsClient() ? m->CastToClient()->CharacterID() : 0);
|
||||
uint8 stance_id = (m->IsBot() ? m->CastToBot()->GetBotStance() : 0);
|
||||
|
||||
if (stance_id == Stance::Passive) {
|
||||
LogBotSettings("{} is currently set to {} [#{}]. No saving or loading required.", m->GetCleanName(), Stance::GetName(Stance::Passive), Stance::Passive);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string query = "";
|
||||
|
||||
if (m->IsClient()) {
|
||||
query = fmt::format("`character_id` = {} AND `stance` = {}", character_id, stance_id);
|
||||
}
|
||||
else {
|
||||
query = fmt::format("`bot_id` = {} AND `stance` = {}", bot_id, stance_id);
|
||||
}
|
||||
|
||||
BotSettingsRepository::DeleteWhere(database, query);
|
||||
|
||||
std::vector<BotSettingsRepository::BotSettings> v;
|
||||
|
||||
if (m->IsBot()) {
|
||||
uint8 bot_stance = m->CastToBot()->GetBotStance();
|
||||
|
||||
for (uint16 i = BotBaseSettings::START_ALL; i <= BotBaseSettings::END; ++i) {
|
||||
if (m->CastToBot()->GetBotBaseSetting(i) != m->CastToBot()->GetDefaultBotBaseSetting(i, bot_stance)) {
|
||||
auto e = BotSettingsRepository::BotSettings{
|
||||
.character_id = character_id,
|
||||
.bot_id = bot_id,
|
||||
.stance = stance_id,
|
||||
.setting_id = static_cast<uint16_t>(i),
|
||||
.setting_type = static_cast<uint8_t>(BotSettingCategories::BaseSetting),
|
||||
.value = static_cast<int32_t>(m->CastToBot()->GetBotBaseSetting(i)),
|
||||
.category_name = Bot::GetBotSpellCategoryName(BotSettingCategories::BaseSetting),
|
||||
.setting_name = Bot::GetBotSettingCategoryName(i)
|
||||
};
|
||||
|
||||
v.emplace_back(e);
|
||||
|
||||
LogBotSettings("{} says, 'Saving {} [{}] - set to [{}] default [{}].'", m->GetCleanName(), Bot::GetBotSettingCategoryName(i), i, e.value, m->CastToBot()->GetDefaultBotBaseSetting(i));
|
||||
}
|
||||
}
|
||||
|
||||
for (uint16 i = BotSettingCategories::START_NO_BASE; i <= BotSettingCategories::END; ++i) {
|
||||
for (uint16 x = BotSpellTypes::START; x <= BotSpellTypes::END; ++x) {
|
||||
if (m->CastToBot()->GetSetting(i, x) != m->CastToBot()->GetDefaultSetting(i, x, bot_stance)) {
|
||||
auto e = BotSettingsRepository::BotSettings{
|
||||
.character_id = character_id,
|
||||
.bot_id = bot_id,
|
||||
.stance = stance_id,
|
||||
.setting_id = static_cast<uint16_t>(x),
|
||||
.setting_type = static_cast<uint8_t>(i),
|
||||
.value = m->CastToBot()->GetSetting(i, x),
|
||||
.category_name = Bot::GetBotSpellCategoryName(i),
|
||||
.setting_name = Bot::GetSpellTypeNameByID(x)
|
||||
};
|
||||
|
||||
v.emplace_back(e);
|
||||
|
||||
LogBotSettings("{} says, 'Saving {} {} [{}] - set to [{}] default [{}].'", m->GetCleanName(), Bot::GetBotSpellCategoryName(i), Bot::GetSpellTypeNameByID(x), x, e.value, m->CastToBot()->GetDefaultSetting(i, x, bot_stance));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (m->IsClient()) {
|
||||
if (m->CastToClient()->GetDefaultBotSettings(BotSettingCategories::BaseSetting, BotBaseSettings::IllusionBlock) != m->CastToClient()->GetIllusionBlock()) { // Only illusion block supported
|
||||
auto e = BotSettingsRepository::BotSettings{
|
||||
.character_id = character_id,
|
||||
.bot_id = bot_id,
|
||||
.stance = stance_id,
|
||||
.setting_id = static_cast<uint16_t>(BotBaseSettings::IllusionBlock),
|
||||
.setting_type = static_cast<uint8_t>(BotSettingCategories::BaseSetting),
|
||||
.value = m->CastToClient()->GetIllusionBlock(),
|
||||
.category_name = Bot::GetBotSpellCategoryName(BotSettingCategories::BaseSetting),
|
||||
.setting_name = Bot::GetBotSettingCategoryName(BotBaseSettings::IllusionBlock)
|
||||
};
|
||||
|
||||
v.emplace_back(e);
|
||||
|
||||
LogBotSettings("{} says, 'Saving {} [{}] - set to [{}] default [{}].'", m->GetCleanName(), Bot::GetBotSettingCategoryName(BotBaseSettings::IllusionBlock), BotBaseSettings::IllusionBlock, e.value, m->CastToClient()->GetIllusionBlock());
|
||||
}
|
||||
|
||||
for (uint16 i = BotSettingCategories::START_CLIENT; i <= BotSettingCategories::END_CLIENT; ++i) {
|
||||
for (uint16 x = BotSpellTypes::START; x <= BotSpellTypes::END; ++x) {
|
||||
LogBotSettings("{} says, 'Checking {} {} [{}] - set to [{}] default [{}].'", m->GetCleanName(), Bot::GetBotSpellCategoryName(i), Bot::GetSpellTypeNameByID(x), x, m->CastToClient()->GetBotSetting(i, x), m->CastToClient()->GetDefaultBotSettings(i, x));
|
||||
if (m->CastToClient()->GetBotSetting(i, x) != m->CastToClient()->GetDefaultBotSettings(i, x)) {
|
||||
auto e = BotSettingsRepository::BotSettings{
|
||||
.character_id = character_id,
|
||||
.bot_id = bot_id,
|
||||
.stance = stance_id,
|
||||
.setting_id = static_cast<uint16_t>(x),
|
||||
.setting_type = static_cast<uint8_t>(i),
|
||||
.value = m->CastToClient()->GetBotSetting(i, x),
|
||||
.category_name = Bot::GetBotSpellCategoryName(i),
|
||||
.setting_name = Bot::GetSpellTypeNameByID(x)
|
||||
};
|
||||
|
||||
v.emplace_back(e);
|
||||
|
||||
LogBotSettings("{} says, 'Saving {} {} [{}] - set to [{}] default [{}].'", m->GetCleanName(), Bot::GetBotSpellCategoryName(i), Bot::GetSpellTypeNameByID(x), x, e.value, m->CastToClient()->GetDefaultBotSettings(i, x));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!v.empty()) {
|
||||
const int inserted = BotSettingsRepository::ReplaceMany(database, v);
|
||||
|
||||
if (!inserted) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BotDatabase::DeleteBotSettings(const uint32 bot_id)
|
||||
{
|
||||
if (!bot_id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
BotSettingsRepository::DeleteWhere(
|
||||
database,
|
||||
fmt::format(
|
||||
"`bot_id` = {}",
|
||||
bot_id
|
||||
)
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BotDatabase::LoadBotBlockedBuffs(Bot* b)
|
||||
{
|
||||
if (!b) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto& l = BotBlockedBuffsRepository::GetWhere(
|
||||
database,
|
||||
fmt::format(
|
||||
"`bot_id` = {}",
|
||||
b->GetBotID()
|
||||
)
|
||||
);
|
||||
|
||||
std::vector<BotBlockedBuffs> v;
|
||||
|
||||
BotBlockedBuffs t{ };
|
||||
|
||||
for (const auto& e : l) {
|
||||
t.spell_id = e.spell_id;
|
||||
t.blocked = e.blocked;
|
||||
t.blocked_pet = e.blocked_pet;
|
||||
|
||||
v.push_back(t);
|
||||
}
|
||||
|
||||
if (!v.empty()) {
|
||||
b->SetBotBlockedBuffs(v);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BotDatabase::SaveBotBlockedBuffs(Bot* b)
|
||||
{
|
||||
if (!b) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!DeleteBotBlockedBuffs(b->GetBotID())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<BotBlockedBuffs> v = b->GetBotBlockedBuffs();
|
||||
|
||||
if (v.empty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<BotBlockedBuffsRepository::BotBlockedBuffs> l;
|
||||
|
||||
if (!v.empty()) {
|
||||
for (auto& blocked_buff : v) {
|
||||
if (blocked_buff.blocked == 0 && blocked_buff.blocked_pet == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto e = BotBlockedBuffsRepository::BotBlockedBuffs{
|
||||
.bot_id = b->GetBotID(),
|
||||
.spell_id = blocked_buff.spell_id,
|
||||
.blocked = blocked_buff.blocked,
|
||||
.blocked_pet = blocked_buff.blocked_pet
|
||||
};
|
||||
|
||||
l.push_back(e);
|
||||
}
|
||||
|
||||
if (l.empty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
BotBlockedBuffsRepository::DeleteWhere(
|
||||
database,
|
||||
fmt::format(
|
||||
"`bot_id` = {}",
|
||||
b->GetBotID()
|
||||
)
|
||||
);
|
||||
|
||||
const int inserted = BotBlockedBuffsRepository::InsertMany(database, l);
|
||||
|
||||
if (!inserted) {
|
||||
DeleteBotBlockedBuffs(b->GetBotID());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BotDatabase::DeleteBotBlockedBuffs(const uint32 bot_id)
|
||||
{
|
||||
if (!bot_id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
BotBlockedBuffsRepository::DeleteWhere(
|
||||
database,
|
||||
fmt::format(
|
||||
"`bot_id` = {}",
|
||||
bot_id
|
||||
)
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
+6
-23
@@ -74,6 +74,9 @@ public:
|
||||
bool SaveTimers(Bot* b);
|
||||
bool DeleteTimers(const uint32 bot_id);
|
||||
|
||||
bool LoadBotBlockedBuffs(Bot* b);
|
||||
bool SaveBotBlockedBuffs(Bot* b);
|
||||
bool DeleteBotBlockedBuffs(const uint32 bot_id);
|
||||
|
||||
/* Bot inventory functions */
|
||||
bool QueryInventoryCount(const uint32 bot_id, uint32& item_count);
|
||||
@@ -88,10 +91,6 @@ public:
|
||||
|
||||
bool SaveEquipmentColor(const uint32 bot_id, const int16 slot_id, const uint32 color);
|
||||
|
||||
bool SaveExpansionBitmask(const uint32 bot_id, const int expansion_bitmask);
|
||||
bool SaveEnforceSpellSetting(const uint32 bot_id, const bool enforce_spell_setting);
|
||||
|
||||
|
||||
/* Bot pet functions */
|
||||
bool LoadPetIndex(const uint32 bot_id, uint32& pet_index);
|
||||
bool LoadPetSpellID(const uint32 bot_id, uint32& pet_spell_id);
|
||||
@@ -120,26 +119,16 @@ public:
|
||||
bool SaveAllArmorColorBySlot(const uint32 owner_id, const int16 slot_id, const uint32 rgb_value);
|
||||
bool SaveAllArmorColors(const uint32 owner_id, const uint32 rgb_value);
|
||||
|
||||
bool SaveHelmAppearance(const uint32 bot_id, const bool show_flag = true);
|
||||
bool SaveAllHelmAppearances(const uint32 owner_id, const bool show_flag = true);
|
||||
|
||||
bool ToggleAllHelmAppearances(const uint32 owner_id);
|
||||
|
||||
bool SaveFollowDistance(const uint32 bot_id, const uint32 follow_distance);
|
||||
bool SaveAllFollowDistances(const uint32 owner_id, const uint32 follow_distance);
|
||||
|
||||
bool CreateCloneBot(const uint32 bot_id, const std::string& clone_name, uint32& clone_id);
|
||||
bool CreateCloneBotInventory(const uint32 bot_id, const uint32 clone_id);
|
||||
|
||||
bool SaveStopMeleeLevel(const uint32 bot_id, const uint8 sml_value);
|
||||
|
||||
bool SaveBotArcherSetting(const uint32 bot_id, const bool bot_archer_setting);
|
||||
|
||||
bool LoadOwnerOptions(Client *owner);
|
||||
bool SaveOwnerOption(const uint32 owner_id, size_t type, const bool flag);
|
||||
bool SaveOwnerOption(const uint32 owner_id, const std::pair<size_t, size_t> type, const std::pair<bool, bool> flag);
|
||||
|
||||
bool SaveBotCasterRange(const uint32 bot_id, const uint32 bot_caster_range_value);
|
||||
bool LoadBotSettings(Mob* m);
|
||||
bool SaveBotSettings(Mob* m);
|
||||
bool DeleteBotSettings(const uint32 bot_id);
|
||||
|
||||
/* Bot group functions */
|
||||
bool LoadGroupedBotsByGroupID(const uint32 owner_id, const uint32 group_id, std::list<uint32>& group_list);
|
||||
@@ -210,12 +199,6 @@ public:
|
||||
static const char* SaveAllInspectMessages();
|
||||
static const char* SaveAllArmorColorBySlot();
|
||||
static const char* SaveAllArmorColors();
|
||||
static const char* SaveAllHelmAppearances();
|
||||
static const char* ToggleAllHelmAppearances();
|
||||
static const char* SaveFollowDistance();
|
||||
static const char* SaveAllFollowDistances();
|
||||
static const char* SaveStopMeleeLevel();
|
||||
static const char* SaveBotCasterRange();
|
||||
|
||||
/* fail::Bot heal rotation functions */
|
||||
static const char* LoadHealRotation();
|
||||
|
||||
+3
-40
@@ -96,19 +96,10 @@ void Raid::HandleBotGroupDisband(uint32 owner, uint32 gid)
|
||||
// Remove the entire BOT group in this case
|
||||
if (b && gid != RAID_GROUPLESS && IsRaidMember(b->GetName()) && IsGroupLeader(b->GetName())) {
|
||||
auto r_group_members = GetRaidGroupMembers(GetGroup(b->GetName()));
|
||||
auto g = new Group(b);
|
||||
entity_list.AddGroup(g);
|
||||
g->AddToGroup(b);
|
||||
database.SetGroupLeaderName(g->GetID(), b->GetName());
|
||||
|
||||
for (auto m: r_group_members) {
|
||||
if (m.member->IsBot()) {
|
||||
auto b_member = m.member->CastToBot();
|
||||
if (strcmp(b_member->GetName(), b->GetName()) == 0) {
|
||||
b->SetFollowID(owner);
|
||||
} else {
|
||||
Bot::AddBotToGroup(b_member, g);
|
||||
}
|
||||
Bot::RemoveBotFromRaid(b_member);
|
||||
}
|
||||
}
|
||||
@@ -142,26 +133,6 @@ void Raid::HandleOfflineBots(uint32 owner) {
|
||||
}
|
||||
}
|
||||
|
||||
uint8 Bot::GetNumberNeedingHealedInRaidGroup(uint8& need_healed, uint8 hpr, bool includePets, Raid* raid) {
|
||||
|
||||
if (raid) {
|
||||
uint32 r_group = raid->GetGroup(GetName());
|
||||
|
||||
for (auto& m: raid->GetRaidGroupMembers(r_group)) {
|
||||
if (m.member && !m.member->qglobal) {
|
||||
if (m.member->GetHPRatio() <= hpr) {
|
||||
need_healed++;
|
||||
}
|
||||
|
||||
if (includePets && m.member->GetPet() && m.member->GetPet()->GetHPRatio() <= hpr) {
|
||||
need_healed++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return need_healed;
|
||||
}
|
||||
|
||||
void Bot::ProcessRaidInvite(Mob* invitee, Client* invitor, bool group_invite) {
|
||||
|
||||
if (!invitee || !invitor) {
|
||||
@@ -176,13 +147,7 @@ void Bot::ProcessRaidInvite(Mob* invitee, Client* invitor, bool group_invite) {
|
||||
// If the Bot Owner is in our raid we need to be able to invite their Bots
|
||||
}
|
||||
else if (invitee->IsBot() && (invitee->CastToBot()->GetBotOwnerCharacterID() != invitor->CharacterID())) {
|
||||
invitor->Message(
|
||||
Chat::Red,
|
||||
fmt::format(
|
||||
"{} is not your Bot. You can only invite your own Bots, or Bots that belong to a Raid member.",
|
||||
invitee->GetCleanName()
|
||||
).c_str()
|
||||
);
|
||||
invitor->Message(Chat::Red, "%s's owner needs to be in your raid to be able to invite them.", invitee->GetCleanName());
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -257,10 +222,6 @@ void Bot::CreateBotRaid(Mob* invitee, Client* invitor, bool group_invite, Raid*
|
||||
} else {
|
||||
raid->AddBot(b);
|
||||
}
|
||||
|
||||
if (new_raid) {
|
||||
invitee->SetFollowID(invitor->GetID());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -323,7 +284,9 @@ void Client::SpawnRaidBotsOnConnect(Raid* raid) {
|
||||
|
||||
if (bot) {
|
||||
bot->SetRaidGrouped(true);
|
||||
bot->SetStoredRaid(raid);
|
||||
bot->p_raid_instance = raid;
|
||||
bot->SetVerifiedRaid(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+61
-2
@@ -20,6 +20,7 @@
|
||||
#define BOT_STRUCTS
|
||||
|
||||
#include "../common/types.h"
|
||||
#include "../common/timer.h"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
@@ -64,7 +65,7 @@ struct BotSpellSetting {
|
||||
bool is_enabled = true;
|
||||
};
|
||||
|
||||
struct BotSpells_Struct {
|
||||
struct BotSpells {
|
||||
uint32 type; // 0 = never, must be one (and only one) of the defined values
|
||||
int16 spellid; // <= 0 = no spell
|
||||
int16 manacost; // -1 = use spdat, -2 = no cast time
|
||||
@@ -81,7 +82,25 @@ struct BotSpells_Struct {
|
||||
uint8 bucket_comparison;
|
||||
};
|
||||
|
||||
struct BotTimer_Struct {
|
||||
struct BotSpells_wIndex {
|
||||
uint32 index; //index of AIBot_spells
|
||||
uint32 type; // 0 = never, must be one (and only one) of the defined values
|
||||
int16 spellid; // <= 0 = no spell
|
||||
int16 manacost; // -1 = use spdat, -2 = no cast time
|
||||
uint32 time_cancast; // when we can cast this spell next
|
||||
int32 recast_delay;
|
||||
int16 priority;
|
||||
int16 resist_adjust;
|
||||
uint8 minlevel;
|
||||
uint8 maxlevel;
|
||||
int16 min_hp; // >0 won't cast if HP is below
|
||||
int16 max_hp; // >0 won't cast if HP is above
|
||||
std::string bucket_name;
|
||||
std::string bucket_value;
|
||||
uint8 bucket_comparison;
|
||||
};
|
||||
|
||||
struct BotTimer {
|
||||
uint32 timer_id;
|
||||
uint32 timer_value;
|
||||
uint32 recast_time;
|
||||
@@ -92,4 +111,44 @@ struct BotTimer_Struct {
|
||||
uint32 item_id;
|
||||
};
|
||||
|
||||
struct BotSpellSettings {
|
||||
uint16 spell_type; // type ID of bot category
|
||||
std::string short_name; // type short name of bot category
|
||||
std::string name; // type name of bot category
|
||||
bool hold; // 0 = allow spell type, 1 = hold spell type
|
||||
uint16 delay; // delay between casts of spell type, 1ms-60,000ms
|
||||
uint8 min_threshold; // minimum target health threshold to allow casting of spell type
|
||||
uint8 max_threshold; // maximum target health threshold to allow casting of spell type
|
||||
uint16 resist_limit; // resist limit to skip spell type
|
||||
bool aggro_check; // whether or not to check for possible aggro before casting
|
||||
uint8 min_mana_pct; // lower mana percentage limit to allow spell cast
|
||||
uint8 max_mana_pct; // upper mana percentage limit to allow spell cast
|
||||
uint8 min_hp_pct; // lower HP percentage limit to allow spell cast
|
||||
uint8 max_hp_pct; // upper HP percentage limit to allow spell cast
|
||||
uint16 idle_priority; // idle priority of the spell type
|
||||
uint16 engaged_priority; // engaged priority of the spell type
|
||||
uint16 pursue_priority; // pursue priority of the spell type
|
||||
uint16 ae_or_group_target_count; // require target count to cast an AE or Group spell type
|
||||
uint16 announce_cast; // announce when casting a certain spell type
|
||||
Timer recast_timer; // recast timer based off delay
|
||||
Timer ai_delay; // spell timer based off delay
|
||||
};
|
||||
|
||||
struct BotSpellTypeOrder {
|
||||
uint16 spellType;
|
||||
uint16 priority;
|
||||
};
|
||||
|
||||
struct BotBlockedBuffs {
|
||||
uint32_t bot_id;
|
||||
uint32_t spell_id;
|
||||
uint8_t blocked;
|
||||
uint8_t blocked_pet;
|
||||
};
|
||||
|
||||
struct BotSpellTypesByClass {
|
||||
uint8_t min_level = 255;
|
||||
std::string description;
|
||||
};
|
||||
|
||||
#endif // BOT_STRUCTS
|
||||
|
||||
+1844
-2310
File diff suppressed because it is too large
Load Diff
+155
-10
@@ -51,6 +51,7 @@ extern volatile bool RunLoops;
|
||||
#include "water_map.h"
|
||||
#include "bot_command.h"
|
||||
#include "string_ids.h"
|
||||
#include "dialogue_window.h"
|
||||
|
||||
#include "guild_mgr.h"
|
||||
#include "quest_parser_collection.h"
|
||||
@@ -148,6 +149,7 @@ Client::Client(EQStreamInterface *ieqs) : Mob(
|
||||
),
|
||||
hpupdate_timer(2000),
|
||||
camp_timer(29000),
|
||||
bot_camp_timer((RuleI(Bots, CampTimer) * 1000)),
|
||||
process_timer(100),
|
||||
consume_food_timer(CONSUMPTION_TIMER),
|
||||
zoneinpacket_timer(1000),
|
||||
@@ -252,6 +254,7 @@ Client::Client(EQStreamInterface *ieqs) : Mob(
|
||||
fishing_timer.Disable();
|
||||
dead_timer.Disable();
|
||||
camp_timer.Disable();
|
||||
bot_camp_timer.Disable();
|
||||
autosave_timer.Disable();
|
||||
GetMercTimer()->Disable();
|
||||
instalog = false;
|
||||
@@ -777,6 +780,10 @@ bool Client::Save(uint8 iCommitNow) {
|
||||
|
||||
database.SaveCharacterEXPModifier(this);
|
||||
|
||||
if (RuleB(Bots, Enabled)) {
|
||||
database.botdb.SaveBotSettings(this);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -5950,7 +5957,7 @@ void Client::SuspendMinion(int value)
|
||||
{
|
||||
m_suspendedminion.SpellID = SpellID;
|
||||
|
||||
m_suspendedminion.HP = CurrentPet->GetHP();;
|
||||
m_suspendedminion.HP = CurrentPet->GetHP();
|
||||
|
||||
m_suspendedminion.Mana = CurrentPet->GetMana();
|
||||
m_suspendedminion.petpower = CurrentPet->GetPetPower();
|
||||
@@ -8802,10 +8809,10 @@ void Client::ProcessAggroMeter()
|
||||
|
||||
// probably should have PVP rules ...
|
||||
if (cur_tar && cur_tar != this) {
|
||||
if (cur_tar->IsNPC() && !cur_tar->IsPetOwnerClient() && cur_tar->GetID() != m_aggrometer.get_target_id()) {
|
||||
if (cur_tar->IsNPC() && !cur_tar->IsPetOwnerOfClientBot() && cur_tar->GetID() != m_aggrometer.get_target_id()) {
|
||||
m_aggrometer.set_target_id(cur_tar->GetID());
|
||||
send_targetinfo = true;
|
||||
} else if ((cur_tar->IsPetOwnerClient() || cur_tar->IsClient()) && cur_tar->GetTarget() && cur_tar->GetTarget()->GetID() != m_aggrometer.get_target_id()) {
|
||||
} else if ((cur_tar->IsPetOwnerOfClientBot() || cur_tar->IsClient()) && cur_tar->GetTarget() && cur_tar->GetTarget()->GetID() != m_aggrometer.get_target_id()) {
|
||||
m_aggrometer.set_target_id(cur_tar->GetTarget()->GetID());
|
||||
send_targetinfo = true;
|
||||
}
|
||||
@@ -12933,11 +12940,11 @@ void Client::AddMoneyToPPWithOverflow(uint64 copper, bool update_client)
|
||||
SaveCurrency();
|
||||
|
||||
LogDebug("Client::AddMoneyToPPWithOverflow() [{}] should have: plat:[{}] gold:[{}] silver:[{}] copper:[{}]",
|
||||
GetName(),
|
||||
m_pp.platinum,
|
||||
m_pp.gold,
|
||||
m_pp.silver,
|
||||
m_pp.copper
|
||||
GetName(),
|
||||
m_pp.platinum,
|
||||
m_pp.gold,
|
||||
m_pp.silver,
|
||||
m_pp.copper
|
||||
);
|
||||
}
|
||||
|
||||
@@ -13120,8 +13127,8 @@ void Client::ClientToNpcAggroProcess()
|
||||
{
|
||||
if (zone->CanDoCombat() && !GetFeigned() && m_client_npc_aggro_scan_timer.Check()) {
|
||||
int npc_scan_count = 0;
|
||||
for (auto &close_mob: GetCloseMobList()) {
|
||||
Mob *mob = close_mob.second;
|
||||
for (auto& close_mob : GetCloseMobList()) {
|
||||
Mob* mob = close_mob.second;
|
||||
if (!mob) {
|
||||
continue;
|
||||
}
|
||||
@@ -13213,6 +13220,144 @@ void Client::ShowZoneShardMenu()
|
||||
}
|
||||
}
|
||||
|
||||
std::string Client::SendBotCommandHelpWindow(const BotCommandHelpParams& params) {
|
||||
unsigned string_length = 0;
|
||||
unsigned current_place = 0;
|
||||
uint16 max_length = RuleI(Command, MaxHelpLineLength); // Line length before splitting
|
||||
const std::string& header_color = "indian_red";
|
||||
const std::string& description_color = "light_grey";
|
||||
const std::string& description_color_secondary = "dark_orange";
|
||||
const std::string& example_color = "goldenrod";
|
||||
const std::string& example_color_secondary = "slate_blue";
|
||||
const std::string& option_color = "light_grey";
|
||||
const std::string& option_color_secondary = "slate_blue";
|
||||
const std::string& filler_line_color = "dark_grey";
|
||||
|
||||
std::string filler_line = "--------------------------------------------------------------------";
|
||||
std::string filler_dia = DialogueWindow::TableRow(DialogueWindow::TableCell(fmt::format("{}", DialogueWindow::ColorMessage(filler_line_color, filler_line))));
|
||||
std::string break_line = DialogueWindow::Break();
|
||||
std::string popup_text;
|
||||
|
||||
if (!params.description.empty()) {
|
||||
popup_text += GetCommandHelpHeader("[Description]", header_color);
|
||||
popup_text += SplitCommandHelpText(params.description, description_color, max_length);
|
||||
}
|
||||
|
||||
if (!params.notes.empty()) {
|
||||
popup_text += break_line + break_line;
|
||||
popup_text += GetCommandHelpHeader("[Notes]", header_color);
|
||||
popup_text += SplitCommandHelpText(params.notes, description_color_secondary, max_length);
|
||||
}
|
||||
|
||||
if (!params.example_format.empty()) {
|
||||
popup_text += filler_dia;
|
||||
popup_text += GetCommandHelpHeader("[Examples]", header_color);
|
||||
popup_text += SplitCommandHelpText(params.example_format, example_color, max_length);
|
||||
}
|
||||
|
||||
if (!params.examples_one.empty()) {
|
||||
popup_text += break_line + break_line;
|
||||
popup_text += SplitCommandHelpText(params.examples_one, example_color, max_length, example_color_secondary);
|
||||
}
|
||||
|
||||
if (!params.examples_two.empty()) {
|
||||
popup_text += SplitCommandHelpText(params.examples_two, example_color, max_length, example_color_secondary);
|
||||
}
|
||||
|
||||
if (!params.examples_three.empty()) {
|
||||
popup_text += SplitCommandHelpText(params.examples_three, example_color, max_length, example_color_secondary);
|
||||
}
|
||||
|
||||
if (!params.options.empty()) {
|
||||
popup_text += filler_dia;
|
||||
popup_text += GetCommandHelpHeader("[Options]", header_color);
|
||||
popup_text += SplitCommandHelpText(params.options, option_color, max_length);
|
||||
}
|
||||
|
||||
if (!params.options_one.empty()) {
|
||||
popup_text += break_line + break_line;
|
||||
popup_text += SplitCommandHelpText(params.options_one, option_color_secondary, max_length);
|
||||
}
|
||||
|
||||
if (!params.options_two.empty()) {
|
||||
popup_text += SplitCommandHelpText(params.options_two, option_color_secondary, max_length);
|
||||
}
|
||||
|
||||
if (!params.options_three.empty()) {
|
||||
popup_text += SplitCommandHelpText(params.options_three, option_color_secondary, max_length);
|
||||
}
|
||||
|
||||
if (!params.actionables.empty()) {
|
||||
popup_text += filler_dia;
|
||||
popup_text += GetCommandHelpHeader("[Actionables]", header_color);
|
||||
popup_text += SplitCommandHelpText(params.actionables, description_color, max_length);
|
||||
}
|
||||
|
||||
popup_text = DialogueWindow::Table(popup_text);
|
||||
|
||||
return popup_text;
|
||||
}
|
||||
|
||||
std::string Client::GetCommandHelpHeader(std::string msg, std::string color) {
|
||||
std::string return_text = DialogueWindow::TableRow(
|
||||
DialogueWindow::TableCell(
|
||||
fmt::format(
|
||||
"{}",
|
||||
DialogueWindow::ColorMessage(color, msg)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
return return_text;
|
||||
}
|
||||
|
||||
std::string Client::SplitCommandHelpText(std::vector<std::string> msg, std::string color, uint16 max_length, std::string secondary_color) {
|
||||
std::string return_text;
|
||||
|
||||
for (int i = 0; i < msg.size(); i++) {
|
||||
std::vector<std::string> msg_split;
|
||||
int string_length = msg[i].length() + 1;
|
||||
int end_count = 0;
|
||||
int new_count = 0;
|
||||
int split_count = 0;
|
||||
|
||||
for (int x = 0; x < string_length; x = end_count) {
|
||||
end_count = std::min(int(string_length), (int(x) + std::min(int(string_length), int(max_length))));
|
||||
|
||||
if ((string_length - (x + 1)) > max_length) {
|
||||
for (int y = end_count; y >= x; --y) {
|
||||
if (msg[i][y] == ' ') {
|
||||
split_count = y - x;
|
||||
msg_split.emplace_back(msg[i].substr(x, split_count));
|
||||
end_count = y + 1;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (y == x) {
|
||||
msg_split.emplace_back(msg[i].substr(x, max_length));
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
msg_split.emplace_back(msg[i].substr(x, (string_length - 1) - x));
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& s : msg_split) {
|
||||
return_text += DialogueWindow::TableRow(
|
||||
DialogueWindow::TableCell(DialogueWindow::ColorMessage(((!secondary_color.empty() && i== 0) ? secondary_color : color), s))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return return_text;
|
||||
}
|
||||
|
||||
void Client::SetAAEXPPercentage(uint8 percentage)
|
||||
{
|
||||
const uint32 before_percentage = m_epp.perAA;
|
||||
|
||||
@@ -74,6 +74,8 @@ namespace EQ
|
||||
#include "../common/repositories/buyer_buy_lines_repository.h"
|
||||
#include "../common/repositories/character_evolving_items_repository.h"
|
||||
|
||||
#include "bot_structs.h"
|
||||
|
||||
#ifdef _WINDOWS
|
||||
// since windows defines these within windef.h (which windows.h include)
|
||||
// we are required to undefine these to use min and max from <algorithm>
|
||||
@@ -197,6 +199,19 @@ struct RespawnOption
|
||||
float heading;
|
||||
};
|
||||
|
||||
struct BotCommandHelpParams {
|
||||
std::vector<std::string> description = {};
|
||||
std::vector<std::string> notes = {};
|
||||
std::vector<std::string> example_format = {};
|
||||
std::vector<std::string> examples_one = {};
|
||||
std::vector<std::string> examples_two = {};
|
||||
std::vector<std::string> examples_three = {};
|
||||
std::vector<std::string> actionables = {};
|
||||
std::vector<std::string> options = {};
|
||||
std::vector<std::string> options_one = {};
|
||||
std::vector<std::string> options_two = {};
|
||||
std::vector<std::string> options_three = {};
|
||||
};
|
||||
|
||||
// do not ask what all these mean because I have no idea!
|
||||
// named from the client's CEverQuest::GetInnateDesc, they're missing some
|
||||
@@ -1264,6 +1279,12 @@ public:
|
||||
PendingTranslocate_Struct PendingTranslocateData;
|
||||
void SendOPTranslocateConfirm(Mob *Caster, uint16 SpellID);
|
||||
|
||||
// Help Window
|
||||
std::string SendBotCommandHelpWindow(const BotCommandHelpParams& params);
|
||||
std::string GetCommandHelpHeader(std::string msg, std::string color);
|
||||
std::string SplitCommandHelpText(std::vector<std::string> msg, std::string color, uint16 max_length, std::string secondary_color = "");
|
||||
void SendSpellTypePrompts(bool commanded_types = false, bool client_only_types = false);
|
||||
|
||||
// Task System Methods
|
||||
void LoadClientTaskState();
|
||||
void RemoveClientTaskState();
|
||||
@@ -2059,6 +2080,7 @@ private:
|
||||
PTimerList p_timers; //persistent timers
|
||||
Timer hpupdate_timer;
|
||||
Timer camp_timer;
|
||||
Timer bot_camp_timer;
|
||||
Timer process_timer;
|
||||
Timer consume_food_timer;
|
||||
Timer zoneinpacket_timer;
|
||||
@@ -2243,6 +2265,8 @@ public:
|
||||
|
||||
bool GetBotPulling() { return m_bot_pulling; }
|
||||
void SetBotPulling(bool flag = true) { m_bot_pulling = flag; }
|
||||
uint32 GetAssistee() { return bot_assistee; }
|
||||
void SetAssistee(uint32 id = 0) { bot_assistee = id; }
|
||||
|
||||
bool GetBotPrecombat() { return m_bot_precombat; }
|
||||
void SetBotPrecombat(bool flag = true) { m_bot_precombat = flag; }
|
||||
@@ -2257,10 +2281,33 @@ public:
|
||||
void CampAllBots(uint8 class_id = Class::None);
|
||||
void SpawnRaidBotsOnConnect(Raid* raid);
|
||||
|
||||
void LoadDefaultBotSettings();
|
||||
int GetDefaultBotSettings(uint8 setting_type, uint16 bot_setting);
|
||||
int GetBotSetting(uint8 setting_type, uint16 bot_setting);
|
||||
void SetBotSetting(uint8 setting_type, uint16 bot_setting, uint32 setting_value);
|
||||
|
||||
uint16 GetDefaultSpellTypeDelay(uint16 spell_type);
|
||||
uint8 GetDefaultSpellTypeMinThreshold(uint16 spell_type);
|
||||
uint8 GetDefaultSpellTypeMaxThreshold(uint16 spell_type);
|
||||
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); }
|
||||
|
||||
void SetIllusionBlock(bool value) { _illusionBlock = value; }
|
||||
bool GetIllusionBlock() const override { return _illusionBlock; }
|
||||
|
||||
private:
|
||||
bool bot_owner_options[_booCount];
|
||||
bool m_bot_pulling;
|
||||
bool m_bot_precombat;
|
||||
uint32 bot_assistee;
|
||||
std::vector<BotSpellSettings> m_bot_spell_settings;
|
||||
bool _illusionBlock;
|
||||
|
||||
bool CanTradeFVNoDropItem();
|
||||
void SendMobPositions();
|
||||
|
||||
+288
-15
@@ -15,10 +15,13 @@ void Client::SetBotOption(BotOwnerOption boo, bool flag) {
|
||||
}
|
||||
}
|
||||
|
||||
uint32 Client::GetBotCreationLimit(uint8 class_id)
|
||||
{
|
||||
uint32 Client::GetBotCreationLimit(uint8 class_id) {
|
||||
uint32 bot_creation_limit = RuleI(Bots, CreationLimit);
|
||||
|
||||
if (Admin() >= RuleI(Bots, MinStatusToBypassCreateLimit)) {
|
||||
return RuleI(Bots, MinStatusBypassCreateLimit);
|
||||
}
|
||||
|
||||
const auto bucket_name = fmt::format(
|
||||
"bot_creation_limit{}",
|
||||
(
|
||||
@@ -39,8 +42,7 @@ uint32 Client::GetBotCreationLimit(uint8 class_id)
|
||||
return bot_creation_limit;
|
||||
}
|
||||
|
||||
int Client::GetBotRequiredLevel(uint8 class_id)
|
||||
{
|
||||
int Client::GetBotRequiredLevel(uint8 class_id) {
|
||||
int bot_character_level = RuleI(Bots, BotCharacterLevel);
|
||||
|
||||
const auto bucket_name = fmt::format(
|
||||
@@ -63,10 +65,13 @@ int Client::GetBotRequiredLevel(uint8 class_id)
|
||||
return bot_character_level;
|
||||
}
|
||||
|
||||
int Client::GetBotSpawnLimit(uint8 class_id)
|
||||
{
|
||||
int Client::GetBotSpawnLimit(uint8 class_id) {
|
||||
int bot_spawn_limit = RuleI(Bots, SpawnLimit);
|
||||
|
||||
if (Admin() >= RuleI(Bots, MinStatusToBypassSpawnLimit)) {
|
||||
return RuleI(Bots, MinStatusBypassSpawnLimit);
|
||||
}
|
||||
|
||||
const auto bucket_name = fmt::format(
|
||||
"bot_spawn_limit{}",
|
||||
(
|
||||
@@ -80,9 +85,21 @@ int Client::GetBotSpawnLimit(uint8 class_id)
|
||||
);
|
||||
|
||||
auto bucket_value = GetBucket(bucket_name);
|
||||
|
||||
if (class_id && !bot_spawn_limit && bucket_value.empty()) {
|
||||
const auto new_bucket_name = "bot_spawn_limit";
|
||||
|
||||
bucket_value = GetBucket(new_bucket_name);
|
||||
|
||||
if (!bucket_value.empty() && Strings::IsNumber(bucket_value)) {
|
||||
bot_spawn_limit = Strings::ToInt(bucket_value);
|
||||
|
||||
return bot_spawn_limit;
|
||||
}
|
||||
}
|
||||
|
||||
if (!bucket_value.empty() && Strings::IsNumber(bucket_value)) {
|
||||
bot_spawn_limit = Strings::ToInt(bucket_value);
|
||||
return bot_spawn_limit;
|
||||
}
|
||||
|
||||
if (RuleB(Bots, QuestableSpawnLimit)) {
|
||||
@@ -93,6 +110,7 @@ int Client::GetBotSpawnLimit(uint8 class_id)
|
||||
);
|
||||
|
||||
auto results = database.QueryDatabase(query); // use 'database' for non-bot table calls
|
||||
|
||||
if (!results.Success() || !results.RowCount()) {
|
||||
return bot_spawn_limit;
|
||||
}
|
||||
@@ -101,11 +119,56 @@ int Client::GetBotSpawnLimit(uint8 class_id)
|
||||
bot_spawn_limit = Strings::ToInt(row[0]);
|
||||
}
|
||||
|
||||
const auto& zones_list = Strings::Split(RuleS(Bots, ZonesWithSpawnLimits), ",");
|
||||
const auto& zones_limits_list = Strings::Split(RuleS(Bots, ZoneSpawnLimits), ",");
|
||||
int i = 0;
|
||||
|
||||
for (const auto& result : zones_list) {
|
||||
try {
|
||||
if (
|
||||
std::stoul(result) == zone->GetZoneID() &&
|
||||
std::stoul(zones_limits_list[i]) < bot_spawn_limit
|
||||
) {
|
||||
bot_spawn_limit = std::stoul(zones_limits_list[i]);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
++i;
|
||||
}
|
||||
|
||||
catch (const std::exception& e) {
|
||||
LogInfo("Invalid entry in Rule VegasScaling:SpecialScalingZones or SpecialScalingZonesVersions: [{}]", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
const auto& zones_forced_list = Strings::Split(RuleS(Bots, ZonesWithForcedSpawnLimits), ",");
|
||||
const auto& zones_forced_limits_list = Strings::Split(RuleS(Bots, ZoneForcedSpawnLimits), ",");
|
||||
i = 0;
|
||||
|
||||
for (const auto& result : zones_forced_list) {
|
||||
try {
|
||||
if (
|
||||
std::stoul(result) == zone->GetZoneID() &&
|
||||
std::stoul(zones_forced_limits_list[i]) != bot_spawn_limit
|
||||
) {
|
||||
bot_spawn_limit = std::stoul(zones_forced_limits_list[i]);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
++i;
|
||||
}
|
||||
|
||||
catch (const std::exception& e) {
|
||||
LogInfo("Invalid entry in Rule VegasScaling:SpecialScalingZones or SpecialScalingZonesVersions: [{}]", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
return bot_spawn_limit;
|
||||
}
|
||||
|
||||
void Client::SetBotCreationLimit(uint32 new_creation_limit, uint8 class_id)
|
||||
{
|
||||
void Client::SetBotCreationLimit(uint32 new_creation_limit, uint8 class_id) {
|
||||
const auto bucket_name = fmt::format(
|
||||
"bot_creation_limit{}",
|
||||
(
|
||||
@@ -121,8 +184,7 @@ void Client::SetBotCreationLimit(uint32 new_creation_limit, uint8 class_id)
|
||||
SetBucket(bucket_name, std::to_string(new_creation_limit));
|
||||
}
|
||||
|
||||
void Client::SetBotRequiredLevel(int new_required_level, uint8 class_id)
|
||||
{
|
||||
void Client::SetBotRequiredLevel(int new_required_level, uint8 class_id) {
|
||||
const auto bucket_name = fmt::format(
|
||||
"bot_required_level{}",
|
||||
(
|
||||
@@ -138,8 +200,7 @@ void Client::SetBotRequiredLevel(int new_required_level, uint8 class_id)
|
||||
SetBucket(bucket_name, std::to_string(new_required_level));
|
||||
}
|
||||
|
||||
void Client::SetBotSpawnLimit(int new_spawn_limit, uint8 class_id)
|
||||
{
|
||||
void Client::SetBotSpawnLimit(int new_spawn_limit, uint8 class_id) {
|
||||
const auto bucket_name = fmt::format(
|
||||
"bot_spawn_limit{}",
|
||||
(
|
||||
@@ -155,7 +216,219 @@ void Client::SetBotSpawnLimit(int new_spawn_limit, uint8 class_id)
|
||||
SetBucket(bucket_name, std::to_string(new_spawn_limit));
|
||||
}
|
||||
|
||||
void Client::CampAllBots(uint8 class_id)
|
||||
{
|
||||
void Client::CampAllBots(uint8 class_id) {
|
||||
Bot::BotOrderCampAll(this, class_id);
|
||||
}
|
||||
|
||||
void Client::LoadDefaultBotSettings() {
|
||||
m_bot_spell_settings.clear();
|
||||
|
||||
// Only illusion block supported currently
|
||||
SetBotSetting(BotSettingCategories::BaseSetting, BotBaseSettings::IllusionBlock, GetDefaultBotSettings(BotSettingCategories::BaseSetting, BotBaseSettings::IllusionBlock));
|
||||
LogBotSettingsDetail("{} says, 'Setting default {} [{}] to [{}]'", GetCleanName(), CastToBot()->GetBotSettingCategoryName(BotBaseSettings::IllusionBlock), BotBaseSettings::IllusionBlock, GetDefaultBotSettings(BotSettingCategories::BaseSetting, BotBaseSettings::IllusionBlock));
|
||||
|
||||
for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) {
|
||||
BotSpellSettings t;
|
||||
|
||||
t.spell_type = i;
|
||||
t.short_name = Bot::GetSpellTypeShortNameByID(i);
|
||||
t.name = Bot::GetSpellTypeNameByID(i);
|
||||
t.delay = GetDefaultSpellTypeDelay(i);
|
||||
t.min_threshold = GetDefaultSpellTypeMinThreshold(i);
|
||||
t.max_threshold = GetDefaultSpellTypeMaxThreshold(i);
|
||||
t.recast_timer.Start();
|
||||
|
||||
m_bot_spell_settings.push_back(t);
|
||||
|
||||
LogBotSettingsDetail("{} says, 'Setting defaults for {} ({}) [#{}]'", GetCleanName(), t.name, t.short_name, t.spell_type);
|
||||
LogBotSettingsDetail("{} says, 'Delay = [{}ms] | MinThreshold = [{}\%] | MaxThreshold = [{}\%]'", GetCleanName(),
|
||||
GetDefaultSpellTypeDelay(i),
|
||||
GetDefaultSpellTypeMinThreshold(i), GetDefaultSpellTypeMaxThreshold(i));
|
||||
}
|
||||
}
|
||||
|
||||
int Client::GetDefaultBotSettings(uint8 setting_type, uint16 bot_setting) {
|
||||
switch (setting_type) {
|
||||
case BotSettingCategories::BaseSetting:
|
||||
return false; // only setting supported currently is illusion block
|
||||
case BotSettingCategories::SpellDelay:
|
||||
return GetDefaultSpellTypeDelay(bot_setting);
|
||||
case BotSettingCategories::SpellMinThreshold:
|
||||
return GetDefaultSpellTypeMinThreshold(bot_setting);
|
||||
case BotSettingCategories::SpellMaxThreshold:
|
||||
return GetDefaultSpellTypeMaxThreshold(bot_setting);
|
||||
}
|
||||
}
|
||||
|
||||
int Client::GetBotSetting(uint8 setting_type, uint16 bot_setting) {
|
||||
switch (setting_type) {
|
||||
case BotSettingCategories::BaseSetting:
|
||||
return GetIllusionBlock(); // only setting supported currently
|
||||
case BotSettingCategories::SpellDelay:
|
||||
return GetSpellTypeDelay(bot_setting);
|
||||
case BotSettingCategories::SpellMinThreshold:
|
||||
return GetSpellTypeMinThreshold(bot_setting);
|
||||
case BotSettingCategories::SpellMaxThreshold:
|
||||
return GetSpellTypeMaxThreshold(bot_setting);
|
||||
}
|
||||
}
|
||||
|
||||
void Client::SetBotSetting(uint8 setting_type, uint16 bot_setting, uint32 setting_value) {
|
||||
switch (setting_type) {
|
||||
case BotSettingCategories::BaseSetting:
|
||||
SetIllusionBlock(setting_value); // only setting supported currently
|
||||
break;
|
||||
case BotSettingCategories::SpellDelay:
|
||||
SetSpellTypeDelay(bot_setting, setting_value);
|
||||
break;
|
||||
case BotSettingCategories::SpellMinThreshold:
|
||||
SetSpellTypeMinThreshold(bot_setting, setting_value);
|
||||
break;
|
||||
case BotSettingCategories::SpellMaxThreshold:
|
||||
SetSpellTypeMaxThreshold(bot_setting, setting_value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Client::SendSpellTypePrompts(bool commanded_types, bool client_only_types) {
|
||||
if (client_only_types) {
|
||||
Message(
|
||||
Chat::Yellow,
|
||||
fmt::format(
|
||||
"You can view client spell types by {} or {}.",
|
||||
Saylink::Silent(
|
||||
fmt::format("^spelltypeids client"), "ID"
|
||||
),
|
||||
Saylink::Silent(
|
||||
fmt::format("^spelltypenames client"), "Shortname"
|
||||
)
|
||||
).c_str()
|
||||
);
|
||||
}
|
||||
else {
|
||||
Message(
|
||||
Chat::Yellow,
|
||||
fmt::format(
|
||||
"You can view spell types by {}, {}, {} or by {}, {}, {}.",
|
||||
Saylink::Silent(
|
||||
fmt::format("^spelltypeids 0-19"), "ID 0-19"
|
||||
),
|
||||
Saylink::Silent(
|
||||
fmt::format("^spelltypeids 20-39"), "20-39"
|
||||
),
|
||||
Saylink::Silent(
|
||||
fmt::format("^spelltypeids 40+"), "40+"
|
||||
),
|
||||
Saylink::Silent(
|
||||
fmt::format("^spelltypenames 0-19"), "Shortname 0-19"
|
||||
),
|
||||
Saylink::Silent(
|
||||
fmt::format("^spelltypenames 20-39"), "20-39"
|
||||
),
|
||||
Saylink::Silent(
|
||||
fmt::format("^spelltypenames 40+"), "40+"
|
||||
)
|
||||
).c_str()
|
||||
);
|
||||
}
|
||||
|
||||
if (commanded_types) {
|
||||
Message(
|
||||
Chat::Yellow,
|
||||
fmt::format(
|
||||
"You can view commanded spell types by {} or {}.",
|
||||
Saylink::Silent(
|
||||
fmt::format("^spelltypeids commanded"), "ID"
|
||||
),
|
||||
Saylink::Silent(
|
||||
fmt::format("^spelltypenames commanded"), "Shortname"
|
||||
)
|
||||
).c_str()
|
||||
);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
uint16 Client::GetDefaultSpellTypeDelay(uint16 spell_type) {
|
||||
switch (spell_type) {
|
||||
case BotSpellTypes::VeryFastHeals:
|
||||
case BotSpellTypes::PetVeryFastHeals:
|
||||
return 1500;
|
||||
case BotSpellTypes::FastHeals:
|
||||
case BotSpellTypes::PetFastHeals:
|
||||
return 2500;
|
||||
case BotSpellTypes::GroupHeals:
|
||||
case BotSpellTypes::RegularHeal:
|
||||
case BotSpellTypes::PetRegularHeals:
|
||||
return 4000;
|
||||
case BotSpellTypes::CompleteHeal:
|
||||
case BotSpellTypes::GroupCompleteHeals:
|
||||
case BotSpellTypes::PetCompleteHeals:
|
||||
return 8000;
|
||||
case BotSpellTypes::GroupHoTHeals:
|
||||
case BotSpellTypes::HoTHeals:
|
||||
case BotSpellTypes::PetHoTHeals:
|
||||
return 22000;
|
||||
case BotSpellTypes::Cure:
|
||||
return 2000;
|
||||
case BotSpellTypes::GroupCures:
|
||||
return 3000;
|
||||
case BotSpellTypes::PetCures:
|
||||
return 5000;
|
||||
default:
|
||||
return 100;
|
||||
}
|
||||
}
|
||||
|
||||
uint8 Client::GetDefaultSpellTypeMinThreshold(uint16 spell_type) {
|
||||
switch (spell_type) {
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
uint8 Client::GetDefaultSpellTypeMaxThreshold(uint16 spell_type) {
|
||||
uint8 client_class = GetClass();
|
||||
|
||||
switch (spell_type) {
|
||||
case BotSpellTypes::VeryFastHeals:
|
||||
case BotSpellTypes::PetVeryFastHeals:
|
||||
return 25;
|
||||
case BotSpellTypes::FastHeals:
|
||||
case BotSpellTypes::PetFastHeals:
|
||||
return 40;
|
||||
case BotSpellTypes::GroupHeals:
|
||||
case BotSpellTypes::RegularHeal:
|
||||
case BotSpellTypes::PetRegularHeals:
|
||||
return 60;
|
||||
case BotSpellTypes::CompleteHeal:
|
||||
case BotSpellTypes::GroupCompleteHeals:
|
||||
case BotSpellTypes::PetCompleteHeals:
|
||||
if (client_class == Class::Necromancer || client_class == Class::Shaman) {
|
||||
return 55;
|
||||
}
|
||||
else {
|
||||
return 80;
|
||||
}
|
||||
case BotSpellTypes::GroupHoTHeals:
|
||||
case BotSpellTypes::HoTHeals:
|
||||
case BotSpellTypes::PetHoTHeals:
|
||||
if (client_class == Class::Necromancer || client_class == Class::Shaman) {
|
||||
return 70;
|
||||
}
|
||||
else {
|
||||
return 90;
|
||||
}
|
||||
case BotSpellTypes::Buff:
|
||||
case BotSpellTypes::Cure:
|
||||
case BotSpellTypes::GroupCures:
|
||||
case BotSpellTypes::PetCures:
|
||||
case BotSpellTypes::PetBuffs:
|
||||
case BotSpellTypes::PetDamageShields:
|
||||
case BotSpellTypes::PetResistBuffs:
|
||||
case BotSpellTypes::ResistBuffs:
|
||||
default:
|
||||
return 100;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -812,7 +812,7 @@ int32 Client::CalcSTR()
|
||||
|
||||
int32 Client::CalcSTA()
|
||||
{
|
||||
int32 val = m_pp.STA + itembonuses.STA + spellbonuses.STA + CalcAlcoholPhysicalEffect();;
|
||||
int32 val = m_pp.STA + itembonuses.STA + spellbonuses.STA + CalcAlcoholPhysicalEffect();
|
||||
int32 mod = aabonuses.STA;
|
||||
STA = val + mod;
|
||||
if (STA < 1) {
|
||||
@@ -827,7 +827,7 @@ int32 Client::CalcSTA()
|
||||
|
||||
int32 Client::CalcAGI()
|
||||
{
|
||||
int32 val = m_pp.AGI + itembonuses.AGI + spellbonuses.AGI - CalcAlcoholPhysicalEffect();;
|
||||
int32 val = m_pp.AGI + itembonuses.AGI + spellbonuses.AGI - CalcAlcoholPhysicalEffect();
|
||||
int32 mod = aabonuses.AGI;
|
||||
int32 str = GetSTR();
|
||||
//Encumbered penalty
|
||||
@@ -852,7 +852,7 @@ int32 Client::CalcAGI()
|
||||
|
||||
int32 Client::CalcDEX()
|
||||
{
|
||||
int32 val = m_pp.DEX + itembonuses.DEX + spellbonuses.DEX - CalcAlcoholPhysicalEffect();;
|
||||
int32 val = m_pp.DEX + itembonuses.DEX + spellbonuses.DEX - CalcAlcoholPhysicalEffect();
|
||||
int32 mod = aabonuses.DEX;
|
||||
DEX = val + mod;
|
||||
if (DEX < 1) {
|
||||
|
||||
+33
-4
@@ -416,7 +416,7 @@ void MapOpcodes()
|
||||
ConnectedOpcodes[OP_TributeUpdate] = &Client::Handle_OP_TributeUpdate;
|
||||
ConnectedOpcodes[OP_VetClaimRequest] = &Client::Handle_OP_VetClaimRequest;
|
||||
ConnectedOpcodes[OP_VoiceMacroIn] = &Client::Handle_OP_VoiceMacroIn;
|
||||
ConnectedOpcodes[OP_UpdateAura] = &Client::Handle_OP_UpdateAura;;
|
||||
ConnectedOpcodes[OP_UpdateAura] = &Client::Handle_OP_UpdateAura;
|
||||
ConnectedOpcodes[OP_WearChange] = &Client::Handle_OP_WearChange;
|
||||
ConnectedOpcodes[OP_WhoAllRequest] = &Client::Handle_OP_WhoAllRequest;
|
||||
ConnectedOpcodes[OP_WorldUnknown001] = &Client::Handle_OP_Ignore;
|
||||
@@ -670,6 +670,10 @@ void Client::CompleteConnect()
|
||||
for (int x1 = 0; x1 < EFFECT_COUNT; x1++) {
|
||||
switch (spell.effect_id[x1]) {
|
||||
case SE_Illusion: {
|
||||
if (GetIllusionBlock()) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (buffs[j1].persistant_buff) {
|
||||
Mob *caster = entity_list.GetMobID(buffs[j1].casterid);
|
||||
ApplySpellEffectIllusion(spell.id, caster, j1, spell.base_value[x1], spell.limit_value[x1], spell.max_value[x1]);
|
||||
@@ -1506,6 +1510,11 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app)
|
||||
LogError("Error loading AA points for [{}]", GetName());
|
||||
}
|
||||
|
||||
if (RuleB(Bots, Enabled)) {
|
||||
LoadDefaultBotSettings();
|
||||
database.botdb.LoadBotSettings(this);
|
||||
}
|
||||
|
||||
if (SPDAT_RECORDS > 0) {
|
||||
for (uint32 z = 0; z < EQ::spells::SPELL_GEM_COUNT; z++) {
|
||||
if (m_pp.mem_spells[z] >= (uint32)SPDAT_RECORDS)
|
||||
@@ -1602,7 +1611,6 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app)
|
||||
LFG = false;
|
||||
}
|
||||
|
||||
/* Load Bots */
|
||||
if (RuleB(Bots, Enabled)) {
|
||||
database.botdb.LoadOwnerOptions(this);
|
||||
// TODO: mod below function for loading spawned botgroups
|
||||
@@ -4303,7 +4311,13 @@ void Client::Handle_OP_Camp(const EQApplicationPacket *app)
|
||||
OnDisconnect(true);
|
||||
return;
|
||||
}
|
||||
|
||||
camp_timer.Start(29000, true);
|
||||
|
||||
if (RuleB(Bots, Enabled)) {
|
||||
bot_camp_timer.Start((RuleI(Bots, CampTimer) * 1000), true);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -11078,10 +11092,17 @@ void Client::Handle_OP_PetCommands(const EQApplicationPacket *app)
|
||||
case PET_ATTACK: {
|
||||
if (!target)
|
||||
break;
|
||||
|
||||
if (RuleB(Map, CheckForLoSCheat) && (!DoLosChecks(target) || !CheckLosCheat(target))) {
|
||||
mypet->SayString(this, NOT_LEGAL_TARGET);
|
||||
break;
|
||||
}
|
||||
|
||||
if (target->IsMezzed()) {
|
||||
MessageString(Chat::NPCQuestSay, CANNOT_WAKE, mypet->GetCleanName(), target->GetCleanName());
|
||||
break;
|
||||
}
|
||||
|
||||
if (mypet->IsFeared())
|
||||
break; //prevent pet from attacking stuff while feared
|
||||
|
||||
@@ -11136,8 +11157,15 @@ void Client::Handle_OP_PetCommands(const EQApplicationPacket *app)
|
||||
if (mypet->IsFeared())
|
||||
break; //prevent pet from attacking stuff while feared
|
||||
|
||||
if (!GetTarget())
|
||||
if (!GetTarget()) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (RuleB(Map, CheckForLoSCheat) && (!DoLosChecks(GetTarget()) || !CheckLosCheat(GetTarget()))) {
|
||||
mypet->SayString(this, NOT_LEGAL_TARGET);
|
||||
break;
|
||||
}
|
||||
|
||||
if (GetTarget()->IsMezzed()) {
|
||||
MessageString(Chat::NPCQuestSay, CANNOT_WAKE, mypet->GetCleanName(), GetTarget()->GetCleanName());
|
||||
break;
|
||||
@@ -14759,6 +14787,7 @@ void Client::Handle_OP_SpawnAppearance(const EQApplicationPacket *app)
|
||||
SetFeigned(false);
|
||||
BindWound(this, false, true);
|
||||
camp_timer.Disable();
|
||||
bot_camp_timer.Disable();
|
||||
}
|
||||
else if (sa->parameter == Animation::Sitting) {
|
||||
SetAppearance(eaSitting);
|
||||
@@ -15216,7 +15245,7 @@ void Client::Handle_OP_TargetMouse(const EQApplicationPacket *app)
|
||||
GetTarget()->IsTargeted(1);
|
||||
return;
|
||||
}
|
||||
else if (GetTarget()->IsPetOwnerClient())
|
||||
else if (GetTarget()->IsPetOwnerOfClientBot())
|
||||
{
|
||||
GetTarget()->IsTargeted(1);
|
||||
return;
|
||||
|
||||
@@ -193,6 +193,12 @@ bool Client::Process() {
|
||||
return false; //delete client
|
||||
}
|
||||
|
||||
if (RuleB(Bots, Enabled)) {
|
||||
if (bot_camp_timer.Check()) {
|
||||
CampAllBots();
|
||||
}
|
||||
}
|
||||
|
||||
if (camp_timer.Check()) {
|
||||
Raid *myraid = entity_list.GetRaidByClient(this);
|
||||
if (myraid) {
|
||||
|
||||
@@ -148,6 +148,7 @@ int command_init(void)
|
||||
command_add("help", "[Search Criteria] - List available commands and their description, specify partial command as argument to search", AccountStatus::Player, command_help) ||
|
||||
command_add("hotfix", "[hotfix_name] - Reloads shared memory into a hotfix, equiv to load_shared_memory followed by apply_shared_memory", AccountStatus::GMImpossible, command_hotfix) ||
|
||||
command_add("hp", "Refresh your HP bar from the server.", AccountStatus::Player, command_hp) ||
|
||||
command_add("illusionblock", "Controls whether or not illusion effects will land on you when cast by other players or bots", AccountStatus::Guide, command_illusion_block) ||
|
||||
command_add("instance", "Modify Instances", AccountStatus::GMMgmt, command_instance) ||
|
||||
command_add("interrogateinv", "use [help] argument for available options", AccountStatus::Player, command_interrogateinv) ||
|
||||
command_add("interrupt", "[Message ID] [Color] - Interrupt your casting. Arguments are optional.", AccountStatus::Guide, command_interrupt) ||
|
||||
@@ -844,6 +845,7 @@ void command_bot(Client *c, const Seperator *sep)
|
||||
#include "gm_commands/grid.cpp"
|
||||
#include "gm_commands/guild.cpp"
|
||||
#include "gm_commands/hp.cpp"
|
||||
#include "gm_commands/illusion_block.cpp"
|
||||
#include "gm_commands/instance.cpp"
|
||||
#include "gm_commands/interrogateinv.cpp"
|
||||
#include "gm_commands/interrupt.cpp"
|
||||
|
||||
@@ -102,6 +102,7 @@ void command_guild(Client *c, const Seperator *sep);
|
||||
void command_help(Client *c, const Seperator *sep);
|
||||
void command_hotfix(Client *c, const Seperator *sep);
|
||||
void command_hp(Client *c, const Seperator *sep);
|
||||
void command_illusion_block(Client* c, const Seperator* sep);
|
||||
void command_instance(Client *c, const Seperator *sep);
|
||||
void command_interrogateinv(Client *c, const Seperator *sep);
|
||||
void command_interrupt(Client *c, const Seperator *sep);
|
||||
|
||||
+1
-1
@@ -1142,7 +1142,7 @@ void EntityList::AESpell(
|
||||
continue;
|
||||
}
|
||||
|
||||
if (spells[spell_id].target_type == ST_TargetAENoPlayersPets && current_mob->IsPetOwnerClient()) {
|
||||
if (spells[spell_id].target_type == ST_TargetAENoPlayersPets && current_mob->IsPetOwnerOfClientBot()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
+43
-50
@@ -1891,13 +1891,13 @@ Bot* EntityList::GetRandomBot(const glm::vec3& location, float distance, Bot* ex
|
||||
|
||||
for (const auto& b : bot_list) {
|
||||
if (
|
||||
b != exclude_bot &&
|
||||
b.second != exclude_bot &&
|
||||
(
|
||||
is_whole_zone ||
|
||||
DistanceSquared(static_cast<glm::vec3>(b->GetPosition()), location) <= distance_squared
|
||||
)
|
||||
DistanceSquared(static_cast<glm::vec3>(b.second->GetPosition()), location) <= distance_squared
|
||||
)
|
||||
) {
|
||||
bots_in_range.push_back(b);
|
||||
bots_in_range.push_back(b.second);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2224,44 +2224,37 @@ Raid* EntityList::GetRaidByClient(Client* client)
|
||||
}
|
||||
Raid* EntityList::GetRaidByBotName(const char* name)
|
||||
{
|
||||
std::list<RaidMember> rm;
|
||||
auto GetMembersWithNames = [&rm](Raid const* r) -> std::list<RaidMember> {
|
||||
for (const auto& m : r->members) {
|
||||
if (strlen(m.member_name) > 0)
|
||||
rm.push_back(m);
|
||||
}
|
||||
return rm;
|
||||
};
|
||||
|
||||
for (const auto& r : raid_list) {
|
||||
for (const auto& m : GetMembersWithNames(r)) {
|
||||
if (strcmp(m.member_name, name) == 0) {
|
||||
for (const auto& m : r->members) {
|
||||
if (m.is_bot && strcmp(m.member_name, name) == 0) {
|
||||
return r;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Raid* EntityList::GetRaidByBot(const Bot* bot)
|
||||
Raid* EntityList::GetRaidByBot(Bot* bot)
|
||||
{
|
||||
std::list<RaidMember> rm;
|
||||
auto GetMembersWhoAreBots = [&rm](Raid* r) -> std::list<RaidMember> {
|
||||
for (auto const& m : r->members) {
|
||||
if (m.is_bot) {
|
||||
rm.push_back(m);
|
||||
}
|
||||
}
|
||||
return rm;
|
||||
};
|
||||
|
||||
for (const auto& r : raid_list) {
|
||||
for (const auto& m : GetMembersWhoAreBots(r)) {
|
||||
if (m.member->CastToBot() == bot) {
|
||||
return r;
|
||||
}
|
||||
}
|
||||
if (bot->p_raid_instance) {
|
||||
return bot->p_raid_instance;
|
||||
}
|
||||
|
||||
std::list<Raid*>::iterator iterator;
|
||||
iterator = raid_list.begin();
|
||||
|
||||
while (iterator != raid_list.end()) {
|
||||
for (const auto& member : (*iterator)->members) {
|
||||
if (member.member && member.is_bot && member.member->CastToBot() == bot) {
|
||||
bot->p_raid_instance = *iterator;
|
||||
return *iterator;
|
||||
}
|
||||
}
|
||||
|
||||
++iterator;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -2951,6 +2944,7 @@ void EntityList::ScanCloseMobs(Mob *scanning_mob)
|
||||
|
||||
for (auto &e : mob_list) {
|
||||
auto mob = e.second;
|
||||
|
||||
if (mob->GetID() <= 0) {
|
||||
continue;
|
||||
}
|
||||
@@ -5136,9 +5130,10 @@ void EntityList::GetClientList(std::list<Client *> &c_list)
|
||||
void EntityList::GetBotList(std::list<Bot *> &b_list)
|
||||
{
|
||||
b_list.clear();
|
||||
|
||||
for (const auto& b : bot_list) {
|
||||
b_list.push_back(b);
|
||||
auto it = bot_list.begin();
|
||||
while (it != bot_list.end()) {
|
||||
b_list.push_back(it->second);
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5150,14 +5145,13 @@ std::vector<Bot *> EntityList::GetBotListByCharacterID(uint32 character_id, uint
|
||||
return client_bot_list;
|
||||
}
|
||||
|
||||
for (const auto& b : bot_list) {
|
||||
if (
|
||||
b->GetOwner() &&
|
||||
b->GetBotOwnerCharacterID() == character_id &&
|
||||
(!class_id || b->GetClass() == class_id)
|
||||
) {
|
||||
client_bot_list.push_back(b);
|
||||
auto it = bot_list.begin();
|
||||
|
||||
while (it != bot_list.end()) {
|
||||
if (it->second->GetOwner() && it->second->GetBotOwnerCharacterID() == character_id && (!class_id || it->second->GetClass() == class_id)) {
|
||||
client_bot_list.push_back(it->second);
|
||||
}
|
||||
++it;
|
||||
}
|
||||
|
||||
return client_bot_list;
|
||||
@@ -5171,14 +5165,13 @@ std::vector<Bot *> EntityList::GetBotListByClientName(std::string client_name, u
|
||||
return client_bot_list;
|
||||
}
|
||||
|
||||
for (const auto& b : bot_list) {
|
||||
if (
|
||||
b->GetOwner() &&
|
||||
Strings::ToLower(b->GetOwner()->GetCleanName()) == Strings::ToLower(client_name) &&
|
||||
(!class_id || b->GetClass() == class_id)
|
||||
) {
|
||||
client_bot_list.push_back(b);
|
||||
auto it = bot_list.begin();
|
||||
|
||||
while (it != bot_list.end()) {
|
||||
if (it->second->GetOwner() && Strings::ToLower(it->second->GetOwner()->GetCleanName()) == Strings::ToLower(client_name) && (!class_id || it->second->GetClass() == class_id)) {
|
||||
client_bot_list.push_back(it->second);
|
||||
}
|
||||
++it;
|
||||
}
|
||||
|
||||
return client_bot_list;
|
||||
@@ -5550,7 +5543,7 @@ Mob *EntityList::GetClosestMobByBodyType(Mob *sender, uint8 BodyType, bool skip_
|
||||
continue;
|
||||
|
||||
// Do not detect client pets
|
||||
if (skip_client_pets && CurrentMob->IsPet() && CurrentMob->IsPetOwnerClient())
|
||||
if (skip_client_pets && CurrentMob->IsPet() && CurrentMob->IsPetOwnerOfClientBot())
|
||||
continue;
|
||||
|
||||
CurrentDistance = ((CurrentMob->GetY() - sender->GetY()) * (CurrentMob->GetY() - sender->GetY())) +
|
||||
|
||||
+5
-6
@@ -200,7 +200,7 @@ public:
|
||||
Raid *GetRaidByClient(Client* client);
|
||||
Raid *GetRaidByID(uint32 id);
|
||||
Raid* GetRaidByBotName(const char* name);
|
||||
Raid* GetRaidByBot(const Bot* bot);
|
||||
Raid* GetRaidByBot(Bot* bot);
|
||||
Raid* GetRaidByName(const char* name);
|
||||
|
||||
Corpse *GetCorpseByOwner(Client* client);
|
||||
@@ -547,7 +547,7 @@ public:
|
||||
inline const std::unordered_map<uint16, NPC *> &GetNPCList() { return npc_list; }
|
||||
inline const std::unordered_map<uint16, Merc *> &GetMercList() { return merc_list; }
|
||||
inline const std::unordered_map<uint16, Client *> &GetClientList() { return client_list; }
|
||||
inline const std::list<Bot *> &GetBotList() { return bot_list; }
|
||||
inline const std::unordered_map<uint16, Bot*> &GetBotList() { return bot_list; }
|
||||
std::vector<Bot *> GetBotListByCharacterID(uint32 character_id, uint8 class_id = Class::None);
|
||||
std::vector<Bot *> GetBotListByClientName(std::string client_name, uint8 class_id = Class::None);
|
||||
void SignalAllBotsByOwnerCharacterID(uint32 character_id, int signal_id);
|
||||
@@ -623,17 +623,16 @@ private:
|
||||
bool RemoveBot(uint16 entityID);
|
||||
Mob* GetMobByBotID(uint32 botID);
|
||||
Bot* GetBotByBotID(uint32 botID);
|
||||
Bot* GetBotByBotName(std::string_view botName);
|
||||
Bot* GetBotByBotName(std::string botName);
|
||||
Client* GetBotOwnerByBotEntityID(uint32 entity_id);
|
||||
Client* GetBotOwnerByBotID(const uint32 bot_id);
|
||||
std::list<Bot*> GetBotsByBotOwnerCharacterID(uint32 botOwnerCharacterID);
|
||||
std::vector<Bot*> GetBotsByBotOwnerCharacterID(uint32 botOwnerCharacterID);
|
||||
|
||||
bool Bot_AICheckCloseBeneficialSpells(Bot* caster, uint8 iChance, float iRange, uint32 iSpellTypes); // TODO: Evaluate this closesly in hopes to eliminate
|
||||
void ShowSpawnWindow(Client* client, int Distance, bool NamedOnly); // TODO: Implement ShowSpawnWindow in the bot class but it needs entity list stuff
|
||||
|
||||
void GetBotList(std::list<Bot*> &b_list);
|
||||
private:
|
||||
std::list<Bot*> bot_list;
|
||||
std::unordered_map<uint16, Bot*> bot_list;
|
||||
};
|
||||
|
||||
class BulkZoneSpawnPacket {
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
#include "../client.h"
|
||||
|
||||
void command_illusion_block(Client* c, const Seperator* sep)
|
||||
{
|
||||
int arguments = sep->argnum;
|
||||
if (!arguments || !strcasecmp(sep->arg[1], "help")) {
|
||||
BotCommandHelpParams p;
|
||||
|
||||
p.description = { "Toggles whether or not you will block the illusion effects of spells cast by players or bots." };
|
||||
p.example_format = { fmt::format("{} [value]", sep->arg[0]) };
|
||||
p.examples_one =
|
||||
{
|
||||
"To enable illusion block:",
|
||||
fmt::format(
|
||||
"{} 1",
|
||||
sep->arg[0]
|
||||
)
|
||||
};
|
||||
p.examples_two =
|
||||
{
|
||||
"To disable illusion block:",
|
||||
fmt::format(
|
||||
"{} 0",
|
||||
sep->arg[0]
|
||||
)
|
||||
};
|
||||
|
||||
std::string popup_text = c->SendBotCommandHelpWindow(p);
|
||||
popup_text = DialogueWindow::Table(popup_text);
|
||||
|
||||
c->SendPopupToClient(sep->arg[0], popup_text.c_str());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (sep->IsNumber(1)) {
|
||||
int set_status = atoi(sep->arg[1]);
|
||||
if (set_status == 0 || set_status == 1) {
|
||||
c->SetIllusionBlock(set_status);
|
||||
c->Message(Chat::Green, "Your Illusion Block has been %s.", (set_status ? "enabled" : "disabled"));
|
||||
}
|
||||
else {
|
||||
c->Message(Chat::Yellow, "You must enter 0 for disabled or 1 for enabled.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (!strcasecmp(sep->arg[1], "current")) {
|
||||
c->Message(Chat::Green, "You're currently %s illusions.", (c->GetIllusionBlock() ? "blocking" : "allowing"));
|
||||
}
|
||||
else {
|
||||
c->Message(Chat::Yellow , "Incorrect argument, use %s help for a list of options.", sep->arg[0]);
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user