Update EquipAction.cpp

This commit is contained in:
avirar
2024-12-14 18:41:32 +11:00
committed by GitHub
parent 34c0759c90
commit 0c16f308db

View File

@@ -65,17 +65,24 @@ 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; bool equippedBag = false;
if (itemProto->Class == ITEM_CLASS_CONTAINER) if (itemProto->Class == ITEM_CLASS_CONTAINER)
{ {
Bag* pBag = (Bag*)&item; // Attempt to equip as a bag
Bag* pBag = reinterpret_cast<Bag*>(item);
uint8 newBagSlot = GetSmallestBagSlot(); uint8 newBagSlot = GetSmallestBagSlot();
if (newBagSlot > 0) if (newBagSlot > 0)
{ {
@@ -86,77 +93,87 @@ void EquipAction::EquipItem(Item* item)
} }
} }
// If we didn't equip as a bag, try to equip as gear
if (!equippedBag) if (!equippedBag)
{ {
uint8 dstSlot = botAI->FindEquipSlot(itemProto, NULL_SLOT, true); 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 isWeapon = (itemProto->Class == ITEM_CLASS_WEAPON);
bool have2HWeapon = false; bool canTitanGrip = bot->CanTitanGrip();
bool isValidTGWeapon = false; bool canDualWield = bot->CanDualWield();
if (bot->CanTitanGrip() && itemProto->InventoryType == INVTYPE_2HWEAPON) 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 || isValidTGWeapon = (itemProto->SubClass == ITEM_SUBCLASS_WEAPON_AXE2 ||
itemProto->SubClass == ITEM_SUBCLASS_WEAPON_MACE2 || itemProto->SubClass == ITEM_SUBCLASS_WEAPON_MACE2 ||
itemProto->SubClass == ITEM_SUBCLASS_WEAPON_SWORD2); itemProto->SubClass == ITEM_SUBCLASS_WEAPON_SWORD2);
} }
// Check if we currently have a 2H weapon in main hand // Check if the main hand currently has a 2H weapon equipped
{ Item* currentMHItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND);
Item* currentWeapon = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND); bool have2HWeaponEquipped = (currentMHItem && currentMHItem->GetTemplate()->InventoryType == INVTYPE_2HWEAPON);
have2HWeapon = currentWeapon && currentWeapon->GetTemplate()->InventoryType == INVTYPE_2HWEAPON;
}
bool canDualWieldOrTG = (bot->CanDualWield() || (bot->CanTitanGrip() && itemProto->InventoryType == INVTYPE_2HWEAPON)); bool canDualWieldOrTG = (canDualWield || (canTitanGrip && isTwoHander));
// Run best-weapon logic only if it's a weapon and can dual wield or Titan Grip. // If this is a weapon and we can dual wield or Titan Grip, check if we can improve main/off-hand setup
// 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) if (isWeapon && canDualWieldOrTG)
{ {
// Fetch current main hand and offhand items
Item* mainHandItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND); Item* mainHandItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND);
Item* offHandItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND); 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); StatsWeightCalculator calculator(bot);
calculator.SetItemSetBonus(false); calculator.SetItemSetBonus(false);
calculator.SetOverflowPenalty(false); calculator.SetOverflowPenalty(false);
// Calculate item scores once and store them
float newItemScore = calculator.CalculateItem(itemId); float newItemScore = calculator.CalculateItem(itemId);
float mainHandScore = mainHandItem ? calculator.CalculateItem(mainHandItem->GetTemplate()->ItemId) : 0.0f; float mainHandScore = mainHandItem ? calculator.CalculateItem(mainHandItem->GetTemplate()->ItemId) : 0.0f;
float offHandScore = offHandItem ? calculator.CalculateItem(offHandItem->GetTemplate()->ItemId) : 0.0f; float offHandScore = offHandItem ? calculator.CalculateItem(offHandItem->GetTemplate()->ItemId) : 0.0f;
bool canGoMain = (itemProto->InventoryType == INVTYPE_WEAPON || // Determine where this weapon can go
itemProto->InventoryType == INVTYPE_WEAPONMAINHAND || bool canGoMain = (invType == INVTYPE_WEAPON ||
(bot->CanTitanGrip() && itemProto->InventoryType == INVTYPE_2HWEAPON)); invType == INVTYPE_WEAPONMAINHAND ||
(canTitanGrip && isTwoHander));
bool canTGOff = false; bool canTGOff = false;
if (bot->CanTitanGrip() && itemProto->InventoryType == INVTYPE_2HWEAPON) if (canTitanGrip && isTwoHander && isValidTGWeapon)
{ canTGOff = true;
canTGOff = isValidTGWeapon;
}
bool canGoOff = (itemProto->InventoryType == INVTYPE_WEAPON || bool canGoOff = (invType == INVTYPE_WEAPON ||
itemProto->InventoryType == INVTYPE_WEAPONOFFHAND || invType == INVTYPE_WEAPONOFFHAND ||
canTGOff); canTGOff);
// Check if the main hand item can go to offhand if needed
bool mainHandCanGoOff = false; bool mainHandCanGoOff = false;
if (mainHandItem) if (mainHandItem)
{ {
const ItemTemplate* mhProto = mainHandItem->GetTemplate(); const ItemTemplate* mhProto = mainHandItem->GetTemplate();
bool mhIsValidTG = (bot->CanTitanGrip() && mhProto->InventoryType == INVTYPE_2HWEAPON && bool mhIsValidTG = false;
(mhProto->SubClass == ITEM_SUBCLASS_WEAPON_AXE2 || if (canTitanGrip && mhProto->InventoryType == INVTYPE_2HWEAPON)
{
mhIsValidTG = (mhProto->SubClass == ITEM_SUBCLASS_WEAPON_AXE2 ||
mhProto->SubClass == ITEM_SUBCLASS_WEAPON_MACE2 || mhProto->SubClass == ITEM_SUBCLASS_WEAPON_MACE2 ||
mhProto->SubClass == ITEM_SUBCLASS_WEAPON_SWORD2)); mhProto->SubClass == ITEM_SUBCLASS_WEAPON_SWORD2);
}
mainHandCanGoOff = (mhProto->InventoryType == INVTYPE_WEAPON || mainHandCanGoOff = (mhProto->InventoryType == INVTYPE_WEAPON ||
mhProto->InventoryType == INVTYPE_WEAPONOFFHAND || mhProto->InventoryType == INVTYPE_WEAPONOFFHAND ||
(mhProto->InventoryType == INVTYPE_2HWEAPON && mhIsValidTG)); (mhProto->InventoryType == INVTYPE_2HWEAPON && mhIsValidTG));
} }
// First priority: If new weapon is better than main hand and can be equipped in main hand, // Priority 1: Replace main hand if the new weapon is strictly better
// do that. // and if conditions allow (e.g. no conflicting 2H logic)
if (canGoMain && newItemScore > mainHandScore && bool betterThanMH = (newItemScore > mainHandScore);
((itemProto->InventoryType != INVTYPE_2HWEAPON && !have2HWeapon) || (bot->CanTitanGrip() && isValidTGWeapon))) bool mhConditionOK = ((invType != INVTYPE_2HWEAPON && !have2HWeaponEquipped) ||
(canTitanGrip && isValidTGWeapon));
if (canGoMain && betterThanMH && mhConditionOK)
{ {
// Equip new weapon in main hand // Equip new weapon in main hand
{ {
@@ -166,14 +183,19 @@ void EquipAction::EquipItem(Item* item)
bot->GetSession()->HandleAutoEquipItemSlotOpcode(eqPacket); bot->GetSession()->HandleAutoEquipItemSlotOpcode(eqPacket);
} }
// If we had a main hand item, consider moving it to offhand if beneficial // Try moving old main hand weapon to offhand if beneficial
if (mainHandItem && mainHandCanGoOff && if (mainHandItem && mainHandCanGoOff && (!offHandItem || mainHandScore > offHandScore))
(!offHandItem || mainHandScore > offHandScore))
{ {
const ItemTemplate* oldMHProto = mainHandItem->GetTemplate();
WorldPacket offhandPacket(CMSG_AUTOEQUIP_ITEM_SLOT, 2); WorldPacket offhandPacket(CMSG_AUTOEQUIP_ITEM_SLOT, 2);
ObjectGuid oldMHGuid = mainHandItem->GetGUID(); ObjectGuid oldMHGuid = mainHandItem->GetGUID();
offhandPacket << oldMHGuid << uint8(EQUIPMENT_SLOT_OFFHAND); offhandPacket << oldMHGuid << uint8(EQUIPMENT_SLOT_OFFHAND);
bot->GetSession()->HandleAutoEquipItemSlotOpcode(offhandPacket); bot->GetSession()->HandleAutoEquipItemSlotOpcode(offhandPacket);
std::ostringstream moveMsg;
moveMsg << "moving " << chat->FormatItem(oldMHProto) << " to offhand";
botAI->TellMaster(moveMsg);
} }
std::ostringstream out; std::ostringstream out;
@@ -181,7 +203,8 @@ void EquipAction::EquipItem(Item* item)
botAI->TellMaster(out); botAI->TellMaster(out);
return; return;
} }
// If not better than main hand, check if it's better than offhand
// Priority 2: If not better than main hand, check if better than offhand
else if (canGoOff && newItemScore > offHandScore) else if (canGoOff && newItemScore > offHandScore)
{ {
// Equip in offhand // Equip in offhand
@@ -205,9 +228,10 @@ void EquipAction::EquipItem(Item* item)
// If not a special dual-wield/TG scenario or no improvement found, fall back to original logic // If not a special dual-wield/TG scenario or no improvement found, fall back to original logic
if (dstSlot == EQUIPMENT_SLOT_FINGER1 || if (dstSlot == EQUIPMENT_SLOT_FINGER1 ||
dstSlot == EQUIPMENT_SLOT_TRINKET1 || dstSlot == EQUIPMENT_SLOT_TRINKET1 ||
(dstSlot == EQUIPMENT_SLOT_MAINHAND && bot->CanDualWield() && (dstSlot == EQUIPMENT_SLOT_MAINHAND && canDualWield &&
((itemProto->InventoryType != INVTYPE_2HWEAPON && !have2HWeapon) || (bot->CanTitanGrip() && isValidTGWeapon)))) ((invType != INVTYPE_2HWEAPON && !have2HWeaponEquipped) || (canTitanGrip && isValidTGWeapon))))
{ {
// Handle ring/trinket dual-slot logic
Item* const equippedItems[2] = { Item* const equippedItems[2] = {
bot->GetItemByPos(INVENTORY_SLOT_BAG_0, dstSlot), bot->GetItemByPos(INVENTORY_SLOT_BAG_0, dstSlot),
bot->GetItemByPos(INVENTORY_SLOT_BAG_0, dstSlot + 1) bot->GetItemByPos(INVENTORY_SLOT_BAG_0, dstSlot + 1)
@@ -217,30 +241,30 @@ void EquipAction::EquipItem(Item* item)
{ {
if (equippedItems[1]) if (equippedItems[1])
{ {
// Both slots are full - determine worst item to replace // Both slots are full - pick the worst item to replace
StatsWeightCalculator calculator(bot); StatsWeightCalculator calc(bot);
calculator.SetItemSetBonus(false); calc.SetItemSetBonus(false);
calculator.SetOverflowPenalty(false); calc.SetOverflowPenalty(false);
float equippedItemScore[2] = { float firstItemScore = calc.CalculateItem(equippedItems[0]->GetTemplate()->ItemId);
calculator.CalculateItem(equippedItems[0]->GetTemplate()->ItemId), float secondItemScore = calc.CalculateItem(equippedItems[1]->GetTemplate()->ItemId);
calculator.CalculateItem(equippedItems[1]->GetTemplate()->ItemId)
};
// Second item is worse than first, equip candidate item in second slot // If the second slot is worse, place the new item there
if (equippedItemScore[0] > equippedItemScore[1]) if (firstItemScore > secondItemScore)
{ {
dstSlot++; dstSlot++;
} }
} }
else else
{ {
// No item in second slot, equip there // Second slot empty, use it
dstSlot++; 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))