From 52de27a6fa3be3935b80959616f2d3a3c202c5d6 Mon Sep 17 00:00:00 2001 From: mostlikely4r Date: Fri, 6 Sep 2024 20:47:00 +0200 Subject: [PATCH] -Rpg enchant: Enchanter bots with rpg player strategy will now enchant items of nearby bots. --- playerbot/strategy/actions/ActionContext.h | 3 + playerbot/strategy/actions/RpgSubActions.cpp | 90 +++++++++++++++++++ playerbot/strategy/actions/RpgSubActions.h | 8 ++ .../strategy/actions/TradeStatusAction.cpp | 3 + playerbot/strategy/generic/RpgStrategy.cpp | 4 + playerbot/strategy/triggers/RpgTriggers.cpp | 37 +++++++- playerbot/strategy/triggers/RpgTriggers.h | 9 +- playerbot/strategy/triggers/TriggerContext.h | 2 + playerbot/strategy/values/CraftValues.cpp | 28 ++++++ playerbot/strategy/values/CraftValues.h | 7 ++ playerbot/strategy/values/TradeValues.cpp | 68 ++++++++++++++ playerbot/strategy/values/TradeValues.h | 7 ++ playerbot/strategy/values/ValueContext.h | 2 + 13 files changed, 266 insertions(+), 2 deletions(-) diff --git a/playerbot/strategy/actions/ActionContext.h b/playerbot/strategy/actions/ActionContext.h index c2ada0f9..85b84d9b 100644 --- a/playerbot/strategy/actions/ActionContext.h +++ b/playerbot/strategy/actions/ActionContext.h @@ -262,6 +262,7 @@ namespace ai creators["rpg spell"] = &ActionContext::rpg_spell; creators["rpg craft"] = &ActionContext::rpg_craft; creators["rpg trade useful"] = &ActionContext::rpg_trade_useful; + creators["rpg enchant"] = &ActionContext::rpg_enchant; creators["rpg duel"] = &ActionContext::rpg_duel; creators["rpg item"] = &ActionContext::rpg_item; @@ -538,6 +539,8 @@ namespace ai static Action* rpg_spell(PlayerbotAI* ai) { return new RpgSpellAction(ai); } static Action* rpg_craft(PlayerbotAI* ai) { return new RpgCraftAction(ai); } static Action* rpg_trade_useful(PlayerbotAI* ai) { return new RpgTradeUsefulAction(ai); } + static Action* rpg_enchant(PlayerbotAI* ai) { return new RpgEnchantAction(ai); } + static Action* rpg_duel(PlayerbotAI* ai) { return new RpgDuelAction(ai); } static Action* rpg_item(PlayerbotAI* ai) { return new RpgItemAction(ai); } diff --git a/playerbot/strategy/actions/RpgSubActions.cpp b/playerbot/strategy/actions/RpgSubActions.cpp index 80384329..9fb27e56 100644 --- a/playerbot/strategy/actions/RpgSubActions.cpp +++ b/playerbot/strategy/actions/RpgSubActions.cpp @@ -329,6 +329,96 @@ bool RpgTradeUsefulAction::Execute(Event& event) return isTrading; } +bool RpgEnchantAction::Execute(Event& event) +{ + rpg->BeforeExecute(); + + GuidPosition guidP = AI_VALUE(GuidPosition, "rpg target"); + + Player* player = guidP.GetPlayer(); + + if (!player) + return false; + + std::list items = AI_VALUE(std::list, "items useful to enchant"); + + if (items.empty()) + return false; + + std::vector enchantSpells = AI_VALUE(std::vector, "enchant spells"); + + //Needs more logic to pick best enchant to apply instead of a random one that's bette than current. + std::shuffle(enchantSpells.begin(), enchantSpells.end(), *GetRandomGenerator()); + + for (auto& spellId : enchantSpells) + { + Item* item = PAI_VALUE2(Item*, "item for spell", spellId); + + if (std::find(items.begin(), items.end(), item) == items.end()) + continue; + + std::ostringstream param; + + param << chat->formatWorldobject(bot); + param << " "; + param << chat->formatItem(item); + + ai->TellDebug(ai->GetMaster(), "enchanting" + param.str(), "debug rpg"); + + if (player->isRealPlayer() && !player->GetTradeData()) //Start the trade from the other side to open the window + { + ai->TellDebug(ai->GetMaster(), "open trade window", "debug rpg"); + WorldPacket packet(CMSG_INITIATE_TRADE); + packet << player->GetObjectGuid(); + bot->GetSession()->HandleInitiateTradeOpcode(packet); + } + + if (!player->GetTradeData() || !player->GetTradeData()->HasItem(item->GetObjectGuid())) + { + ai->TellDebug(ai->GetMaster(), "starting trade", "debug rpg"); + player->GetPlayerbotAI()->DoSpecificAction("trade", Event("rpg action", param.str().c_str()), true); + } + + bool isTrading = bot->GetTradeData(); + + if (isTrading) + { + if (player->GetTradeData()->HasItem(item->GetObjectGuid())) //Did we manage to add the item to the trade? + { + uint32 duration; + Unit* target = nullptr; + + ai->TellDebug(ai->GetMaster(), "set enchant spell", "debug rpg"); + bool didCast = ai->CastSpell(spellId, target, item, true, &duration); + + if (didCast) + { + ai->TellDebug(ai->GetMaster(), "accept trade", "debug rpg"); + if (bot->GetGroup() && bot->GetGroup()->IsMember(guidP)) + ai->TellPlayerNoFacing(GetMaster(), "Let me enchant this " + chat->formatItem(item) + " with " + chat->formatSpell(spellId) + " for you " + player->GetName() + ".", PlayerbotSecurityLevel::PLAYERBOT_SECURITY_ALLOW_ALL, false); + else + bot->Say("Let me enchant this " + chat->formatItem(item) + " with " + chat->formatSpell(spellId) + " for you " + player->GetName() + ".", (bot->GetTeam() == ALLIANCE ? LANG_COMMON : LANG_ORCISH)); + + WorldPacket p; + uint32 status = TRADE_STATUS_TRADE_ACCEPT; + p << status; + bot->GetSession()->HandleAcceptTradeOpcode(p); + } + } + + ai->SetActionDuration(sPlayerbotAIConfig.rpgDelay); + } + + rpg->AfterExecute(isTrading, true, isTrading ? "rpg enchant" : "rpg"); + + DoDelay(); + + return isTrading; + } + + return false; +} + bool RpgDuelAction::isUseful() { // do not offer duel in non pvp areas diff --git a/playerbot/strategy/actions/RpgSubActions.h b/playerbot/strategy/actions/RpgSubActions.h index cb27c088..bd1535fc 100644 --- a/playerbot/strategy/actions/RpgSubActions.h +++ b/playerbot/strategy/actions/RpgSubActions.h @@ -281,6 +281,14 @@ namespace ai virtual bool Execute(Event& event); }; + class RpgEnchantAction : public RpgTradeUsefulAction + { + public: + RpgEnchantAction(PlayerbotAI* ai, std::string name = "rpg enchant") : RpgTradeUsefulAction(ai, name) {} + + virtual bool Execute(Event& event); + }; + class RpgDuelAction : public RpgSubAction { public: diff --git a/playerbot/strategy/actions/TradeStatusAction.cpp b/playerbot/strategy/actions/TradeStatusAction.cpp index 3b235656..af7fc8ab 100644 --- a/playerbot/strategy/actions/TradeStatusAction.cpp +++ b/playerbot/strategy/actions/TradeStatusAction.cpp @@ -169,6 +169,9 @@ bool TradeStatusAction::CheckTrade() break; } } + + if (!isGettingItem) //Enchanting an item. + isGettingItem = (bot->GetTradeData()->GetItem(TRADE_SLOT_NONTRADED) && trader->GetTradeData()->GetSpell()); if (isGettingItem) { diff --git a/playerbot/strategy/generic/RpgStrategy.cpp b/playerbot/strategy/generic/RpgStrategy.cpp index 4ddbc0f3..26a9c84e 100644 --- a/playerbot/strategy/generic/RpgStrategy.cpp +++ b/playerbot/strategy/generic/RpgStrategy.cpp @@ -171,6 +171,10 @@ void RpgPlayerStrategy::InitNonCombatTriggers(std::list& triggers) "rpg trade useful", NextAction::array(0, new NextAction("rpg trade useful", 1.030f), NULL))); + triggers.push_back(new TriggerNode( + "rpg enchant", + NextAction::array(0, new NextAction("rpg enchant", 1.029f), NULL))); + triggers.push_back(new TriggerNode( "rpg duel", NextAction::array(0, new NextAction("rpg duel", 1.010f), NULL))); diff --git a/playerbot/strategy/triggers/RpgTriggers.cpp b/playerbot/strategy/triggers/RpgTriggers.cpp index 4f8d6d21..7084478d 100644 --- a/playerbot/strategy/triggers/RpgTriggers.cpp +++ b/playerbot/strategy/triggers/RpgTriggers.cpp @@ -609,7 +609,42 @@ bool RpgTradeUsefulTrigger::IsActive() if (bot->GetTrader() && bot->GetTrader() != player) return false; - if (AI_VALUE_LAZY(std::list, "items useful to give").empty()) + if (AI_VALUE(std::list, "items useful to give").empty()) + return false; + + return true; +} + +bool RpgEnchantTrigger::IsActive() +{ + GuidPosition guidP(getGuidP()); + + if (!guidP.IsPlayer()) + return false; + + Player* player = guidP.GetPlayer(); + + + if (!player) + return false; + + //if (player->GetTrader() == bot && bot->GetTrader() == player) //Continue trading please. + // return true; + + if (!isFriend(player)) + return false; + + if (!player->IsWithinLOSInMap(bot)) + return false; + + //Trading with someone else + //if (player->GetTrader() && player->GetTrader() != bot) + // return false; + + //if (bot->GetTrader() && bot->GetTrader() != player) + // return false; + + if (AI_VALUE(std::list, "items useful to enchant").empty()) return false; return true; diff --git a/playerbot/strategy/triggers/RpgTriggers.h b/playerbot/strategy/triggers/RpgTriggers.h index 860b6f02..3d9a661f 100644 --- a/playerbot/strategy/triggers/RpgTriggers.h +++ b/playerbot/strategy/triggers/RpgTriggers.h @@ -203,10 +203,17 @@ namespace ai public: RpgTradeUsefulTrigger(PlayerbotAI* ai, std::string name = "rpg trade useful") : RpgTrigger(ai, name) {} virtual bool IsActive(); - private: + protected: virtual bool isFriend(Player* player); //Move to value later. }; + class RpgEnchantTrigger : public RpgTradeUsefulTrigger + { + public: + RpgEnchantTrigger(PlayerbotAI* ai, std::string name = "rpg enchant") : RpgTradeUsefulTrigger(ai, name) {} + virtual bool IsActive(); + }; + class RpgDuelTrigger : public RpgTrigger { public: diff --git a/playerbot/strategy/triggers/TriggerContext.h b/playerbot/strategy/triggers/TriggerContext.h index fb7c2c84..263c732a 100644 --- a/playerbot/strategy/triggers/TriggerContext.h +++ b/playerbot/strategy/triggers/TriggerContext.h @@ -238,6 +238,7 @@ namespace ai creators["rpg spell"] = &TriggerContext::rpg_spell; creators["rpg craft"] = &TriggerContext::rpg_craft; creators["rpg trade useful"] = &TriggerContext::rpg_trade_useful; + creators["rpg enchant"] = &TriggerContext::rpg_enchant; creators["rpg duel"] = &TriggerContext::rpg_duel; creators["rpg item"] = &TriggerContext::rpg_item; @@ -494,6 +495,7 @@ namespace ai static Trigger* rpg_spell(PlayerbotAI* ai) { return new RpgSpellTrigger(ai); } static Trigger* rpg_craft(PlayerbotAI* ai) { return new RpgCraftTrigger(ai); } static Trigger* rpg_trade_useful(PlayerbotAI* ai) { return new RpgTradeUsefulTrigger(ai); } + static Trigger* rpg_enchant(PlayerbotAI* ai) { return new RpgEnchantTrigger(ai); } static Trigger* rpg_duel(PlayerbotAI* ai) { return new RpgDuelTrigger(ai); } static Trigger* rpg_item(PlayerbotAI* ai) { return new RpgItemTrigger(ai); } diff --git a/playerbot/strategy/values/CraftValues.cpp b/playerbot/strategy/values/CraftValues.cpp index eeaeb988..c30bf390 100644 --- a/playerbot/strategy/values/CraftValues.cpp +++ b/playerbot/strategy/values/CraftValues.cpp @@ -38,6 +38,34 @@ std::vector CraftSpellsValue::Calculate() return spellIds; } +std::vector EnchantSpellsValue::Calculate() +{ + std::vector spellIds; + + PlayerSpellMap const& spellMap = bot->GetSpellMap(); + + for (auto& spell : spellMap) + { + uint32 spellId = spell.first; + + if (spell.second.state == PLAYERSPELL_REMOVED || spell.second.disabled || IsPassiveSpell(spellId)) + continue; + + const SpellEntry* pSpellInfo = sServerFacade.LookupSpellInfo(spellId); + if (!pSpellInfo) + continue; + + if (pSpellInfo->Effect[0] != SPELL_EFFECT_ENCHANT_ITEM || !pSpellInfo->ReagentCount[0]) + continue; + + + spellIds.push_back(spellId); + break; + } + + return spellIds; +} + uint32 HasReagentsForValue::Calculate() { if (ai->HasCheat(BotCheatMask::item)) diff --git a/playerbot/strategy/values/CraftValues.h b/playerbot/strategy/values/CraftValues.h index e4ae0a35..3cb0a056 100644 --- a/playerbot/strategy/values/CraftValues.h +++ b/playerbot/strategy/values/CraftValues.h @@ -72,6 +72,13 @@ namespace ai virtual std::vector Calculate() override; }; + class EnchantSpellsValue : public CalculatedValue> //All enchanting spells + { + public: + EnchantSpellsValue(PlayerbotAI* ai, std::string name = "enchant spells", int checkInterval = 10) : CalculatedValue>(ai, name, checkInterval) {} + virtual std::vector Calculate() override; + }; + class HasReagentsForValue : public Uint32CalculatedValue, public Qualified //Does the bot have reagents to cast this craft spell? { public: diff --git a/playerbot/strategy/values/TradeValues.cpp b/playerbot/strategy/values/TradeValues.cpp index 38896678..8b12469a 100644 --- a/playerbot/strategy/values/TradeValues.cpp +++ b/playerbot/strategy/values/TradeValues.cpp @@ -3,6 +3,8 @@ #include "TradeValues.h" #include "ItemUsageValue.h" #include "playerbot/TravelMgr.h" +#include "playerbot/RandomItemMgr.h" +#include "playerbot/ServerFacade.h" using namespace ai; @@ -75,3 +77,69 @@ std::list ItemsUsefulToGiveValue::Calculate() return giveItems; } + +std::list ItemsUsefulToEnchantValue::Calculate() +{ + GuidPosition guidP = AI_VALUE(GuidPosition, "rpg target"); + + Player* player = guidP.GetPlayer(); + + std::list enchantItems; + + if (ai->HasActivePlayerMaster() || !player->GetPlayerbotAI()) + return enchantItems; + + std::vector enchantSpells = AI_VALUE(std::vector, "enchant spells"); + + for (auto& spellId : enchantSpells) + { + const SpellEntry* pSpellInfo = sServerFacade.LookupSpellInfo(spellId); + + if (!pSpellInfo) + continue; + + uint32 castCount = AI_VALUE2(uint32, "has reagents for", spellId); + + if (!castCount) + continue; + + Item* item = PAI_VALUE2(Item*, "item for spell", spellId); + + if (!item) + continue; + + if (PHAS_AI_VALUE2("force item usage", item->GetProto()->ItemId)) + continue; + + // Only trade equipped items + if (!item->IsEquipped()) + continue; + + TradeData* trade = bot->GetTradeData(); + + if (trade) + { + + if (trade->HasItem(item->GetObjectGuid())) //This specific item isn't being traded. + continue; + + if (std::any_of(enchantItems.begin(), enchantItems.end(), [item](Item* i) {return i->GetEntry() == item->GetEntry(); })) //We didn't already add a simular item to this list. + continue; + } + + uint32 enchant_id = pSpellInfo->EffectMiscValue[0]; + + uint32 currentEnchnatWeight = 0; + if (item->GetEnchantmentId(PERM_ENCHANTMENT_SLOT)) + currentEnchnatWeight = sRandomItemMgr.CalculateEnchantWeight(bot->getClass(), sRandomItemMgr.GetPlayerSpecId(bot), item->GetEnchantmentId(PERM_ENCHANTMENT_SLOT)); + + uint32 newEnchantWeight = sRandomItemMgr.CalculateEnchantWeight(bot->getClass(), sRandomItemMgr.GetPlayerSpecId(bot), enchant_id); + + if (!item->GetEnchantmentId(PERM_ENCHANTMENT_SLOT) || currentEnchnatWeight >= newEnchantWeight) + continue; + + enchantItems.push_back(item); + } + + return enchantItems; +} \ No newline at end of file diff --git a/playerbot/strategy/values/TradeValues.h b/playerbot/strategy/values/TradeValues.h index d932eb59..20b9c701 100644 --- a/playerbot/strategy/values/TradeValues.h +++ b/playerbot/strategy/values/TradeValues.h @@ -12,4 +12,11 @@ namespace ai private: bool IsTradingItem(uint32 entry); }; + + class ItemsUsefulToEnchantValue : public CalculatedValue< std::list>, public Qualified + { + public: + ItemsUsefulToEnchantValue(PlayerbotAI* ai, std::string name = "useful to enchant") : CalculatedValue(ai, name), Qualified() {} + std::list Calculate(); + }; } \ No newline at end of file diff --git a/playerbot/strategy/values/ValueContext.h b/playerbot/strategy/values/ValueContext.h index 9632cd34..19efda3f 100644 --- a/playerbot/strategy/values/ValueContext.h +++ b/playerbot/strategy/values/ValueContext.h @@ -342,6 +342,7 @@ namespace ai creators["vendor has useful item"] = [](PlayerbotAI* ai) { return new VendorHasUsefulItemValue(ai); }; creators["craft spells"] = [](PlayerbotAI* ai) { return new CraftSpellsValue(ai); }; + creators["enchant spells"] = [](PlayerbotAI* ai) { return new EnchantSpellsValue(ai); }; creators["has reagents for"] = [](PlayerbotAI* ai) { return new HasReagentsForValue(ai); }; creators["can craft spell"] = [](PlayerbotAI* ai) { return new CanCraftSpellValue(ai); }; creators["should craft spell"] = [](PlayerbotAI* ai) { return new ShouldCraftSpellValue(ai); }; @@ -366,6 +367,7 @@ namespace ai creators["entry loot usage"] = [](PlayerbotAI* ai) { return new EntryLootUsageValue(ai); }; creators["has upgrade"] = [](PlayerbotAI* ai) { return new HasUpgradeValue(ai); }; creators["items useful to give"] = [](PlayerbotAI* ai) { return new ItemsUsefulToGiveValue(ai); }; + creators["items useful to enchant"] = [](PlayerbotAI* ai) { return new ItemsUsefulToEnchantValue(ai); }; creators["see spell location"] = [](PlayerbotAI* ai) { return new SeeSpellLocationValue(ai); }; creators["RTSC selected"] = [](PlayerbotAI* ai) { return new RTSCSelectedValue(ai); };