From c4a4d3a9e61c22d81870e15257854b5a49eb1bff Mon Sep 17 00:00:00 2001 From: avirar Date: Tue, 18 Mar 2025 01:10:33 +1100 Subject: [PATCH] Rogue bots can unlock items in their bags and in the trade window (#1055) --- src/PlayerbotAI.cpp | 118 +++++++++++++++++- src/PlayerbotAI.h | 2 + src/strategy/actions/ChatActionContext.h | 6 + src/strategy/actions/OpenItemAction.cpp | 53 +------- src/strategy/actions/OpenItemAction.h | 3 - src/strategy/actions/TradeStatusAction.cpp | 11 +- .../actions/TradeStatusExtendedAction.cpp | 84 +++++++++++++ .../actions/TradeStatusExtendedAction.h | 17 +++ src/strategy/actions/UnlockItemAction.cpp | 38 ++++++ src/strategy/actions/UnlockItemAction.h | 19 +++ .../actions/UnlockTradedItemAction.cpp | 98 +++++++++++++++ src/strategy/actions/UnlockTradedItemAction.h | 20 +++ .../actions/WorldPacketActionContext.h | 3 + .../generic/ChatCommandHandlerStrategy.cpp | 6 + .../generic/WorldPacketHandlerStrategy.cpp | 7 +- src/strategy/triggers/ChatTriggerContext.h | 4 + .../triggers/WorldPacketTriggerContext.h | 2 + src/strategy/values/ItemForSpellValue.cpp | 3 + 18 files changed, 437 insertions(+), 57 deletions(-) create mode 100644 src/strategy/actions/TradeStatusExtendedAction.cpp create mode 100644 src/strategy/actions/TradeStatusExtendedAction.h create mode 100644 src/strategy/actions/UnlockItemAction.cpp create mode 100644 src/strategy/actions/UnlockItemAction.h create mode 100644 src/strategy/actions/UnlockTradedItemAction.cpp create mode 100644 src/strategy/actions/UnlockTradedItemAction.h diff --git a/src/PlayerbotAI.cpp b/src/PlayerbotAI.cpp index 06b6971b..d6be9874 100644 --- a/src/PlayerbotAI.cpp +++ b/src/PlayerbotAI.cpp @@ -178,6 +178,7 @@ PlayerbotAI::PlayerbotAI(Player* bot) botOutgoingPacketHandlers.AddHandler(SMSG_RESURRECT_REQUEST, "resurrect request"); botOutgoingPacketHandlers.AddHandler(SMSG_INVENTORY_CHANGE_FAILURE, "cannot equip"); botOutgoingPacketHandlers.AddHandler(SMSG_TRADE_STATUS, "trade status"); + botOutgoingPacketHandlers.AddHandler(SMSG_TRADE_STATUS_EXTENDED, "trade status extended"); botOutgoingPacketHandlers.AddHandler(SMSG_LOOT_RESPONSE, "loot response"); botOutgoingPacketHandlers.AddHandler(SMSG_ITEM_PUSH_RESULT, "item push result"); botOutgoingPacketHandlers.AddHandler(SMSG_PARTY_COMMAND_RESULT, "party command"); @@ -3173,7 +3174,8 @@ bool PlayerbotAI::CastSpell(uint32 spellId, Unit* target, Item* itemTarget) Spell* spell = new Spell(bot, spellInfo, TRIGGERED_NONE); SpellCastTargets targets; - if (spellInfo->Targets & TARGET_FLAG_ITEM) + if (spellInfo->Effects[0].Effect != SPELL_EFFECT_OPEN_LOCK && + (spellInfo->Targets & TARGET_FLAG_ITEM || spellInfo->Targets & TARGET_FLAG_GAMEOBJECT_ITEM)) { Item* item = itemTarget ? itemTarget : aiObjectContext->GetValue("item for spell", spellId)->Get(); targets.SetItemTarget(item); @@ -3216,6 +3218,20 @@ bool PlayerbotAI::CastSpell(uint32 spellId, Unit* target, Item* itemTarget) targets.SetGOTarget(go); faceTo = go; } + else if (itemTarget) + { + Player* trader = bot->GetTrader(); + if (trader) + { + targets.SetTradeItemTarget(bot); + targets.SetUnitTarget(bot); + faceTo = trader; + } + else + { + targets.SetItemTarget(itemTarget); + } + } else { if (Unit* creature = GetUnit(loot.guid)) @@ -3252,6 +3268,58 @@ bool PlayerbotAI::CastSpell(uint32 spellId, Unit* target, Item* itemTarget) // LOG_DEBUG("playerbots", "Spell cast failed. - target name: {}, spellid: {}, bot name: {}, result: {}", // target->GetName(), spellId, bot->GetName(), result); // } + if (HasStrategy("debug spell", BOT_STATE_NON_COMBAT)) + { + std::ostringstream out; + out << "Spell cast failed - "; + out << "Spell ID: " << spellId << " (" << ChatHelper::FormatSpell(spellInfo) << "), "; + out << "Error Code: " << static_cast(result) << " (0x" << std::hex << static_cast(result) << std::dec << "), "; + out << "Bot: " << bot->GetName() << ", "; + + // Check spell target type + if (targets.GetUnitTarget()) + { + out << "Target: Unit (" << targets.GetUnitTarget()->GetName() + << ", Low GUID: " << targets.GetUnitTarget()->GetGUID().GetCounter() + << ", High GUID: " << static_cast(targets.GetUnitTarget()->GetGUID().GetHigh()) << "), "; + } + + if (targets.GetGOTarget()) + { + out << "Target: GameObject (Low GUID: " << targets.GetGOTarget()->GetGUID().GetCounter() + << ", High GUID: " << static_cast(targets.GetGOTarget()->GetGUID().GetHigh()) << "), "; + } + + if (targets.GetItemTarget()) + { + out << "Target: Item (Low GUID: " << targets.GetItemTarget()->GetGUID().GetCounter() + << ", High GUID: " << static_cast(targets.GetItemTarget()->GetGUID().GetHigh()) << "), "; + } + + // Check if bot is in trade mode + if (bot->GetTradeData()) + { + out << "Trade Mode: Active, "; + Item* tradeItem = bot->GetTradeData()->GetTraderData()->GetItem(TRADE_SLOT_NONTRADED); + if (tradeItem) + { + out << "Trade Item: " << tradeItem->GetEntry() + << " (Low GUID: " << tradeItem->GetGUID().GetCounter() + << ", High GUID: " << static_cast(tradeItem->GetGUID().GetHigh()) << "), "; + } + else + { + out << "Trade Item: None, "; + } + } + else + { + out << "Trade Mode: Inactive, "; + } + + TellMasterNoFacing(out); + } + return false; } // if (spellInfo->Effects[0].Effect == SPELL_EFFECT_OPEN_LOCK || spellInfo->Effects[0].Effect == @@ -3348,7 +3416,7 @@ bool PlayerbotAI::CastSpell(uint32 spellId, float x, float y, float z, Item* ite Spell* spell = new Spell(bot, spellInfo, TRIGGERED_NONE); SpellCastTargets targets; - if (spellInfo->Targets & TARGET_FLAG_ITEM) + if (spellInfo->Targets & TARGET_FLAG_ITEM || spellInfo->Targets & TARGET_FLAG_GAMEOBJECT_ITEM) { Item* item = itemTarget ? itemTarget : aiObjectContext->GetValue("item for spell", spellId)->Get(); targets.SetItemTarget(item); @@ -4904,6 +4972,52 @@ Item* PlayerbotAI::FindBandage() const { return pItemProto->Class == ITEM_CLASS_CONSUMABLE && pItemProto->SubClass == ITEM_SUBCLASS_BANDAGE; }); } +Item* PlayerbotAI::FindOpenableItem() const +{ + return FindItemInInventory([this](ItemTemplate const* itemTemplate) -> bool + { + return (itemTemplate->Flags & ITEM_FLAG_HAS_LOOT) && + (itemTemplate->LockID == 0 || !this->bot->GetItemByEntry(itemTemplate->ItemId)->IsLocked()); + }); +} + +Item* PlayerbotAI::FindLockedItem() const +{ + return FindItemInInventory([this](ItemTemplate const* itemTemplate) -> bool + { + if (!this->bot->HasSkill(SKILL_LOCKPICKING)) // Ensure bot has Lockpicking skill + return false; + + if (itemTemplate->LockID == 0) // Ensure the item is actually locked + return false; + + Item* item = this->bot->GetItemByEntry(itemTemplate->ItemId); + if (!item || !item->IsLocked()) // Ensure item instance is locked + return false; + + // Check if bot has enough Lockpicking skill + LockEntry const* lockInfo = sLockStore.LookupEntry(itemTemplate->LockID); + if (!lockInfo) + return false; + + for (uint8 j = 0; j < 8; ++j) + { + if (lockInfo->Type[j] == LOCK_KEY_SKILL) + { + uint32 skillId = SkillByLockType(LockType(lockInfo->Index[j])); + if (skillId == SKILL_LOCKPICKING) + { + uint32 requiredSkill = lockInfo->Skill[j]; + uint32 botSkill = this->bot->GetSkillValue(SKILL_LOCKPICKING); + return botSkill >= requiredSkill; + } + } + } + + return false; + }); +} + static const uint32 uPriorizedSharpStoneIds[8] = {ADAMANTITE_SHARPENING_DISPLAYID, FEL_SHARPENING_DISPLAYID, ELEMENTAL_SHARPENING_DISPLAYID, DENSE_SHARPENING_DISPLAYID, SOLID_SHARPENING_DISPLAYID, HEAVY_SHARPENING_DISPLAYID, diff --git a/src/PlayerbotAI.h b/src/PlayerbotAI.h index 9b4602cb..09a7f6fd 100644 --- a/src/PlayerbotAI.h +++ b/src/PlayerbotAI.h @@ -467,6 +467,8 @@ public: Item* FindPoison() const; Item* FindAmmo() const; Item* FindBandage() const; + Item* FindOpenableItem() const; + Item* FindLockedItem() const; Item* FindConsumable(uint32 displayId) const; Item* FindStoneFor(Item* weapon) const; Item* FindOilFor(Item* weapon) const; diff --git a/src/strategy/actions/ChatActionContext.h b/src/strategy/actions/ChatActionContext.h index 52ca826a..7b7e10b2 100644 --- a/src/strategy/actions/ChatActionContext.h +++ b/src/strategy/actions/ChatActionContext.h @@ -75,6 +75,8 @@ #include "WhoAction.h" #include "WtsAction.h" #include "OpenItemAction.h" +#include "UnlockItemAction.h" +#include "UnlockTradedItemAction.h" class ChatActionContext : public NamedObjectContext { @@ -82,6 +84,8 @@ public: ChatActionContext() { creators["open items"] = &ChatActionContext::open_items; + creators["unlock items"] = &ChatActionContext::unlock_items; + creators["unlock traded item"] = &ChatActionContext::unlock_traded_item; creators["range"] = &ChatActionContext::range; creators["stats"] = &ChatActionContext::stats; creators["quests"] = &ChatActionContext::quests; @@ -184,6 +188,8 @@ public: private: static Action* open_items(PlayerbotAI* botAI) { return new OpenItemAction(botAI); } + static Action* unlock_items(PlayerbotAI* botAI) { return new UnlockItemAction(botAI); } + static Action* unlock_traded_item(PlayerbotAI* botAI) { return new UnlockTradedItemAction(botAI); } static Action* range(PlayerbotAI* botAI) { return new RangeAction(botAI); } static Action* flag(PlayerbotAI* botAI) { return new FlagAction(botAI); } static Action* craft(PlayerbotAI* botAI) { return new SetCraftAction(botAI); } diff --git a/src/strategy/actions/OpenItemAction.cpp b/src/strategy/actions/OpenItemAction.cpp index 2076b84d..ff8407cc 100644 --- a/src/strategy/actions/OpenItemAction.cpp +++ b/src/strategy/actions/OpenItemAction.cpp @@ -4,64 +4,23 @@ #include "WorldPacket.h" #include "Player.h" #include "ObjectMgr.h" - bool OpenItemAction::Execute(Event event) { bool foundOpenable = false; - // Check main inventory slots - for (uint8 slot = EQUIPMENT_SLOT_START; slot < INVENTORY_SLOT_ITEM_END; ++slot) + Item* item = botAI->FindOpenableItem(); + if (item) { - Item* item = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot); + uint8 bag = item->GetBagSlot(); // Retrieves the bag slot (0 for main inventory) + uint8 slot = item->GetSlot(); // Retrieves the actual slot inside the bag - if (item && CanOpenItem(item)) - { - OpenItem(item, INVENTORY_SLOT_BAG_0, slot); - foundOpenable = true; - } - } - - // Check items in the bags - for (uint8 bag = INVENTORY_SLOT_BAG_START; bag < INVENTORY_SLOT_BAG_END; ++bag) - { - Bag* bagItem = bot->GetBagByPos(bag); - if (!bagItem) - continue; - - for (uint32 slot = 0; slot < bagItem->GetBagSize(); ++slot) - { - Item* item = bot->GetItemByPos(bag, slot); - - if (item && CanOpenItem(item)) - { - OpenItem(item, bag, slot); - foundOpenable = true; - } - } - } - - // If no openable items found - if (!foundOpenable) - { - botAI->TellError("No openable items in inventory."); + OpenItem(item, bag, slot); + foundOpenable = true; } return foundOpenable; } -bool OpenItemAction::CanOpenItem(Item* item) -{ - if (!item) - return false; - - ItemTemplate const* itemTemplate = item->GetTemplate(); - if (!itemTemplate) - return false; - - // Check if the item has the openable flag - return itemTemplate->Flags & ITEM_FLAG_HAS_LOOT; -} - void OpenItemAction::OpenItem(Item* item, uint8 bag, uint8 slot) { WorldPacket packet(CMSG_OPEN_ITEM); diff --git a/src/strategy/actions/OpenItemAction.h b/src/strategy/actions/OpenItemAction.h index e60dd33e..6ab73b79 100644 --- a/src/strategy/actions/OpenItemAction.h +++ b/src/strategy/actions/OpenItemAction.h @@ -21,9 +21,6 @@ public: bool Execute(Event event) override; private: - // Checks if the given item can be opened (i.e., has the openable flag) - bool CanOpenItem(Item* item); - // Performs the action of opening the item void OpenItem(Item* item, uint8 bag, uint8 slot); }; diff --git a/src/strategy/actions/TradeStatusAction.cpp b/src/strategy/actions/TradeStatusAction.cpp index 577d46d1..ad421367 100644 --- a/src/strategy/actions/TradeStatusAction.cpp +++ b/src/strategy/actions/TradeStatusAction.cpp @@ -24,12 +24,17 @@ bool TradeStatusAction::Execute(Event event) return false; PlayerbotAI* traderBotAI = GET_PLAYERBOT_AI(trader); - if (trader != master && !traderBotAI) + + // Allow the master and group members to trade + if (trader != master && !traderBotAI && (!bot->GetGroup() || !bot->GetGroup()->IsMember(trader->GetGUID()))) { bot->Whisper("I'm kind of busy now", LANG_UNIVERSAL, trader); + return false; } - if ((trader != master || !botAI->GetSecurity()->CheckLevelFor(PLAYERBOT_SECURITY_ALLOW_ALL, true, master)) && + // Allow trades from group members or bots + if ((!bot->GetGroup() || !bot->GetGroup()->IsMember(trader->GetGUID())) && + (trader != master || !botAI->GetSecurity()->CheckLevelFor(PLAYERBOT_SECURITY_ALLOW_ALL, true, master)) && !traderBotAI) { WorldPacket p; @@ -109,9 +114,9 @@ bool TradeStatusAction::Execute(Event event) bot->SetFacingToObject(trader); BeginTrade(); + return true; } - return false; } diff --git a/src/strategy/actions/TradeStatusExtendedAction.cpp b/src/strategy/actions/TradeStatusExtendedAction.cpp new file mode 100644 index 00000000..ada779e3 --- /dev/null +++ b/src/strategy/actions/TradeStatusExtendedAction.cpp @@ -0,0 +1,84 @@ +#include "TradeStatusExtendedAction.h" +#include "Event.h" +#include "Player.h" +#include "PlayerbotAI.h" +#include "WorldPacket.h" +#include "TradeData.h" + +bool TradeStatusExtendedAction::Execute(Event event) +{ + Player* trader = bot->GetTrader(); + if (!trader) + return false; + + TradeData* tradeData = trader->GetTradeData(); + if (!tradeData) + return false; + + WorldPacket p(event.getPacket()); + p.rpos(0); + + uint8 isTraderData; + uint32 unknown1, slotCount1, slotCount2, tradeGold, spellCast; + p >> isTraderData; + p >> unknown1; + p >> slotCount1; + p >> slotCount2; + p >> tradeGold; + p >> spellCast; + + for (uint8 i = 0; i < TRADE_SLOT_COUNT; ++i) + { + uint8 tradeSlot; + p >> tradeSlot; + + if (tradeSlot >= TRADE_SLOT_COUNT) + break; // End of packet + + uint32 itemId, displayId, count, wrapped, lockId; + uint64 giftCreator, creator; + uint32 permEnchant, gem1, gem2, gem3; + uint32 spellCharges, suffixFactor, randomProp, maxDurability, durability; + + p >> itemId; + p >> displayId; + p >> count; + p >> wrapped; + p >> giftCreator; + p >> permEnchant; + p >> gem1; + p >> gem2; + p >> gem3; + p >> creator; + p >> spellCharges; + p >> suffixFactor; + p >> randomProp; + p >> lockId; + p >> maxDurability; + p >> durability; + + // Check for locked items in "Do Not Trade" slot + if (tradeSlot == TRADE_SLOT_NONTRADED && lockId > 0) + { + // Get the actual item reference from TradeData + Item* lockbox = tradeData->GetItem(TRADE_SLOT_NONTRADED); + if (!lockbox) + { + return false; + } + + if (bot->getClass() == CLASS_ROGUE && bot->HasSpell(1804) && lockbox->IsLocked()) // Pick Lock spell + { + // botAI->CastSpell(1804, bot, lockbox); // Attempt to cast Pick Lock on the lockbox + botAI->DoSpecificAction("unlock traded item"); + botAI->SetNextCheckDelay(4000); // Delay before accepting trade + } + else + { + botAI->TellMaster("I can't unlock this item."); + } + } + } + + return true; +} diff --git a/src/strategy/actions/TradeStatusExtendedAction.h b/src/strategy/actions/TradeStatusExtendedAction.h new file mode 100644 index 00000000..8797d8dc --- /dev/null +++ b/src/strategy/actions/TradeStatusExtendedAction.h @@ -0,0 +1,17 @@ +#ifndef _PLAYERBOT_TRADESTATUSEXTENDEDACTION_H +#define _PLAYERBOT_TRADESTATUSEXTENDEDACTION_H + +#include "QueryItemUsageAction.h" + +class Player; +class PlayerbotAI; + +class TradeStatusExtendedAction : public QueryItemUsageAction +{ +public: + TradeStatusExtendedAction(PlayerbotAI* botAI) : QueryItemUsageAction(botAI, "trade status extended") {} + + bool Execute(Event event) override; +}; + +#endif diff --git a/src/strategy/actions/UnlockItemAction.cpp b/src/strategy/actions/UnlockItemAction.cpp new file mode 100644 index 00000000..367a4fcb --- /dev/null +++ b/src/strategy/actions/UnlockItemAction.cpp @@ -0,0 +1,38 @@ +#include "UnlockItemAction.h" +#include "PlayerbotAI.h" +#include "ItemTemplate.h" +#include "WorldPacket.h" +#include "Player.h" +#include "ObjectMgr.h" +#include "SpellInfo.h" + +#define PICK_LOCK_SPELL_ID 1804 + +bool UnlockItemAction::Execute(Event event) +{ + bool foundLockedItem = false; + + Item* item = botAI->FindLockedItem(); + if (item) + { + UnlockItem(item); + foundLockedItem = true; + } + + return foundLockedItem; +} + +void UnlockItemAction::UnlockItem(Item* item) +{ + // Use CastSpell to unlock the item + if (botAI->CastSpell(PICK_LOCK_SPELL_ID, bot, item)) + { + std::ostringstream out; + out << "Used Pick Lock on: " << item->GetTemplate()->Name1; + botAI->TellMaster(out.str()); + } + else + { + botAI->TellError("Failed to cast Pick Lock."); + } +} diff --git a/src/strategy/actions/UnlockItemAction.h b/src/strategy/actions/UnlockItemAction.h new file mode 100644 index 00000000..a4738da7 --- /dev/null +++ b/src/strategy/actions/UnlockItemAction.h @@ -0,0 +1,19 @@ +#ifndef _PLAYERBOT_UNLOCKITEMACTION_H +#define _PLAYERBOT_UNLOCKITEMACTION_H + +#include "Action.h" + +class PlayerbotAI; + +class UnlockItemAction : public Action +{ +public: + UnlockItemAction(PlayerbotAI* botAI) : Action(botAI, "unlock item") { } + + bool Execute(Event event) override; + +private: + void UnlockItem(Item* item); +}; + +#endif diff --git a/src/strategy/actions/UnlockTradedItemAction.cpp b/src/strategy/actions/UnlockTradedItemAction.cpp new file mode 100644 index 00000000..22d9c145 --- /dev/null +++ b/src/strategy/actions/UnlockTradedItemAction.cpp @@ -0,0 +1,98 @@ +#include "UnlockTradedItemAction.h" +#include "Playerbots.h" +#include "TradeData.h" +#include "SpellInfo.h" + +#define PICK_LOCK_SPELL_ID 1804 + +bool UnlockTradedItemAction::Execute(Event event) +{ + Player* trader = bot->GetTrader(); + if (!trader) + return false; // No active trade session + + TradeData* tradeData = trader->GetTradeData(); + if (!tradeData) + return false; // No trade data available + + Item* lockbox = tradeData->GetItem(TRADE_SLOT_NONTRADED); + if (!lockbox) + { + botAI->TellError("No item in the Do Not Trade slot."); + return false; + } + + if (!CanUnlockItem(lockbox)) + { + botAI->TellError("Cannot unlock this item."); + return false; + } + + UnlockItem(lockbox); + return true; +} + +bool UnlockTradedItemAction::CanUnlockItem(Item* item) +{ + if (!item) + return false; + + ItemTemplate const* itemTemplate = item->GetTemplate(); + if (!itemTemplate) + return false; + + // Ensure the bot is a rogue and has Lockpicking skill + if (bot->getClass() != CLASS_ROGUE || !botAI->HasSkill(SKILL_LOCKPICKING)) + return false; + + // Ensure the item is actually locked + if (itemTemplate->LockID == 0 || !item->IsLocked()) + return false; + + // Check if the bot's Lockpicking skill is high enough + uint32 lockId = itemTemplate->LockID; + LockEntry const* lockInfo = sLockStore.LookupEntry(lockId); + if (!lockInfo) + return false; + + uint32 botSkill = bot->GetSkillValue(SKILL_LOCKPICKING); + for (uint8 j = 0; j < 8; ++j) + { + if (lockInfo->Type[j] == LOCK_KEY_SKILL && SkillByLockType(LockType(lockInfo->Index[j])) == SKILL_LOCKPICKING) + { + uint32 requiredSkill = lockInfo->Skill[j]; + if (botSkill >= requiredSkill) + return true; + else + { + std::ostringstream out; + out << "Lockpicking skill too low (" << botSkill << "/" << requiredSkill << ") to unlock: " + << item->GetTemplate()->Name1; + botAI->TellMaster(out.str()); + } + } + } + + return false; +} + +void UnlockTradedItemAction::UnlockItem(Item* item) +{ + if (!bot->HasSpell(PICK_LOCK_SPELL_ID)) + { + botAI->TellError("Cannot unlock, Pick Lock spell is missing."); + return; + } + + // Use CastSpell to unlock the item + if (botAI->CastSpell(PICK_LOCK_SPELL_ID, bot->GetTrader(), item)) // Unit target is trader + { + std::ostringstream out; + out << "Picking Lock on traded item: " << item->GetTemplate()->Name1; + botAI->TellMaster(out.str()); + } + else + { + botAI->TellError("Failed to cast Pick Lock."); + } +} diff --git a/src/strategy/actions/UnlockTradedItemAction.h b/src/strategy/actions/UnlockTradedItemAction.h new file mode 100644 index 00000000..6349ab56 --- /dev/null +++ b/src/strategy/actions/UnlockTradedItemAction.h @@ -0,0 +1,20 @@ +#ifndef _PLAYERBOT_UNLOCKTRADEDITEMACTION_H +#define _PLAYERBOT_UNLOCKTRADEDITEMACTION_H + +#include "Action.h" + +class PlayerbotAI; + +class UnlockTradedItemAction : public Action +{ +public: + UnlockTradedItemAction(PlayerbotAI* botAI) : Action(botAI, "unlock traded item") {} + + bool Execute(Event event) override; + +private: + bool CanUnlockItem(Item* item); + void UnlockItem(Item* item); +}; + +#endif diff --git a/src/strategy/actions/WorldPacketActionContext.h b/src/strategy/actions/WorldPacketActionContext.h index a119b3a7..2c32d118 100644 --- a/src/strategy/actions/WorldPacketActionContext.h +++ b/src/strategy/actions/WorldPacketActionContext.h @@ -37,6 +37,7 @@ #include "TellCastFailedAction.h" #include "TellMasterAction.h" #include "TradeStatusAction.h" +#include "TradeStatusExtendedAction.h" #include "UseMeetingStoneAction.h" #include "NamedObjectContext.h" @@ -65,6 +66,7 @@ public: creators["check mount state"] = &WorldPacketActionContext::check_mount_state; creators["remember taxi"] = &WorldPacketActionContext::remember_taxi; creators["accept trade"] = &WorldPacketActionContext::accept_trade; + creators["trade status extended"] = &WorldPacketActionContext::trade_status_extended; creators["store loot"] = &WorldPacketActionContext::store_loot; // quest @@ -118,6 +120,7 @@ private: static Action* party_command(PlayerbotAI* botAI) { return new PartyCommandAction(botAI); } static Action* store_loot(PlayerbotAI* botAI) { return new StoreLootAction(botAI); } static Action* accept_trade(PlayerbotAI* botAI) { return new TradeStatusAction(botAI); } + static Action* trade_status_extended(PlayerbotAI* botAI) { return new TradeStatusExtendedAction(botAI); } static Action* remember_taxi(PlayerbotAI* botAI) { return new RememberTaxiAction(botAI); } static Action* check_mount_state(PlayerbotAI* botAI) { return new CheckMountStateAction(botAI); } static Action* area_trigger(PlayerbotAI* botAI) { return new AreaTriggerAction(botAI); } diff --git a/src/strategy/generic/ChatCommandHandlerStrategy.cpp b/src/strategy/generic/ChatCommandHandlerStrategy.cpp index 4cf71cae..27f46f93 100644 --- a/src/strategy/generic/ChatCommandHandlerStrategy.cpp +++ b/src/strategy/generic/ChatCommandHandlerStrategy.cpp @@ -96,6 +96,10 @@ void ChatCommandHandlerStrategy::InitTriggers(std::vector& trigger new TriggerNode("open items", NextAction::array(0, new NextAction("open items", relevance), nullptr))); triggers.push_back( new TriggerNode("qi", NextAction::array(0, new NextAction("query item usage", relevance), nullptr))); + triggers.push_back( + new TriggerNode("unlock items", NextAction::array(0, new NextAction("unlock items", relevance), nullptr))); + triggers.push_back( + new TriggerNode("unlock traded item", NextAction::array(0, new NextAction("unlock traded item", relevance), nullptr))); } ChatCommandHandlerStrategy::ChatCommandHandlerStrategy(PlayerbotAI* botAI) : PassTroughStrategy(botAI) @@ -172,4 +176,6 @@ ChatCommandHandlerStrategy::ChatCommandHandlerStrategy(PlayerbotAI* botAI) : Pas supported.push_back("calc"); supported.push_back("open items"); supported.push_back("qi"); + supported.push_back("unlock items"); + supported.push_back("unlock traded item"); } diff --git a/src/strategy/generic/WorldPacketHandlerStrategy.cpp b/src/strategy/generic/WorldPacketHandlerStrategy.cpp index efdc06ef..dacac309 100644 --- a/src/strategy/generic/WorldPacketHandlerStrategy.cpp +++ b/src/strategy/generic/WorldPacketHandlerStrategy.cpp @@ -35,12 +35,15 @@ void WorldPacketHandlerStrategy::InitTriggers(std::vector& trigger new NextAction("taxi", relevance), nullptr))); triggers.push_back(new TriggerNode("taxi done", NextAction::array(0, new NextAction("taxi", relevance), nullptr))); triggers.push_back(new TriggerNode("trade status", NextAction::array(0, new NextAction("accept trade", relevance), new NextAction("equip upgrades", relevance), nullptr))); + triggers.push_back(new TriggerNode("trade status extended", NextAction::array(0, new NextAction("trade status extended", relevance), nullptr))); triggers.push_back(new TriggerNode("area trigger", NextAction::array(0, new NextAction("reach area trigger", relevance), nullptr))); triggers.push_back(new TriggerNode("within area trigger", NextAction::array(0, new NextAction("area trigger", relevance), nullptr))); triggers.push_back(new TriggerNode("loot response", NextAction::array(0, new NextAction("store loot", relevance), nullptr))); - triggers.push_back(new TriggerNode("item push result", NextAction::array(0, new NextAction("query item usage", relevance), new NextAction("equip upgrades", relevance), nullptr))); + triggers.push_back(new TriggerNode("item push result", NextAction::array(0, new NextAction("unlock items", relevance), + new NextAction("open items", relevance), + new NextAction("query item usage", relevance), + new NextAction("equip upgrades", relevance), nullptr))); triggers.push_back(new TriggerNode("item push result", NextAction::array(0, new NextAction("quest item push result", relevance), nullptr))); - triggers.push_back(new TriggerNode("ready check finished", NextAction::array(0, new NextAction("finish ready check", relevance), nullptr))); // triggers.push_back(new TriggerNode("often", NextAction::array(0, new NextAction("security check", relevance), new NextAction("check mail", relevance), nullptr))); triggers.push_back(new TriggerNode("guild invite", NextAction::array(0, new NextAction("guild accept", relevance), nullptr))); diff --git a/src/strategy/triggers/ChatTriggerContext.h b/src/strategy/triggers/ChatTriggerContext.h index be2c97d5..ebc11214 100644 --- a/src/strategy/triggers/ChatTriggerContext.h +++ b/src/strategy/triggers/ChatTriggerContext.h @@ -17,6 +17,8 @@ public: ChatTriggerContext() { creators["open items"] = &ChatTriggerContext::open_items; + creators["unlock items"] = &ChatTriggerContext::unlock_items; + creators["unlock traded item"] = &ChatTriggerContext::unlock_traded_item; creators["quests"] = &ChatTriggerContext::quests; creators["stats"] = &ChatTriggerContext::stats; creators["leave"] = &ChatTriggerContext::leave; @@ -133,6 +135,8 @@ public: private: static Trigger* open_items(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "open items"); } + static Trigger* unlock_items(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "unlock items"); } + static Trigger* unlock_traded_item(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "unlock traded item"); } static Trigger* ra(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "ra"); } static Trigger* range(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "range"); } static Trigger* flag(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "flag"); } diff --git a/src/strategy/triggers/WorldPacketTriggerContext.h b/src/strategy/triggers/WorldPacketTriggerContext.h index 72ad3e5e..5404a220 100644 --- a/src/strategy/triggers/WorldPacketTriggerContext.h +++ b/src/strategy/triggers/WorldPacketTriggerContext.h @@ -29,6 +29,7 @@ public: creators["check mount state"] = &WorldPacketTriggerContext::check_mount_state; creators["activate taxi"] = &WorldPacketTriggerContext::taxi; creators["trade status"] = &WorldPacketTriggerContext::trade_status; + creators["trade status extended"] = &WorldPacketTriggerContext::trade_status_extended; creators["loot response"] = &WorldPacketTriggerContext::loot_response; creators["out of react range"] = &WorldPacketTriggerContext::out_of_react_range; @@ -108,6 +109,7 @@ private: static Trigger* out_of_react_range(PlayerbotAI* botAI) { return new OutOfReactRangeTrigger(botAI); } static Trigger* loot_response(PlayerbotAI* botAI) { return new WorldPacketTrigger(botAI, "loot response"); } static Trigger* trade_status(PlayerbotAI* botAI) { return new WorldPacketTrigger(botAI, "trade status"); } + static Trigger* trade_status_extended(PlayerbotAI* botAI) { return new WorldPacketTrigger(botAI, "trade status extended"); } static Trigger* cannot_equip(PlayerbotAI* botAI) { return new WorldPacketTrigger(botAI, "cannot equip"); } static Trigger* check_mount_state(PlayerbotAI* botAI) { return new WorldPacketTrigger(botAI, "check mount state"); } static Trigger* area_trigger(PlayerbotAI* botAI) { return new WorldPacketTrigger(botAI, "area trigger"); } diff --git a/src/strategy/values/ItemForSpellValue.cpp b/src/strategy/values/ItemForSpellValue.cpp index 95751b4a..366f38a5 100644 --- a/src/strategy/values/ItemForSpellValue.cpp +++ b/src/strategy/values/ItemForSpellValue.cpp @@ -71,6 +71,9 @@ Item* ItemForSpellValue::Calculate() if (!strcmpi(spellInfo->SpellName[0], "disenchant")) return nullptr; + if (!strcmpi(spellInfo->SpellName[0], "pick lock")) + return nullptr; + for (uint8 slot = EQUIPMENT_SLOT_START; slot < EQUIPMENT_SLOT_END; slot++) { itemForSpell = GetItemFitsToSpellRequirements(slot, spellInfo);