Files
mod-playerbots/src/LootObjectStack.cpp
2024-08-04 10:23:36 +08:00

312 lines
8.3 KiB
C++

/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, 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"
#include "Unit.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<LootTarget>::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);
if (!botAI)
{
return;
}
Creature* creature = botAI->GetCreature(lootGUID);
if (creature && creature->getDeathState() == DeathState::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)
{
bool isQuestItemOnly = false;
GameObjectQuestItemList const* items = sObjectMgr->GetGameObjectQuestItemList(go->GetEntry());
for (int i = 0; i < MAX_GAMEOBJECT_QUEST_ITEMS; i++)
{
if (!items || i >= items->size())
break;
auto itemId = uint32((*items)[i]);
if (IsNeededForQuest(bot, itemId))
{
this->guid = lootGUID;
return;
}
isQuestItemOnly |= itemId > 0;
}
if (isQuestItemOnly)
return;
uint32 goId = go->GetEntry();
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 (goId == 13891 || goId == 19535) // Serpentbloom
{
this->guid = lootGUID;
}
else 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;
}
}
}
}
bool LootObject::IsNeededForQuest(Player* bot, uint32 itemId)
{
for (int qs = 0; qs < MAX_QUEST_LOG_SIZE; ++qs)
{
uint32 questId = bot->GetQuestSlotQuestId(qs);
if (questId == 0)
continue;
QuestStatusData& qData = bot->getQuestStatusMap()[questId];
if (qData.Status != QUEST_STATUS_INCOMPLETE)
continue;
Quest const* qInfo = sObjectMgr->GetQuestTemplate(questId);
if (!qInfo)
continue;
for (int i = 0; i < QUEST_ITEM_OBJECTIVES_COUNT; ++i)
{
if (!qInfo->RequiredItemCount[i] || (qInfo->RequiredItemCount[i] - qData.ItemCount[i]) <= 0)
continue;
if (qInfo->RequiredItemId[i] != itemId)
continue;
return true;
}
}
return false;
}
WorldObject* LootObject::GetWorldObject(Player* bot)
{
Refresh(bot, guid);
PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
if (!botAI)
{
return nullptr;
}
Creature* creature = botAI->GetCreature(guid);
if (creature && creature->getDeathState() == DeathState::Corpse && creature->IsInWorld())
return creature;
GameObject* go = botAI->GetGameObject(guid);
if (go && go->isSpawned() && go->IsInWorld())
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 (!botAI)
{
return false;
}
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() == DeathState::Corpse)
{
if (!bot->isAllowedToLoot(creature) && 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.size() >= MAX_LOOT_OBJECT_COUNT)
{
availableLoot.shrink(time(nullptr) - 30);
}
if (availableLoot.size() >= MAX_LOOT_OBJECT_COUNT)
{
availableLoot.clear();
}
if (!availableLoot.insert(guid).second)
return false;
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<LootObject> ordered = OrderByDistance(maxDistance);
return !ordered.empty();
}
LootObject LootObjectStack::GetLoot(float maxDistance)
{
std::vector<LootObject> ordered = OrderByDistance(maxDistance);
return ordered.empty() ? LootObject() : *ordered.begin();
}
std::vector<LootObject> LootObjectStack::OrderByDistance(float maxDistance)
{
availableLoot.shrink(time(nullptr) - 30);
std::map<float, LootObject> 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<LootObject> result;
for (std::map<float, LootObject>::iterator i = sortedMap.begin(); i != sortedMap.end(); i++)
result.push_back(i->second);
return result;
}