mirror of
https://github.com/mod-playerbots/mod-playerbots.git
synced 2026-01-23 05:26:22 +00:00
Resolved issues with herb gathering (#926)
* Check game objects loot tables and determine if loot is valid * Removed LOS checks since they already occur and removed enemy near node check * Dismount if mounted, decresed interaction distance, added looting delay * Decreased interaction distance * oops, wrong file * Check game objects loot tables and determine if loot is valid
This commit is contained in:
@@ -81,7 +81,8 @@ void LootObject::Refresh(Player* bot, ObjectGuid lootGUID)
|
|||||||
GameObject* go = botAI->GetGameObject(lootGUID);
|
GameObject* go = botAI->GetGameObject(lootGUID);
|
||||||
if (go && go->isSpawned() && go->GetGoState() == GO_STATE_READY)
|
if (go && go->isSpawned() && go->GetGoState() == GO_STATE_READY)
|
||||||
{
|
{
|
||||||
bool isQuestItemOnly = false;
|
bool onlyHasQuestItems = true;
|
||||||
|
bool hasAnyQuestItems = false;
|
||||||
|
|
||||||
GameObjectQuestItemList const* items = sObjectMgr->GetGameObjectQuestItemList(go->GetEntry());
|
GameObjectQuestItemList const* items = sObjectMgr->GetGameObjectQuestItemList(go->GetEntry());
|
||||||
for (int i = 0; i < MAX_GAMEOBJECT_QUEST_ITEMS; i++)
|
for (int i = 0; i < MAX_GAMEOBJECT_QUEST_ITEMS; i++)
|
||||||
@@ -89,19 +90,88 @@ void LootObject::Refresh(Player* bot, ObjectGuid lootGUID)
|
|||||||
if (!items || i >= items->size())
|
if (!items || i >= items->size())
|
||||||
break;
|
break;
|
||||||
|
|
||||||
auto itemId = uint32((*items)[i]);
|
uint32 itemId = uint32((*items)[i]);
|
||||||
|
if (!itemId)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
hasAnyQuestItems = true;
|
||||||
|
|
||||||
if (IsNeededForQuest(bot, itemId))
|
if (IsNeededForQuest(bot, itemId))
|
||||||
{
|
{
|
||||||
this->guid = lootGUID;
|
this->guid = lootGUID;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
isQuestItemOnly |= itemId > 0;
|
|
||||||
|
const ItemTemplate* proto = sObjectMgr->GetItemTemplate(itemId);
|
||||||
|
if (!proto)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (proto->Class != ITEM_CLASS_QUEST)
|
||||||
|
{
|
||||||
|
onlyHasQuestItems = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isQuestItemOnly)
|
// Retrieve the correct loot table entry
|
||||||
|
uint32 lootEntry = go->GetGOInfo()->GetLootId();
|
||||||
|
if (lootEntry == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// Check the main loot template
|
||||||
|
if (const LootTemplate* lootTemplate = LootTemplates_Gameobject.GetLootFor(lootEntry))
|
||||||
|
{
|
||||||
|
Loot loot;
|
||||||
|
lootTemplate->Process(loot, LootTemplates_Gameobject, 1, bot);
|
||||||
|
|
||||||
|
for (const LootItem& item : loot.items)
|
||||||
|
{
|
||||||
|
uint32 itemId = item.itemid;
|
||||||
|
if (!itemId)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const ItemTemplate* proto = sObjectMgr->GetItemTemplate(itemId);
|
||||||
|
if (!proto)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (proto->Class != ITEM_CLASS_QUEST)
|
||||||
|
{
|
||||||
|
onlyHasQuestItems = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If this item references another loot table, process it
|
||||||
|
if (const LootTemplate* refLootTemplate = LootTemplates_Reference.GetLootFor(itemId))
|
||||||
|
{
|
||||||
|
Loot refLoot;
|
||||||
|
refLootTemplate->Process(refLoot, LootTemplates_Reference, 1, bot);
|
||||||
|
|
||||||
|
for (const LootItem& refItem : refLoot.items)
|
||||||
|
{
|
||||||
|
uint32 refItemId = refItem.itemid;
|
||||||
|
if (!refItemId)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const ItemTemplate* refProto = sObjectMgr->GetItemTemplate(refItemId);
|
||||||
|
if (!refProto)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (refProto->Class != ITEM_CLASS_QUEST)
|
||||||
|
{
|
||||||
|
onlyHasQuestItems = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If gameobject has only quest items that bot doesn’t need, skip it.
|
||||||
|
if (hasAnyQuestItems && onlyHasQuestItems)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Otherwise, loot it.
|
||||||
|
guid = lootGUID;
|
||||||
|
|
||||||
uint32 goId = go->GetEntry();
|
uint32 goId = go->GetEntry();
|
||||||
uint32 lockId = go->GetGOInfo()->GetLockId();
|
uint32 lockId = go->GetGOInfo()->GetLockId();
|
||||||
LockEntry const* lockInfo = sLockStore.LookupEntry(lockId);
|
LockEntry const* lockInfo = sLockStore.LookupEntry(lockId);
|
||||||
@@ -119,6 +189,7 @@ void LootObject::Refresh(Player* bot, ObjectGuid lootGUID)
|
|||||||
guid = lootGUID;
|
guid = lootGUID;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case LOCK_KEY_SKILL:
|
case LOCK_KEY_SKILL:
|
||||||
if (goId == 13891 || goId == 19535) // Serpentbloom
|
if (goId == 13891 || goId == 19535) // Serpentbloom
|
||||||
{
|
{
|
||||||
@@ -131,6 +202,7 @@ void LootObject::Refresh(Player* bot, ObjectGuid lootGUID)
|
|||||||
guid = lootGUID;
|
guid = lootGUID;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case LOCK_KEY_NONE:
|
case LOCK_KEY_NONE:
|
||||||
guid = lootGUID;
|
guid = lootGUID;
|
||||||
break;
|
break;
|
||||||
@@ -200,7 +272,11 @@ LootObject::LootObject(LootObject const& other)
|
|||||||
|
|
||||||
bool LootObject::IsLootPossible(Player* bot)
|
bool LootObject::IsLootPossible(Player* bot)
|
||||||
{
|
{
|
||||||
if (IsEmpty() || !GetWorldObject(bot))
|
if (IsEmpty() || !bot)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
WorldObject* worldObj = GetWorldObject(bot); // Store result to avoid multiple calls
|
||||||
|
if (!worldObj)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
|
PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
|
||||||
@@ -211,7 +287,7 @@ bool LootObject::IsLootPossible(Player* bot)
|
|||||||
if (reqItem && !bot->HasItemCount(reqItem, 1))
|
if (reqItem && !bot->HasItemCount(reqItem, 1))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (abs(GetWorldObject(bot)->GetPositionZ() - bot->GetPositionZ()) > INTERACTION_DISTANCE)
|
if (abs(worldObj->GetPositionZ() - bot->GetPositionZ()) > INTERACTION_DISTANCE -2.0f)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Creature* creature = botAI->GetCreature(guid);
|
Creature* creature = botAI->GetCreature(guid);
|
||||||
@@ -236,25 +312,32 @@ bool LootObject::IsLootPossible(Player* bot)
|
|||||||
uint32 skillValue = uint32(bot->GetSkillValue(skillId));
|
uint32 skillValue = uint32(bot->GetSkillValue(skillId));
|
||||||
if (reqSkillValue > skillValue)
|
if (reqSkillValue > skillValue)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (skillId == SKILL_MINING && !bot->HasItemCount(756, 1) &&
|
if (skillId == SKILL_MINING &&
|
||||||
!bot->HasItemCount(778, 1) &&
|
!bot->HasItemCount(756, 1) &&
|
||||||
!bot->HasItemCount(1819, 1) &&
|
!bot->HasItemCount(778, 1) &&
|
||||||
!bot->HasItemCount(1893, 1) &&
|
!bot->HasItemCount(1819, 1) &&
|
||||||
!bot->HasItemCount(1959, 1) &&
|
!bot->HasItemCount(1893, 1) &&
|
||||||
!bot->HasItemCount(2901, 1) &&
|
!bot->HasItemCount(1959, 1) &&
|
||||||
!bot->HasItemCount(9465, 1) &&
|
!bot->HasItemCount(2901, 1) &&
|
||||||
!bot->HasItemCount(20723, 1) &&
|
!bot->HasItemCount(9465, 1) &&
|
||||||
!bot->HasItemCount(40772, 1) &&
|
!bot->HasItemCount(20723, 1) &&
|
||||||
!bot->HasItemCount(40892, 1) &&
|
!bot->HasItemCount(40772, 1) &&
|
||||||
!bot->HasItemCount(40893, 1) )
|
!bot->HasItemCount(40892, 1) &&
|
||||||
|
!bot->HasItemCount(40893, 1))
|
||||||
if (skillId == SKILL_SKINNING && !bot->HasItemCount(7005, 1) &&
|
{
|
||||||
!bot->HasItemCount(40772, 1) &&
|
return false; // Bot is missing a mining pick
|
||||||
!bot->HasItemCount(40893, 1) &&
|
}
|
||||||
!bot->HasItemCount(12709, 1) &&
|
|
||||||
!bot->HasItemCount(19901, 1) )
|
if (skillId == SKILL_SKINNING &&
|
||||||
return false;
|
!bot->HasItemCount(7005, 1) &&
|
||||||
|
!bot->HasItemCount(40772, 1) &&
|
||||||
|
!bot->HasItemCount(40893, 1) &&
|
||||||
|
!bot->HasItemCount(12709, 1) &&
|
||||||
|
!bot->HasItemCount(19901, 1))
|
||||||
|
{
|
||||||
|
return false; // Bot is missing a skinning knife
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -297,7 +380,6 @@ LootObject LootObjectStack::GetLoot(float maxDistance)
|
|||||||
std::vector<LootObject> ordered = OrderByDistance(maxDistance);
|
std::vector<LootObject> ordered = OrderByDistance(maxDistance);
|
||||||
return ordered.empty() ? LootObject() : *ordered.begin();
|
return ordered.empty() ? LootObject() : *ordered.begin();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<LootObject> LootObjectStack::OrderByDistance(float maxDistance)
|
std::vector<LootObject> LootObjectStack::OrderByDistance(float maxDistance)
|
||||||
{
|
{
|
||||||
availableLoot.shrink(time(nullptr) - 30);
|
availableLoot.shrink(time(nullptr) - 30);
|
||||||
@@ -308,17 +390,23 @@ std::vector<LootObject> LootObjectStack::OrderByDistance(float maxDistance)
|
|||||||
{
|
{
|
||||||
ObjectGuid guid = i->guid;
|
ObjectGuid guid = i->guid;
|
||||||
LootObject lootObject(bot, guid);
|
LootObject lootObject(bot, guid);
|
||||||
if (!lootObject.IsLootPossible(bot))
|
if (!lootObject.IsLootPossible(bot)) // Ensure loot object is valid
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
float distance = bot->GetDistance(lootObject.GetWorldObject(bot));
|
WorldObject* worldObj = lootObject.GetWorldObject(bot);
|
||||||
|
if (!worldObj) // Prevent null pointer dereference
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
float distance = bot->GetDistance(worldObj);
|
||||||
if (!maxDistance || distance <= maxDistance)
|
if (!maxDistance || distance <= maxDistance)
|
||||||
sortedMap[distance] = lootObject;
|
sortedMap[distance] = lootObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<LootObject> result;
|
std::vector<LootObject> result;
|
||||||
for (std::map<float, LootObject>::iterator i = sortedMap.begin(); i != sortedMap.end(); i++)
|
for (auto& [_, lootObject] : sortedMap)
|
||||||
result.push_back(i->second);
|
result.push_back(lootObject);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,29 +51,11 @@ bool AddGatheringLootAction::AddLoot(ObjectGuid guid)
|
|||||||
if (loot.IsEmpty() || !wo)
|
if (loot.IsEmpty() || !wo)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!bot->IsWithinLOSInMap(wo))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (loot.skillId == SKILL_NONE)
|
if (loot.skillId == SKILL_NONE)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!loot.IsLootPossible(bot))
|
if (!loot.IsLootPossible(bot))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (sServerFacade->IsDistanceGreaterThan(sServerFacade->GetDistance2d(bot, wo), INTERACTION_DISTANCE))
|
|
||||||
{
|
|
||||||
std::list<Unit*> targets;
|
|
||||||
Acore::AnyUnfriendlyUnitInObjectRangeCheck u_check(bot, bot, sPlayerbotAIConfig->lootDistance);
|
|
||||||
Acore::UnitListSearcher<Acore::AnyUnfriendlyUnitInObjectRangeCheck> searcher(bot, targets, u_check);
|
|
||||||
Cell::VisitAllObjects(bot, searcher, sPlayerbotAIConfig->lootDistance * 1.5f);
|
|
||||||
if (!targets.empty())
|
|
||||||
{
|
|
||||||
std::ostringstream out;
|
|
||||||
out << "Kill that " << targets.front()->GetName() << " so I can loot freely";
|
|
||||||
botAI->TellError(out.str());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return AddAllLootAction::AddLoot(guid);
|
return AddAllLootAction::AddLoot(guid);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -79,9 +79,16 @@ bool OpenLootAction::DoLoot(LootObject& lootObject)
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
Creature* creature = botAI->GetCreature(lootObject.guid);
|
Creature* creature = botAI->GetCreature(lootObject.guid);
|
||||||
if (creature && bot->GetDistance(creature) > INTERACTION_DISTANCE)
|
if (creature && bot->GetDistance(creature) > INTERACTION_DISTANCE - 2.0f)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
// Dismount if the bot is mounted
|
||||||
|
if (bot->IsMounted())
|
||||||
|
{
|
||||||
|
bot->Dismount();
|
||||||
|
botAI->SetNextCheckDelay(sPlayerbotAIConfig->lootDelay); // Small delay to avoid animation issues
|
||||||
|
}
|
||||||
|
|
||||||
if (creature && creature->HasFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE))
|
if (creature && creature->HasFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE))
|
||||||
{
|
{
|
||||||
WorldPacket packet(CMSG_LOOT, 8);
|
WorldPacket packet(CMSG_LOOT, 8);
|
||||||
@@ -116,7 +123,7 @@ bool OpenLootAction::DoLoot(LootObject& lootObject)
|
|||||||
}
|
}
|
||||||
|
|
||||||
GameObject* go = botAI->GetGameObject(lootObject.guid);
|
GameObject* go = botAI->GetGameObject(lootObject.guid);
|
||||||
if (go && bot->GetDistance(go) > INTERACTION_DISTANCE)
|
if (go && bot->GetDistance(go) > INTERACTION_DISTANCE - 2.0f)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (go && (go->GetGoState() != GO_STATE_READY))
|
if (go && (go->GetGoState() != GO_STATE_READY))
|
||||||
@@ -418,6 +425,7 @@ bool StoreLootAction::Execute(Event event)
|
|||||||
WorldPacket packet(CMSG_AUTOSTORE_LOOT_ITEM, 1);
|
WorldPacket packet(CMSG_AUTOSTORE_LOOT_ITEM, 1);
|
||||||
packet << itemindex;
|
packet << itemindex;
|
||||||
bot->GetSession()->HandleAutostoreLootItemOpcode(packet);
|
bot->GetSession()->HandleAutostoreLootItemOpcode(packet);
|
||||||
|
botAI->SetNextCheckDelay(sPlayerbotAIConfig->lootDelay);
|
||||||
|
|
||||||
if (proto->Quality > ITEM_QUALITY_NORMAL && !urand(0, 50) && botAI->HasStrategy("emote", BOT_STATE_NON_COMBAT) && sPlayerbotAIConfig->randomBotEmote)
|
if (proto->Quality > ITEM_QUALITY_NORMAL && !urand(0, 50) && botAI->HasStrategy("emote", BOT_STATE_NON_COMBAT) && sPlayerbotAIConfig->randomBotEmote)
|
||||||
botAI->PlayEmote(TEXT_EMOTE_CHEER);
|
botAI->PlayEmote(TEXT_EMOTE_CHEER);
|
||||||
|
|||||||
@@ -13,9 +13,9 @@ bool LootAvailableTrigger::IsActive()
|
|||||||
{
|
{
|
||||||
return AI_VALUE(bool, "has available loot") &&
|
return AI_VALUE(bool, "has available loot") &&
|
||||||
(sServerFacade->IsDistanceLessOrEqualThan(AI_VALUE2(float, "distance", "loot target"),
|
(sServerFacade->IsDistanceLessOrEqualThan(AI_VALUE2(float, "distance", "loot target"),
|
||||||
INTERACTION_DISTANCE) ||
|
INTERACTION_DISTANCE - 2.0f) ||
|
||||||
AI_VALUE(GuidVector, "all targets").empty()) &&
|
AI_VALUE(GuidVector, "all targets").empty()) &&
|
||||||
!AI_VALUE2(bool, "combat", "self target") && !AI_VALUE2(bool, "mounted", "self target");
|
!AI_VALUE2(bool, "combat", "self target");
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FarFromCurrentLootTrigger::IsActive()
|
bool FarFromCurrentLootTrigger::IsActive()
|
||||||
@@ -24,7 +24,7 @@ bool FarFromCurrentLootTrigger::IsActive()
|
|||||||
if (!loot.IsLootPossible(bot))
|
if (!loot.IsLootPossible(bot))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return AI_VALUE2(float, "distance", "loot target") > INTERACTION_DISTANCE;
|
return AI_VALUE2(float, "distance", "loot target") >= INTERACTION_DISTANCE - 2.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CanLootTrigger::IsActive() { return AI_VALUE(bool, "can loot"); }
|
bool CanLootTrigger::IsActive() { return AI_VALUE(bool, "can loot"); }
|
||||||
|
|||||||
Reference in New Issue
Block a user