mirror of
https://github.com/Laupetin/OpenAssetTools.git
synced 2026-07-02 22:08:11 +00:00
feat: t6 flametables (#845)
* feat: add flametable loading and dumping for T6 * chore: add additional validation logic of game for t6 weapon loading
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
@@ -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 <cassert>
|
||||
#include <format>
|
||||
|
||||
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<SubAssetFlameTable>& registration,
|
||||
const cspField_t* fields,
|
||||
const size_t fieldCount)
|
||||
: InfoStringToStructConverter(infoString, &flameTable, zoneScriptStrings, memory, context, registration, fields, fieldCount)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class FlameTableLoaderT6 final : public SubAssetCreator<SubAssetFlameTable>
|
||||
{
|
||||
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<FlameTable>();
|
||||
AssetRegistration<SubAssetFlameTable> registration(assetName, flameTable);
|
||||
InfoStringToFlameTableConverter converter(
|
||||
infoString, *flameTable, m_zone.m_script_strings, m_memory, context, registration, flameTableFields, std::extent_v<decltype(flameTableFields)>);
|
||||
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<ISubAssetCreator> CreateFlameTableLoaderT6(MemoryManager& memory, ISearchPath& searchPath, Zone& zone)
|
||||
{
|
||||
return std::make_unique<FlameTableLoaderT6>(memory, searchPath, zone);
|
||||
}
|
||||
} // namespace weapon
|
||||
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "Asset/IAssetCreator.h"
|
||||
#include "SearchPath/ISearchPath.h"
|
||||
#include "Utils/MemoryManager.h"
|
||||
#include "Zone/Zone.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace weapon
|
||||
{
|
||||
std::unique_ptr<ISubAssetCreator> CreateFlameTableLoaderT6(MemoryManager& memory, ISearchPath& searchPath, Zone& zone);
|
||||
} // namespace weapon
|
||||
@@ -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 <cassert>
|
||||
@@ -442,6 +443,32 @@ namespace
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LoadFlameTable(const char* flameTableName, FlameTable*& flameTablePtr, AssetRegistration<AssetWeapon>& registration, AssetCreationContext& context)
|
||||
{
|
||||
if (flameTableName && flameTableName[0] != '\0')
|
||||
{
|
||||
auto* flameTableAsset = context.LoadSubAsset<SubAssetFlameTable>(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<AssetWeapon>& 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<float>(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<float>(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<char*>(weapon.weapVariantDef.szAmmoName));
|
||||
|
||||
if (weapon.weapVariantDef.szClipName)
|
||||
utils::MakeStringLowerCase(const_cast<char*>(weapon.weapVariantDef.szClipName));
|
||||
}
|
||||
|
||||
void CheckAttachModelTags(const WeaponFullDef& weapon)
|
||||
{
|
||||
for (auto* tag : weapon.attachViewModelTag)
|
||||
{
|
||||
if (tag)
|
||||
utils::MakeStringLowerCase(const_cast<char*>(tag));
|
||||
}
|
||||
|
||||
for (auto* tag : weapon.attachWorldModelTag)
|
||||
{
|
||||
if (tag)
|
||||
utils::MakeStringLowerCase(const_cast<char*>(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<float>(weapon.weapVariantDef.iAdsTransInTime);
|
||||
|
||||
if (weapon.weapVariantDef.iAdsTransOutTime <= 0)
|
||||
weapon.weapVariantDef.fOOPosAnimLength[1] = 0.0020000001f;
|
||||
else
|
||||
weapon.weapVariantDef.fOOPosAnimLength[1] = 1.0f / static_cast<float>(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)));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user