diff --git a/dGame/Entity.cpp b/dGame/Entity.cpp index 5de645346..3c905563d 100644 --- a/dGame/Entity.cpp +++ b/dGame/Entity.cpp @@ -493,6 +493,13 @@ void Entity::Initialize() { comp->SetFaction(levelFactions->front(), true); for (const auto faction : *levelFactions | std::views::drop(1)) comp->AddFaction(faction, true); } + + // Level files can give a smashable its own loot matrix in place of the template's + const auto levelLootMatrix = DestroyableComponent::GetLevelLootMatrix(*this); + if (!m_Character && levelLootMatrix) { + comp->SetLootMatrixID(levelLootMatrix.value()); + Loot::CacheMatrix(levelLootMatrix.value()); + } } const auto characterID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::CHARACTER); diff --git a/dGame/dComponents/DestroyableComponent.cpp b/dGame/dComponents/DestroyableComponent.cpp index 5b523fefd..a0233e597 100644 --- a/dGame/dComponents/DestroyableComponent.cpp +++ b/dGame/dComponents/DestroyableComponent.cpp @@ -806,6 +806,15 @@ std::optional> DestroyableComponent::GetLevelFactions(const return factions; } +std::optional DestroyableComponent::GetLevelLootMatrix(const Entity& entity) { + if (entity.HasVar(u"smashable_loot_matrix_set") && !entity.GetVar(u"smashable_loot_matrix_set")) return std::nullopt; + if (!entity.HasVar(u"smashable_loot_matrix")) return std::nullopt; + + const auto lootMatrix = entity.GetVar(u"smashable_loot_matrix"); + if (lootMatrix == -1) return std::nullopt; + return lootMatrix; +} + void DestroyableComponent::SetFaction(int32_t factionID, bool ignoreChecks) { m_FactionIDs.clear(); m_EnemyFactionIDs.clear(); diff --git a/dGame/dComponents/DestroyableComponent.h b/dGame/dComponents/DestroyableComponent.h index 25f52d919..9f95ef9d1 100644 --- a/dGame/dComponents/DestroyableComponent.h +++ b/dGame/dComponents/DestroyableComponent.h @@ -377,6 +377,14 @@ public: */ static std::optional> GetLevelFactions(const Entity& entity); + /** + * The loot matrix a level object's smashable_loot_matrix gives it in place of the template's LootMatrixIndex, + * resolved as the client does (LWODestroyableComponent::LoadConfigData 0x00c44cb0): used when + * smashable_loot_matrix_set is true or absent, unless the matrix is -1. + * @return the loot matrix, or nullopt when the template's stays + */ + static std::optional GetLevelLootMatrix(const Entity& entity); + /** * Returns the ID of the entity that killed this entity, if any * @return the ID of the entity that killed this entity, if any diff --git a/tests/dGameTests/dComponentsTests/DestroyableLevelConfigTests.cpp b/tests/dGameTests/dComponentsTests/DestroyableLevelConfigTests.cpp index b3637576e..7d19b5ff1 100644 --- a/tests/dGameTests/dComponentsTests/DestroyableLevelConfigTests.cpp +++ b/tests/dGameTests/dComponentsTests/DestroyableLevelConfigTests.cpp @@ -44,3 +44,33 @@ TEST_F(DestroyableLevelConfigTests, OverrideFactionFalseIgnoresSetFaction) { entity->SetVar(u"override_faction", false); EXPECT_FALSE(DestroyableComponent::GetLevelFactions(*entity)); } + +TEST_F(DestroyableLevelConfigTests, NoLootMatrixKeysKeepTheTemplateLootMatrix) { + EXPECT_FALSE(DestroyableComponent::GetLevelLootMatrix(*entity)); + entity->SetVar(u"smashable_loot_matrix_set", true); + EXPECT_FALSE(DestroyableComponent::GetLevelLootMatrix(*entity)); +} + +TEST_F(DestroyableLevelConfigTests, LootMatrixWithoutTheSetKeyIsUsed) { + entity->SetVar(u"smashable_loot_matrix", 748); + EXPECT_EQ(DestroyableComponent::GetLevelLootMatrix(*entity), 748); +} + +TEST_F(DestroyableLevelConfigTests, LootMatrixWithTheSetKeyTrueIsUsed) { + entity->SetVar(u"smashable_loot_matrix", 352); + entity->SetVar(u"smashable_loot_matrix_set", true); + EXPECT_EQ(DestroyableComponent::GetLevelLootMatrix(*entity), 352); +} + +TEST_F(DestroyableLevelConfigTests, LootMatrixWithTheSetKeyFalseIsIgnored) { + entity->SetVar(u"smashable_loot_matrix", 227); + entity->SetVar(u"smashable_loot_matrix_set", false); + EXPECT_FALSE(DestroyableComponent::GetLevelLootMatrix(*entity)); +} + +TEST_F(DestroyableLevelConfigTests, LootMatrixMinusOneIsIgnored) { + entity->SetVar(u"smashable_loot_matrix", -1); + EXPECT_FALSE(DestroyableComponent::GetLevelLootMatrix(*entity)); + entity->SetVar(u"smashable_loot_matrix_set", true); + EXPECT_FALSE(DestroyableComponent::GetLevelLootMatrix(*entity)); +}