mirror of
https://github.com/mod-playerbots/mod-playerbots
synced 2025-11-29 15:58:20 +08:00
Update EquipAction.cpp
This commit is contained in:
@@ -65,182 +65,206 @@ void EquipAction::EquipItem(Item* item)
|
|||||||
uint8 slot = item->GetSlot();
|
uint8 slot = item->GetSlot();
|
||||||
const ItemTemplate* itemProto = item->GetTemplate();
|
const ItemTemplate* itemProto = item->GetTemplate();
|
||||||
uint32 itemId = itemProto->ItemId;
|
uint32 itemId = itemProto->ItemId;
|
||||||
|
uint8 invType = itemProto->InventoryType;
|
||||||
|
|
||||||
if (itemProto->InventoryType == INVTYPE_AMMO)
|
// Handle ammunition separately
|
||||||
|
if (invType == INVTYPE_AMMO)
|
||||||
{
|
{
|
||||||
bot->SetAmmo(itemId);
|
bot->SetAmmo(itemId);
|
||||||
|
std::ostringstream out;
|
||||||
|
out << "equipping " << chat->FormatItem(itemProto);
|
||||||
|
botAI->TellMaster(out);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
// Handle bags first
|
||||||
|
bool equippedBag = false;
|
||||||
|
if (itemProto->Class == ITEM_CLASS_CONTAINER)
|
||||||
{
|
{
|
||||||
bool equippedBag = false;
|
// Attempt to equip as a bag
|
||||||
if (itemProto->Class == ITEM_CLASS_CONTAINER)
|
Bag* pBag = reinterpret_cast<Bag*>(item);
|
||||||
|
uint8 newBagSlot = GetSmallestBagSlot();
|
||||||
|
if (newBagSlot > 0)
|
||||||
{
|
{
|
||||||
Bag* pBag = (Bag*)&item;
|
uint16 src = ((bagIndex << 8) | slot);
|
||||||
uint8 newBagSlot = GetSmallestBagSlot();
|
uint16 dst = ((INVENTORY_SLOT_BAG_0 << 8) | newBagSlot);
|
||||||
if (newBagSlot > 0)
|
bot->SwapItem(src, dst);
|
||||||
|
equippedBag = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we didn't equip as a bag, try to equip as gear
|
||||||
|
if (!equippedBag)
|
||||||
|
{
|
||||||
|
uint8 dstSlot = botAI->FindEquipSlot(itemProto, NULL_SLOT, true);
|
||||||
|
|
||||||
|
// Check if the item is a weapon and whether the bot can dual wield or use Titan Grip
|
||||||
|
bool isWeapon = (itemProto->Class == ITEM_CLASS_WEAPON);
|
||||||
|
bool canTitanGrip = bot->CanTitanGrip();
|
||||||
|
bool canDualWield = bot->CanDualWield();
|
||||||
|
|
||||||
|
bool isTwoHander = (invType == INVTYPE_2HWEAPON);
|
||||||
|
bool isValidTGWeapon = false;
|
||||||
|
if (canTitanGrip && isTwoHander)
|
||||||
|
{
|
||||||
|
// Titan Grip-valid 2H weapon subclasses: Axe2, Mace2, Sword2
|
||||||
|
isValidTGWeapon = (itemProto->SubClass == ITEM_SUBCLASS_WEAPON_AXE2 ||
|
||||||
|
itemProto->SubClass == ITEM_SUBCLASS_WEAPON_MACE2 ||
|
||||||
|
itemProto->SubClass == ITEM_SUBCLASS_WEAPON_SWORD2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the main hand currently has a 2H weapon equipped
|
||||||
|
Item* currentMHItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND);
|
||||||
|
bool have2HWeaponEquipped = (currentMHItem && currentMHItem->GetTemplate()->InventoryType == INVTYPE_2HWEAPON);
|
||||||
|
|
||||||
|
bool canDualWieldOrTG = (canDualWield || (canTitanGrip && isTwoHander));
|
||||||
|
|
||||||
|
// If this is a weapon and we can dual wield or Titan Grip, check if we can improve main/off-hand setup
|
||||||
|
if (isWeapon && canDualWieldOrTG)
|
||||||
|
{
|
||||||
|
// Fetch current main hand and offhand items
|
||||||
|
Item* mainHandItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND);
|
||||||
|
Item* offHandItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND);
|
||||||
|
|
||||||
|
// Set up the stats calculator once and reuse results for performance
|
||||||
|
StatsWeightCalculator calculator(bot);
|
||||||
|
calculator.SetItemSetBonus(false);
|
||||||
|
calculator.SetOverflowPenalty(false);
|
||||||
|
|
||||||
|
// Calculate item scores once and store them
|
||||||
|
float newItemScore = calculator.CalculateItem(itemId);
|
||||||
|
float mainHandScore = mainHandItem ? calculator.CalculateItem(mainHandItem->GetTemplate()->ItemId) : 0.0f;
|
||||||
|
float offHandScore = offHandItem ? calculator.CalculateItem(offHandItem->GetTemplate()->ItemId) : 0.0f;
|
||||||
|
|
||||||
|
// Determine where this weapon can go
|
||||||
|
bool canGoMain = (invType == INVTYPE_WEAPON ||
|
||||||
|
invType == INVTYPE_WEAPONMAINHAND ||
|
||||||
|
(canTitanGrip && isTwoHander));
|
||||||
|
|
||||||
|
bool canTGOff = false;
|
||||||
|
if (canTitanGrip && isTwoHander && isValidTGWeapon)
|
||||||
|
canTGOff = true;
|
||||||
|
|
||||||
|
bool canGoOff = (invType == INVTYPE_WEAPON ||
|
||||||
|
invType == INVTYPE_WEAPONOFFHAND ||
|
||||||
|
canTGOff);
|
||||||
|
|
||||||
|
// Check if the main hand item can go to offhand if needed
|
||||||
|
bool mainHandCanGoOff = false;
|
||||||
|
if (mainHandItem)
|
||||||
{
|
{
|
||||||
uint16 src = ((bagIndex << 8) | slot);
|
const ItemTemplate* mhProto = mainHandItem->GetTemplate();
|
||||||
uint16 dst = ((INVENTORY_SLOT_BAG_0 << 8) | newBagSlot);
|
bool mhIsValidTG = false;
|
||||||
bot->SwapItem(src, dst);
|
if (canTitanGrip && mhProto->InventoryType == INVTYPE_2HWEAPON)
|
||||||
equippedBag = true;
|
{
|
||||||
|
mhIsValidTG = (mhProto->SubClass == ITEM_SUBCLASS_WEAPON_AXE2 ||
|
||||||
|
mhProto->SubClass == ITEM_SUBCLASS_WEAPON_MACE2 ||
|
||||||
|
mhProto->SubClass == ITEM_SUBCLASS_WEAPON_SWORD2);
|
||||||
|
}
|
||||||
|
|
||||||
|
mainHandCanGoOff = (mhProto->InventoryType == INVTYPE_WEAPON ||
|
||||||
|
mhProto->InventoryType == INVTYPE_WEAPONOFFHAND ||
|
||||||
|
(mhProto->InventoryType == INVTYPE_2HWEAPON && mhIsValidTG));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Priority 1: Replace main hand if the new weapon is strictly better
|
||||||
|
// and if conditions allow (e.g. no conflicting 2H logic)
|
||||||
|
bool betterThanMH = (newItemScore > mainHandScore);
|
||||||
|
bool mhConditionOK = ((invType != INVTYPE_2HWEAPON && !have2HWeaponEquipped) ||
|
||||||
|
(canTitanGrip && isValidTGWeapon));
|
||||||
|
|
||||||
|
if (canGoMain && betterThanMH && mhConditionOK)
|
||||||
|
{
|
||||||
|
// Equip new weapon in main hand
|
||||||
|
{
|
||||||
|
WorldPacket eqPacket(CMSG_AUTOEQUIP_ITEM_SLOT, 2);
|
||||||
|
ObjectGuid newItemGuid = item->GetGUID();
|
||||||
|
eqPacket << newItemGuid << uint8(EQUIPMENT_SLOT_MAINHAND);
|
||||||
|
bot->GetSession()->HandleAutoEquipItemSlotOpcode(eqPacket);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try moving old main hand weapon to offhand if beneficial
|
||||||
|
if (mainHandItem && mainHandCanGoOff && (!offHandItem || mainHandScore > offHandScore))
|
||||||
|
{
|
||||||
|
const ItemTemplate* oldMHProto = mainHandItem->GetTemplate();
|
||||||
|
|
||||||
|
WorldPacket offhandPacket(CMSG_AUTOEQUIP_ITEM_SLOT, 2);
|
||||||
|
ObjectGuid oldMHGuid = mainHandItem->GetGUID();
|
||||||
|
offhandPacket << oldMHGuid << uint8(EQUIPMENT_SLOT_OFFHAND);
|
||||||
|
bot->GetSession()->HandleAutoEquipItemSlotOpcode(offhandPacket);
|
||||||
|
|
||||||
|
std::ostringstream moveMsg;
|
||||||
|
moveMsg << "moving " << chat->FormatItem(oldMHProto) << " to offhand";
|
||||||
|
botAI->TellMaster(moveMsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostringstream out;
|
||||||
|
out << "equipping " << chat->FormatItem(itemProto) << " in main hand as an upgrade";
|
||||||
|
botAI->TellMaster(out);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Priority 2: If not better than main hand, check if better than offhand
|
||||||
|
else if (canGoOff && newItemScore > offHandScore)
|
||||||
|
{
|
||||||
|
// Equip in offhand
|
||||||
|
WorldPacket eqPacket(CMSG_AUTOEQUIP_ITEM_SLOT, 2);
|
||||||
|
ObjectGuid newItemGuid = item->GetGUID();
|
||||||
|
eqPacket << newItemGuid << uint8(EQUIPMENT_SLOT_OFFHAND);
|
||||||
|
bot->GetSession()->HandleAutoEquipItemSlotOpcode(eqPacket);
|
||||||
|
|
||||||
|
std::ostringstream out;
|
||||||
|
out << "equipping " << chat->FormatItem(itemProto) << " in offhand as an upgrade";
|
||||||
|
botAI->TellMaster(out);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// No improvement, do nothing
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!equippedBag)
|
// If not a special dual-wield/TG scenario or no improvement found, fall back to original logic
|
||||||
|
if (dstSlot == EQUIPMENT_SLOT_FINGER1 ||
|
||||||
|
dstSlot == EQUIPMENT_SLOT_TRINKET1 ||
|
||||||
|
(dstSlot == EQUIPMENT_SLOT_MAINHAND && canDualWield &&
|
||||||
|
((invType != INVTYPE_2HWEAPON && !have2HWeaponEquipped) || (canTitanGrip && isValidTGWeapon))))
|
||||||
{
|
{
|
||||||
uint8 dstSlot = botAI->FindEquipSlot(itemProto, NULL_SLOT, true);
|
// Handle ring/trinket dual-slot logic
|
||||||
|
Item* const equippedItems[2] = {
|
||||||
|
bot->GetItemByPos(INVENTORY_SLOT_BAG_0, dstSlot),
|
||||||
|
bot->GetItemByPos(INVENTORY_SLOT_BAG_0, dstSlot + 1)
|
||||||
|
};
|
||||||
|
|
||||||
bool isWeapon = (itemProto->Class == ITEM_CLASS_WEAPON);
|
if (equippedItems[0])
|
||||||
bool have2HWeapon = false;
|
|
||||||
bool isValidTGWeapon = false;
|
|
||||||
|
|
||||||
if (bot->CanTitanGrip() && itemProto->InventoryType == INVTYPE_2HWEAPON)
|
|
||||||
{
|
{
|
||||||
isValidTGWeapon = (itemProto->SubClass == ITEM_SUBCLASS_WEAPON_AXE2 ||
|
if (equippedItems[1])
|
||||||
itemProto->SubClass == ITEM_SUBCLASS_WEAPON_MACE2 ||
|
|
||||||
itemProto->SubClass == ITEM_SUBCLASS_WEAPON_SWORD2);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if we currently have a 2H weapon in main hand
|
|
||||||
{
|
|
||||||
Item* currentWeapon = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND);
|
|
||||||
have2HWeapon = currentWeapon && currentWeapon->GetTemplate()->InventoryType == INVTYPE_2HWEAPON;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool canDualWieldOrTG = (bot->CanDualWield() || (bot->CanTitanGrip() && itemProto->InventoryType == INVTYPE_2HWEAPON));
|
|
||||||
|
|
||||||
// Run best-weapon logic only if it's a weapon and can dual wield or Titan Grip.
|
|
||||||
// Remove conditions that previously restricted running this logic based on dstSlot.
|
|
||||||
// We'll always check if this new weapon is better, and then decide final slot.
|
|
||||||
if (isWeapon && canDualWieldOrTG)
|
|
||||||
{
|
|
||||||
Item* mainHandItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND);
|
|
||||||
Item* offHandItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND);
|
|
||||||
|
|
||||||
StatsWeightCalculator calculator(bot);
|
|
||||||
calculator.SetItemSetBonus(false);
|
|
||||||
calculator.SetOverflowPenalty(false);
|
|
||||||
|
|
||||||
float newItemScore = calculator.CalculateItem(itemId);
|
|
||||||
float mainHandScore = mainHandItem ? calculator.CalculateItem(mainHandItem->GetTemplate()->ItemId) : 0.0f;
|
|
||||||
float offHandScore = offHandItem ? calculator.CalculateItem(offHandItem->GetTemplate()->ItemId) : 0.0f;
|
|
||||||
|
|
||||||
bool canGoMain = (itemProto->InventoryType == INVTYPE_WEAPON ||
|
|
||||||
itemProto->InventoryType == INVTYPE_WEAPONMAINHAND ||
|
|
||||||
(bot->CanTitanGrip() && itemProto->InventoryType == INVTYPE_2HWEAPON));
|
|
||||||
|
|
||||||
bool canTGOff = false;
|
|
||||||
if (bot->CanTitanGrip() && itemProto->InventoryType == INVTYPE_2HWEAPON)
|
|
||||||
{
|
{
|
||||||
canTGOff = isValidTGWeapon;
|
// Both slots are full - pick the worst item to replace
|
||||||
}
|
StatsWeightCalculator calc(bot);
|
||||||
|
calc.SetItemSetBonus(false);
|
||||||
|
calc.SetOverflowPenalty(false);
|
||||||
|
|
||||||
bool canGoOff = (itemProto->InventoryType == INVTYPE_WEAPON ||
|
float firstItemScore = calc.CalculateItem(equippedItems[0]->GetTemplate()->ItemId);
|
||||||
itemProto->InventoryType == INVTYPE_WEAPONOFFHAND ||
|
float secondItemScore = calc.CalculateItem(equippedItems[1]->GetTemplate()->ItemId);
|
||||||
canTGOff);
|
|
||||||
|
|
||||||
bool mainHandCanGoOff = false;
|
// If the second slot is worse, place the new item there
|
||||||
if (mainHandItem)
|
if (firstItemScore > secondItemScore)
|
||||||
{
|
|
||||||
const ItemTemplate* mhProto = mainHandItem->GetTemplate();
|
|
||||||
bool mhIsValidTG = (bot->CanTitanGrip() && mhProto->InventoryType == INVTYPE_2HWEAPON &&
|
|
||||||
(mhProto->SubClass == ITEM_SUBCLASS_WEAPON_AXE2 ||
|
|
||||||
mhProto->SubClass == ITEM_SUBCLASS_WEAPON_MACE2 ||
|
|
||||||
mhProto->SubClass == ITEM_SUBCLASS_WEAPON_SWORD2));
|
|
||||||
|
|
||||||
mainHandCanGoOff = (mhProto->InventoryType == INVTYPE_WEAPON ||
|
|
||||||
mhProto->InventoryType == INVTYPE_WEAPONOFFHAND ||
|
|
||||||
(mhProto->InventoryType == INVTYPE_2HWEAPON && mhIsValidTG));
|
|
||||||
}
|
|
||||||
|
|
||||||
// First priority: If new weapon is better than main hand and can be equipped in main hand,
|
|
||||||
// do that.
|
|
||||||
if (canGoMain && newItemScore > mainHandScore &&
|
|
||||||
((itemProto->InventoryType != INVTYPE_2HWEAPON && !have2HWeapon) || (bot->CanTitanGrip() && isValidTGWeapon)))
|
|
||||||
{
|
|
||||||
// Equip new weapon in main hand
|
|
||||||
{
|
{
|
||||||
WorldPacket eqPacket(CMSG_AUTOEQUIP_ITEM_SLOT, 2);
|
|
||||||
ObjectGuid newItemGuid = item->GetGUID();
|
|
||||||
eqPacket << newItemGuid << uint8(EQUIPMENT_SLOT_MAINHAND);
|
|
||||||
bot->GetSession()->HandleAutoEquipItemSlotOpcode(eqPacket);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we had a main hand item, consider moving it to offhand if beneficial
|
|
||||||
if (mainHandItem && mainHandCanGoOff &&
|
|
||||||
(!offHandItem || mainHandScore > offHandScore))
|
|
||||||
{
|
|
||||||
WorldPacket offhandPacket(CMSG_AUTOEQUIP_ITEM_SLOT, 2);
|
|
||||||
ObjectGuid oldMHGuid = mainHandItem->GetGUID();
|
|
||||||
offhandPacket << oldMHGuid << uint8(EQUIPMENT_SLOT_OFFHAND);
|
|
||||||
bot->GetSession()->HandleAutoEquipItemSlotOpcode(offhandPacket);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::ostringstream out;
|
|
||||||
out << "equipping " << chat->FormatItem(itemProto) << " in main hand as an upgrade";
|
|
||||||
botAI->TellMaster(out);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// If not better than main hand, check if it's better than offhand
|
|
||||||
else if (canGoOff && newItemScore > offHandScore)
|
|
||||||
{
|
|
||||||
// Equip in offhand
|
|
||||||
WorldPacket eqPacket(CMSG_AUTOEQUIP_ITEM_SLOT, 2);
|
|
||||||
ObjectGuid newItemGuid = item->GetGUID();
|
|
||||||
eqPacket << newItemGuid << uint8(EQUIPMENT_SLOT_OFFHAND);
|
|
||||||
bot->GetSession()->HandleAutoEquipItemSlotOpcode(eqPacket);
|
|
||||||
|
|
||||||
std::ostringstream out;
|
|
||||||
out << "equipping " << chat->FormatItem(itemProto) << " in offhand as an upgrade";
|
|
||||||
botAI->TellMaster(out);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// No improvement, do nothing
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If not a special dual-wield/TG scenario or no improvement found, fall back to original logic
|
|
||||||
if (dstSlot == EQUIPMENT_SLOT_FINGER1 ||
|
|
||||||
dstSlot == EQUIPMENT_SLOT_TRINKET1 ||
|
|
||||||
(dstSlot == EQUIPMENT_SLOT_MAINHAND && bot->CanDualWield() &&
|
|
||||||
((itemProto->InventoryType != INVTYPE_2HWEAPON && !have2HWeapon) || (bot->CanTitanGrip() && isValidTGWeapon))))
|
|
||||||
{
|
|
||||||
Item* const equippedItems[2] = {
|
|
||||||
bot->GetItemByPos(INVENTORY_SLOT_BAG_0, dstSlot),
|
|
||||||
bot->GetItemByPos(INVENTORY_SLOT_BAG_0, dstSlot + 1)
|
|
||||||
};
|
|
||||||
|
|
||||||
if (equippedItems[0])
|
|
||||||
{
|
|
||||||
if (equippedItems[1])
|
|
||||||
{
|
|
||||||
// Both slots are full - determine worst item to replace
|
|
||||||
StatsWeightCalculator calculator(bot);
|
|
||||||
calculator.SetItemSetBonus(false);
|
|
||||||
calculator.SetOverflowPenalty(false);
|
|
||||||
|
|
||||||
float equippedItemScore[2] = {
|
|
||||||
calculator.CalculateItem(equippedItems[0]->GetTemplate()->ItemId),
|
|
||||||
calculator.CalculateItem(equippedItems[1]->GetTemplate()->ItemId)
|
|
||||||
};
|
|
||||||
|
|
||||||
// Second item is worse than first, equip candidate item in second slot
|
|
||||||
if (equippedItemScore[0] > equippedItemScore[1])
|
|
||||||
{
|
|
||||||
dstSlot++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// No item in second slot, equip there
|
|
||||||
dstSlot++;
|
dstSlot++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Second slot empty, use it
|
||||||
|
dstSlot++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equip the item in the chosen slot
|
||||||
|
{
|
||||||
WorldPacket packet(CMSG_AUTOEQUIP_ITEM_SLOT, 2);
|
WorldPacket packet(CMSG_AUTOEQUIP_ITEM_SLOT, 2);
|
||||||
ObjectGuid itemguid = item->GetGUID();
|
ObjectGuid itemguid = item->GetGUID();
|
||||||
packet << itemguid << dstSlot;
|
packet << itemguid << dstSlot;
|
||||||
@@ -253,6 +277,7 @@ void EquipAction::EquipItem(Item* item)
|
|||||||
botAI->TellMaster(out);
|
botAI->TellMaster(out);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool EquipUpgradesAction::Execute(Event event)
|
bool EquipUpgradesAction::Execute(Event event)
|
||||||
{
|
{
|
||||||
if (!sPlayerbotAIConfig->autoEquipUpgradeLoot && !sRandomPlayerbotMgr->IsRandomBot(bot))
|
if (!sPlayerbotAIConfig->autoEquipUpgradeLoot && !sRandomPlayerbotMgr->IsRandomBot(bot))
|
||||||
|
|||||||
Reference in New Issue
Block a user