Fix skill slot assignment to use equipLocation instead of itemType

- Add FindBehaviorSlotByEquipLocation method to map equipLocation strings to BehaviorSlot enum
- Update AddItemSkills and RemoveItemSkills to use equipLocation instead of itemType
- This fixes issue where items with same skill but different equipLocations would conflict
- Add unit tests to verify the new behavior
- Based on issue #1339: Server doesn't allow same skill in multiple slots

Co-authored-by: aronwk-aaron <26027722+aronwk-aaron@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2025-09-02 21:10:50 +00:00
parent a66abfa1e9
commit 7be7b37b8d
4 changed files with 57 additions and 2 deletions

View File

@@ -1168,7 +1168,7 @@ LOT InventoryComponent::GetConsumable() const {
void InventoryComponent::AddItemSkills(const LOT lot) { void InventoryComponent::AddItemSkills(const LOT lot) {
const auto info = Inventory::FindItemComponent(lot); const auto info = Inventory::FindItemComponent(lot);
const auto slot = FindBehaviorSlot(static_cast<eItemType>(info.itemType)); const auto slot = FindBehaviorSlotByEquipLocation(info.equipLocation);
if (slot == BehaviorSlot::Invalid) { if (slot == BehaviorSlot::Invalid) {
return; return;
@@ -1203,7 +1203,7 @@ void InventoryComponent::FixInvisibleItems() {
void InventoryComponent::RemoveItemSkills(const LOT lot) { void InventoryComponent::RemoveItemSkills(const LOT lot) {
const auto info = Inventory::FindItemComponent(lot); const auto info = Inventory::FindItemComponent(lot);
const auto slot = FindBehaviorSlot(static_cast<eItemType>(info.itemType)); const auto slot = FindBehaviorSlotByEquipLocation(info.equipLocation);
if (slot == BehaviorSlot::Invalid) { if (slot == BehaviorSlot::Invalid) {
return; return;
@@ -1332,6 +1332,20 @@ BehaviorSlot InventoryComponent::FindBehaviorSlot(const eItemType type) {
} }
} }
BehaviorSlot InventoryComponent::FindBehaviorSlotByEquipLocation(const std::string& equipLocation) {
if (equipLocation == "special_r") {
return BehaviorSlot::Primary;
} else if (equipLocation == "hair") {
return BehaviorSlot::Head;
} else if (equipLocation == "special_l") {
return BehaviorSlot::Offhand;
} else if (equipLocation == "clavicle") {
return BehaviorSlot::Neck;
} else {
return BehaviorSlot::Invalid;
}
}
bool InventoryComponent::IsTransferInventory(eInventoryType type, bool includeVault) { bool InventoryComponent::IsTransferInventory(eInventoryType type, bool includeVault) {
return type == VENDOR_BUYBACK || (includeVault && (type == VAULT_ITEMS || type == VAULT_MODELS)) || type == TEMP_ITEMS || type == TEMP_MODELS || type == MODELS_IN_BBB; return type == VENDOR_BUYBACK || (includeVault && (type == VAULT_ITEMS || type == VAULT_MODELS)) || type == TEMP_ITEMS || type == TEMP_MODELS || type == MODELS_IN_BBB;
} }

View File

@@ -368,6 +368,13 @@ public:
*/ */
static BehaviorSlot FindBehaviorSlot(eItemType type); static BehaviorSlot FindBehaviorSlot(eItemType type);
/**
* Returns the behavior slot for the given equipment location
* @param equipLocation the equipment location to find the behavior slot for
* @return the behavior slot for the given equipment location
*/
static BehaviorSlot FindBehaviorSlotByEquipLocation(const std::string& equipLocation);
/** /**
* Checks if the inventory type is a temp inventory * Checks if the inventory type is a temp inventory
* @param type the inventory type to check * @param type the inventory type to check

View File

@@ -1,5 +1,6 @@
set(DCOMPONENTS_TESTS set(DCOMPONENTS_TESTS
"DestroyableComponentTests.cpp" "DestroyableComponentTests.cpp"
"InventoryComponentTests.cpp"
"PetComponentTests.cpp" "PetComponentTests.cpp"
"SimplePhysicsComponentTests.cpp" "SimplePhysicsComponentTests.cpp"
"SavingTests.cpp" "SavingTests.cpp"

View File

@@ -0,0 +1,33 @@
#include "GameDependencies.h"
#include <gtest/gtest.h>
#include "InventoryComponent.h"
#include "BehaviorSlot.h"
class InventoryComponentTest : public GameDependenciesTest {
protected:
void SetUp() override {
SetUpDependencies();
}
void TearDown() override {
TearDownDependencies();
}
};
/**
* Test that FindBehaviorSlotByEquipLocation correctly maps equipLocation strings to BehaviorSlot enum values
*/
TEST_F(InventoryComponentTest, FindBehaviorSlotByEquipLocationTest) {
// Test the mappings from the issue comments
EXPECT_EQ(InventoryComponent::FindBehaviorSlotByEquipLocation("special_r"), BehaviorSlot::Primary);
EXPECT_EQ(InventoryComponent::FindBehaviorSlotByEquipLocation("hair"), BehaviorSlot::Head);
EXPECT_EQ(InventoryComponent::FindBehaviorSlotByEquipLocation("special_l"), BehaviorSlot::Offhand);
EXPECT_EQ(InventoryComponent::FindBehaviorSlotByEquipLocation("clavicle"), BehaviorSlot::Neck);
// Test that unknown equipLocations return Invalid
EXPECT_EQ(InventoryComponent::FindBehaviorSlotByEquipLocation("unknown"), BehaviorSlot::Invalid);
EXPECT_EQ(InventoryComponent::FindBehaviorSlotByEquipLocation(""), BehaviorSlot::Invalid);
EXPECT_EQ(InventoryComponent::FindBehaviorSlotByEquipLocation("root"), BehaviorSlot::Invalid);
EXPECT_EQ(InventoryComponent::FindBehaviorSlotByEquipLocation("leftHand"), BehaviorSlot::Invalid);
}