diff --git a/src/Common/Game/T6/T6.h b/src/Common/Game/T6/T6.h index 7e9f8306..38cc8262 100644 --- a/src/Common/Game/T6/T6.h +++ b/src/Common/Game/T6/T6.h @@ -91,6 +91,8 @@ namespace T6 WFT_EXPLOSION_TAG, WFT_NOTETRACKSOUNDMAP, WFT_WEAPON_CAMO, + WFT_ATTACHMENTS, + WFT_ATTACHMENT_UNIQUES, WFT_NUM_FIELD_TYPES }; diff --git a/src/ObjCommon/Game/T6/InfoString/WeaponFields.h b/src/ObjCommon/Game/T6/InfoString/WeaponFields.h index 552f7c02..4bff9ac3 100644 --- a/src/ObjCommon/Game/T6/InfoString/WeaponFields.h +++ b/src/ObjCommon/Game/T6/InfoString/WeaponFields.h @@ -1031,5 +1031,7 @@ namespace T6 {"customBool0", offsetof(WeaponFullDef, weapDef.customBool0), CSPFT_BOOL}, {"customBool1", offsetof(WeaponFullDef, weapDef.customBool1), CSPFT_BOOL}, {"customBool2", offsetof(WeaponFullDef, weapDef.customBool2), CSPFT_BOOL}, + {"attachments", offsetof(WeaponFullDef, attachments), WFT_ATTACHMENTS}, + {"attachmentUniques", offsetof(WeaponFullDef, attachmentUniques), WFT_ATTACHMENT_UNIQUES} }; } \ No newline at end of file diff --git a/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderWeapon.cpp b/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderWeapon.cpp index 91ac9f66..735fdbc9 100644 --- a/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderWeapon.cpp +++ b/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderWeapon.cpp @@ -30,7 +30,7 @@ namespace T6 if (valueArray.size() > std::extent::value) { - std::cout << "Cannot have more than " << std::extent::value << "hide tags!" << std::endl; + std::cout << "Cannot have more than " << std::extent::value << " hide tags!" << std::endl; return false; } @@ -79,15 +79,15 @@ namespace T6 _NODISCARD bool ConvertNotetrackSoundMap(const cspField_t& field, const std::string& value) { std::vector> pairs; - if(!ParseAsPairs(value, pairs)) + if (!ParseAsPairs(value, pairs)) { std::cout << "Failed to parse notetracksoundmap as pairs" << std::endl; return false; } - if(pairs.size() > std::extent::value) + if (pairs.size() > std::extent::value) { - std::cout << "Cannot have more than " << std::extent::value << "notetracksoundmap entries!" << std::endl; + std::cout << "Cannot have more than " << std::extent::value << " notetracksoundmap entries!" << std::endl; return false; } @@ -141,6 +141,115 @@ namespace T6 return true; } + _NODISCARD bool ConvertAttachments(const cspField_t& field, const std::string& value) + { + std::vector valueArray; + if (!ParseAsArray(value, valueArray)) + { + std::cout << "Failed to parse attachments as array" << std::endl; + return false; + } + + auto** attachments = reinterpret_cast(reinterpret_cast(m_structure) + field.iOffset); + + for (const auto& attachmentName : valueArray) + { + auto* attachmentAssetInfo = m_loading_manager->LoadDependency(ASSET_TYPE_ATTACHMENT, attachmentName); + if (attachmentAssetInfo == nullptr) + { + std::cout << "Failed to load attachment asset \"" << attachmentName << "\"" << std::endl; + return false; + } + + auto* attachmentAsset = static_cast(attachmentAssetInfo->m_ptr); + + if (static_cast(attachmentAsset->attachmentType) >= ATTACHMENT_TYPE_COUNT) + { + std::cout << "Invalid attachment type " << attachmentAsset->attachmentType << " for attachment asset \"" << attachmentName << "\"" << std::endl; + return false; + } + + if (attachments[attachmentAsset->attachmentType] != nullptr) + { + std::cout << "Already loaded attachment with same type " << attachmentAsset->attachmentType + << ": \"" << attachments[attachmentAsset->attachmentType]->szInternalName << "\", \"" + << attachmentName << "\"" << std::endl; + return false; + } + + attachments[attachmentAsset->attachmentType] = attachmentAsset; + m_dependencies.emplace(attachmentAssetInfo); + } + + return true; + } + + _NODISCARD static bool HasMoreThanOneAttachmentSetInMask(const int mask) + { + // Check if int has more than 1 bit set + return (mask & (mask - 1)) != 0; + } + + _NODISCARD bool ConvertAttachmentUniques(const cspField_t& field, const std::string& value) + { + std::vector valueArray; + if (!ParseAsArray(value, valueArray)) + { + std::cout << "Failed to parse attachment uniques as array" << std::endl; + return false; + } + + auto** attachmentUniques = reinterpret_cast(reinterpret_cast(m_structure) + field.iOffset); + auto attachmentCombinationIndex = std::extent::value; + + for (const auto& attachmentUniqueName : valueArray) + { + auto* attachmentUniqueAssetInfo = m_loading_manager->LoadDependency(ASSET_TYPE_ATTACHMENT_UNIQUE, attachmentUniqueName); + if (attachmentUniqueAssetInfo == nullptr) + { + std::cout << "Failed to load attachment unique asset \"" << attachmentUniqueName << "\"" << std::endl; + return false; + } + + auto* attachmentUniqueAsset = static_cast(attachmentUniqueAssetInfo->m_ptr); + + if (HasMoreThanOneAttachmentSetInMask(attachmentUniqueAsset->combinedAttachmentTypeMask)) + { + if (attachmentCombinationIndex >= std::extent::value) + { + std::cout << "Cannot have more than " + << (std::extent::value - std::extent::value) + << " combined attachment attachment unique entries!" << std::endl; + return false; + } + + attachmentUniques[attachmentCombinationIndex++] = attachmentUniqueAsset; + m_dependencies.emplace(attachmentUniqueAssetInfo); + } + else + { + if (static_cast(attachmentUniqueAsset->attachmentType) >= ATTACHMENT_TYPE_COUNT) + { + std::cout << "Invalid attachment type " << attachmentUniqueAsset->attachmentType << " for attachment unique asset \"" << attachmentUniqueName << "\"" << std::endl; + return false; + } + + if (attachmentUniques[attachmentUniqueAsset->attachmentType] != nullptr) + { + std::cout << "Already loaded attachment unique with same type " << attachmentUniqueAsset->attachmentType + << ": \"" << attachmentUniques[attachmentUniqueAsset->attachmentType]->szInternalName << "\", \"" + << attachmentUniqueName << "\"" << std::endl; + return false; + } + + attachmentUniques[attachmentUniqueAsset->attachmentType] = attachmentUniqueAsset; + m_dependencies.emplace(attachmentUniqueAssetInfo); + } + } + + return true; + } + protected: bool ConvertExtensionField(const cspField_t& field, const std::string& value) override { @@ -228,6 +337,12 @@ namespace T6 case WFT_WEAPON_CAMO: return ConvertWeaponCamo(field, value); + case WFT_ATTACHMENTS: + return ConvertAttachments(field, value); + + case WFT_ATTACHMENT_UNIQUES: + return ConvertAttachmentUniques(field, value); + case WFT_NUM_FIELD_TYPES: default: assert(false); @@ -268,7 +383,7 @@ void AssetLoaderWeapon::CalculateWeaponFields(WeaponFullDef* weapon) { // iAttachments weapon->weapVariantDef.iAttachments = 0; - for(auto i = 1u; i < sizeof(WeaponVariantDef::iAttachments) * 8; i++) // Bit for default attachment always 0 + for (auto i = 1u; i < sizeof(WeaponVariantDef::iAttachments) * 8; i++) // Bit for default attachment always 0 { if (weapon->attachments[i]) weapon->weapVariantDef.iAttachments |= 1 << i; diff --git a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperWeapon.cpp b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperWeapon.cpp index 64fef188..f96e8523 100644 --- a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperWeapon.cpp +++ b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperWeapon.cpp @@ -214,6 +214,50 @@ namespace T6 break; } + case WFT_ATTACHMENTS: + { + const auto* attachments = reinterpret_cast(reinterpret_cast(m_structure) + field.iOffset); + std::stringstream ss; + auto first = true; + + for (auto i = 0u; i < std::extent::value; i++) + { + if (attachments[i]) + { + if (!first) + ss << "\n"; + else + first = false; + ss << AssetName(attachments[i]->szInternalName); + } + } + + m_info_string.SetValueForKey(std::string(field.szName), ss.str()); + break; + } + + case WFT_ATTACHMENT_UNIQUES: + { + const auto* attachmentUniques = reinterpret_cast(reinterpret_cast(m_structure) + field.iOffset); + std::stringstream ss; + auto first = true; + + for (auto i = 0u; i < std::extent::value; i++) + { + if (attachmentUniques[i]) + { + if (!first) + ss << "\n"; + else + first = false; + ss << AssetName(attachmentUniques[i]->szInternalName); + } + } + + m_info_string.SetValueForKey(std::string(field.szName), ss.str()); + break; + } + case WFT_NUM_FIELD_TYPES: default: assert(false);