#include "../bot_command.h" void bot_command_cast(Client* c, const Seperator* sep) { if (helper_is_help_or_usage(sep->arg[1])) { std::vector description = { "Commands bots to force cast a specific spell type, ignoring all settings (holds, delays, thresholds, etc)" }; std::vector 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" }; std::vector example_format = { fmt::format( "{} [Type Shortname] [actionable]" , sep->arg[0] ), fmt::format( "{} [Type ID] [actionable]" , sep->arg[0] ) }; std::vector examples_one = { "To tell everyone to Nuke the target:", fmt::format( "{} {} spawned", sep->arg[0], c->GetSpellTypeShortNameByID(BotSpellTypes::Nuke) ), fmt::format( "{} {} spawned", sep->arg[0], BotSpellTypes::Nuke ) }; std::vector examples_two = { "To tell all Enchanters to slow the target:", fmt::format( "{} {} byclass {}", sep->arg[0], Class::Enchanter, c->GetSpellTypeShortNameByID(BotSpellTypes::Slow) ), fmt::format( "{} {} byclass {}", sep->arg[0], Class::Enchanter, BotSpellTypes::Slow ) }; std::vector examples_three = { "To tell Clrbot to resurrect the targeted corpse:", fmt::format( "{} {} byname Clrbot", sep->arg[0], c->GetSpellTypeShortNameByID(BotSpellTypes::Resurrect) ), fmt::format( "{} {} byname Clrbot", sep->arg[0], BotSpellTypes::Resurrect ) }; std::vector actionables = { "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets mmr, byclass, byrace, spawned" }; std::vector options = { }; std::vector options_one = { }; std::vector options_two = { }; std::vector options_three = { }; std::string popup_text = c->SendCommandHelpWindow( c, description, notes, example_format, examples_one, examples_two, examples_three, actionables, options, options_one, options_two, options_three ); popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], "", "", true); 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]; if (!arg1.compare("listid") || !arg1.compare("listname")) { c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], sep->arg[1], sep->arg[2]); return; } int ab_arg = 2; uint16 spellType = 0; // String/Int type checks if (sep->IsNumber(1)) { spellType = atoi(sep->arg[1]); if (spellType < BotSpellTypes::START || spellType > 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 (c->GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { spellType = c->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 ( spellType == BotSpellTypes::PetBuffs || spellType == BotSpellTypes::PetCompleteHeals || spellType == BotSpellTypes::PetFastHeals || spellType == BotSpellTypes::PetHoTHeals || spellType == BotSpellTypes::PetRegularHeals || spellType == BotSpellTypes::PetVeryFastHeals ) { c->Message(Chat::Yellow, "Pet type heals and buffs are not supported, use the regular spell type."); return; } Mob* tar = c->GetTarget(); LogTestDebug("{}: Attempting {} on {}", __LINE__, c->GetSpellTypeNameByID(spellType), (tar ? tar->GetCleanName() : "NOBODY")); //deleteme if (spellType != BotSpellTypes::Escape && spellType != BotSpellTypes::Pet) { if (!tar) { c->Message(Chat::Yellow, "You need a target for that."); return; } if (BOT_SPELL_TYPES_DETRIMENTAL(spellType) && !c->IsAttackAllowed(tar)) { c->Message(Chat::Yellow, "You cannot attack [%s].", tar->GetCleanName()); return; } if (BOT_SPELL_TYPES_BENEFICIAL(spellType)) { if (!tar->IsOfClientBot() && !(tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot())) { c->Message(Chat::Yellow, "[%s] is an invalid target.", tar->GetCleanName()); return; } } } LogTestDebug("{}: Attempting {} on {}", __LINE__, c->GetSpellTypeNameByID(spellType), (tar ? tar->GetCleanName() : "NOBODY")); //deleteme switch (spellType) { case BotSpellTypes::Stun: case BotSpellTypes::AEStun: if (tar->GetSpecialAbility(SpecialAbility::StunImmunity)) { c->Message(Chat::Yellow, "[%s] is immune to stuns.", tar->GetCleanName()); return; } break; case BotSpellTypes::Resurrect: if (!tar->IsCorpse() || !tar->CastToCorpse()->IsPlayerCorpse()) { c->Message(Chat::Yellow, "[%s] is an invalid target. I can only resurrect player corpses.", tar->GetCleanName()); return; } break; default: break; } 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::list 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); BotSpell botSpell; botSpell.SpellId = 0; botSpell.SpellIndex = 0; botSpell.ManaCost = 0; bool isSuccess = false; uint16 successCount = 0; Bot* firstFound = nullptr; for (auto bot_iter : sbl) { if (!bot_iter->IsInGroupOrRaid()) { continue; } /* TODO bot rewrite - FIX: Snares, Group Cures, OOC Song, Precombat, HateRedux, Fear/AE Fear ICB (SK) casting hate on friendly but not hostile? NEED TO CHECK: precombat, AE Dispel, AE Lifetap DO I NEED A PBAE CHECK??? */ if (bot_iter->GetHoldFlag() || bot_iter->GetAppearance() == eaDead || bot_iter->IsFeared() || bot_iter->IsStunned() || bot_iter->IsMezzed() || bot_iter->DivineAura() || bot_iter->GetHP() < 0) { continue; } Mob* newTar = tar; LogTestDebug("{}: Attempting {} on {}", __LINE__, c->GetSpellTypeNameByID(spellType), (newTar ? newTar->GetCleanName() : "NOBODY")); //deleteme if (!SpellTypeRequiresTarget(spellType, bot_iter->GetClass())) { newTar = bot_iter; } if (!newTar) { continue; } if (BOT_SPELL_TYPES_DETRIMENTAL(spellType, bot_iter->GetClass()) && !bot_iter->IsAttackAllowed(newTar)) { bot_iter->BotGroupSay( bot_iter, fmt::format( "I cannot attack [{}].", newTar->GetCleanName() ).c_str() ); continue; } LogTestDebug("{}: Attempting {} on {}", __LINE__, c->GetSpellTypeNameByID(spellType), (newTar ? newTar->GetCleanName() : "NOBODY")); //deleteme bot_iter->SetCommandedSpell(true); if (bot_iter->AICastSpell(newTar, 100, spellType)) { if (!firstFound) { firstFound = bot_iter; } isSuccess = true; ++successCount; } bot_iter->SetCommandedSpell(false); continue; } if (!isSuccess) { c->Message( Chat::Yellow, fmt::format( "No bots are capable of casting [{}] on {}.", c->GetSpellTypeNameByID(spellType), tar ? tar->GetCleanName() : "your target" ).c_str() ); } else { c->Message( Chat::Yellow, fmt::format( "{} {} [{}]{}", ((successCount == 1 && firstFound) ? firstFound->GetCleanName() : (fmt::format("{}", successCount).c_str())), ((successCount == 1 && firstFound) ? "casted" : "of your bots casted"), c->GetSpellTypeNameByID(spellType), tar ? (fmt::format(" on {}.", tar->GetCleanName()).c_str()) : "." ).c_str() ); } }