diff --git a/src/Common/Game/T6/GameT6.cpp b/src/Common/Game/T6/GameT6.cpp index e854a3ae..c69c81b7 100644 --- a/src/Common/Game/T6/GameT6.cpp +++ b/src/Common/Game/T6/GameT6.cpp @@ -77,6 +77,7 @@ namespace "vertexdecl", "vertexshader", "pixelshader", + "flametable", }; static_assert(std::extent_v == SUB_ASSET_TYPE_COUNT); } // namespace diff --git a/src/Common/Game/T6/T6.h b/src/Common/Game/T6/T6.h index 47bd2ad7..fe1c8654 100644 --- a/src/Common/Game/T6/T6.h +++ b/src/Common/Game/T6/T6.h @@ -86,6 +86,7 @@ namespace T6 SUB_ASSET_TYPE_VERTEX_DECL, SUB_ASSET_TYPE_VERTEX_SHADER, SUB_ASSET_TYPE_PIXEL_SHADER, + SUB_ASSET_TYPE_FLAME_TABLE, SUB_ASSET_TYPE_COUNT }; @@ -292,6 +293,7 @@ namespace T6 using SubAssetVertexDecl = SubAsset; using SubAssetVertexShader = SubAsset; using SubAssetPixelShader = SubAsset; + using SubAssetFlameTable = SubAsset; } // namespace T6 DEFINE_ASSET_NAME_ACCESSOR(T6::AssetPhysPreset, name); diff --git a/src/ObjCommon/Game/T6/ObjConstantsT6.h b/src/ObjCommon/Game/T6/ObjConstantsT6.h index 0c94ec00..5f1b82cf 100644 --- a/src/ObjCommon/Game/T6/ObjConstantsT6.h +++ b/src/ObjCommon/Game/T6/ObjConstantsT6.h @@ -2,6 +2,7 @@ namespace T6 { + static constexpr auto INFO_STRING_PREFIX_FLAME_TABLE = "FLAMETABLEFILE"; static constexpr auto INFO_STRING_PREFIX_PHYS_CONSTRAINTS = "PHYSCONSTRAINTS"; static constexpr auto INFO_STRING_PREFIX_PHYS_PRESET = "PHYSIC"; static constexpr auto INFO_STRING_PREFIX_TRACER = "TRACER"; diff --git a/src/ObjCommon/Game/T6/Weapon/FlameTableFields.h b/src/ObjCommon/Game/T6/Weapon/FlameTableFields.h new file mode 100644 index 00000000..0ae2c6c5 --- /dev/null +++ b/src/ObjCommon/Game/T6/Weapon/FlameTableFields.h @@ -0,0 +1,130 @@ +#pragma once + +#include "Game/T6/T6.h" + +namespace T6 +{ + inline cspField_t flameTableFields[]{ + {"flameVar_streamChunkGravityStart", offsetof(FlameTable, flameVar_streamChunkGravityStart), CSPFT_FLOAT }, + {"flameVar_streamChunkGravityEnd", offsetof(FlameTable, flameVar_streamChunkGravityEnd), CSPFT_FLOAT }, + {"flameVar_streamChunkMaxSize", offsetof(FlameTable, flameVar_streamChunkMaxSize), CSPFT_FLOAT }, + {"flameVar_streamChunkStartSize", offsetof(FlameTable, flameVar_streamChunkStartSize), CSPFT_FLOAT }, + {"flameVar_streamChunkEndSize", offsetof(FlameTable, flameVar_streamChunkEndSize), CSPFT_FLOAT }, + {"flameVar_streamChunkStartSizeRand", offsetof(FlameTable, flameVar_streamChunkStartSizeRand), CSPFT_FLOAT }, + {"flameVar_streamChunkEndSizeRand", offsetof(FlameTable, flameVar_streamChunkEndSizeRand), CSPFT_FLOAT }, + {"flameVar_streamChunkDistScalar", offsetof(FlameTable, flameVar_streamChunkDistScalar), CSPFT_FLOAT }, + {"flameVar_streamChunkDistSwayScale", offsetof(FlameTable, flameVar_streamChunkDistSwayScale), CSPFT_FLOAT }, + {"flameVar_streamChunkDistSwayVelMax", offsetof(FlameTable, flameVar_streamChunkDistSwayVelMax), CSPFT_FLOAT }, + {"flameVar_streamChunkSpeed", offsetof(FlameTable, flameVar_streamChunkSpeed), CSPFT_FLOAT }, + {"flameVar_streamChunkDecel", offsetof(FlameTable, flameVar_streamChunkDecel), CSPFT_FLOAT }, + {"flameVar_streamChunkVelocityAddScale", offsetof(FlameTable, flameVar_streamChunkVelocityAddScale), CSPFT_FLOAT }, + {"flameVar_streamChunkDuration", offsetof(FlameTable, flameVar_streamChunkDuration), CSPFT_FLOAT }, + {"flameVar_streamChunkDurationScaleMaxVel", offsetof(FlameTable, flameVar_streamChunkDurationScaleMaxVel), CSPFT_FLOAT }, + {"flameVar_streamChunkDurationVelScalar", offsetof(FlameTable, flameVar_streamChunkDurationVelScalar), CSPFT_FLOAT }, + {"flameVar_streamChunkSizeSpeedScale", offsetof(FlameTable, flameVar_streamChunkSizeSpeedScale), CSPFT_FLOAT }, + {"flameVar_streamChunkSizeAgeScale", offsetof(FlameTable, flameVar_streamChunkSizeAgeScale), CSPFT_FLOAT }, + {"flameVar_streamChunkSpawnFireIntervalStart", offsetof(FlameTable, flameVar_streamChunkSpawnFireIntervalStart), CSPFT_FLOAT }, + {"flameVar_streamChunkSpawnFireIntervalEnd", offsetof(FlameTable, flameVar_streamChunkSpawnFireIntervalEnd), CSPFT_FLOAT }, + {"flameVar_streamChunkSpawnFireMinLifeFrac", offsetof(FlameTable, flameVar_streamChunkSpawnFireMinLifeFrac), CSPFT_FLOAT }, + {"flameVar_streamChunkSpawnFireMaxLifeFrac", offsetof(FlameTable, flameVar_streamChunkSpawnFireMaxLifeFrac), CSPFT_FLOAT }, + {"flameVar_streamChunkFireMinLifeFrac", offsetof(FlameTable, flameVar_streamChunkFireMinLifeFrac), CSPFT_FLOAT }, + {"flameVar_streamChunkFireMinLifeFracStart", offsetof(FlameTable, flameVar_streamChunkFireMinLifeFracStart), CSPFT_FLOAT }, + {"flameVar_streamChunkFireMinLifeFracEnd", offsetof(FlameTable, flameVar_streamChunkFireMinLifeFracEnd), CSPFT_FLOAT }, + {"flameVar_streamChunkDripsMinLifeFrac", offsetof(FlameTable, flameVar_streamChunkDripsMinLifeFrac), CSPFT_FLOAT }, + {"flameVar_streamChunkDripsMinLifeFracStart", offsetof(FlameTable, flameVar_streamChunkDripsMinLifeFracStart), CSPFT_FLOAT }, + {"flameVar_streamChunkDripsMinLifeFracEnd", offsetof(FlameTable, flameVar_streamChunkDripsMinLifeFracEnd), CSPFT_FLOAT }, + {"flameVar_streamChunkRotationRange", offsetof(FlameTable, flameVar_streamChunkRotationRange), CSPFT_FLOAT }, + {"flameVar_streamSizeRandSinWave", offsetof(FlameTable, flameVar_streamSizeRandSinWave), CSPFT_FLOAT }, + {"flameVar_streamSizeRandCosWave", offsetof(FlameTable, flameVar_streamSizeRandCosWave), CSPFT_FLOAT }, + {"flameVar_streamDripsChunkInterval", offsetof(FlameTable, flameVar_streamDripsChunkInterval), CSPFT_FLOAT }, + {"flameVar_streamDripsChunkMinFrac", offsetof(FlameTable, flameVar_streamDripsChunkMinFrac), CSPFT_FLOAT }, + {"flameVar_streamDripsChunkRandFrac", offsetof(FlameTable, flameVar_streamDripsChunkRandFrac), CSPFT_FLOAT }, + {"flameVar_streamSmokeChunkInterval", offsetof(FlameTable, flameVar_streamSmokeChunkInterval), CSPFT_FLOAT }, + {"flameVar_streamSmokeChunkMinFrac", offsetof(FlameTable, flameVar_streamSmokeChunkMinFrac), CSPFT_FLOAT }, + {"flameVar_streamSmokeChunkRandFrac", offsetof(FlameTable, flameVar_streamSmokeChunkRandFrac), CSPFT_FLOAT }, + {"flameVar_streamChunkCullDistSizeFrac", offsetof(FlameTable, flameVar_streamChunkCullDistSizeFrac), CSPFT_FLOAT }, + {"flameVar_streamChunkCullMinLife", offsetof(FlameTable, flameVar_streamChunkCullMinLife), CSPFT_FLOAT }, + {"flameVar_streamChunkCullMaxLife", offsetof(FlameTable, flameVar_streamChunkCullMaxLife), CSPFT_FLOAT }, + {"flameVar_streamFuelSizeStart", offsetof(FlameTable, flameVar_streamFuelSizeStart), CSPFT_FLOAT }, + {"flameVar_streamFuelSizeEnd", offsetof(FlameTable, flameVar_streamFuelSizeEnd), CSPFT_FLOAT }, + {"flameVar_streamFuelLength", offsetof(FlameTable, flameVar_streamFuelLength), CSPFT_FLOAT }, + {"flameVar_streamFuelNumSegments", offsetof(FlameTable, flameVar_streamFuelNumSegments), CSPFT_FLOAT }, + {"flameVar_streamFuelAnimLoopTime", offsetof(FlameTable, flameVar_streamFuelAnimLoopTime), CSPFT_FLOAT }, + {"flameVar_streamFlameSizeStart", offsetof(FlameTable, flameVar_streamFlameSizeStart), CSPFT_FLOAT }, + {"flameVar_streamFlameSizeEnd", offsetof(FlameTable, flameVar_streamFlameSizeEnd), CSPFT_FLOAT }, + {"flameVar_streamFlameLength", offsetof(FlameTable, flameVar_streamFlameLength), CSPFT_FLOAT }, + {"flameVar_streamFlameNumSegments", offsetof(FlameTable, flameVar_streamFlameNumSegments), CSPFT_FLOAT }, + {"flameVar_streamFlameAnimLoopTime", offsetof(FlameTable, flameVar_streamFlameAnimLoopTime), CSPFT_FLOAT }, + {"flameVar_streamPrimaryLightRadius", offsetof(FlameTable, flameVar_streamPrimaryLightRadius), CSPFT_FLOAT }, + {"flameVar_streamPrimaryLightRadiusFlutter", offsetof(FlameTable, flameVar_streamPrimaryLightRadiusFlutter), CSPFT_FLOAT }, + {"flameVar_streamPrimaryLightR", offsetof(FlameTable, flameVar_streamPrimaryLightR), CSPFT_FLOAT }, + {"flameVar_streamPrimaryLightG", offsetof(FlameTable, flameVar_streamPrimaryLightG), CSPFT_FLOAT }, + {"flameVar_streamPrimaryLightB", offsetof(FlameTable, flameVar_streamPrimaryLightB), CSPFT_FLOAT }, + {"flameVar_streamPrimaryLightFlutterR", offsetof(FlameTable, flameVar_streamPrimaryLightFlutterR), CSPFT_FLOAT }, + {"flameVar_streamPrimaryLightFlutterG", offsetof(FlameTable, flameVar_streamPrimaryLightFlutterG), CSPFT_FLOAT }, + {"flameVar_streamPrimaryLightFlutterB", offsetof(FlameTable, flameVar_streamPrimaryLightFlutterB), CSPFT_FLOAT }, + {"flameVar_fireLife", offsetof(FlameTable, flameVar_fireLife), CSPFT_FLOAT }, + {"flameVar_fireLifeRand", offsetof(FlameTable, flameVar_fireLifeRand), CSPFT_FLOAT }, + {"flameVar_fireSpeedScale", offsetof(FlameTable, flameVar_fireSpeedScale), CSPFT_FLOAT }, + {"flameVar_fireSpeedScaleRand", offsetof(FlameTable, flameVar_fireSpeedScaleRand), CSPFT_FLOAT }, + {"flameVar_fireVelocityAddZ", offsetof(FlameTable, flameVar_fireVelocityAddZ), CSPFT_FLOAT }, + {"flameVar_fireVelocityAddZRand", offsetof(FlameTable, flameVar_fireVelocityAddZRand), CSPFT_FLOAT }, + {"flameVar_fireVelocityAddSideways", offsetof(FlameTable, flameVar_fireVelocityAddSideways), CSPFT_FLOAT }, + {"flameVar_fireGravity", offsetof(FlameTable, flameVar_fireGravity), CSPFT_FLOAT }, + {"flameVar_fireGravityEnd", offsetof(FlameTable, flameVar_fireGravityEnd), CSPFT_FLOAT }, + {"flameVar_fireMaxRotVel", offsetof(FlameTable, flameVar_fireMaxRotVel), CSPFT_FLOAT }, + {"flameVar_fireFriction", offsetof(FlameTable, flameVar_fireFriction), CSPFT_FLOAT }, + {"flameVar_fireEndSizeAdd", offsetof(FlameTable, flameVar_fireEndSizeAdd), CSPFT_FLOAT }, + {"flameVar_fireStartSizeScale", offsetof(FlameTable, flameVar_fireStartSizeScale), CSPFT_FLOAT }, + {"flameVar_fireEndSizeScale", offsetof(FlameTable, flameVar_fireEndSizeScale), CSPFT_FLOAT }, + {"flameVar_fireBrightness", offsetof(FlameTable, flameVar_fireBrightness), CSPFT_FLOAT }, + {"flameVar_dripsLife", offsetof(FlameTable, flameVar_dripsLife), CSPFT_FLOAT }, + {"flameVar_dripsLifeRand", offsetof(FlameTable, flameVar_dripsLifeRand), CSPFT_FLOAT }, + {"flameVar_dripsSpeedScale", offsetof(FlameTable, flameVar_dripsSpeedScale), CSPFT_FLOAT }, + {"flameVar_dripsSpeedScaleRand", offsetof(FlameTable, flameVar_dripsSpeedScaleRand), CSPFT_FLOAT }, + {"flameVar_dripsVelocityAddZ", offsetof(FlameTable, flameVar_dripsVelocityAddZ), CSPFT_FLOAT }, + {"flameVar_dripsVelocityAddZRand", offsetof(FlameTable, flameVar_dripsVelocityAddZRand), CSPFT_FLOAT }, + {"flameVar_dripsVelocityAddSideways", offsetof(FlameTable, flameVar_dripsVelocityAddSideways), CSPFT_FLOAT }, + {"flameVar_dripsGravity", offsetof(FlameTable, flameVar_dripsGravity), CSPFT_FLOAT }, + {"flameVar_dripsGravityEnd", offsetof(FlameTable, flameVar_dripsGravityEnd), CSPFT_FLOAT }, + {"flameVar_dripsMaxRotVel", offsetof(FlameTable, flameVar_dripsMaxRotVel), CSPFT_FLOAT }, + {"flameVar_dripsFriction", offsetof(FlameTable, flameVar_dripsFriction), CSPFT_FLOAT }, + {"flameVar_dripsEndSizeAdd", offsetof(FlameTable, flameVar_dripsEndSizeAdd), CSPFT_FLOAT }, + {"flameVar_dripsStartSizeScale", offsetof(FlameTable, flameVar_dripsStartSizeScale), CSPFT_FLOAT }, + {"flameVar_dripsEndSizeScale", offsetof(FlameTable, flameVar_dripsEndSizeScale), CSPFT_FLOAT }, + {"flameVar_dripsBrightness", offsetof(FlameTable, flameVar_dripsBrightness), CSPFT_FLOAT }, + {"flameVar_smokeLife", offsetof(FlameTable, flameVar_smokeLife), CSPFT_FLOAT }, + {"flameVar_smokeLifeRand", offsetof(FlameTable, flameVar_smokeLifeRand), CSPFT_FLOAT }, + {"flameVar_smokeSpeedScale", offsetof(FlameTable, flameVar_smokeSpeedScale), CSPFT_FLOAT }, + {"flameVar_smokeVelocityAddZ", offsetof(FlameTable, flameVar_smokeVelocityAddZ), CSPFT_FLOAT }, + {"flameVar_smokeGravity", offsetof(FlameTable, flameVar_smokeGravity), CSPFT_FLOAT }, + {"flameVar_smokeGravityEnd", offsetof(FlameTable, flameVar_smokeGravityEnd), CSPFT_FLOAT }, + {"flameVar_smokeMaxRotation", offsetof(FlameTable, flameVar_smokeMaxRotation), CSPFT_FLOAT }, + {"flameVar_smokeMaxRotVel", offsetof(FlameTable, flameVar_smokeMaxRotVel), CSPFT_FLOAT }, + {"flameVar_smokeFriction", offsetof(FlameTable, flameVar_smokeFriction), CSPFT_FLOAT }, + {"flameVar_smokeEndSizeAdd", offsetof(FlameTable, flameVar_smokeEndSizeAdd), CSPFT_FLOAT }, + {"flameVar_smokeStartSizeAdd", offsetof(FlameTable, flameVar_smokeStartSizeAdd), CSPFT_FLOAT }, + {"flameVar_smokeOriginSizeOfsZScale", offsetof(FlameTable, flameVar_smokeOriginSizeOfsZScale), CSPFT_FLOAT }, + {"flameVar_smokeOriginOfsZ", offsetof(FlameTable, flameVar_smokeOriginOfsZ), CSPFT_FLOAT }, + {"flameVar_smokeFadein", offsetof(FlameTable, flameVar_smokeFadein), CSPFT_FLOAT }, + {"flameVar_smokeFadeout", offsetof(FlameTable, flameVar_smokeFadeout), CSPFT_FLOAT }, + {"flameVar_smokeMaxAlpha", offsetof(FlameTable, flameVar_smokeMaxAlpha), CSPFT_FLOAT }, + {"flameVar_smokeBrightness", offsetof(FlameTable, flameVar_smokeBrightness), CSPFT_FLOAT }, + {"flameVar_smokeOriginOffset", offsetof(FlameTable, flameVar_smokeOriginOffset), CSPFT_FLOAT }, + {"flameVar_collisionSpeedScale", offsetof(FlameTable, flameVar_collisionSpeedScale), CSPFT_FLOAT }, + {"flameVar_collisionVolumeScale", offsetof(FlameTable, flameVar_collisionVolumeScale), CSPFT_FLOAT }, + {"name", offsetof(FlameTable, name), CSPFT_STRING }, + {"fire", offsetof(FlameTable, fire), CSPFT_MATERIAL}, + {"smoke", offsetof(FlameTable, smoke), CSPFT_MATERIAL}, + {"heat", offsetof(FlameTable, heat), CSPFT_MATERIAL}, + {"drips", offsetof(FlameTable, drips), CSPFT_MATERIAL}, + {"streamFuel", offsetof(FlameTable, streamFuel), CSPFT_MATERIAL}, + {"streamFuel2", offsetof(FlameTable, streamFuel2), CSPFT_MATERIAL}, + {"streamFlame", offsetof(FlameTable, streamFlame), CSPFT_MATERIAL}, + {"streamFlame2", offsetof(FlameTable, streamFlame2), CSPFT_MATERIAL}, + {"flameOffLoopSound", offsetof(FlameTable, flameOffLoopSound), CSPFT_STRING }, + {"flameIgniteSound", offsetof(FlameTable, flameIgniteSound), CSPFT_STRING }, + {"flameOnLoopSound", offsetof(FlameTable, flameOnLoopSound), CSPFT_STRING }, + {"flameCooldownSound", offsetof(FlameTable, flameCooldownSound), CSPFT_STRING }, + }; +} diff --git a/src/ObjLoading/Game/T6/ObjLoaderT6.cpp b/src/ObjLoading/Game/T6/ObjLoaderT6.cpp index 51808774..e229c05a 100644 --- a/src/ObjLoading/Game/T6/ObjLoaderT6.cpp +++ b/src/ObjLoading/Game/T6/ObjLoaderT6.cpp @@ -43,6 +43,7 @@ #include "Weapon/AttachmentUniqueGdtLoaderT6.h" #include "Weapon/AttachmentUniqueRawLoaderT6.h" #include "Weapon/CamoJsonLoaderT6.h" +#include "Weapon/FlameTableLoaderT6.h" #include "Weapon/WeaponGdtLoaderT6.h" #include "Weapon/WeaponRawLoaderT6.h" #include "ZBarrier/GdtLoaderZBarrierT6.h" @@ -441,6 +442,7 @@ namespace T6 collection.AddSubAssetCreator(techset::CreateVertexShaderLoaderT6(memory, searchPath)); collection.AddSubAssetCreator(techset::CreatePixelShaderLoaderT6(memory, searchPath)); + collection.AddSubAssetCreator(weapon::CreateFlameTableLoaderT6(memory, searchPath, zone)); } } // namespace diff --git a/src/ObjLoading/Game/T6/Weapon/FlameTableLoaderT6.cpp b/src/ObjLoading/Game/T6/Weapon/FlameTableLoaderT6.cpp new file mode 100644 index 00000000..572939fd --- /dev/null +++ b/src/ObjLoading/Game/T6/Weapon/FlameTableLoaderT6.cpp @@ -0,0 +1,93 @@ +#include "FlameTableLoaderT6.h" + +#include "Game/T6/InfoString/InfoStringToStructConverter.h" +#include "Game/T6/ObjConstantsT6.h" +#include "Game/T6/T6.h" +#include "Game/T6/Weapon/FlameTableFields.h" +#include "Weapon/WeaponCommon.h" + +#include +#include + +using namespace T6; + +namespace +{ + class InfoStringToFlameTableConverter final : public InfoStringToStructConverter + { + protected: + bool ConvertExtensionField(const cspField_t& field, const std::string& value) override + { + assert(false); + return false; + } + + public: + InfoStringToFlameTableConverter(const InfoString& infoString, + FlameTable& flameTable, + ZoneScriptStrings& zoneScriptStrings, + MemoryManager& memory, + AssetCreationContext& context, + AssetRegistration& registration, + const cspField_t* fields, + const size_t fieldCount) + : InfoStringToStructConverter(infoString, &flameTable, zoneScriptStrings, memory, context, registration, fields, fieldCount) + { + } + }; + + class FlameTableLoaderT6 final : public SubAssetCreator + { + public: + FlameTableLoaderT6(MemoryManager& memory, ISearchPath& searchPath, Zone& zone) + : m_memory(memory), + m_search_path(searchPath), + m_zone(zone) + { + } + + AssetCreationResult CreateSubAsset(const std::string& assetName, AssetCreationContext& context) override + { + const auto fileName = weapon::GetFileNameForFlameTable(assetName); + const auto file = m_search_path.Open(fileName); + if (!file.IsOpen()) + return AssetCreationResult::NoAction(); + + InfoString infoString; + if (!infoString.FromStream(INFO_STRING_PREFIX_FLAME_TABLE, *file.m_stream)) + { + con::error("Could not parse as info string file: \"{}\"", fileName); + return AssetCreationResult::Failure(); + } + + auto* flameTable = m_memory.Alloc(); + AssetRegistration registration(assetName, flameTable); + InfoStringToFlameTableConverter converter( + infoString, *flameTable, m_zone.m_script_strings, m_memory, context, registration, flameTableFields, std::extent_v); + if (!converter.Convert()) + { + con::error("Failed to parse flame table: \"{}\"", assetName); + return AssetCreationResult::Failure(); + } + + // The flametable infostring contains the name but we don't to use it from there + // It's kept to keep compatiblity with the official modtools and game + flameTable->name = m_memory.Dup(assetName.c_str()); + + return AssetCreationResult::Success(context.AddSubAsset(std::move(registration))); + } + + private: + MemoryManager& m_memory; + ISearchPath& m_search_path; + Zone& m_zone; + }; +} // namespace + +namespace weapon +{ + std::unique_ptr CreateFlameTableLoaderT6(MemoryManager& memory, ISearchPath& searchPath, Zone& zone) + { + return std::make_unique(memory, searchPath, zone); + } +} // namespace weapon diff --git a/src/ObjLoading/Game/T6/Weapon/FlameTableLoaderT6.h b/src/ObjLoading/Game/T6/Weapon/FlameTableLoaderT6.h new file mode 100644 index 00000000..50c73c3a --- /dev/null +++ b/src/ObjLoading/Game/T6/Weapon/FlameTableLoaderT6.h @@ -0,0 +1,13 @@ +#pragma once + +#include "Asset/IAssetCreator.h" +#include "SearchPath/ISearchPath.h" +#include "Utils/MemoryManager.h" +#include "Zone/Zone.h" + +#include + +namespace weapon +{ + std::unique_ptr CreateFlameTableLoaderT6(MemoryManager& memory, ISearchPath& searchPath, Zone& zone); +} // namespace weapon diff --git a/src/ObjLoading/Game/T6/Weapon/WeaponInfoStringLoaderT6.cpp b/src/ObjLoading/Game/T6/Weapon/WeaponInfoStringLoaderT6.cpp index d7ea67b6..596912c2 100644 --- a/src/ObjLoading/Game/T6/Weapon/WeaponInfoStringLoaderT6.cpp +++ b/src/ObjLoading/Game/T6/Weapon/WeaponInfoStringLoaderT6.cpp @@ -6,6 +6,7 @@ #include "Game/T6/Weapon/WeaponFields.h" #include "Game/T6/Weapon/WeaponStrings.h" #include "Utils/Logging/Log.h" +#include "Utils/StringUtils.h" #include "Weapon/AccuracyGraphLoader.h" #include @@ -442,6 +443,32 @@ namespace return true; } + bool LoadFlameTable(const char* flameTableName, FlameTable*& flameTablePtr, AssetRegistration& registration, AssetCreationContext& context) + { + if (flameTableName && flameTableName[0] != '\0') + { + auto* flameTableAsset = context.LoadSubAsset(flameTableName); + if (!flameTableAsset) + return false; + + for (auto* dependency : flameTableAsset->m_dependencies) + registration.AddDependency(dependency); + + assert(flameTableAsset->m_used_script_strings.empty()); + assert(flameTableAsset->m_indirect_asset_references.empty()); + + flameTablePtr = flameTableAsset->Asset(); + } + + return true; + } + + bool LoadFlameTables(WeaponFullDef& weaponFullDef, AssetRegistration& registration, AssetCreationContext& context) + { + return LoadFlameTable(weaponFullDef.weapDef.flameTableFirstPerson, weaponFullDef.weapDef.flameTableFirstPersonPtr, registration, context) + && LoadFlameTable(weaponFullDef.weapDef.flameTableThirdPerson, weaponFullDef.weapDef.flameTableThirdPersonPtr, registration, context); + } + void LinkWeaponFullDefSubStructs(WeaponFullDef& weapon) { weapon.weapVariantDef.weapDef = &weapon.weapDef; @@ -462,25 +489,109 @@ namespace weapon.weapDef.locationDamageMultipliers = weapon.locationDamageMultipliers; } - void CalculateWeaponFields(WeaponFullDef& weapon) + bool IsDefaultWeapon(const WeaponFullDef& weapon) + { + return strcmp(weapon.weapVariantDef.szInternalName, "defaultweapon") == 0 || strcmp(weapon.weapVariantDef.szInternalName, "defaultweapon_mp") == 0; + } + + void SetWeaponDefaults(WeaponFullDef& weapon) + { + if (IsDefaultWeapon(weapon)) + return; + + if (!weapon.weapDef.viewLastShotEjectEffect) + weapon.weapDef.viewLastShotEjectEffect = weapon.weapDef.viewShellEjectEffect; + if (!weapon.weapDef.worldLastShotEjectEffect) + weapon.weapDef.worldLastShotEjectEffect = weapon.weapDef.worldShellEjectEffect; + if (!weapon.weapDef.raiseSound) + weapon.weapDef.raiseSound = "wpn_default_raise"; + if (!weapon.weapDef.putawaySound) + weapon.weapDef.putawaySound = "wpn_default_putaway"; + if (!weapon.weapDef.pickupSound) + weapon.weapDef.pickupSound = "wpn_default_pickup"; + if (!weapon.weapDef.ammoPickupSound) + weapon.weapDef.ammoPickupSound = "wpn_default_ammo_pickup"; + if (!weapon.weapDef.emptyFireSound) + weapon.weapDef.emptyFireSound = "wpn_default_no_ammo"; + } + + void SetupTransitionTimes(WeaponFullDef& weapon) + { + if (weapon.weapVariantDef.iAdsTransInTime <= 0) + weapon.weapVariantDef.fOOPosAnimLength[0] = 1.0f / 300.0f; // 0.0033333334f; + else + weapon.weapVariantDef.fOOPosAnimLength[0] = 1.0f / static_cast(weapon.weapVariantDef.iAdsTransInTime); + + if (weapon.weapVariantDef.iAdsTransOutTime <= 0) + weapon.weapVariantDef.fOOPosAnimLength[1] = 1.0f / 500.0f; // 0.0020000001f + else + weapon.weapVariantDef.fOOPosAnimLength[1] = 1.0f / static_cast(weapon.weapVariantDef.iAdsTransOutTime); + } + + void CheckWeaponDamageRanges(WeaponFullDef& weapon) + { + if (strcmp(weapon.weapVariantDef.szInternalName, "none") == 0) + return; + + if (weapon.weapDef.damageRange[0] <= 0.0) + weapon.weapDef.damageRange[0] = 999999.0f; + if (weapon.weapDef.damageRange[5] <= 0.0) + weapon.weapDef.damageRange[5] = 999999.12f; // oddly specific number, no clue + } + + void CheckCrosshairValues(WeaponFullDef& weapon) + { + if (weapon.weapDef.enemyCrosshairRange > 15000.0f) + con::warn("Weapon {}: Enemy crosshair ranges should be less than 15000", weapon.weapVariantDef.szInternalName); + } + + void CheckProjectileValues(WeaponFullDef& weapon) + { + if (weapon.weapDef.weapType != WEAPTYPE_PROJECTILE) + return; + + if (weapon.weapDef.iProjectileSpeed <= 0) + con::warn("Weapon {}: Projectile speed must be greater than 0.0", weapon.weapVariantDef.szDisplayName); + + if (weapon.weapDef.destabilizationCurvatureMax >= 1000000000.0f || weapon.weapDef.destabilizationCurvatureMax < 0.0f) + con::warn("Weapon {}: Destabilization angle must be between 0 and 45 degrees", weapon.weapVariantDef.szDisplayName); + + if (weapon.weapDef.destabilizationRateTime < 0.0f) + con::warn("Weapon {}: Destabilization rate time must be non-negative", weapon.weapVariantDef.szDisplayName); + } + + void CheckSharedAmmoValues(const WeaponFullDef& weapon) + { + if (weapon.weapVariantDef.szAmmoName) + utils::MakeStringLowerCase(const_cast(weapon.weapVariantDef.szAmmoName)); + + if (weapon.weapVariantDef.szClipName) + utils::MakeStringLowerCase(const_cast(weapon.weapVariantDef.szClipName)); + } + + void CheckAttachModelTags(const WeaponFullDef& weapon) + { + for (auto* tag : weapon.attachViewModelTag) + { + if (tag) + utils::MakeStringLowerCase(const_cast(tag)); + } + + for (auto* tag : weapon.attachWorldModelTag) + { + if (tag) + utils::MakeStringLowerCase(const_cast(tag)); + } + } + + void SetupAttachmentField(WeaponFullDef& weapon) { - // iAttachments weapon.weapVariantDef.iAttachments = 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; } - - if (weapon.weapVariantDef.iAdsTransInTime <= 0) - weapon.weapVariantDef.fOOPosAnimLength[0] = 0.0033333334f; - else - weapon.weapVariantDef.fOOPosAnimLength[0] = 1.0f / static_cast(weapon.weapVariantDef.iAdsTransInTime); - - if (weapon.weapVariantDef.iAdsTransOutTime <= 0) - weapon.weapVariantDef.fOOPosAnimLength[1] = 0.0020000001f; - else - weapon.weapVariantDef.fOOPosAnimLength[1] = 1.0f / static_cast(weapon.weapVariantDef.iAdsTransOutTime); } bool IsStringOverride(const char* baseString, const char* overrideString) @@ -625,10 +736,27 @@ namespace weapon return AssetCreationResult::Failure(); } - CalculateWeaponFields(*weaponFullDef); - CalculateAttachmentFields(*weaponFullDef); + if (!LoadAccuracyGraphs(*weaponFullDef, m_memory, m_search_path, context)) + { + con::error("Failed to load accuracy tables of weapon: \"{}\"", assetName); + return AssetCreationResult::Failure(); + } - LoadAccuracyGraphs(*weaponFullDef, m_memory, m_search_path, context); + if (!LoadFlameTables(*weaponFullDef, registration, context)) + { + con::error("Failed to load flame tables of weapon: \"{}\"", assetName); + return AssetCreationResult::Failure(); + } + + SetWeaponDefaults(*weaponFullDef); + SetupTransitionTimes(*weaponFullDef); + CheckWeaponDamageRanges(*weaponFullDef); + SetupAttachmentField(*weaponFullDef); + CalculateAttachmentFields(*weaponFullDef); + CheckCrosshairValues(*weaponFullDef); + CheckProjectileValues(*weaponFullDef); + CheckSharedAmmoValues(*weaponFullDef); + CheckAttachModelTags(*weaponFullDef); return AssetCreationResult::Success(context.AddAsset(std::move(registration))); } diff --git a/src/ObjWriting/Game/T6/Weapon/WeaponDumperT6.cpp b/src/ObjWriting/Game/T6/Weapon/WeaponDumperT6.cpp index e4743f69..c9f2a689 100644 --- a/src/ObjWriting/Game/T6/Weapon/WeaponDumperT6.cpp +++ b/src/ObjWriting/Game/T6/Weapon/WeaponDumperT6.cpp @@ -1,7 +1,9 @@ #include "WeaponDumperT6.h" +#include "Dumping/SubAssetDeduplicationDumperState.h" #include "Game/T6/InfoString/InfoStringFromStructConverter.h" #include "Game/T6/ObjConstantsT6.h" +#include "Game/T6/Weapon/FlameTableFields.h" #include "Game/T6/Weapon/WeaponFields.h" #include "Game/T6/Weapon/WeaponStrings.h" #include "InfoString/InfoString.h" @@ -267,6 +269,24 @@ namespace } }; + class InfoStringFromFlameTableConverter final : public InfoStringFromStructConverter + { + protected: + void FillFromExtensionField(const cspField_t& field) override + { + assert(false); + } + + public: + InfoStringFromFlameTableConverter(const FlameTable* structure, + const cspField_t* fields, + const size_t fieldCount, + std::function scriptStringValueCallback) + : InfoStringFromStructConverter(structure, fields, fieldCount, std::move(scriptStringValueCallback)) + { + } + }; + GenericGraph2D ConvertAccuracyGraph(const char* graphName, const vec2_t* originalKnots, const unsigned originalKnotCount) { GenericGraph2D graph; @@ -457,6 +477,47 @@ namespace weapDef->originalAiVsPlayerAccuracyGraphKnotCount)); } } + + void DumpFlameTable(const AssetDumpingContext& context, + SubAssetDeduplicationDumperState& deduplicator, + const char* flameTableName, + const FlameTable* flameTable) + { + if (!flameTable || !flameTableName || flameTableName[0] == '\0') + return; + + if (!deduplicator.ShouldDumpSubAsset(flameTable)) + return; + + const auto assetFile = context.OpenAssetFile(weapon::GetFileNameForFlameTable(flameTableName)); + + if (!assetFile) + return; + + auto& stream = *assetFile; + InfoStringFromFlameTableConverter converter(flameTable, + flameTableFields, + std::extent_v, + [](const scr_string_t scrStr) -> std::string + { + assert(false); + return ""; + }); + + const auto infoString = converter.Convert(); + + const auto stringValue = infoString.ToString(INFO_STRING_PREFIX_FLAME_TABLE); + stream.write(stringValue.c_str(), stringValue.size()); + } + + void DumpFlameTables(AssetDumpingContext& context, const XAssetInfo& asset) + { + auto* deduplicator = context.GetZoneAssetDumperState>(); + const auto weapon = asset.Asset(); + + DumpFlameTable(context, *deduplicator, weapon->weapDef->flameTableFirstPerson, weapon->weapDef->flameTableFirstPersonPtr); + DumpFlameTable(context, *deduplicator, weapon->weapDef->flameTableThirdPerson, weapon->weapDef->flameTableThirdPersonPtr); + } } // namespace namespace weapon @@ -485,5 +546,6 @@ namespace weapon } DumpAccuracyGraphs(context, asset); + DumpFlameTables(context, asset); } } // namespace weapon