/* * 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 "LootObjectStack.h" #include "LootMgr.h" #include "Playerbots.h" #define MAX_LOOT_OBJECT_COUNT 10 LootTarget::LootTarget(ObjectGuid guid) : guid(guid), asOfTime(time(nullptr)) { } LootTarget::LootTarget(LootTarget const& other) { guid = other.guid; asOfTime = other.asOfTime; } LootTarget& LootTarget::operator=(LootTarget const& other) { if ((void*)this == (void*)&other) return *this; guid = other.guid; asOfTime = other.asOfTime; return *this; } bool LootTarget::operator<(LootTarget const& other) const { return guid < other.guid; } void LootTargetList::shrink(time_t fromTime) { for (std::set::iterator i = begin(); i != end(); ) { if (i->asOfTime <= fromTime) erase(i++); else ++i; } } LootObject::LootObject(Player* bot, ObjectGuid guid) : guid(), skillId(SKILL_NONE), reqSkillValue(0), reqItem(0) { Refresh(bot, guid); } void LootObject::Refresh(Player* bot, ObjectGuid lootGUID) { skillId = SKILL_NONE; reqSkillValue = 0; reqItem = 0; guid.Clear(); PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot); Creature* creature = botAI->GetCreature(lootGUID); if (creature && creature->getDeathState() == CORPSE) { if (creature->HasFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE)) guid = lootGUID; if (creature->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE)) { skillId = creature->GetCreatureTemplate()->GetRequiredLootSkill(); uint32 targetLevel = creature->getLevel(); reqSkillValue = targetLevel < 10 ? 1 : targetLevel < 20 ? (targetLevel - 10) * 10 : targetLevel * 5; if (botAI->HasSkill((SkillType) skillId) && bot->GetSkillValue(skillId) >= reqSkillValue) guid = lootGUID; } return; } GameObject* go = botAI->GetGameObject(lootGUID); if (go && go->isSpawned() && go->GetGoState() == GO_STATE_READY) { uint32 lockId = go->GetGOInfo()->GetLockId(); LockEntry const* lockInfo = sLockStore.LookupEntry(lockId); if (!lockInfo) return; for (uint8 i = 0; i < 8; ++i) { switch (lockInfo->Type[i]) { case LOCK_KEY_ITEM: if (lockInfo->Index[i] > 0) { reqItem = lockInfo->Index[i]; guid = lootGUID; } break; case LOCK_KEY_SKILL: if (SkillByLockType(LockType(lockInfo->Index[i])) > 0) { skillId = SkillByLockType(LockType(lockInfo->Index[i])); reqSkillValue = std::max((uint32)1, lockInfo->Skill[i]); guid = lootGUID; } break; case LOCK_KEY_NONE: guid = lootGUID; break; } } } } WorldObject* LootObject::GetWorldObject(Player* bot) { Refresh(bot, guid); PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot); Creature* creature = botAI->GetCreature(guid); if (creature && creature->getDeathState() == CORPSE) return creature; GameObject* go = botAI->GetGameObject(guid); if (go && go->isSpawned()) return go; return nullptr; } LootObject::LootObject(LootObject const& other) { guid = other.guid; skillId = other.skillId; reqSkillValue = other.reqSkillValue; reqItem = other.reqItem; } bool LootObject::IsLootPossible(Player* bot) { if (IsEmpty() || !GetWorldObject(bot)) return false; PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot); if (reqItem && !bot->HasItemCount(reqItem, 1)) return false; if (abs(GetWorldObject(bot)->GetPositionZ() - bot->GetPositionZ()) > INTERACTION_DISTANCE) return false; Creature* creature = botAI->GetCreature(guid); if (creature && creature->getDeathState() == CORPSE) { if (!creature->loot.hasItemFor(bot) && skillId != SKILL_SKINNING) return false; } if (skillId == SKILL_NONE) return true; if (skillId == SKILL_FISHING) return false; if (!botAI->HasSkill((SkillType)skillId)) return false; if (!reqSkillValue) return true; uint32 skillValue = uint32(bot->GetSkillValue(skillId)); if (reqSkillValue > skillValue) return false; if (skillId == SKILL_MINING && !bot->HasItemCount(2901, 1)) return false; if (skillId == SKILL_SKINNING && !bot->HasItemCount(7005, 1)) return false; return true; } bool LootObjectStack::Add(ObjectGuid guid) { if (!availableLoot.insert(guid).second) return false; if (availableLoot.size() < MAX_LOOT_OBJECT_COUNT) return true; std::vector ordered = OrderByDistance(); for (size_t i = MAX_LOOT_OBJECT_COUNT; i < ordered.size(); i++) Remove(ordered[i].guid); return true; } void LootObjectStack::Remove(ObjectGuid guid) { LootTargetList::iterator i = availableLoot.find(guid); if (i != availableLoot.end()) availableLoot.erase(i); } void LootObjectStack::Clear() { availableLoot.clear(); } bool LootObjectStack::CanLoot(float maxDistance) { std::vector ordered = OrderByDistance(maxDistance); return !ordered.empty(); } LootObject LootObjectStack::GetLoot(float maxDistance) { std::vector ordered = OrderByDistance(maxDistance); return ordered.empty() ? LootObject() : *ordered.begin(); } std::vector LootObjectStack::OrderByDistance(float maxDistance) { availableLoot.shrink(time(nullptr) - 30); std::map sortedMap; LootTargetList safeCopy(availableLoot); for (LootTargetList::iterator i = safeCopy.begin(); i != safeCopy.end(); i++) { ObjectGuid guid = i->guid; LootObject lootObject(bot, guid); if (!lootObject.IsLootPossible(bot)) continue; float distance = bot->GetDistance(lootObject.GetWorldObject(bot)); if (!maxDistance || distance <= maxDistance) sortedMap[distance] = lootObject; } std::vector result; for (std::map::iterator i = sortedMap.begin(); i != sortedMap.end(); i++) result.push_back(i->second); return result; }