diff --git a/src/game/WorldHandlers/SpellEffects.cpp b/src/game/WorldHandlers/SpellEffects.cpp index b18a7b3b4..26b335353 100644 --- a/src/game/WorldHandlers/SpellEffects.cpp +++ b/src/game/WorldHandlers/SpellEffects.cpp @@ -2515,6 +2515,11 @@ void Spell::EffectOpenLock(SpellEffectIndex eff_idx) itemTarget->SetFlag(ITEM_FIELD_FLAGS, ITEM_DYNFLAG_UNLOCKED); } + if (gameObjTarget) + { + gameObjTarget->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_LOCKED); + } + SendLoot(guid, LOOT_SKINNING, LockType(m_spellInfo->EffectMiscValue[eff_idx])); // not allow use skill grow at item base open diff --git a/src/modules/Bots/playerbot/PlayerbotAI.cpp b/src/modules/Bots/playerbot/PlayerbotAI.cpp index f28036599..fd8969744 100644 --- a/src/modules/Bots/playerbot/PlayerbotAI.cpp +++ b/src/modules/Bots/playerbot/PlayerbotAI.cpp @@ -119,7 +119,7 @@ PlayerbotAI::PlayerbotAI(Player* bot) : //masterIncomingPacketHandlers.AddHandler(CMSG_GAMEOBJ_REPORT_USE, "use game object"); masterIncomingPacketHandlers.AddHandler(CMSG_AREATRIGGER, "area trigger"); masterIncomingPacketHandlers.AddHandler(CMSG_GAMEOBJ_USE, "use game object"); - masterIncomingPacketHandlers.AddHandler(CMSG_LOOT_ROLL, "loot roll"); + botOutgoingPacketHandlers.AddHandler(SMSG_LOOT_START_ROLL, "loot roll"); masterIncomingPacketHandlers.AddHandler(CMSG_GOSSIP_HELLO, "gossip hello"); masterIncomingPacketHandlers.AddHandler(CMSG_QUESTGIVER_HELLO, "gossip hello"); masterIncomingPacketHandlers.AddHandler(CMSG_QUESTGIVER_COMPLETE_QUEST, "complete quest"); diff --git a/src/modules/Bots/playerbot/strategy/actions/AddLootAction.cpp b/src/modules/Bots/playerbot/strategy/actions/AddLootAction.cpp index 47980ca33..5e8b624f0 100644 --- a/src/modules/Bots/playerbot/strategy/actions/AddLootAction.cpp +++ b/src/modules/Bots/playerbot/strategy/actions/AddLootAction.cpp @@ -66,6 +66,11 @@ bool AddGatheringLootAction::AddLoot(ObjectGuid guid) return false; } + if (bot->GetMap()->IsDungeon() && loot.skillId != SKILL_LOCKPICKING) + { + return false; + } + if (!loot.IsLootPossible(bot)) { return false; @@ -76,12 +81,6 @@ bool AddGatheringLootAction::AddLoot(ObjectGuid guid) bool AddGatheringLootAction::isUseful() { - // Don't gather in dungeons or raids - if (bot->GetMap()->IsDungeon()) - { - return false; - } - // NC gathering is a problem if you are supposed to be following Player* master = ai->GetMaster(); if (master && bot->GetGroup()) diff --git a/src/modules/Bots/playerbot/strategy/actions/LootAction.cpp b/src/modules/Bots/playerbot/strategy/actions/LootAction.cpp index 276db1923..6b15a9245 100644 --- a/src/modules/Bots/playerbot/strategy/actions/LootAction.cpp +++ b/src/modules/Bots/playerbot/strategy/actions/LootAction.cpp @@ -103,6 +103,23 @@ bool OpenLootAction::DoLoot(LootObject& lootObject) } bot->GetMotionMaster()->Clear(); + + if (go && go->GetGoState() == GO_STATE_ACTIVE) + { + if (bot->GetLootGuid() == lootObject.guid) + return false; + + WorldPacket* const packet = new WorldPacket(CMSG_LOOT, 8); + *packet << lootObject.guid; + bot->GetSession()->QueuePacket(packet); + return true; + } + + if (bot->IsNonMeleeSpellCasted(false)) + { + return false; + } + if (lootObject.skillId == SKILL_MINING) { return bot->HasSkill(SKILL_MINING) ? ai->CastSpell(MINING, bot) : false; @@ -286,7 +303,7 @@ bool StoreLootAction::Execute(Event event) continue; } - if (loot_type != LOOT_SKINNING && !IsLootAllowed(itemid)) + if (!IsLootAllowed(itemid)) { continue; } diff --git a/src/modules/Bots/playerbot/strategy/actions/LootRollAction.cpp b/src/modules/Bots/playerbot/strategy/actions/LootRollAction.cpp index d8ba993f7..7c8b8dd55 100644 --- a/src/modules/Bots/playerbot/strategy/actions/LootRollAction.cpp +++ b/src/modules/Bots/playerbot/strategy/actions/LootRollAction.cpp @@ -1,7 +1,7 @@ #include "botpch.h" #include "../../playerbot.h" #include "LootRollAction.h" - +#include "../values/ItemUsageValue.h" using namespace ai; @@ -9,14 +9,18 @@ bool LootRollAction::Execute(Event event) { Player *bot = QueryItemUsageAction::ai->GetBot(); - WorldPacket p(event.getPacket()); //WorldPacket packet for CMSG_LOOT_ROLL, (8+4+1) - ObjectGuid guid; + WorldPacket p(event.getPacket()); + ObjectGuid lootTargetGuid; uint32 slot; - uint8 rollType; - p.rpos(0); //reset packet pointer - p >> guid; //guid of the item rolled - p >> slot; //number of players invited to roll - p >> rollType; //need,greed or pass on roll + uint32 itemid; + uint32 randomSuffix; + uint32 itemRandomPropId; + p.rpos(0); + p >> lootTargetGuid; + p >> slot; + p >> itemid; + p >> randomSuffix; + p >> itemRandomPropId; Group* group = bot->GetGroup(); if (!group) @@ -26,23 +30,30 @@ bool LootRollAction::Execute(Event event) RollVote vote = ROLL_PASS; - ItemPrototype const *proto = sItemStorage.LookupEntry(guid.GetEntry()); + ItemPrototype const *proto = sItemStorage.LookupEntry(itemid); if (proto) { + AiObjectContext* context = QueryItemUsageAction::context; + ostringstream out; out << itemid; + ItemUsage usage = AI_VALUE2(ItemUsage, "item usage", out.str()); + switch (proto->Class) { case ITEM_CLASS_WEAPON: case ITEM_CLASS_ARMOR: - if (QueryItemUsage(proto)) - { + if (usage == ITEM_USAGE_EQUIP || usage == ITEM_USAGE_REPLACE) vote = ROLL_NEED; - } + else if (bot->CanUseItem(proto) == EQUIP_ERR_OK && proto->Bonding != BIND_WHEN_PICKED_UP) + vote = ROLL_GREED; break; default: - if (IsLootAllowed(guid.GetEntry())) - { + if (usage == ITEM_USAGE_SKILL || usage == ITEM_USAGE_USE) + vote = ROLL_NEED; + else if (proto->StartQuest || proto->Bonding == BIND_QUEST_ITEM || + proto->Bonding == BIND_QUEST_ITEM1 || proto->Class == ITEM_CLASS_QUEST) vote = ROLL_NEED; - } + else if (proto->SellPrice > 0 && proto->Bonding != BIND_WHEN_PICKED_UP) + vote = ROLL_GREED; break; } } @@ -51,10 +62,10 @@ bool LootRollAction::Execute(Event event) { case MASTER_LOOT: case FREE_FOR_ALL: - group->CountRollVote(bot, guid, slot, ROLL_PASS); + group->CountRollVote(bot, lootTargetGuid, slot, ROLL_PASS); break; default: - group->CountRollVote(bot, guid, slot, vote); + group->CountRollVote(bot, lootTargetGuid, slot, vote); break; } diff --git a/src/modules/Bots/playerbot/strategy/values/ItemUsageValue.cpp b/src/modules/Bots/playerbot/strategy/values/ItemUsageValue.cpp index 0293cfc81..12c0a5680 100644 --- a/src/modules/Bots/playerbot/strategy/values/ItemUsageValue.cpp +++ b/src/modules/Bots/playerbot/strategy/values/ItemUsageValue.cpp @@ -87,6 +87,63 @@ ItemUsage ItemUsageValue::QueryItemUsageForEquip(ItemPrototype const * item) return ITEM_USAGE_NONE; } +static bool IsClothMaterial(uint32 itemId, uint32 botSkill) +{ + static std::map firstAidMaterials; + static bool initialized = false; + if (!initialized) + { + initialized = true; + for (uint32 i = 0; i < sSkillLineAbilityStore.GetNumRows(); ++i) + { + SkillLineAbilityEntry const* entry = sSkillLineAbilityStore.LookupEntry(i); + if (!entry || entry->skillId != SKILL_FIRST_AID) + continue; + SpellEntry const* spell = sSpellStore.LookupEntry(entry->spellId); + if (!spell) + continue; + for (int r = 0; r < MAX_SPELL_REAGENTS; ++r) + { + if (spell->Reagent[r] <= 0) + continue; + uint32 reagentId = (uint32)spell->Reagent[r]; + uint32 greyAt = entry->max_value; + auto it = firstAidMaterials.find(reagentId); + if (it == firstAidMaterials.end() || greyAt > it->second) + firstAidMaterials[reagentId] = greyAt; + } + } + } + auto it = firstAidMaterials.find(itemId); + if (it == firstAidMaterials.end()) + return false; + return it->second == 0 || botSkill < it->second; +} + +static bool IsSkillMaterial(uint32 skillId, uint32 itemId) +{ + static std::map> skillMaterials; + if (skillMaterials[skillId].size()==0) + { + for (uint32 i = 0; i < sSkillLineAbilityStore.GetNumRows(); ++i) + { + SkillLineAbilityEntry const* entry = sSkillLineAbilityStore.LookupEntry(i); + if (!entry || entry->skillId != skillId) + continue; + SpellEntry const* spell = sSpellStore.LookupEntry(entry->spellId); + if (!spell) + continue; + for (int r = 0; r < MAX_SPELL_REAGENTS; ++r) + { + if (spell->Reagent[r] <= 0) + continue; + skillMaterials[skillId].insert((uint32)spell->Reagent[r]); + } + } + } + return skillMaterials[skillId].count(itemId) > 0; +} + bool ItemUsageValue::IsItemUsefulForSkill(ItemPrototype const * proto) { switch (proto->Class) @@ -99,6 +156,24 @@ bool ItemUsageValue::IsItemUsefulForSkill(ItemPrototype const * proto) case ITEM_SUBCLASS_DEVICES: return bot->HasSkill(SKILL_ENGINEERING); } + if (bot->HasSkill(SKILL_FIRST_AID) && IsClothMaterial(proto->ItemId, bot->GetSkillValue(SKILL_FIRST_AID))) + return true; + if (bot->HasSkill(SKILL_HERBALISM) && IsSkillMaterial(SKILL_ALCHEMY, proto->ItemId)) + return true; + if (bot->HasSkill(SKILL_ALCHEMY) && IsSkillMaterial(SKILL_ALCHEMY, proto->ItemId)) + return true; + if (bot->HasSkill(SKILL_TAILORING) && IsSkillMaterial(SKILL_TAILORING, proto->ItemId)) + return true; + if (bot->HasSkill(SKILL_SKINNING) && IsSkillMaterial(SKILL_LEATHERWORKING, proto->ItemId)) + return true; + if (bot->HasSkill(SKILL_LEATHERWORKING) && IsSkillMaterial(SKILL_LEATHERWORKING, proto->ItemId)) + return true; + if (bot->HasSkill(SKILL_MINING) && IsSkillMaterial(SKILL_MINING, proto->ItemId)) + return true; + if (bot->HasSkill(SKILL_BLACKSMITHING) && IsSkillMaterial(SKILL_BLACKSMITHING, proto->ItemId)) + return true; + if (bot->HasSkill(SKILL_ENGINEERING) && IsSkillMaterial(SKILL_ENGINEERING, proto->ItemId)) + return true; break; case ITEM_CLASS_RECIPE: {