/* * Copyright (C) 2016+ AzerothCore , released under GNU GPL v2 license, you may redistribute it * and/or modify it under version 2 of the License, or (at your option), any later version. */ #include "CastCustomSpellAction.h" #include "ChatHelper.h" #include "Event.h" #include "ItemUsageValue.h" #include "Playerbots.h" #include "ServerFacade.h" size_t FindLastSeparator(std::string const text, std::string const sep) { size_t pos = text.rfind(sep); if (pos == std::string::npos) return pos; size_t lastLinkBegin = text.rfind("|H"); size_t lastLinkEnd = text.find("|h|r", lastLinkBegin + 1); if (pos >= lastLinkBegin && pos <= lastLinkEnd) pos = text.find_last_of(sep, lastLinkBegin); return pos; } static inline void ltrim(std::string& s) { s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](unsigned char ch) { return !std::isspace(ch); })); } bool CastCustomSpellAction::Execute(Event event) { // only allow proper vehicle seats if (botAI->IsInVehicle() && !botAI->IsInVehicle(false, false, true)) return false; Player* master = GetMaster(); Unit* target = nullptr; std::string text = event.getParam(); GuidVector gos = chat->parseGameobjects(text); if (!gos.empty()) { for (auto go : gos) { if (!target) target = botAI->GetUnit(go); chat->eraseAllSubStr(text, chat->FormatWorldobject(botAI->GetUnit(go))); } ltrim(text); } if (!target) if (master && master->GetTarget()) target = botAI->GetUnit(master->GetTarget()); if (!target) target = bot; if (!master) // Use self as master for permissions. master = bot; Item* itemTarget = nullptr; size_t pos = FindLastSeparator(text, " "); uint32 castCount = 1; if (pos != std::string::npos) { std::string const param = text.substr(pos + 1); std::vector items = InventoryAction::parseItems(param, ITERATE_ITEMS_IN_BAGS); if (!items.empty()) itemTarget = *items.begin(); else { if (atoi(param.c_str()) > 0) text = text.substr(0, pos); } } uint32 spell = AI_VALUE2(uint32, "spell id", text); std::ostringstream msg; if (!spell) { msg << "Unknown spell " << text; botAI->TellError(msg.str()); return false; } SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spell); if (!spellInfo) { msg << "Unknown spell " << text; botAI->TellError(msg.str()); return false; } if (target != bot && !bot->HasInArc(CAST_ANGLE_IN_FRONT, target, sPlayerbotAIConfig->sightDistance)) { sServerFacade->SetFacingTo(bot, target); botAI->SetNextCheckDelay(sPlayerbotAIConfig->reactDelay); msg << "cast " << text; botAI->HandleCommand(CHAT_MSG_WHISPER, msg.str(), master); return true; } std::ostringstream spellName; spellName << ChatHelper::FormatSpell(spellInfo) << " on "; if (bot->GetTrader()) spellName << "trade item"; else if (itemTarget) spellName << chat->FormatItem(itemTarget->GetTemplate()); else if (target == bot) spellName << "self"; else spellName << target->GetName(); if (!bot->GetTrader() && !botAI->CanCastSpell(spell, target, true, itemTarget)) { msg << "Cannot cast " << spellName.str(); botAI->TellError(msg.str()); return false; } bool result = spell ? botAI->CastSpell(spell, target, itemTarget) : botAI->CastSpell(text, target, itemTarget); if (result) { msg << "Casting " << spellName.str(); if (castCount > 1) { std::ostringstream cmd; cmd << castString(target) << " " << text << " " << (castCount - 1); botAI->HandleCommand(CHAT_MSG_WHISPER, cmd.str(), master); msg << "|cffffff00(x" << (castCount - 1) << " left)|r"; } botAI->TellMasterNoFacing(msg.str()); } else { msg << "Cast " << spellName.str() << " is failed"; botAI->TellError(msg.str()); } return result; } bool CastCustomNcSpellAction::isUseful() { return !bot->IsInCombat(); } std::string const CastCustomNcSpellAction::castString(WorldObject* target) { return "castnc " + chat->FormatWorldobject(target); } bool CastRandomSpellAction::AcceptSpell(SpellInfo const* spellInfo) { bool isTradeSkill = spellInfo->Effects[EFFECT_0].Effect == SPELL_EFFECT_CREATE_ITEM && spellInfo->ReagentCount[EFFECT_0] > 0 && spellInfo->SchoolMask == 1; return !isTradeSkill && spellInfo->GetRecoveryTime() < MINUTE * IN_MILLISECONDS; } bool CastRandomSpellAction::Execute(Event event) { std::vector> spellMap = GetSpellList(); Player* master = GetMaster(); Unit* target = nullptr; GameObject* got = nullptr; std::string name = event.getParam(); if (name.empty()) name = getName(); GuidVector wos = chat->parseGameobjects(name); for (auto wo : wos) { target = botAI->GetUnit(wo); got = botAI->GetGameObject(wo); if (got || target) break; } if (!got && !target && bot->GetTarget()) { target = botAI->GetUnit(bot->GetTarget()); got = botAI->GetGameObject(bot->GetTarget()); } if (!got && !target) if (master && master->GetTarget()) target = botAI->GetUnit(master->GetTarget()); if (!got && !target) target = bot; std::vector>> spellList; for (auto& spell : spellMap) { uint32 spellId = spell.first; SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId); if (!spellInfo) continue; if (!AcceptSpell(spellInfo)) continue; if (bot->HasSpell(spellId)) { uint32 spellPriority = GetSpellPriority(spellInfo); if (target && botAI->CanCastSpell(spellId, target, true)) spellList.push_back(std::make_pair(spellId, std::make_pair(spellPriority, target))); if (got && botAI->CanCastSpell(spellId, got->GetPositionX(), got->GetPositionY(), got->GetPositionZ(), true)) spellList.push_back(std::make_pair(spellId, std::make_pair(spellPriority, got))); if (botAI->CanCastSpell(spellId, bot, true)) spellList.push_back(std::make_pair(spellId, std::make_pair(spellPriority, bot))); } } if (spellList.empty()) return false; bool isCast = false; std::sort(spellList.begin(), spellList.end(), [](std::pair> i, std::pair> j) { return i.first > j.first; }); uint32 rndBound = spellList.size() / 4; rndBound = std::min(rndBound, (uint32)10); rndBound = std::max(rndBound, (uint32)0); for (uint32 i = 0; i < 5; i++) { uint32 rnd = urand(0, rndBound); auto spell = spellList[rnd]; uint32 spellId = spell.first; WorldObject* wo = spell.second.second; bool isCast = castSpell(spellId, wo); if (isCast) { if (MultiCast && ((wo && bot->HasInArc(CAST_ANGLE_IN_FRONT, wo, sPlayerbotAIConfig->sightDistance)))) { std::ostringstream cmd; cmd << "castnc " << chat->FormatWorldobject(wo) + " " << spellId << " " << 19; botAI->HandleCommand(CHAT_MSG_WHISPER, cmd.str(), bot); } return true; } } return false; } bool CraftRandomItemAction::AcceptSpell(SpellInfo const* spellInfo) { return spellInfo->Effects[EFFECT_0].Effect == SPELL_EFFECT_CREATE_ITEM && spellInfo->ReagentCount[EFFECT_0] > 0 && spellInfo->SchoolMask == 0; } uint32 CraftRandomItemAction::GetSpellPriority(SpellInfo const* spellInfo) { if (spellInfo->Effects[EFFECT_0].Effect != SPELL_EFFECT_CREATE_ITEM) { uint32 newItemId = spellInfo->Effects[EFFECT_0].ItemType; if (newItemId) { ItemUsage usage = AI_VALUE2(ItemUsage, "item usage", newItemId); if (usage == ITEM_USAGE_REPLACE || usage == ITEM_USAGE_EQUIP || usage == ITEM_USAGE_AMMO || usage == ITEM_USAGE_QUEST || usage == ITEM_USAGE_SKILL || usage == ITEM_USAGE_USE) return 10; } if (ItemUsageValue::SpellGivesSkillUp(spellInfo->Id, bot)) return 8; } return 1; } bool CastRandomSpellAction::castSpell(uint32 spellId, WorldObject* wo) { if (wo->GetGUID().IsUnit()) return botAI->CastSpell(spellId, (Unit*)(wo)); else return botAI->CastSpell(spellId, wo->GetPositionX(), wo->GetPositionY(), wo->GetPositionZ()); } bool DisEnchantRandomItemAction::Execute(Event event) { std::vector items = AI_VALUE2(std::vector, "inventory items", "usage " + std::to_string(ITEM_USAGE_DISENCHANT)); std::reverse(items.begin(), items.end()); for (auto& item : items) { // don't touch rare+ items if with real player/guild if ((botAI->HasRealPlayerMaster() || botAI->IsInRealGuild()) && item->GetTemplate()->Quality > ITEM_QUALITY_UNCOMMON) return false; if (CastCustomSpellAction::Execute( Event("disenchant random item", "13262 " + chat->FormatQItem(item->GetEntry())))) return true; } return false; } bool DisEnchantRandomItemAction::isUseful() { return botAI->HasSkill(SKILL_ENCHANTING) && !bot->IsInCombat() && AI_VALUE2(uint32, "item count", "usage " + std::to_string(ITEM_USAGE_DISENCHANT)) > 0; } bool EnchantRandomItemAction::isUseful() { return botAI->HasSkill(SKILL_ENCHANTING) && !bot->IsInCombat(); } bool EnchantRandomItemAction::AcceptSpell(SpellInfo const* spellInfo) { return spellInfo->Effects[EFFECT_0].Effect == SPELL_EFFECT_ENCHANT_ITEM && spellInfo->ReagentCount[EFFECT_0] > 0; } uint32 EnchantRandomItemAction::GetSpellPriority(SpellInfo const* spellInfo) { if (spellInfo->Effects[EFFECT_0].Effect == SPELL_EFFECT_ENCHANT_ITEM) { if (AI_VALUE2(Item*, "item for spell", spellInfo->Id) && ItemUsageValue::SpellGivesSkillUp(spellInfo->Id, bot)) return 10; } return 1; }