mirror of
https://github.com/mod-playerbots/mod-playerbots.git
synced 2026-01-15 18:00:27 +00:00
Big update.
This commit is contained in:
558
src/strategy/actions/UseItemAction.cpp
Normal file
558
src/strategy/actions/UseItemAction.cpp
Normal file
@@ -0,0 +1,558 @@
|
||||
/*
|
||||
* 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 "UseItemAction.h"
|
||||
#include "ChatHelper.h"
|
||||
#include "Event.h"
|
||||
#include "ItemUsageValue.h"
|
||||
#include "Playerbots.h"
|
||||
|
||||
bool UseItemAction::Execute(Event event)
|
||||
{
|
||||
std::string name = event.getParam();
|
||||
if (name.empty())
|
||||
name = getName();
|
||||
|
||||
std::vector<Item*> items = AI_VALUE2(std::vector<Item*>, "inventory items", name);
|
||||
GuidVector gos = chat->parseGameobjects(name);
|
||||
|
||||
if (gos.empty())
|
||||
{
|
||||
if (items.size() > 1)
|
||||
{
|
||||
std::vector<Item*>::iterator i = items.begin();
|
||||
Item* item = *i++;
|
||||
Item* itemTarget = *i;
|
||||
|
||||
if (item->IsPotion() || item->GetTemplate()->Class == ITEM_CLASS_CONSUMABLE)
|
||||
return UseItemAuto(item);
|
||||
else
|
||||
return UseItemOnItem(item, itemTarget);
|
||||
}
|
||||
else if (!items.empty())
|
||||
return UseItemAuto(*items.begin());
|
||||
}
|
||||
else
|
||||
{
|
||||
if (items.empty())
|
||||
return UseGameObject(*gos.begin());
|
||||
else
|
||||
return UseItemOnGameObject(*items.begin(), *gos.begin());
|
||||
}
|
||||
|
||||
botAI->TellError("No items (or game objects) available");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool UseItemAction::UseGameObject(ObjectGuid guid)
|
||||
{
|
||||
GameObject* go = botAI->GetGameObject(guid);
|
||||
if (!go || !go->isSpawned() || go->GetGoState() != GO_STATE_READY)
|
||||
return false;
|
||||
|
||||
go->Use(bot);
|
||||
|
||||
std::ostringstream out;
|
||||
out << "Using " << chat->FormatGameobject(go);
|
||||
botAI->TellMasterNoFacing(out.str());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UseItemAction::UseItemAuto(Item* item)
|
||||
{
|
||||
return UseItem(item, ObjectGuid::Empty, nullptr);
|
||||
}
|
||||
|
||||
bool UseItemAction::UseItemOnGameObject(Item* item, ObjectGuid go)
|
||||
{
|
||||
return UseItem(item, go, nullptr);
|
||||
}
|
||||
|
||||
bool UseItemAction::UseItemOnItem(Item* item, Item* itemTarget)
|
||||
{
|
||||
return UseItem(item, ObjectGuid::Empty, itemTarget);
|
||||
}
|
||||
|
||||
bool UseItemAction::UseItem(Item* item, ObjectGuid goGuid, Item* itemTarget, Unit* unitTarget)
|
||||
{
|
||||
if (bot->CanUseItem(item) != EQUIP_ERR_OK)
|
||||
return false;
|
||||
|
||||
if (bot->IsNonMeleeSpellCast(true))
|
||||
return false;
|
||||
|
||||
uint8 bagIndex = item->GetBagSlot();
|
||||
uint8 slot = item->GetSlot();
|
||||
uint8 spell_index = 0;
|
||||
uint8 cast_count = 1;
|
||||
ObjectGuid item_guid = item->GetGUID();
|
||||
uint32 glyphIndex = 0;
|
||||
uint8 castFlags = 0;
|
||||
uint32 targetFlag = TARGET_FLAG_NONE;
|
||||
uint32 spellId = 0;
|
||||
for (uint8 i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i)
|
||||
{
|
||||
if (item->GetTemplate()->Spells[i].SpellId > 0)
|
||||
{
|
||||
spellId = item->GetTemplate()->Spells[i].SpellId;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
WorldPacket packet(CMSG_USE_ITEM);
|
||||
packet << bagIndex << slot << cast_count << spellId << item_guid << glyphIndex << castFlags;
|
||||
|
||||
bool targetSelected = false;
|
||||
|
||||
std::ostringstream out;
|
||||
out << "Using " << chat->FormatItem(item->GetTemplate());
|
||||
|
||||
if (item->GetTemplate()->Stackable > 1)
|
||||
{
|
||||
uint32 count = item->GetCount();
|
||||
if (count > 1)
|
||||
out << " (" << count << " available) ";
|
||||
else
|
||||
out << " (the last one!)";
|
||||
}
|
||||
|
||||
if (goGuid)
|
||||
{
|
||||
GameObject* go = botAI->GetGameObject(goGuid);
|
||||
if (!go || !go->isSpawned())
|
||||
return false;
|
||||
|
||||
targetFlag = TARGET_FLAG_GAMEOBJECT;
|
||||
|
||||
packet << targetFlag;
|
||||
packet << goGuid.WriteAsPacked();
|
||||
out << " on " << chat->FormatGameobject(go);
|
||||
targetSelected = true;
|
||||
}
|
||||
|
||||
if (itemTarget)
|
||||
{
|
||||
if (item->GetTemplate()->Class == ITEM_CLASS_GEM)
|
||||
{
|
||||
bool fit = SocketItem(itemTarget, item) || SocketItem(itemTarget, item, true);
|
||||
if (!fit)
|
||||
botAI->TellMaster("Socket does not fit");
|
||||
|
||||
return fit;
|
||||
}
|
||||
else
|
||||
{
|
||||
targetFlag = TARGET_FLAG_ITEM;
|
||||
packet << targetFlag;
|
||||
packet << itemTarget->GetGUID().WriteAsPacked();
|
||||
out << " on " << chat->FormatItem(itemTarget->GetTemplate());
|
||||
targetSelected = true;
|
||||
}
|
||||
}
|
||||
|
||||
Player* master = GetMaster();
|
||||
if (!targetSelected && item->GetTemplate()->Class != ITEM_CLASS_CONSUMABLE && master && botAI->HasActivePlayerMaster() && !selfOnly)
|
||||
{
|
||||
if (ObjectGuid masterSelection = master->GetTarget())
|
||||
{
|
||||
Unit* unit = botAI->GetUnit(masterSelection);
|
||||
if (unit)
|
||||
{
|
||||
targetFlag = TARGET_FLAG_UNIT;
|
||||
packet << targetFlag << masterSelection.WriteAsPacked();
|
||||
out << " on " << unit->GetName();
|
||||
targetSelected = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!targetSelected && item->GetTemplate()->Class != ITEM_CLASS_CONSUMABLE && unitTarget)
|
||||
{
|
||||
targetFlag = TARGET_FLAG_UNIT;
|
||||
packet << targetFlag << unitTarget->GetGUID().WriteAsPacked();
|
||||
out << " on " << unitTarget->GetName();
|
||||
targetSelected = true;
|
||||
}
|
||||
|
||||
|
||||
if (uint32 questid = item->GetTemplate()->StartQuest)
|
||||
{
|
||||
if (Quest const* qInfo = sObjectMgr->GetQuestTemplate(questid))
|
||||
{
|
||||
WorldPacket packet(CMSG_QUESTGIVER_ACCEPT_QUEST, 8 + 4 + 4);
|
||||
packet << item_guid;
|
||||
packet << questid;
|
||||
packet << uint32(0);
|
||||
bot->GetSession()->HandleQuestgiverAcceptQuestOpcode(packet);
|
||||
|
||||
std::ostringstream out;
|
||||
out << "Got quest " << chat->FormatQuest(qInfo);
|
||||
botAI->TellMasterNoFacing(out.str());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bot->ClearUnitState(UNIT_STATE_CHASE);
|
||||
bot->ClearUnitState(UNIT_STATE_FOLLOW);
|
||||
|
||||
if (bot->isMoving())
|
||||
{
|
||||
bot->StopMoving();
|
||||
botAI->SetNextCheckDelay(sPlayerbotAIConfig->globalCoolDown);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (uint8 i = 0; i < MAX_ITEM_PROTO_SPELLS; i++)
|
||||
{
|
||||
spellId = item->GetTemplate()->Spells[i].SpellId;
|
||||
if (!spellId)
|
||||
continue;
|
||||
|
||||
if (!botAI->CanCastSpell(spellId, bot, false))
|
||||
continue;
|
||||
|
||||
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId);
|
||||
if (spellInfo->Targets & TARGET_FLAG_ITEM)
|
||||
{
|
||||
Item* itemForSpell = AI_VALUE2(Item*, "item for spell", spellId);
|
||||
if (!itemForSpell)
|
||||
continue;
|
||||
|
||||
if (itemForSpell->GetEnchantmentId(TEMP_ENCHANTMENT_SLOT))
|
||||
continue;
|
||||
|
||||
if (bot->GetTrader())
|
||||
{
|
||||
if (selfOnly)
|
||||
return false;
|
||||
|
||||
targetFlag = TARGET_FLAG_TRADE_ITEM;
|
||||
packet << targetFlag << (uint8)1 << ObjectGuid((uint64)TRADE_SLOT_NONTRADED).WriteAsPacked();
|
||||
targetSelected = true;
|
||||
out << " on traded item";
|
||||
}
|
||||
else
|
||||
{
|
||||
targetFlag = TARGET_FLAG_ITEM;
|
||||
packet << targetFlag;
|
||||
packet << itemForSpell->GetGUID().WriteAsPacked();
|
||||
targetSelected = true;
|
||||
out << " on "<< chat->FormatItem(itemForSpell->GetTemplate());
|
||||
}
|
||||
|
||||
Spell* spell = new Spell(bot, spellInfo, TRIGGERED_NONE);
|
||||
botAI->WaitForSpellCast(spell);
|
||||
delete spell;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (!targetSelected)
|
||||
{
|
||||
targetFlag = TARGET_FLAG_NONE;
|
||||
packet << targetFlag;
|
||||
packet << bot->GetPackGUID();
|
||||
targetSelected = true;
|
||||
out << " on self";
|
||||
}
|
||||
|
||||
ItemTemplate const* proto = item->GetTemplate();
|
||||
bool isDrink = proto->Spells[0].SpellCategory == 59;
|
||||
bool isFood = proto->Spells[0].SpellCategory == 11;
|
||||
if (proto->Class == ITEM_CLASS_CONSUMABLE && (proto->SubClass == ITEM_SUBCLASS_FOOD || proto->SubClass == ITEM_SUBCLASS_CONSUMABLE) && (isFood || isDrink))
|
||||
{
|
||||
if (bot->IsInCombat())
|
||||
return false;
|
||||
|
||||
bot->AddUnitState(UNIT_STAND_STATE_SIT);
|
||||
botAI->InterruptSpell();
|
||||
|
||||
float hp = bot->GetHealthPct();
|
||||
float mp = bot->GetPower(POWER_MANA) * 100.0f / bot->GetMaxPower(POWER_MANA);
|
||||
float p = 0.f;
|
||||
if (isDrink && isFood)
|
||||
{
|
||||
p = std::min(hp, mp);
|
||||
TellConsumableUse(item, "Feasting", p);
|
||||
}
|
||||
else if (isDrink)
|
||||
{
|
||||
p = mp;
|
||||
TellConsumableUse(item, "Drinking", p);
|
||||
}
|
||||
else if (isFood)
|
||||
{
|
||||
p = hp;
|
||||
TellConsumableUse(item, "Eating", p);
|
||||
}
|
||||
|
||||
if (!bot->IsInCombat() && !bot->InBattleground())
|
||||
botAI->SetNextCheckDelay(27000.0f * (100 - p) / 100.0f);
|
||||
|
||||
if (!bot->IsInCombat() && bot->InBattleground())
|
||||
botAI->SetNextCheckDelay(20000.0f * (100 - p) / 100.0f);
|
||||
|
||||
//botAI->SetNextCheckDelay(27000.0f * (100 - p) / 100.0f);
|
||||
bot->GetSession()->HandleUseItemOpcode(packet);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!spellId)
|
||||
return false;
|
||||
|
||||
botAI->SetNextCheckDelay(sPlayerbotAIConfig->globalCoolDown);
|
||||
botAI->TellMasterNoFacing(out.str());
|
||||
bot->GetSession()->HandleUseItemOpcode(packet);
|
||||
return true;
|
||||
}
|
||||
|
||||
void UseItemAction::TellConsumableUse(Item* item, std::string const action, float percent)
|
||||
{
|
||||
std::ostringstream out;
|
||||
out << action << " " << chat->FormatItem(item->GetTemplate());
|
||||
|
||||
if (item->GetTemplate()->Stackable > 1)
|
||||
out << "/x" << item->GetCount();
|
||||
|
||||
out << " (" << round(percent) << "%)";
|
||||
botAI->TellMasterNoFacing(out.str());
|
||||
}
|
||||
|
||||
bool UseItemAction::SocketItem(Item* item, Item* gem, bool replace)
|
||||
{
|
||||
WorldPacket* const packet = new WorldPacket(CMSG_SOCKET_GEMS);
|
||||
*packet << item->GetGUID();
|
||||
|
||||
bool fits = false;
|
||||
for (uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot < SOCK_ENCHANTMENT_SLOT + MAX_GEM_SOCKETS; ++enchant_slot)
|
||||
{
|
||||
uint8 SocketColor = item->GetTemplate()->Socket[enchant_slot - SOCK_ENCHANTMENT_SLOT].Color;
|
||||
GemPropertiesEntry const* gemProperty = sGemPropertiesStore.LookupEntry(gem->GetTemplate()->GemProperties);
|
||||
if (gemProperty && (gemProperty->color & SocketColor))
|
||||
{
|
||||
if (fits)
|
||||
{
|
||||
*packet << ObjectGuid::Empty;
|
||||
continue;
|
||||
}
|
||||
|
||||
uint32 enchant_id = item->GetEnchantmentId(EnchantmentSlot(enchant_slot));
|
||||
if (!enchant_id)
|
||||
{
|
||||
*packet << gem->GetGUID();
|
||||
fits = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
SpellItemEnchantmentEntry const* enchantEntry = sSpellItemEnchantmentStore.LookupEntry(enchant_id);
|
||||
if (!enchantEntry || !enchantEntry->GemID)
|
||||
{
|
||||
*packet << gem->GetGUID();
|
||||
fits = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (replace && enchantEntry->GemID != gem->GetTemplate()->ItemId)
|
||||
{
|
||||
*packet << gem->GetGUID();
|
||||
fits = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
*packet << ObjectGuid::Empty;
|
||||
}
|
||||
|
||||
if (fits)
|
||||
{
|
||||
std::ostringstream out;
|
||||
out << "Socketing " << chat->FormatItem(item->GetTemplate());
|
||||
out << " with " << chat->FormatItem(gem->GetTemplate());
|
||||
botAI->TellMaster(out);
|
||||
|
||||
bot->GetSession()->HandleSocketOpcode(*packet);
|
||||
}
|
||||
|
||||
return fits;
|
||||
}
|
||||
|
||||
bool UseItemAction::isPossible()
|
||||
{
|
||||
return getName() == "use" || AI_VALUE2(uint32, "item count", getName()) > 0;
|
||||
}
|
||||
|
||||
bool UseSpellItemAction::isUseful()
|
||||
{
|
||||
return AI_VALUE2(bool, "spell cast useful", getName());
|
||||
}
|
||||
|
||||
bool UseHealingPotion::isUseful()
|
||||
{
|
||||
return AI_VALUE2(bool, "combat", "self target");
|
||||
}
|
||||
|
||||
bool UseManaPotion::isUseful()
|
||||
{
|
||||
return AI_VALUE2(bool, "combat", "self target");
|
||||
}
|
||||
|
||||
bool UseHearthStone::Execute(Event event)
|
||||
{
|
||||
if (bot->isMoving())
|
||||
{
|
||||
MotionMaster& mm = *bot->GetMotionMaster();
|
||||
bot->StopMoving();
|
||||
mm.Clear();
|
||||
}
|
||||
|
||||
bool used = UseItemAction::Execute(event);
|
||||
if (used)
|
||||
{
|
||||
RESET_AI_VALUE(bool, "combat::self target");
|
||||
RESET_AI_VALUE(WorldPosition, "current position");
|
||||
botAI->SetNextCheckDelay(10 * IN_MILLISECONDS);
|
||||
}
|
||||
|
||||
return used;
|
||||
}
|
||||
|
||||
bool UseHearthStone::isUseful()
|
||||
{
|
||||
return !bot->InBattleground();
|
||||
}
|
||||
|
||||
bool UseRandomRecipe::isUseful()
|
||||
{
|
||||
return !bot->IsInCombat() && !botAI->HasActivePlayerMaster() && !bot->InBattleground();
|
||||
}
|
||||
|
||||
bool UseRandomRecipe::isPossible()
|
||||
{
|
||||
return AI_VALUE2(uint32, "item count", "recipe") > 0;
|
||||
}
|
||||
|
||||
bool UseRandomRecipe::Execute(Event event)
|
||||
{
|
||||
std::vector<Item*> recipes = AI_VALUE2(std::vector<Item*>, "inventory items", "recipe");
|
||||
|
||||
std::string recipeName = "";
|
||||
|
||||
for (auto& recipe : recipes)
|
||||
{
|
||||
recipeName = recipe->GetTemplate()->Name1;
|
||||
}
|
||||
|
||||
if (recipeName.empty())
|
||||
return false;
|
||||
|
||||
bool used = UseItemAction::Execute(Event(name,recipeName));
|
||||
|
||||
if (used)
|
||||
botAI->SetNextCheckDelay(3.0 * IN_MILLISECONDS);
|
||||
|
||||
return used;
|
||||
}
|
||||
|
||||
bool UseRandomQuestItem::isUseful()
|
||||
{
|
||||
return !botAI->HasActivePlayerMaster() && !bot->InBattleground() && !bot->HasUnitState(UNIT_STATE_IN_FLIGHT);
|
||||
}
|
||||
|
||||
bool UseRandomQuestItem::isPossible()
|
||||
{
|
||||
return AI_VALUE2(uint32, "item count", "quest") > 0;
|
||||
}
|
||||
|
||||
bool UseRandomQuestItem::Execute(Event event)
|
||||
{
|
||||
Unit* unitTarget = nullptr;
|
||||
ObjectGuid goTarget;
|
||||
|
||||
std::vector<Item*> questItems = AI_VALUE2(std::vector<Item*>, "inventory items", "quest");
|
||||
|
||||
Item* item = nullptr;
|
||||
uint32 delay = 0;
|
||||
|
||||
if (questItems.empty())
|
||||
return false;
|
||||
|
||||
for (uint8 i = 0; i < 5; i++)
|
||||
{
|
||||
auto itr = questItems.begin();
|
||||
std::advance(itr, urand(0, questItems.size() - 1));
|
||||
Item* questItem = *itr;
|
||||
|
||||
ItemTemplate const* proto = questItem->GetTemplate();
|
||||
|
||||
if (proto->StartQuest)
|
||||
{
|
||||
Quest const* qInfo = sObjectMgr->GetQuestTemplate(proto->StartQuest);
|
||||
if (bot->CanTakeQuest(qInfo, false))
|
||||
{
|
||||
item = questItem;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uint32 spellId = proto->Spells[0].SpellId;
|
||||
if (spellId)
|
||||
{
|
||||
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId);
|
||||
|
||||
GuidVector npcs = AI_VALUE(GuidVector, ("nearest npcs"));
|
||||
for (auto& npc : npcs)
|
||||
{
|
||||
Unit* unit = botAI->GetUnit(npc);
|
||||
if (botAI->CanCastSpell(spellId, unit, false))
|
||||
{
|
||||
item = questItem;
|
||||
unitTarget = unit;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
GuidVector gos = AI_VALUE(GuidVector, ("nearest game objects"));
|
||||
for (auto& go : gos)
|
||||
{
|
||||
GameObject* gameObject = botAI->GetGameObject(go);
|
||||
GameObjectTemplate const* goInfo = gameObject->GetGOInfo();
|
||||
if (!goInfo->GetLockId())
|
||||
continue;
|
||||
|
||||
LockEntry const* lock = sLockStore.LookupEntry(goInfo->GetLockId());
|
||||
for (uint8 i = 0; i < MAX_LOCK_CASE; ++i)
|
||||
{
|
||||
if (!lock->Type[i])
|
||||
continue;
|
||||
if (lock->Type[i] != LOCK_KEY_ITEM)
|
||||
continue;
|
||||
|
||||
if (lock->Index[i] == proto->ItemId)
|
||||
{
|
||||
item = questItem;
|
||||
goTarget = go;
|
||||
unitTarget = nullptr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!item)
|
||||
return false;
|
||||
|
||||
if (!goTarget && !unitTarget)
|
||||
return false;
|
||||
|
||||
bool used = UseItem(item, goTarget, nullptr, unitTarget);
|
||||
|
||||
if (used)
|
||||
botAI->SetNextCheckDelay(delay);
|
||||
|
||||
return used;
|
||||
}
|
||||
Reference in New Issue
Block a user