mirror of
https://github.com/Laupetin/OpenAssetTools.git
synced 2025-04-20 16:15:43 +00:00
Merge pull request #172 from Laupetin/feature/iw5-weapons
feat: load iw5 attachments and weapons
This commit is contained in:
commit
ff9cb67d84
@ -3941,7 +3941,7 @@ namespace IW5
|
|||||||
SoundOverrideTypes soundType;
|
SoundOverrideTypes soundType;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum FXOverrideTypes : unsigned int
|
enum FxOverrideTypes : unsigned int
|
||||||
{
|
{
|
||||||
FXTYPE_NONE = 0x0,
|
FXTYPE_NONE = 0x0,
|
||||||
FXTYPE_VIEW_FLASH = 0x1,
|
FXTYPE_VIEW_FLASH = 0x1,
|
||||||
@ -3952,13 +3952,13 @@ namespace IW5
|
|||||||
FXTYPE_COUNT
|
FXTYPE_COUNT
|
||||||
};
|
};
|
||||||
|
|
||||||
struct FXOverrideEntry
|
struct FxOverrideEntry
|
||||||
{
|
{
|
||||||
WeaponAttachmentCombination attachment1;
|
WeaponAttachmentCombination attachment1;
|
||||||
WeaponAttachmentCombination attachment2;
|
WeaponAttachmentCombination attachment2;
|
||||||
FxEffectDef* overrideFX;
|
FxEffectDef* overrideFx;
|
||||||
FxEffectDef* altmodeFX;
|
FxEffectDef* altmodeFx;
|
||||||
FXOverrideTypes fxType;
|
FxOverrideTypes fxType;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ReloadStateTimerEntry
|
struct ReloadStateTimerEntry
|
||||||
@ -3991,8 +3991,8 @@ namespace IW5
|
|||||||
AnimOverrideEntry* animOverrides;
|
AnimOverrideEntry* animOverrides;
|
||||||
unsigned int numSoundOverrides;
|
unsigned int numSoundOverrides;
|
||||||
SoundOverrideEntry* soundOverrides;
|
SoundOverrideEntry* soundOverrides;
|
||||||
unsigned int numFXOverrides;
|
unsigned int numFxOverrides;
|
||||||
FXOverrideEntry* fxOverrides;
|
FxOverrideEntry* fxOverrides;
|
||||||
unsigned int numReloadStateTimerOverrides;
|
unsigned int numReloadStateTimerOverrides;
|
||||||
ReloadStateTimerEntry* reloadOverrides;
|
ReloadStateTimerEntry* reloadOverrides;
|
||||||
unsigned int numNotetrackOverrides;
|
unsigned int numNotetrackOverrides;
|
||||||
|
@ -972,12 +972,12 @@ namespace IW5
|
|||||||
};
|
};
|
||||||
static_assert(std::extent_v<decltype(s_vehicleAxleTypeNames)> == VEH_AXLE_COUNT);
|
static_assert(std::extent_v<decltype(s_vehicleAxleTypeNames)> == VEH_AXLE_COUNT);
|
||||||
|
|
||||||
inline const char* bounceSoundSuffixes[]{
|
inline const char* surfaceTypeSoundSuffixes[]{
|
||||||
"_default", "_bark", "_brick", "_carpet", "_cloth", "_concrete", "_dirt", "_flesh", "_foliage", "_glass", "_grass",
|
"_default", "_bark", "_brick", "_carpet", "_cloth", "_concrete", "_dirt", "_flesh", "_foliage", "_glass", "_grass",
|
||||||
"_gravel", "_ice", "_metal", "_mud", "_paper", "_plaster", "_rock", "_sand", "_snow", "_water", "_wood",
|
"_gravel", "_ice", "_metal", "_mud", "_paper", "_plaster", "_rock", "_sand", "_snow", "_water", "_wood",
|
||||||
"_asphalt", "_ceramic", "_plastic", "_rubber", "_cushion", "_fruit", "_painted_metal", "_riot_shield", "_slush",
|
"_asphalt", "_ceramic", "_plastic", "_rubber", "_cushion", "_fruit", "_painted_metal", "_riot_shield", "_slush",
|
||||||
};
|
};
|
||||||
static_assert(std::extent_v<decltype(bounceSoundSuffixes)> == SURF_TYPE_COUNT);
|
static_assert(std::extent_v<decltype(surfaceTypeSoundSuffixes)> == SURF_TYPE_COUNT);
|
||||||
|
|
||||||
inline const char* weapAnimFilesNames[]{
|
inline const char* weapAnimFilesNames[]{
|
||||||
"root", "idle", "empty_idle", "fire", "hold_fire", "lastshot", "rechamber", "melee",
|
"root", "idle", "empty_idle", "fire", "hold_fire", "lastshot", "rechamber", "melee",
|
||||||
|
@ -510,6 +510,10 @@ namespace IW5
|
|||||||
{WEAPCLASS_ITEM, "item" },
|
{WEAPCLASS_ITEM, "item" },
|
||||||
});
|
});
|
||||||
|
|
||||||
|
constexpr auto ATTACHMENT_WORLD_MODEL_COUNT = 16u;
|
||||||
|
constexpr auto ATTACHMENT_VIEW_MODEL_COUNT = 16u;
|
||||||
|
constexpr auto ATTACHMENT_RETICLE_VIEW_MODEL_COUNT = 8u;
|
||||||
|
|
||||||
class JsonWeaponAttachment
|
class JsonWeaponAttachment
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -62,7 +62,7 @@ namespace
|
|||||||
|
|
||||||
_NODISCARD bool ConvertBounceSounds(const cspField_t& field, const std::string& value) const
|
_NODISCARD bool ConvertBounceSounds(const cspField_t& field, const std::string& value) const
|
||||||
{
|
{
|
||||||
auto*** bounceSound = reinterpret_cast<const char***>(reinterpret_cast<uintptr_t>(m_structure) + field.iOffset);
|
auto** bounceSound = reinterpret_cast<SndAliasCustom**>(reinterpret_cast<uintptr_t>(m_structure) + field.iOffset);
|
||||||
if (value.empty())
|
if (value.empty())
|
||||||
{
|
{
|
||||||
*bounceSound = nullptr;
|
*bounceSound = nullptr;
|
||||||
@ -70,19 +70,22 @@ namespace
|
|||||||
}
|
}
|
||||||
|
|
||||||
assert(std::extent_v<decltype(bounceSoundSuffixes)> == SURF_TYPE_NUM);
|
assert(std::extent_v<decltype(bounceSoundSuffixes)> == SURF_TYPE_NUM);
|
||||||
*bounceSound = static_cast<const char**>(m_memory->Alloc(sizeof(const char*) * SURF_TYPE_NUM));
|
*bounceSound = static_cast<SndAliasCustom*>(m_memory->Alloc(sizeof(SndAliasCustom) * SURF_TYPE_NUM));
|
||||||
for (auto i = 0u; i < SURF_TYPE_NUM; i++)
|
for (auto i = 0u; i < SURF_TYPE_NUM; i++)
|
||||||
{
|
{
|
||||||
const auto currentBounceSound = value + bounceSoundSuffixes[i];
|
const auto currentBounceSound = value + bounceSoundSuffixes[i];
|
||||||
(*bounceSound)[i] = m_memory->Dup(currentBounceSound.c_str());
|
|
||||||
|
(*bounceSound)[i].name = static_cast<snd_alias_list_name*>(m_memory->Alloc(sizeof(snd_alias_list_name)));
|
||||||
|
(*bounceSound)[i].name->soundName = m_memory->Dup(currentBounceSound.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
_NODISCARD bool ConvertNotetrackMap(const cspField_t& field, const std::string& value, const char* mapName, const size_t keyAndValueCount)
|
_NODISCARD bool ConvertNotetrackMap(const cspField_t& field, const std::string& value, const char* mapName, const size_t keyAndValueCount)
|
||||||
{
|
{
|
||||||
std::vector<std::pair<std::string, std::string>> pairs;
|
std::vector<std::array<std::string, 2>> pairs;
|
||||||
if (!ParseAsPairs(value, pairs))
|
if (!ParseAsArray(value, pairs))
|
||||||
{
|
{
|
||||||
std::cerr << "Failed to parse notetrack" << mapName << "map as pairs\n";
|
std::cerr << "Failed to parse notetrack" << mapName << "map as pairs\n";
|
||||||
return false;
|
return false;
|
||||||
@ -107,10 +110,10 @@ namespace
|
|||||||
for (; currentEntryNum < pairs.size(); currentEntryNum++)
|
for (; currentEntryNum < pairs.size(); currentEntryNum++)
|
||||||
{
|
{
|
||||||
const auto& currentValue = pairs[currentEntryNum];
|
const auto& currentValue = pairs[currentEntryNum];
|
||||||
const auto keyScriptString = !currentValue.first.empty() ? m_zone_script_strings.AddOrGetScriptString(currentValue.first)
|
const auto keyScriptString = !currentValue[0].empty() ? m_zone_script_strings.AddOrGetScriptString(currentValue[0])
|
||||||
: m_zone_script_strings.AddOrGetScriptString(nullptr);
|
: m_zone_script_strings.AddOrGetScriptString(nullptr);
|
||||||
const auto valueScriptString = !currentValue.second.empty() ? m_zone_script_strings.AddOrGetScriptString(currentValue.second)
|
const auto valueScriptString = !currentValue[1].empty() ? m_zone_script_strings.AddOrGetScriptString(currentValue[1])
|
||||||
: m_zone_script_strings.AddOrGetScriptString(nullptr);
|
: m_zone_script_strings.AddOrGetScriptString(nullptr);
|
||||||
|
|
||||||
keys[currentEntryNum] = keyScriptString;
|
keys[currentEntryNum] = keyScriptString;
|
||||||
m_used_script_string_list.emplace(keyScriptString);
|
m_used_script_string_list.emplace(keyScriptString);
|
||||||
@ -255,7 +258,6 @@ namespace
|
|||||||
weapon->weapDef.parallelBounce = weapon->parallelBounce;
|
weapon->weapDef.parallelBounce = weapon->parallelBounce;
|
||||||
weapon->weapDef.perpendicularBounce = weapon->perpendicularBounce;
|
weapon->weapDef.perpendicularBounce = weapon->perpendicularBounce;
|
||||||
weapon->weapDef.locationDamageMultipliers = weapon->locationDamageMultipliers;
|
weapon->weapDef.locationDamageMultipliers = weapon->locationDamageMultipliers;
|
||||||
weapon->weapCompleteDef.szInternalName = "";
|
|
||||||
|
|
||||||
for (const auto& field : weapon_fields)
|
for (const auto& field : weapon_fields)
|
||||||
{
|
{
|
||||||
@ -330,17 +332,6 @@ namespace
|
|||||||
if (!weaponDef.worldLastShotEjectEffect)
|
if (!weaponDef.worldLastShotEjectEffect)
|
||||||
weaponDef.worldLastShotEjectEffect = weaponDef.worldShellEjectEffect;
|
weaponDef.worldLastShotEjectEffect = weaponDef.worldShellEjectEffect;
|
||||||
|
|
||||||
if (!weaponDef.raiseSound.name)
|
|
||||||
weaponDef.raiseSound.name = CreateSoundAliasListName("weap_raise", memory);
|
|
||||||
if (!weaponDef.putawaySound.name)
|
|
||||||
weaponDef.putawaySound.name = CreateSoundAliasListName("weap_putaway", memory);
|
|
||||||
if (!weaponDef.pickupSound.name)
|
|
||||||
weaponDef.pickupSound.name = CreateSoundAliasListName("weap_pickup", memory);
|
|
||||||
if (!weaponDef.ammoPickupSound.name)
|
|
||||||
weaponDef.ammoPickupSound.name = CreateSoundAliasListName("weap_ammo_pickup", memory);
|
|
||||||
if (!weaponDef.emptyFireSound.name)
|
|
||||||
weaponDef.emptyFireSound.name = CreateSoundAliasListName("weap_dryfire_smg_npc", memory);
|
|
||||||
|
|
||||||
if (weaponCompleteDef.iAdsTransInTime <= 0)
|
if (weaponCompleteDef.iAdsTransInTime <= 0)
|
||||||
weaponDef.fOOPosAnimLength[0] = 0.0033333334f;
|
weaponDef.fOOPosAnimLength[0] = 0.0033333334f;
|
||||||
else
|
else
|
||||||
@ -351,9 +342,9 @@ namespace
|
|||||||
else
|
else
|
||||||
weaponDef.fOOPosAnimLength[1] = 1.0f / static_cast<float>(weaponCompleteDef.iAdsTransOutTime);
|
weaponDef.fOOPosAnimLength[1] = 1.0f / static_cast<float>(weaponCompleteDef.iAdsTransOutTime);
|
||||||
|
|
||||||
if (weaponDef.fMaxDamageRange <= 0.0f)
|
if (weaponDef.fMaxDamageRange < 0.0f)
|
||||||
weaponDef.fMaxDamageRange = 999999.0f;
|
weaponDef.fMaxDamageRange = 999999.0f;
|
||||||
if (weaponDef.fMinDamageRange <= 0.0f)
|
if (weaponDef.fMinDamageRange < 0.0f)
|
||||||
weaponDef.fMinDamageRange = 999999.12f;
|
weaponDef.fMinDamageRange = 999999.12f;
|
||||||
|
|
||||||
if (weaponDef.enemyCrosshairRange > 15000.0f)
|
if (weaponDef.enemyCrosshairRange > 15000.0f)
|
||||||
@ -394,6 +385,7 @@ namespace
|
|||||||
auto* weaponFullDef = memory->Create<WeaponFullDef>();
|
auto* weaponFullDef = memory->Create<WeaponFullDef>();
|
||||||
memset(weaponFullDef, 0, sizeof(WeaponFullDef));
|
memset(weaponFullDef, 0, sizeof(WeaponFullDef));
|
||||||
InitWeaponFullDef(weaponFullDef);
|
InitWeaponFullDef(weaponFullDef);
|
||||||
|
weaponFullDef->weapCompleteDef.szInternalName = memory->Dup(assetName.c_str());
|
||||||
|
|
||||||
InfoStringToWeaponConverter converter(
|
InfoStringToWeaponConverter converter(
|
||||||
infoString, weaponFullDef, zone->m_script_strings, memory, manager, weapon_fields, std::extent_v<decltype(weapon_fields)>);
|
infoString, weaponFullDef, zone->m_script_strings, memory, manager, weapon_fields, std::extent_v<decltype(weapon_fields)>);
|
||||||
@ -403,8 +395,6 @@ namespace
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
weaponFullDef->weapCompleteDef.szInternalName = memory->Dup(assetName.c_str());
|
|
||||||
|
|
||||||
CalculateWeaponFields(weaponFullDef, memory);
|
CalculateWeaponFields(weaponFullDef, memory);
|
||||||
|
|
||||||
manager->AddAsset(ASSET_TYPE_WEAPON,
|
manager->AddAsset(ASSET_TYPE_WEAPON,
|
||||||
|
895
src/ObjLoading/Game/IW5/AssetLoaders/AssetLoaderWeapon.cpp
Normal file
895
src/ObjLoading/Game/IW5/AssetLoaders/AssetLoaderWeapon.cpp
Normal file
@ -0,0 +1,895 @@
|
|||||||
|
#include "AssetLoaderWeapon.h"
|
||||||
|
|
||||||
|
#include "Game/IW5/IW5.h"
|
||||||
|
#include "Game/IW5/InfoString/InfoStringToStructConverter.h"
|
||||||
|
#include "Game/IW5/InfoString/WeaponFields.h"
|
||||||
|
#include "Game/IW5/ObjConstantsIW5.h"
|
||||||
|
#include "InfoString/InfoString.h"
|
||||||
|
#include "ObjLoading.h"
|
||||||
|
#include "Pool/GlobalAssetPool.h"
|
||||||
|
#include "Utils/StringUtils.h"
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
#include <format>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
using namespace IW5;
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
class InfoStringToWeaponConverter final : public InfoStringToStructConverter
|
||||||
|
{
|
||||||
|
bool ConvertHideTags(const cspField_t& field, const std::string& value)
|
||||||
|
{
|
||||||
|
std::vector<std::string> valueArray;
|
||||||
|
if (!ParseAsArray(value, valueArray))
|
||||||
|
{
|
||||||
|
std::cerr << "Failed to parse hide tags as array\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (valueArray.size() > std::extent_v<decltype(WeaponFullDef::hideTags)>)
|
||||||
|
{
|
||||||
|
std::cerr << "Cannot have more than " << std::extent_v<decltype(WeaponFullDef::hideTags)> << " hide tags!\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* hideTags = reinterpret_cast<scr_string_t*>(reinterpret_cast<uintptr_t>(m_structure) + field.iOffset);
|
||||||
|
|
||||||
|
if (valueArray.size() < std::extent_v<decltype(WeaponFullDef::hideTags)>)
|
||||||
|
{
|
||||||
|
m_used_script_string_list.emplace(m_zone_script_strings.AddOrGetScriptString(nullptr));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto currentHideTag = 0u;
|
||||||
|
for (; currentHideTag < valueArray.size(); currentHideTag++)
|
||||||
|
{
|
||||||
|
const auto& currentValue = valueArray[currentHideTag];
|
||||||
|
const auto scrString =
|
||||||
|
!currentValue.empty() ? m_zone_script_strings.AddOrGetScriptString(currentValue) : m_zone_script_strings.AddOrGetScriptString(nullptr);
|
||||||
|
hideTags[currentHideTag] = scrString;
|
||||||
|
m_used_script_string_list.emplace(scrString);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (; currentHideTag < std::extent_v<decltype(WeaponFullDef::hideTags)>; currentHideTag++)
|
||||||
|
{
|
||||||
|
hideTags[currentHideTag] = m_zone_script_strings.GetScriptString(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
_NODISCARD bool ConvertPerSurfaceTypeSound(const cspField_t& field, const std::string& value) const
|
||||||
|
{
|
||||||
|
auto** perSurfaceTypeSound = reinterpret_cast<SndAliasCustom**>(reinterpret_cast<uintptr_t>(m_structure) + field.iOffset);
|
||||||
|
if (value.empty())
|
||||||
|
{
|
||||||
|
*perSurfaceTypeSound = nullptr;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
*perSurfaceTypeSound = static_cast<SndAliasCustom*>(m_memory->Alloc(sizeof(SndAliasCustom) * SURF_TYPE_COUNT));
|
||||||
|
for (auto i = 0u; i < SURF_TYPE_COUNT; i++)
|
||||||
|
{
|
||||||
|
const auto currentPerSurfaceTypeSound = value + surfaceTypeSoundSuffixes[i];
|
||||||
|
|
||||||
|
(*perSurfaceTypeSound)[i].name = static_cast<snd_alias_list_name*>(m_memory->Alloc(sizeof(snd_alias_list_name)));
|
||||||
|
(*perSurfaceTypeSound)[i].name->soundName = m_memory->Dup(currentPerSurfaceTypeSound.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
_NODISCARD bool ConvertNoteTrackMap(const cspField_t& field, const std::string& value, const char* mapName, const size_t keyAndValueCount)
|
||||||
|
{
|
||||||
|
std::vector<std::array<std::string, 2>> pairs;
|
||||||
|
if (!ParseAsArray(value, pairs))
|
||||||
|
{
|
||||||
|
std::cerr << "Failed to parse notetrack" << mapName << "map as pairs\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pairs.size() > std::extent_v<decltype(WeaponFullDef::notetrackSoundMapKeys)>)
|
||||||
|
{
|
||||||
|
std::cerr << "Cannot have more than " << std::extent_v<decltype(WeaponFullDef::notetrackSoundMapKeys)> << " notetrack" << mapName
|
||||||
|
<< "map entries!\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* keys = reinterpret_cast<scr_string_t*>(reinterpret_cast<uintptr_t>(m_structure) + field.iOffset);
|
||||||
|
auto* values = &keys[keyAndValueCount];
|
||||||
|
auto currentEntryNum = 0u;
|
||||||
|
|
||||||
|
if (pairs.size() < keyAndValueCount)
|
||||||
|
{
|
||||||
|
m_used_script_string_list.emplace(m_zone_script_strings.AddOrGetScriptString(nullptr));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (; currentEntryNum < pairs.size(); currentEntryNum++)
|
||||||
|
{
|
||||||
|
const auto& currentValue = pairs[currentEntryNum];
|
||||||
|
const auto keyScriptString = !currentValue[0].empty() ? m_zone_script_strings.AddOrGetScriptString(currentValue[0])
|
||||||
|
: m_zone_script_strings.AddOrGetScriptString(nullptr);
|
||||||
|
const auto valueScriptString = !currentValue[1].empty() ? m_zone_script_strings.AddOrGetScriptString(currentValue[1])
|
||||||
|
: m_zone_script_strings.AddOrGetScriptString(nullptr);
|
||||||
|
|
||||||
|
keys[currentEntryNum] = keyScriptString;
|
||||||
|
m_used_script_string_list.emplace(keyScriptString);
|
||||||
|
|
||||||
|
values[currentEntryNum] = valueScriptString;
|
||||||
|
m_used_script_string_list.emplace(valueScriptString);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (; currentEntryNum < keyAndValueCount; currentEntryNum++)
|
||||||
|
{
|
||||||
|
const auto emptyScr = m_zone_script_strings.GetScriptString(nullptr);
|
||||||
|
keys[currentEntryNum] = emptyScr;
|
||||||
|
values[currentEntryNum] = emptyScr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ConvertAnimName(const cspField_t& field, const std::string& value)
|
||||||
|
{
|
||||||
|
if (ConvertString(value, field.iOffset))
|
||||||
|
{
|
||||||
|
if (!value.empty())
|
||||||
|
{
|
||||||
|
auto lowerValue = value;
|
||||||
|
utils::MakeStringLowerCase(lowerValue);
|
||||||
|
m_indirect_asset_references.emplace(m_loading_manager->LoadIndirectAssetReference(ASSET_TYPE_XANIMPARTS, lowerValue));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ConvertAttachments(const std::string& value)
|
||||||
|
{
|
||||||
|
std::vector<std::string> valueArray;
|
||||||
|
if (!ParseAsArray(value, valueArray))
|
||||||
|
{
|
||||||
|
std::cerr << "Failed to parse attachments as array\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto currentScope = 0u;
|
||||||
|
auto currentUnderBarrel = 0u;
|
||||||
|
auto currentOther = 0u;
|
||||||
|
for (const auto& attachmentName : valueArray)
|
||||||
|
{
|
||||||
|
auto* attachmentInfo = static_cast<XAssetInfo<WeaponAttachment>*>(m_loading_manager->LoadDependency(ASSET_TYPE_ATTACHMENT, attachmentName));
|
||||||
|
if (!attachmentInfo)
|
||||||
|
return false;
|
||||||
|
m_dependencies.emplace(attachmentInfo);
|
||||||
|
auto* attachment = attachmentInfo->Asset();
|
||||||
|
|
||||||
|
if (attachment->type == ATTACHMENT_SCOPE)
|
||||||
|
{
|
||||||
|
if (currentScope >= std::extent_v<decltype(WeaponFullDef::scopes)>)
|
||||||
|
{
|
||||||
|
std::cerr << "Cannot have more than " << std::extent_v<decltype(WeaponFullDef::scopes)> << " scopes\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_weapon->scopes[currentScope++] = attachment;
|
||||||
|
}
|
||||||
|
else if (attachment->type == ATTACHMENT_UNDERBARREL)
|
||||||
|
{
|
||||||
|
if (currentUnderBarrel >= std::extent_v<decltype(WeaponFullDef::underBarrels)>)
|
||||||
|
{
|
||||||
|
std::cerr << "Cannot have more than " << std::extent_v<decltype(WeaponFullDef::underBarrels)> << " under barrels\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_weapon->underBarrels[currentUnderBarrel++] = attachment;
|
||||||
|
}
|
||||||
|
else if (attachment->type == ATTACHMENT_OTHER)
|
||||||
|
{
|
||||||
|
if (currentOther >= std::extent_v<decltype(WeaponFullDef::others)>)
|
||||||
|
{
|
||||||
|
std::cerr << "Cannot have more than " << std::extent_v<decltype(WeaponFullDef::others)> << " other attachments\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_weapon->others[currentOther++] = attachment;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ConvertAnimOverrides(const std::string& value)
|
||||||
|
{
|
||||||
|
std::vector<std::array<std::string, 7>> valueArray;
|
||||||
|
if (!ParseAsArray(value, valueArray))
|
||||||
|
{
|
||||||
|
std::cerr << "Failed to parse anim overrides as array\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* animOverrides = static_cast<AnimOverrideEntry*>(m_memory->Alloc(sizeof(AnimOverrideEntry) * valueArray.size()));
|
||||||
|
|
||||||
|
auto i = 0u;
|
||||||
|
for (const auto& overrideValues : valueArray)
|
||||||
|
{
|
||||||
|
auto& animOverride = animOverrides[i++];
|
||||||
|
|
||||||
|
if (!ParseSingleWeaponAttachment(overrideValues[0], animOverride.attachment1))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!ParseSingleWeaponAttachment(overrideValues[1], animOverride.attachment2))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!ParseAnimFile(overrideValues[2], animOverride.animTreeType))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
ParseAnim(overrideValues[3], animOverride.overrideAnim);
|
||||||
|
ParseAnim(overrideValues[4], animOverride.altmodeAnim);
|
||||||
|
|
||||||
|
if (!ParseInt(overrideValues[5], animOverride.animTime))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!ParseInt(overrideValues[6], animOverride.altTime))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_weapon->weapCompleteDef.animOverrides = animOverrides;
|
||||||
|
m_weapon->weapCompleteDef.numAnimOverrides = valueArray.size();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ConvertSoundOverrides(const std::string& value)
|
||||||
|
{
|
||||||
|
std::vector<std::array<std::string, 5>> valueArray;
|
||||||
|
if (!ParseAsArray(value, valueArray))
|
||||||
|
{
|
||||||
|
std::cerr << "Failed to parse sound overrides as array\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* soundOverrides = static_cast<SoundOverrideEntry*>(m_memory->Alloc(sizeof(SoundOverrideEntry) * valueArray.size()));
|
||||||
|
|
||||||
|
auto i = 0u;
|
||||||
|
for (const auto& overrideValues : valueArray)
|
||||||
|
{
|
||||||
|
auto& soundOverride = soundOverrides[i++];
|
||||||
|
|
||||||
|
if (!ParseSingleWeaponAttachment(overrideValues[0], soundOverride.attachment1))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!ParseSingleWeaponAttachment(overrideValues[1], soundOverride.attachment2))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!ParseSoundType(overrideValues[2], soundOverride.soundType))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
ParseSoundAlias(overrideValues[3], soundOverride.overrideSound);
|
||||||
|
ParseSoundAlias(overrideValues[4], soundOverride.altmodeSound);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_weapon->weapCompleteDef.soundOverrides = soundOverrides;
|
||||||
|
m_weapon->weapCompleteDef.numSoundOverrides = valueArray.size();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ConvertFxOverrides(const std::string& value)
|
||||||
|
{
|
||||||
|
std::vector<std::array<std::string, 5>> valueArray;
|
||||||
|
if (!ParseAsArray(value, valueArray))
|
||||||
|
{
|
||||||
|
std::cerr << "Failed to parse attachments as array\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* fxOverrides = static_cast<FxOverrideEntry*>(m_memory->Alloc(sizeof(FxOverrideEntry) * valueArray.size()));
|
||||||
|
|
||||||
|
auto i = 0u;
|
||||||
|
for (const auto& overrideValues : valueArray)
|
||||||
|
{
|
||||||
|
auto& fxOverride = fxOverrides[i++];
|
||||||
|
|
||||||
|
if (!ParseSingleWeaponAttachment(overrideValues[0], fxOverride.attachment1))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!ParseSingleWeaponAttachment(overrideValues[1], fxOverride.attachment2))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!ParseFxType(overrideValues[2], fxOverride.fxType))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!ParseFxEffectDef(overrideValues[3], fxOverride.overrideFx))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!ParseFxEffectDef(overrideValues[4], fxOverride.altmodeFx))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_weapon->weapCompleteDef.fxOverrides = fxOverrides;
|
||||||
|
m_weapon->weapCompleteDef.numFxOverrides = valueArray.size();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ConvertReloadOverrides(const std::string& value)
|
||||||
|
{
|
||||||
|
std::vector<std::array<std::string, 3>> valueArray;
|
||||||
|
if (!ParseAsArray(value, valueArray))
|
||||||
|
{
|
||||||
|
std::cerr << "Failed to parse reload overrides as array\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* reloadOverrides = static_cast<ReloadStateTimerEntry*>(m_memory->Alloc(sizeof(ReloadStateTimerEntry) * valueArray.size()));
|
||||||
|
|
||||||
|
auto i = 0u;
|
||||||
|
for (const auto& overrideValues : valueArray)
|
||||||
|
{
|
||||||
|
auto& reloadOverride = reloadOverrides[i++];
|
||||||
|
|
||||||
|
if (!ParseSingleWeaponAttachment(overrideValues[0], reloadOverride.attachment))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
reloadOverride.unused = 0u;
|
||||||
|
|
||||||
|
if (!ParseInt(overrideValues[1], reloadOverride.reloadAddTime))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!ParseInt(overrideValues[2], reloadOverride.reloadStartAddTime))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_weapon->weapCompleteDef.reloadOverrides = reloadOverrides;
|
||||||
|
m_weapon->weapCompleteDef.numReloadStateTimerOverrides = valueArray.size();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ConvertNoteTrackOverrides(const std::string& value)
|
||||||
|
{
|
||||||
|
std::vector<std::array<std::string, 3>> valueArray;
|
||||||
|
if (!ParseAsArray(value, valueArray))
|
||||||
|
{
|
||||||
|
std::cerr << "Failed to parse note track overrides as array\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<NoteTrackToSoundEntry> overrideVector;
|
||||||
|
NoteTrackToSoundEntry currentOverride;
|
||||||
|
auto currentOverrideKeyOffset = 0u;
|
||||||
|
currentOverride.unused = 0u;
|
||||||
|
std::string lastAttachment;
|
||||||
|
|
||||||
|
auto i = 0u;
|
||||||
|
for (const auto& overrideValues : valueArray)
|
||||||
|
{
|
||||||
|
if (i == 0u || lastAttachment != overrideValues[0])
|
||||||
|
{
|
||||||
|
if (currentOverrideKeyOffset > 0u)
|
||||||
|
overrideVector.emplace_back(currentOverride);
|
||||||
|
|
||||||
|
if (!ParseSingleWeaponAttachment(overrideValues[0], currentOverride.attachment))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
currentOverride.notetrackSoundMapKeys = static_cast<ScriptString*>(m_memory->Alloc(sizeof(ScriptString) * 24u));
|
||||||
|
memset(currentOverride.notetrackSoundMapKeys, 0u, sizeof(ScriptString) * 24u);
|
||||||
|
currentOverride.notetrackSoundMapValues = static_cast<ScriptString*>(m_memory->Alloc(sizeof(ScriptString) * 24u));
|
||||||
|
memset(currentOverride.notetrackSoundMapValues, 0u, sizeof(ScriptString) * 24u);
|
||||||
|
lastAttachment = overrideValues[0];
|
||||||
|
currentOverrideKeyOffset = 0u;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentOverrideKeyOffset >= 24u)
|
||||||
|
{
|
||||||
|
std::cerr << "Cannot have more than " << 24u << " note track overrides per attachment\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ParseScriptString(overrideValues[1], currentOverride.notetrackSoundMapKeys[currentOverrideKeyOffset]);
|
||||||
|
ParseScriptString(overrideValues[2], currentOverride.notetrackSoundMapValues[currentOverrideKeyOffset]);
|
||||||
|
currentOverrideKeyOffset++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentOverrideKeyOffset > 0u)
|
||||||
|
overrideVector.emplace_back(currentOverride);
|
||||||
|
|
||||||
|
m_weapon->weapCompleteDef.notetrackOverrides =
|
||||||
|
static_cast<NoteTrackToSoundEntry*>(m_memory->Alloc(sizeof(NoteTrackToSoundEntry) * overrideVector.size()));
|
||||||
|
memcpy(m_weapon->weapCompleteDef.notetrackOverrides, overrideVector.data(), sizeof(NoteTrackToSoundEntry) * overrideVector.size());
|
||||||
|
m_weapon->weapCompleteDef.numNotetrackOverrides = overrideVector.size();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ParseSingleWeaponAttachment(const std::string& value, WeaponAttachmentCombination& attachment)
|
||||||
|
{
|
||||||
|
attachment.fields = 0u;
|
||||||
|
if (value == "none")
|
||||||
|
return true;
|
||||||
|
|
||||||
|
for (auto i = 0u; i < std::extent_v<decltype(WeaponFullDef::scopes)>; i++)
|
||||||
|
{
|
||||||
|
const auto* scope = m_weapon->scopes[i];
|
||||||
|
if (scope && scope->szInternalName && value == scope->szInternalName)
|
||||||
|
{
|
||||||
|
attachment.scope = static_cast<unsigned short>(i + 1);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto i = 0u; i < std::extent_v<decltype(WeaponFullDef::underBarrels)>; i++)
|
||||||
|
{
|
||||||
|
const auto* underBarrel = m_weapon->underBarrels[i];
|
||||||
|
if (underBarrel && underBarrel->szInternalName && value == underBarrel->szInternalName)
|
||||||
|
{
|
||||||
|
attachment.underBarrel = static_cast<unsigned short>(i + 1);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto i = 0u; i < std::extent_v<decltype(WeaponFullDef::others)>; i++)
|
||||||
|
{
|
||||||
|
const auto* other = m_weapon->others[i];
|
||||||
|
if (other && other->szInternalName && value == other->szInternalName)
|
||||||
|
{
|
||||||
|
attachment.other = static_cast<unsigned short>(1u << i);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cerr << "Weapon does not have attachment \"" << value << "\"\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParseAnim(const std::string& value, const char*& animName)
|
||||||
|
{
|
||||||
|
if (value == "none")
|
||||||
|
{
|
||||||
|
animName = nullptr;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
animName = m_memory->Dup(value.c_str());
|
||||||
|
m_indirect_asset_references.emplace(m_loading_manager->LoadIndirectAssetReference(ASSET_TYPE_XANIMPARTS, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParseSoundAlias(const std::string& value, SndAliasCustom& soundAlias)
|
||||||
|
{
|
||||||
|
if (value == "none")
|
||||||
|
{
|
||||||
|
soundAlias.name = nullptr;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
soundAlias.name = static_cast<snd_alias_list_name*>(m_memory->Alloc(sizeof(snd_alias_list_name)));
|
||||||
|
soundAlias.name->soundName = m_memory->Dup(value.c_str());
|
||||||
|
m_indirect_asset_references.emplace(m_loading_manager->LoadIndirectAssetReference(ASSET_TYPE_SOUND, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ParseFxEffectDef(const std::string& value, FxEffectDef*& fx)
|
||||||
|
{
|
||||||
|
if (value == "none")
|
||||||
|
{
|
||||||
|
fx = nullptr;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* fxInfo = static_cast<XAssetInfo<FxEffectDef>*>(m_loading_manager->LoadDependency(ASSET_TYPE_FX, value));
|
||||||
|
if (!fxInfo)
|
||||||
|
{
|
||||||
|
std::cerr << "Failed to load fx for override \"" << value << "\"\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fx = fxInfo->Asset();
|
||||||
|
m_dependencies.emplace(fxInfo);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool ParseAnimFile(const std::string& value, weapAnimFiles_t& animFile)
|
||||||
|
{
|
||||||
|
for (auto i = 0u; i < std::extent_v<decltype(weapAnimFilesNames)>; i++)
|
||||||
|
{
|
||||||
|
if (value == weapAnimFilesNames[i])
|
||||||
|
{
|
||||||
|
animFile = static_cast<weapAnimFiles_t>(i);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cerr << "Unknown anim file \"" << value << "\"\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool ParseSoundType(const std::string& value, SoundOverrideTypes& soundType)
|
||||||
|
{
|
||||||
|
for (auto i = 0u; i < std::extent_v<decltype(soundOverrideTypeNames)>; i++)
|
||||||
|
{
|
||||||
|
if (value == soundOverrideTypeNames[i])
|
||||||
|
{
|
||||||
|
soundType = static_cast<SoundOverrideTypes>(i);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cerr << "Unknown sound type \"" << value << "\"\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool ParseFxType(const std::string& value, FxOverrideTypes& fxType)
|
||||||
|
{
|
||||||
|
for (auto i = 0u; i < std::extent_v<decltype(fxOverrideTypeNames)>; i++)
|
||||||
|
{
|
||||||
|
if (value == fxOverrideTypeNames[i])
|
||||||
|
{
|
||||||
|
fxType = static_cast<FxOverrideTypes>(i);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cerr << "Unknown fx type \"" << value << "\"\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool ParseInt(const std::string& value, int& out)
|
||||||
|
{
|
||||||
|
size_t size;
|
||||||
|
out = std::stoi(value, &size);
|
||||||
|
|
||||||
|
if (size != value.size())
|
||||||
|
{
|
||||||
|
std::cerr << "Invalid int value: \"" << value << "\"\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParseScriptString(const std::string& value, ScriptString& out)
|
||||||
|
{
|
||||||
|
out = m_zone_script_strings.AddOrGetScriptString(value);
|
||||||
|
m_used_script_string_list.emplace(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool ConvertExtensionField(const cspField_t& field, const std::string& value) override
|
||||||
|
{
|
||||||
|
switch (static_cast<weapFieldType_t>(field.iFieldType))
|
||||||
|
{
|
||||||
|
case WFT_WEAPONTYPE:
|
||||||
|
return ConvertEnumInt(value, field.iOffset, szWeapTypeNames, std::extent_v<decltype(szWeapTypeNames)>);
|
||||||
|
|
||||||
|
case WFT_WEAPONCLASS:
|
||||||
|
return ConvertEnumInt(value, field.iOffset, szWeapClassNames, std::extent_v<decltype(szWeapClassNames)>);
|
||||||
|
|
||||||
|
case WFT_OVERLAYRETICLE:
|
||||||
|
return ConvertEnumInt(value, field.iOffset, szWeapOverlayReticleNames, std::extent_v<decltype(szWeapOverlayReticleNames)>);
|
||||||
|
|
||||||
|
case WFT_PENETRATE_TYPE:
|
||||||
|
return ConvertEnumInt(value, field.iOffset, penetrateTypeNames, std::extent_v<decltype(penetrateTypeNames)>);
|
||||||
|
|
||||||
|
case WFT_IMPACT_TYPE:
|
||||||
|
return ConvertEnumInt(value, field.iOffset, impactTypeNames, std::extent_v<decltype(impactTypeNames)>);
|
||||||
|
|
||||||
|
case WFT_STANCE:
|
||||||
|
return ConvertEnumInt(value, field.iOffset, szWeapStanceNames, std::extent_v<decltype(szWeapStanceNames)>);
|
||||||
|
|
||||||
|
case WFT_PROJ_EXPLOSION:
|
||||||
|
return ConvertEnumInt(value, field.iOffset, szProjectileExplosionNames, std::extent_v<decltype(szProjectileExplosionNames)>);
|
||||||
|
|
||||||
|
case WFT_OFFHAND_CLASS:
|
||||||
|
return ConvertEnumInt(value, field.iOffset, offhandClassNames, std::extent_v<decltype(offhandClassNames)>);
|
||||||
|
|
||||||
|
case WFT_ANIMTYPE:
|
||||||
|
return ConvertEnumInt(value, field.iOffset, playerAnimTypeNames, std::extent_v<decltype(playerAnimTypeNames)>);
|
||||||
|
|
||||||
|
case WFT_ACTIVE_RETICLE_TYPE:
|
||||||
|
return ConvertEnumInt(value, field.iOffset, activeReticleNames, std::extent_v<decltype(activeReticleNames)>);
|
||||||
|
|
||||||
|
case WFT_GUIDED_MISSILE_TYPE:
|
||||||
|
return ConvertEnumInt(value, field.iOffset, guidedMissileNames, std::extent_v<decltype(guidedMissileNames)>);
|
||||||
|
|
||||||
|
case WFT_PER_SURFACE_TYPE_SOUND:
|
||||||
|
return ConvertPerSurfaceTypeSound(field, value);
|
||||||
|
|
||||||
|
case WFT_STICKINESS:
|
||||||
|
return ConvertEnumInt(value, field.iOffset, stickinessNames, std::extent_v<decltype(stickinessNames)>);
|
||||||
|
|
||||||
|
case WFT_OVERLAYINTERFACE:
|
||||||
|
return ConvertEnumInt(value, field.iOffset, overlayInterfaceNames, std::extent_v<decltype(overlayInterfaceNames)>);
|
||||||
|
|
||||||
|
case WFT_INVENTORYTYPE:
|
||||||
|
return ConvertEnumInt(value, field.iOffset, szWeapInventoryTypeNames, std::extent_v<decltype(szWeapInventoryTypeNames)>);
|
||||||
|
|
||||||
|
case WFT_FIRETYPE:
|
||||||
|
return ConvertEnumInt(value, field.iOffset, szWeapFireTypeNames, std::extent_v<decltype(szWeapFireTypeNames)>);
|
||||||
|
|
||||||
|
case WFT_AMMOCOUNTER_CLIPTYPE:
|
||||||
|
return ConvertEnumInt(value, field.iOffset, ammoCounterClipNames, std::extent_v<decltype(ammoCounterClipNames)>);
|
||||||
|
|
||||||
|
case WFT_ICONRATIO_HUD:
|
||||||
|
case WFT_ICONRATIO_PICKUP:
|
||||||
|
case WFT_ICONRATIO_AMMOCOUNTER:
|
||||||
|
case WFT_ICONRATIO_KILL:
|
||||||
|
case WFT_ICONRATIO_DPAD:
|
||||||
|
return ConvertEnumInt(value, field.iOffset, weapIconRatioNames, std::extent_v<decltype(weapIconRatioNames)>);
|
||||||
|
|
||||||
|
case WFT_HIDETAGS:
|
||||||
|
return ConvertHideTags(field, value);
|
||||||
|
|
||||||
|
case WFT_NOTETRACKSOUNDMAP:
|
||||||
|
return ConvertNoteTrackMap(field, value, "sound", std::extent_v<decltype(WeaponFullDef::notetrackSoundMapKeys)>);
|
||||||
|
|
||||||
|
case WFT_NOTETRACKRUMBLEMAP:
|
||||||
|
return ConvertNoteTrackMap(field, value, "rumble", std::extent_v<decltype(WeaponFullDef::notetrackRumbleMapKeys)>);
|
||||||
|
|
||||||
|
case WFT_ANIM_NAME:
|
||||||
|
return ConvertAnimName(field, value);
|
||||||
|
|
||||||
|
case WFT_ATTACHMENT:
|
||||||
|
return ConvertAttachments(value);
|
||||||
|
|
||||||
|
case WFT_ANIM_OVERRIDES:
|
||||||
|
return ConvertAnimOverrides(value);
|
||||||
|
|
||||||
|
case WFT_SOUND_OVERRIDES:
|
||||||
|
return ConvertSoundOverrides(value);
|
||||||
|
|
||||||
|
case WFT_FX_OVERRIDES:
|
||||||
|
return ConvertFxOverrides(value);
|
||||||
|
|
||||||
|
case WFT_RELOAD_OVERRIDES:
|
||||||
|
return ConvertReloadOverrides(value);
|
||||||
|
|
||||||
|
case WFT_NOTETRACK_OVERRIDES:
|
||||||
|
return ConvertNoteTrackOverrides(value);
|
||||||
|
|
||||||
|
default:
|
||||||
|
assert(false);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
InfoStringToWeaponConverter(const InfoString& infoString,
|
||||||
|
WeaponFullDef* weaponFullDef,
|
||||||
|
ZoneScriptStrings& zoneScriptStrings,
|
||||||
|
MemoryManager* memory,
|
||||||
|
IAssetLoadingManager* manager,
|
||||||
|
const cspField_t* fields,
|
||||||
|
const size_t fieldCount)
|
||||||
|
: InfoStringToStructConverter(infoString, weaponFullDef, zoneScriptStrings, memory, manager, fields, fieldCount)
|
||||||
|
{
|
||||||
|
m_weapon = weaponFullDef;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
WeaponFullDef* m_weapon;
|
||||||
|
};
|
||||||
|
|
||||||
|
void InitWeaponFullDef(WeaponFullDef* weapon)
|
||||||
|
{
|
||||||
|
weapon->weapCompleteDef.weapDef = &weapon->weapDef;
|
||||||
|
weapon->weapCompleteDef.hideTags = weapon->hideTags;
|
||||||
|
weapon->weapCompleteDef.szXAnims = weapon->szXAnims;
|
||||||
|
weapon->weapCompleteDef.scopes = weapon->scopes;
|
||||||
|
weapon->weapCompleteDef.underBarrels = weapon->underBarrels;
|
||||||
|
weapon->weapCompleteDef.others = weapon->others;
|
||||||
|
weapon->weapDef.gunXModel = weapon->gunXModel;
|
||||||
|
weapon->weapDef.szXAnimsRightHanded = weapon->szXAnimsRightHanded;
|
||||||
|
weapon->weapDef.szXAnimsLeftHanded = weapon->szXAnimsLeftHanded;
|
||||||
|
weapon->weapDef.notetrackSoundMapKeys = weapon->notetrackSoundMapKeys;
|
||||||
|
weapon->weapDef.notetrackSoundMapValues = weapon->notetrackSoundMapValues;
|
||||||
|
weapon->weapDef.notetrackRumbleMapKeys = weapon->notetrackRumbleMapKeys;
|
||||||
|
weapon->weapDef.notetrackRumbleMapValues = weapon->notetrackRumbleMapValues;
|
||||||
|
weapon->weapDef.worldModel = weapon->worldModel;
|
||||||
|
weapon->weapDef.parallelBounce = weapon->parallelBounce;
|
||||||
|
weapon->weapDef.perpendicularBounce = weapon->perpendicularBounce;
|
||||||
|
weapon->weapDef.locationDamageMultipliers = weapon->locationDamageMultipliers;
|
||||||
|
|
||||||
|
for (const auto& field : weapon_fields)
|
||||||
|
{
|
||||||
|
if (field.iFieldType != CSPFT_STRING)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
*reinterpret_cast<const char**>(reinterpret_cast<char*>(weapon) + field.iOffset) = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
snd_alias_list_name* CreateSoundAliasListName(const char* value, MemoryManager* memory)
|
||||||
|
{
|
||||||
|
auto* name = static_cast<snd_alias_list_name*>(memory->Alloc(sizeof(snd_alias_list_name)));
|
||||||
|
name->soundName = memory->Dup(value);
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CheckProjectileValues(const WeaponCompleteDef& weaponCompleteDef, const WeaponDef& weaponDef)
|
||||||
|
{
|
||||||
|
if (weaponDef.iProjectileSpeed <= 0)
|
||||||
|
std::cerr << std::format("Projectile speed for WeapType {} must be greater than 0.0", weaponCompleteDef.szDisplayName);
|
||||||
|
if (weaponDef.destabilizationCurvatureMax >= 1000000000.0f || weaponDef.destabilizationCurvatureMax < 0.0f)
|
||||||
|
std::cerr << std::format("Destabilization angle for for WeapType {} must be between 0 and 45 degrees", weaponCompleteDef.szDisplayName);
|
||||||
|
if (weaponDef.destabilizationRateTime < 0.0f)
|
||||||
|
std::cerr << std::format("Destabilization rate time for for WeapType {} must be non-negative", weaponCompleteDef.szDisplayName);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CheckTurretBarrelSpin(const WeaponCompleteDef& weaponCompleteDef, const WeaponDef& weaponDef)
|
||||||
|
{
|
||||||
|
if (weaponDef.weapClass != WEAPCLASS_TURRET)
|
||||||
|
std::cerr << std::format("Rotating barrel set for non-turret weapon {}.", weaponCompleteDef.szInternalName);
|
||||||
|
|
||||||
|
if (0.0f == weaponDef.turretBarrelSpinSpeed)
|
||||||
|
{
|
||||||
|
std::cerr << std::format(
|
||||||
|
"Rotating barrel spin speed '{}' is invalid for weapon {}.", weaponDef.turretBarrelSpinSpeed, weaponCompleteDef.szInternalName);
|
||||||
|
}
|
||||||
|
if (0.0f < weaponDef.turretOverheatUpRate && 0.0f >= weaponDef.turretOverheatDownRate)
|
||||||
|
{
|
||||||
|
std::cerr << std::format("Turret overheat Up rate is set, but the down rate '{}' is invalid for weapon {}.",
|
||||||
|
weaponDef.turretOverheatDownRate,
|
||||||
|
weaponCompleteDef.szInternalName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CheckThermalScope(const WeaponCompleteDef& weaponCompleteDef, const WeaponDef& weaponDef)
|
||||||
|
{
|
||||||
|
if (0.0f != weaponDef.fAdsZoomInFrac)
|
||||||
|
{
|
||||||
|
std::cerr << std::format("Weapon {} ({}) has thermal scope set. ADS Zoom In frac should be 0 to prevent zoom-in blur ({}).\n",
|
||||||
|
weaponCompleteDef.szInternalName,
|
||||||
|
weaponCompleteDef.szDisplayName,
|
||||||
|
weaponDef.fAdsZoomInFrac);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (0.0f != weaponDef.fAdsZoomOutFrac)
|
||||||
|
{
|
||||||
|
std::cerr << std::format("Weapon {} ({}) has thermal scope set. ADS Zoom Out frac should be 0 to prevent zoom-out blur ({}).\n",
|
||||||
|
weaponCompleteDef.szInternalName,
|
||||||
|
weaponCompleteDef.szDisplayName,
|
||||||
|
weaponDef.fAdsZoomOutFrac);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CalculateWeaponFields(WeaponFullDef* weapon, MemoryManager* memory)
|
||||||
|
{
|
||||||
|
auto& weaponCompleteDef = weapon->weapCompleteDef;
|
||||||
|
auto& weaponDef = weapon->weapDef;
|
||||||
|
|
||||||
|
if (!weaponDef.viewLastShotEjectEffect)
|
||||||
|
weaponDef.viewLastShotEjectEffect = weaponDef.viewShellEjectEffect;
|
||||||
|
if (!weaponDef.worldLastShotEjectEffect)
|
||||||
|
weaponDef.worldLastShotEjectEffect = weaponDef.worldShellEjectEffect;
|
||||||
|
|
||||||
|
if (weaponCompleteDef.iAdsTransInTime <= 0)
|
||||||
|
weaponDef.fOOPosAnimLength[0] = 0.0033333334f;
|
||||||
|
else
|
||||||
|
weaponDef.fOOPosAnimLength[0] = 1.0f / static_cast<float>(weaponCompleteDef.iAdsTransInTime);
|
||||||
|
|
||||||
|
if (weaponCompleteDef.iAdsTransOutTime <= 0)
|
||||||
|
weaponDef.fOOPosAnimLength[1] = 0.0020000001f;
|
||||||
|
else
|
||||||
|
weaponDef.fOOPosAnimLength[1] = 1.0f / static_cast<float>(weaponCompleteDef.iAdsTransOutTime);
|
||||||
|
|
||||||
|
if (weaponDef.fMaxDamageRange < 0.0f)
|
||||||
|
weaponDef.fMaxDamageRange = 999999.0f;
|
||||||
|
if (weaponDef.fMinDamageRange < 0.0f)
|
||||||
|
weaponDef.fMinDamageRange = 999999.12f;
|
||||||
|
|
||||||
|
if (weaponDef.enemyCrosshairRange > 15000.0f)
|
||||||
|
std::cerr << std::format("Enemy crosshair ranges should be less than {}\n", 15000.0f);
|
||||||
|
|
||||||
|
if (weaponDef.weapType == WEAPTYPE_PROJECTILE)
|
||||||
|
CheckProjectileValues(weaponCompleteDef, weaponDef);
|
||||||
|
|
||||||
|
if (weaponDef.turretBarrelSpinEnabled)
|
||||||
|
CheckTurretBarrelSpin(weaponCompleteDef, weaponDef);
|
||||||
|
|
||||||
|
if (weaponDef.thermalScope)
|
||||||
|
CheckThermalScope(weaponCompleteDef, weaponDef);
|
||||||
|
|
||||||
|
if (weaponDef.offhandClass && !weaponDef.bClipOnly)
|
||||||
|
{
|
||||||
|
std::cerr << std::format(
|
||||||
|
"Weapon {} ({}) is an offhand weapon but is not set to clip only, which is not supported since we can't reload the offhand slot.\n",
|
||||||
|
weaponCompleteDef.szInternalName,
|
||||||
|
weaponCompleteDef.szDisplayName);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (weaponDef.weapType == WEAPTYPE_BULLET)
|
||||||
|
{
|
||||||
|
if (weaponDef.bulletExplDmgMult <= 0.0f)
|
||||||
|
std::cerr << std::format("Detected invalid bulletExplDmgMult of '{}' for weapon '{}'; please update weapon settings.\n",
|
||||||
|
weaponDef.bulletExplDmgMult,
|
||||||
|
weaponCompleteDef.szInternalName);
|
||||||
|
if (weaponDef.bulletExplRadiusMult <= 0.0f)
|
||||||
|
std::cerr << std::format("Detected invalid bulletExplRadiusMult of '{}' for weapon '{}'; please update weapon settings.\n",
|
||||||
|
weaponDef.bulletExplRadiusMult,
|
||||||
|
weaponCompleteDef.szInternalName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LoadFromInfoString(const InfoString& infoString, const std::string& assetName, MemoryManager* memory, IAssetLoadingManager* manager, Zone* zone)
|
||||||
|
{
|
||||||
|
auto* weaponFullDef = memory->Create<WeaponFullDef>();
|
||||||
|
memset(weaponFullDef, 0, sizeof(WeaponFullDef));
|
||||||
|
InitWeaponFullDef(weaponFullDef);
|
||||||
|
weaponFullDef->weapCompleteDef.szInternalName = memory->Dup(assetName.c_str());
|
||||||
|
|
||||||
|
InfoStringToWeaponConverter converter(
|
||||||
|
infoString, weaponFullDef, zone->m_script_strings, memory, manager, weapon_fields, std::extent_v<decltype(weapon_fields)>);
|
||||||
|
if (!converter.Convert())
|
||||||
|
{
|
||||||
|
std::cerr << "Failed to parse weapon: \"" << assetName << "\"\n";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
CalculateWeaponFields(weaponFullDef, memory);
|
||||||
|
|
||||||
|
manager->AddAsset(ASSET_TYPE_WEAPON,
|
||||||
|
assetName,
|
||||||
|
&weaponFullDef->weapCompleteDef,
|
||||||
|
converter.GetDependencies(),
|
||||||
|
converter.GetUsedScriptStrings(),
|
||||||
|
converter.GetIndirectAssetReferences());
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
void* AssetLoaderWeapon::CreateEmptyAsset(const std::string& assetName, MemoryManager* memory)
|
||||||
|
{
|
||||||
|
auto* weapon = memory->Create<WeaponCompleteDef>();
|
||||||
|
memset(weapon, 0, sizeof(WeaponCompleteDef));
|
||||||
|
weapon->szInternalName = memory->Dup(assetName.c_str());
|
||||||
|
return weapon;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AssetLoaderWeapon::CanLoadFromGdt() const
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AssetLoaderWeapon::LoadFromGdt(
|
||||||
|
const std::string& assetName, IGdtQueryable* gdtQueryable, MemoryManager* memory, IAssetLoadingManager* manager, Zone* zone) const
|
||||||
|
{
|
||||||
|
const auto* gdtEntry = gdtQueryable->GetGdtEntryByGdfAndName(ObjConstants::GDF_FILENAME_WEAPON, assetName);
|
||||||
|
if (gdtEntry == nullptr)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
InfoString infoString;
|
||||||
|
if (!infoString.FromGdtProperties(*gdtEntry))
|
||||||
|
{
|
||||||
|
std::cerr << "Failed to read weapon gdt entry: \"" << assetName << "\"\n";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return LoadFromInfoString(infoString, assetName, memory, manager, zone);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AssetLoaderWeapon::CanLoadFromRaw() const
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AssetLoaderWeapon::LoadFromRaw(
|
||||||
|
const std::string& assetName, ISearchPath* searchPath, MemoryManager* memory, IAssetLoadingManager* manager, Zone* zone) const
|
||||||
|
{
|
||||||
|
const auto fileName = std::format("weapons/{}", assetName);
|
||||||
|
const auto file = searchPath->Open(fileName);
|
||||||
|
if (!file.IsOpen())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
InfoString infoString;
|
||||||
|
if (!infoString.FromStream(ObjConstants::INFO_STRING_PREFIX_WEAPON, *file.m_stream))
|
||||||
|
{
|
||||||
|
std::cerr << "Failed to read weapon raw file: \"" << fileName << "\"\n";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return LoadFromInfoString(infoString, assetName, memory, manager, zone);
|
||||||
|
}
|
20
src/ObjLoading/Game/IW5/AssetLoaders/AssetLoaderWeapon.h
Normal file
20
src/ObjLoading/Game/IW5/AssetLoaders/AssetLoaderWeapon.h
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "AssetLoading/BasicAssetLoader.h"
|
||||||
|
#include "Game/IW5/IW5.h"
|
||||||
|
#include "SearchPath/ISearchPath.h"
|
||||||
|
|
||||||
|
namespace IW5
|
||||||
|
{
|
||||||
|
class AssetLoaderWeapon final : public BasicAssetLoader<ASSET_TYPE_WEAPON, WeaponCompleteDef>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
_NODISCARD void* CreateEmptyAsset(const std::string& assetName, MemoryManager* memory) override;
|
||||||
|
_NODISCARD bool CanLoadFromGdt() const override;
|
||||||
|
bool LoadFromGdt(
|
||||||
|
const std::string& assetName, IGdtQueryable* gdtQueryable, MemoryManager* memory, IAssetLoadingManager* manager, Zone* zone) const override;
|
||||||
|
_NODISCARD bool CanLoadFromRaw() const override;
|
||||||
|
bool
|
||||||
|
LoadFromRaw(const std::string& assetName, ISearchPath* searchPath, MemoryManager* memory, IAssetLoadingManager* manager, Zone* zone) const override;
|
||||||
|
};
|
||||||
|
} // namespace IW5
|
@ -0,0 +1,47 @@
|
|||||||
|
#include "AssetLoaderWeaponAttachment.h"
|
||||||
|
|
||||||
|
#include "Game/IW5/IW5.h"
|
||||||
|
#include "Game/IW5/Weapon/JsonWeaponAttachmentLoader.h"
|
||||||
|
#include "Pool/GlobalAssetPool.h"
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
#include <format>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
using namespace IW5;
|
||||||
|
|
||||||
|
void* AssetLoaderWeaponAttachment::CreateEmptyAsset(const std::string& assetName, MemoryManager* memory)
|
||||||
|
{
|
||||||
|
auto* attachment = memory->Create<WeaponAttachment>();
|
||||||
|
memset(attachment, 0, sizeof(WeaponAttachment));
|
||||||
|
attachment->szInternalName = memory->Dup(assetName.c_str());
|
||||||
|
|
||||||
|
return attachment;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AssetLoaderWeaponAttachment::CanLoadFromRaw() const
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AssetLoaderWeaponAttachment::LoadFromRaw(
|
||||||
|
const std::string& assetName, ISearchPath* searchPath, MemoryManager* memory, IAssetLoadingManager* manager, Zone* zone) const
|
||||||
|
{
|
||||||
|
const auto file = searchPath->Open(std::format("attachment/{}.json", assetName));
|
||||||
|
if (!file.IsOpen())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
auto* attachment = static_cast<WeaponAttachment*>(memory->Alloc(sizeof(WeaponAttachment)));
|
||||||
|
memset(attachment, 0, sizeof(Material));
|
||||||
|
attachment->szInternalName = memory->Dup(assetName.c_str());
|
||||||
|
|
||||||
|
std::vector<XAssetInfoGeneric*> dependencies;
|
||||||
|
std::vector<IndirectAssetReference> indirectAssetReferences;
|
||||||
|
if (LoadWeaponAttachmentAsJson(*file.m_stream, *attachment, memory, manager, dependencies, indirectAssetReferences))
|
||||||
|
manager->AddAsset(
|
||||||
|
ASSET_TYPE_ATTACHMENT, assetName, attachment, std::move(dependencies), std::vector<scr_string_t>(), std::move(indirectAssetReferences));
|
||||||
|
else
|
||||||
|
std::cerr << "Failed to load attachment \"" << assetName << "\"\n";
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "AssetLoading/BasicAssetLoader.h"
|
||||||
|
#include "AssetLoading/IAssetLoadingManager.h"
|
||||||
|
#include "Game/IW5/IW5.h"
|
||||||
|
#include "SearchPath/ISearchPath.h"
|
||||||
|
|
||||||
|
namespace IW5
|
||||||
|
{
|
||||||
|
class AssetLoaderWeaponAttachment final : public BasicAssetLoader<ASSET_TYPE_ATTACHMENT, WeaponAttachment>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
_NODISCARD void* CreateEmptyAsset(const std::string& assetName, MemoryManager* memory) override;
|
||||||
|
_NODISCARD bool CanLoadFromRaw() const override;
|
||||||
|
bool
|
||||||
|
LoadFromRaw(const std::string& assetName, ISearchPath* searchPath, MemoryManager* memory, IAssetLoadingManager* manager, Zone* zone) const override;
|
||||||
|
};
|
||||||
|
} // namespace IW5
|
@ -1,3 +1,230 @@
|
|||||||
#include "InfoStringToStructConverter.h"
|
#include "InfoStringToStructConverter.h"
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
using namespace IW5;
|
using namespace IW5;
|
||||||
|
|
||||||
|
InfoStringToStructConverter::InfoStringToStructConverter(const InfoString& infoString,
|
||||||
|
void* structure,
|
||||||
|
ZoneScriptStrings& zoneScriptStrings,
|
||||||
|
MemoryManager* memory,
|
||||||
|
IAssetLoadingManager* manager,
|
||||||
|
const cspField_t* fields,
|
||||||
|
const size_t fieldCount)
|
||||||
|
: InfoStringToStructConverterBase(infoString, structure, zoneScriptStrings, memory),
|
||||||
|
m_loading_manager(manager),
|
||||||
|
m_fields(fields),
|
||||||
|
m_field_count(fieldCount)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InfoStringToStructConverter::ConvertBaseField(const cspField_t& field, const std::string& value)
|
||||||
|
{
|
||||||
|
switch (static_cast<csParseFieldType_t>(field.iFieldType))
|
||||||
|
{
|
||||||
|
case CSPFT_STRING:
|
||||||
|
return ConvertString(value, field.iOffset);
|
||||||
|
|
||||||
|
case CSPFT_STRING_MAX_STRING_CHARS:
|
||||||
|
return ConvertStringBuffer(value, field.iOffset, 1024);
|
||||||
|
|
||||||
|
case CSPFT_STRING_MAX_QPATH:
|
||||||
|
return ConvertStringBuffer(value, field.iOffset, 64);
|
||||||
|
|
||||||
|
case CSPFT_STRING_MAX_OSPATH:
|
||||||
|
return ConvertStringBuffer(value, field.iOffset, 256);
|
||||||
|
|
||||||
|
case CSPFT_INT:
|
||||||
|
return ConvertInt(value, field.iOffset);
|
||||||
|
|
||||||
|
case CSPFT_BOOL:
|
||||||
|
return ConvertBool(value, field.iOffset);
|
||||||
|
|
||||||
|
case CSPFT_QBOOLEAN:
|
||||||
|
return ConvertQBoolean(value, field.iOffset);
|
||||||
|
|
||||||
|
case CSPFT_FLOAT:
|
||||||
|
return ConvertFloat(value, field.iOffset);
|
||||||
|
|
||||||
|
case CSPFT_MILLISECONDS:
|
||||||
|
return ConvertMilliseconds(value, field.iOffset);
|
||||||
|
|
||||||
|
case CSPFT_FX:
|
||||||
|
{
|
||||||
|
if (value.empty())
|
||||||
|
{
|
||||||
|
*reinterpret_cast<void**>(reinterpret_cast<uintptr_t>(m_structure) + field.iOffset) = nullptr;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* fx = m_loading_manager->LoadDependency(ASSET_TYPE_FX, value);
|
||||||
|
|
||||||
|
if (fx == nullptr)
|
||||||
|
{
|
||||||
|
std::cout << "Failed to load fx asset \"" << value << "\"\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_dependencies.emplace(fx);
|
||||||
|
*reinterpret_cast<void**>(reinterpret_cast<uintptr_t>(m_structure) + field.iOffset) = fx->m_ptr;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
case CSPFT_XMODEL:
|
||||||
|
{
|
||||||
|
if (value.empty())
|
||||||
|
{
|
||||||
|
*reinterpret_cast<void**>(reinterpret_cast<uintptr_t>(m_structure) + field.iOffset) = nullptr;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* xmodel = m_loading_manager->LoadDependency(ASSET_TYPE_XMODEL, value);
|
||||||
|
|
||||||
|
if (xmodel == nullptr)
|
||||||
|
{
|
||||||
|
std::cout << "Failed to load xmodel asset \"" << value << "\"\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_dependencies.emplace(xmodel);
|
||||||
|
*reinterpret_cast<void**>(reinterpret_cast<uintptr_t>(m_structure) + field.iOffset) = xmodel->m_ptr;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
case CSPFT_MATERIAL:
|
||||||
|
{
|
||||||
|
if (value.empty())
|
||||||
|
{
|
||||||
|
*reinterpret_cast<void**>(reinterpret_cast<uintptr_t>(m_structure) + field.iOffset) = nullptr;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* material = m_loading_manager->LoadDependency(ASSET_TYPE_MATERIAL, value);
|
||||||
|
|
||||||
|
if (material == nullptr)
|
||||||
|
{
|
||||||
|
std::cout << "Failed to load material asset \"" << value << "\"\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_dependencies.emplace(material);
|
||||||
|
*reinterpret_cast<void**>(reinterpret_cast<uintptr_t>(m_structure) + field.iOffset) = material->m_ptr;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
case CSPFT_TRACER:
|
||||||
|
{
|
||||||
|
if (value.empty())
|
||||||
|
{
|
||||||
|
*reinterpret_cast<void**>(reinterpret_cast<uintptr_t>(m_structure) + field.iOffset) = nullptr;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* tracer = m_loading_manager->LoadDependency(ASSET_TYPE_TRACER, value);
|
||||||
|
|
||||||
|
if (tracer == nullptr)
|
||||||
|
{
|
||||||
|
std::cout << "Failed to load tracer asset \"" << value << "\"\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_dependencies.emplace(tracer);
|
||||||
|
*reinterpret_cast<void**>(reinterpret_cast<uintptr_t>(m_structure) + field.iOffset) = tracer->m_ptr;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
case CSPFT_MPH_TO_INCHES_PER_SEC:
|
||||||
|
{
|
||||||
|
char* endPtr;
|
||||||
|
*reinterpret_cast<float*>(reinterpret_cast<uintptr_t>(m_structure) + field.iOffset) = strtof(value.c_str(), &endPtr) * 17.6f;
|
||||||
|
|
||||||
|
if (endPtr != &value[value.size()])
|
||||||
|
{
|
||||||
|
std::cout << "Failed to parse value \"" << value << "\" as mph\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
case CSPFT_PHYS_COLLMAP:
|
||||||
|
{
|
||||||
|
if (value.empty())
|
||||||
|
{
|
||||||
|
*reinterpret_cast<void**>(reinterpret_cast<uintptr_t>(m_structure) + field.iOffset) = nullptr;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* collmap = m_loading_manager->LoadDependency(ASSET_TYPE_PHYSCOLLMAP, value);
|
||||||
|
|
||||||
|
if (collmap == nullptr)
|
||||||
|
{
|
||||||
|
std::cout << "Failed to load collmap asset \"" << value << "\"\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_dependencies.emplace(collmap);
|
||||||
|
*reinterpret_cast<void**>(reinterpret_cast<uintptr_t>(m_structure) + field.iOffset) = collmap->m_ptr;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
case CSPFT_SOUND:
|
||||||
|
{
|
||||||
|
if (value.empty())
|
||||||
|
{
|
||||||
|
reinterpret_cast<SndAliasCustom*>(reinterpret_cast<uintptr_t>(m_structure) + field.iOffset)->name = nullptr;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* name = static_cast<snd_alias_list_name*>(m_memory->Alloc(sizeof(snd_alias_list_name)));
|
||||||
|
name->soundName = m_memory->Dup(value.c_str());
|
||||||
|
|
||||||
|
reinterpret_cast<SndAliasCustom*>(reinterpret_cast<uintptr_t>(m_structure) + field.iOffset)->name = name;
|
||||||
|
|
||||||
|
m_indirect_asset_references.emplace(ASSET_TYPE_SOUND, value);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
case CSPFT_SCRIPT_STRING:
|
||||||
|
return ConvertScriptString(value, field.iOffset);
|
||||||
|
|
||||||
|
case CSPFT_NUM_BASE_FIELD_TYPES:
|
||||||
|
default:
|
||||||
|
assert(false);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InfoStringToStructConverter::Convert()
|
||||||
|
{
|
||||||
|
for (auto fieldIndex = 0u; fieldIndex < m_field_count; fieldIndex++)
|
||||||
|
{
|
||||||
|
const auto& field = m_fields[fieldIndex];
|
||||||
|
assert(field.iFieldType >= 0);
|
||||||
|
|
||||||
|
auto foundValue = false;
|
||||||
|
const auto& value = m_info_string.GetValueForKey(std::string(field.szName), &foundValue);
|
||||||
|
|
||||||
|
if (foundValue)
|
||||||
|
{
|
||||||
|
if (field.iFieldType < CSPFT_NUM_BASE_FIELD_TYPES)
|
||||||
|
{
|
||||||
|
if (!ConvertBaseField(field, value))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!ConvertExtensionField(field, value))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
@ -7,6 +7,8 @@
|
|||||||
#include "AssetLoaders/AssetLoaderRawFile.h"
|
#include "AssetLoaders/AssetLoaderRawFile.h"
|
||||||
#include "AssetLoaders/AssetLoaderScriptFile.h"
|
#include "AssetLoaders/AssetLoaderScriptFile.h"
|
||||||
#include "AssetLoaders/AssetLoaderStringTable.h"
|
#include "AssetLoaders/AssetLoaderStringTable.h"
|
||||||
|
#include "AssetLoaders/AssetLoaderWeapon.h"
|
||||||
|
#include "AssetLoaders/AssetLoaderWeaponAttachment.h"
|
||||||
#include "AssetLoading/AssetLoadingManager.h"
|
#include "AssetLoading/AssetLoadingManager.h"
|
||||||
#include "Game/IW5/GameAssetPoolIW5.h"
|
#include "Game/IW5/GameAssetPoolIW5.h"
|
||||||
#include "Game/IW5/GameIW5.h"
|
#include "Game/IW5/GameIW5.h"
|
||||||
@ -55,8 +57,8 @@ ObjLoader::ObjLoader()
|
|||||||
REGISTER_ASSET_LOADER(AssetLoaderMenuList)
|
REGISTER_ASSET_LOADER(AssetLoaderMenuList)
|
||||||
REGISTER_ASSET_LOADER(AssetLoaderMenuDef)
|
REGISTER_ASSET_LOADER(AssetLoaderMenuDef)
|
||||||
REGISTER_ASSET_LOADER(AssetLoaderLocalizeEntry)
|
REGISTER_ASSET_LOADER(AssetLoaderLocalizeEntry)
|
||||||
REGISTER_ASSET_LOADER(BASIC_LOADER(ASSET_TYPE_ATTACHMENT, WeaponAttachment))
|
REGISTER_ASSET_LOADER(AssetLoaderWeaponAttachment)
|
||||||
REGISTER_ASSET_LOADER(BASIC_LOADER(ASSET_TYPE_WEAPON, WeaponCompleteDef))
|
REGISTER_ASSET_LOADER(AssetLoaderWeapon)
|
||||||
REGISTER_ASSET_LOADER(BASIC_LOADER(ASSET_TYPE_FX, FxEffectDef))
|
REGISTER_ASSET_LOADER(BASIC_LOADER(ASSET_TYPE_FX, FxEffectDef))
|
||||||
REGISTER_ASSET_LOADER(BASIC_LOADER(ASSET_TYPE_IMPACT_FX, FxImpactTable))
|
REGISTER_ASSET_LOADER(BASIC_LOADER(ASSET_TYPE_IMPACT_FX, FxImpactTable))
|
||||||
REGISTER_ASSET_LOADER(BASIC_LOADER(ASSET_TYPE_SURFACE_FX, SurfaceFxTable))
|
REGISTER_ASSET_LOADER(BASIC_LOADER(ASSET_TYPE_SURFACE_FX, SurfaceFxTable))
|
||||||
|
644
src/ObjLoading/Game/IW5/Weapon/JsonWeaponAttachmentLoader.cpp
Normal file
644
src/ObjLoading/Game/IW5/Weapon/JsonWeaponAttachmentLoader.cpp
Normal file
@ -0,0 +1,644 @@
|
|||||||
|
#include "JsonWeaponAttachmentLoader.h"
|
||||||
|
|
||||||
|
#include "Game/IW5/CommonIW5.h"
|
||||||
|
#include "Game/IW5/Weapon/JsonWeaponAttachment.h"
|
||||||
|
|
||||||
|
#include <format>
|
||||||
|
#include <iostream>
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
|
using namespace nlohmann;
|
||||||
|
using namespace IW5;
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
class JsonLoader
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
JsonLoader(std::istream& stream,
|
||||||
|
MemoryManager& memory,
|
||||||
|
IAssetLoadingManager& manager,
|
||||||
|
std::vector<XAssetInfoGeneric*>& dependencies,
|
||||||
|
std::vector<IndirectAssetReference>& indirectAssetReferences)
|
||||||
|
: m_stream(stream),
|
||||||
|
m_memory(memory),
|
||||||
|
m_manager(manager),
|
||||||
|
m_dependencies(dependencies),
|
||||||
|
m_indirect_asset_references(indirectAssetReferences)
|
||||||
|
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Load(WeaponAttachment& attachment) const
|
||||||
|
{
|
||||||
|
const auto jRoot = json::parse(m_stream);
|
||||||
|
std::string type;
|
||||||
|
unsigned version;
|
||||||
|
|
||||||
|
jRoot.at("_type").get_to(type);
|
||||||
|
jRoot.at("_version").get_to(version);
|
||||||
|
|
||||||
|
if (type != "attachment" || version != 1u)
|
||||||
|
{
|
||||||
|
std::cerr << "Tried to load attachment \"" << attachment.szInternalName << "\" but did not find expected type attachment of version 1\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto jAttachment = jRoot.get<JsonWeaponAttachment>();
|
||||||
|
return CreateWeaponAttachmentFromJson(jAttachment, attachment);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
static void PrintError(const WeaponAttachment& attachment, const std::string& message)
|
||||||
|
{
|
||||||
|
std::cerr << "Cannot load attachment \"" << attachment.szInternalName << "\": " << message << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CreateWeaponAttachmentFromJson(const JsonWeaponAttachment& jAttachment, WeaponAttachment& attachment) const
|
||||||
|
{
|
||||||
|
#define CONVERT_XMODEL_ARRAY(propertyName, count) \
|
||||||
|
CreateXModelArrayFromJson(jAttachment.propertyName, attachment.propertyName, #propertyName, count, attachment);
|
||||||
|
|
||||||
|
#define CONVERT_ATTRIBUTE(attributeClass, attributeName) \
|
||||||
|
if (jAttachment.attributeName) \
|
||||||
|
{ \
|
||||||
|
using AttributeType = std::remove_pointer_t<decltype(attachment.attributeName)>; \
|
||||||
|
attachment.attributeName = static_cast<AttributeType*>(m_memory.Alloc(sizeof(AttributeType))); \
|
||||||
|
if (!Create##attributeClass##FromJson(jAttachment.attributeName.value(), *attachment.attributeName, attachment)) \
|
||||||
|
return false; \
|
||||||
|
} \
|
||||||
|
else \
|
||||||
|
attachment.attributeName = nullptr;
|
||||||
|
|
||||||
|
attachment.szDisplayName = m_memory.Dup(jAttachment.displayName.c_str());
|
||||||
|
attachment.type = jAttachment.type;
|
||||||
|
attachment.weaponType = jAttachment.weaponType;
|
||||||
|
attachment.weapClass = jAttachment.weapClass;
|
||||||
|
|
||||||
|
CONVERT_XMODEL_ARRAY(worldModels, ATTACHMENT_WORLD_MODEL_COUNT)
|
||||||
|
CONVERT_XMODEL_ARRAY(viewModels, ATTACHMENT_VIEW_MODEL_COUNT)
|
||||||
|
CONVERT_XMODEL_ARRAY(reticleViewModels, ATTACHMENT_RETICLE_VIEW_MODEL_COUNT)
|
||||||
|
|
||||||
|
CONVERT_ATTRIBUTE(AttAmmoGeneral, ammoGeneral)
|
||||||
|
CONVERT_ATTRIBUTE(AttSight, sight)
|
||||||
|
CONVERT_ATTRIBUTE(AttReload, reload)
|
||||||
|
CONVERT_ATTRIBUTE(AttAddOns, addOns)
|
||||||
|
CONVERT_ATTRIBUTE(AttGeneral, general)
|
||||||
|
CONVERT_ATTRIBUTE(AttAimAssist, aimAssist)
|
||||||
|
CONVERT_ATTRIBUTE(AttAmmunition, ammunition)
|
||||||
|
CONVERT_ATTRIBUTE(AttDamage, damage)
|
||||||
|
CONVERT_ATTRIBUTE(AttLocationDamage, locationDamage)
|
||||||
|
CONVERT_ATTRIBUTE(AttIdleSettings, idleSettings)
|
||||||
|
CONVERT_ATTRIBUTE(AttADSSettings, adsSettings)
|
||||||
|
CONVERT_ATTRIBUTE(AttADSSettings, adsSettingsMain)
|
||||||
|
CONVERT_ATTRIBUTE(AttHipSpread, hipSpread)
|
||||||
|
CONVERT_ATTRIBUTE(AttGunKick, gunKick)
|
||||||
|
CONVERT_ATTRIBUTE(AttViewKick, viewKick)
|
||||||
|
CONVERT_ATTRIBUTE(AttADSOverlay, adsOverlay)
|
||||||
|
CONVERT_ATTRIBUTE(AttUI, ui)
|
||||||
|
CONVERT_ATTRIBUTE(AttRumbles, rumbles)
|
||||||
|
CONVERT_ATTRIBUTE(AttProjectile, projectile)
|
||||||
|
|
||||||
|
attachment.ammunitionScale = jAttachment.ammunitionScale;
|
||||||
|
attachment.damageScale = jAttachment.damageScale;
|
||||||
|
attachment.damageScaleMin = jAttachment.damageScaleMin;
|
||||||
|
attachment.stateTimersScale = jAttachment.stateTimersScale;
|
||||||
|
attachment.fireTimersScale = jAttachment.fireTimersScale;
|
||||||
|
attachment.idleSettingsScale = jAttachment.idleSettingsScale;
|
||||||
|
attachment.adsSettingsScale = jAttachment.adsSettingsScale;
|
||||||
|
attachment.adsSettingsScaleMain = jAttachment.adsSettingsScaleMain;
|
||||||
|
attachment.hipSpreadScale = jAttachment.hipSpreadScale;
|
||||||
|
attachment.gunKickScale = jAttachment.gunKickScale;
|
||||||
|
attachment.viewKickScale = jAttachment.viewKickScale;
|
||||||
|
attachment.viewCenterScale = jAttachment.viewCenterScale;
|
||||||
|
attachment.loadIndex = jAttachment.loadIndex;
|
||||||
|
attachment.hideIronSightsWithThisAttachment = jAttachment.hideIronSightsWithThisAttachment;
|
||||||
|
attachment.shareAmmoWithAlt = jAttachment.shareAmmoWithAlt;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CreateTracerFromJson(const std::string& assetName, TracerDef*& tracerPtr, const WeaponAttachment& attachment) const
|
||||||
|
{
|
||||||
|
auto* tracer = static_cast<XAssetInfo<TracerDef>*>(m_manager.LoadDependency(ASSET_TYPE_TRACER, assetName));
|
||||||
|
if (!tracer)
|
||||||
|
{
|
||||||
|
PrintError(attachment, std::format("Could not find tracer {}", assetName));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
m_dependencies.push_back(tracer);
|
||||||
|
tracerPtr = tracer->Asset();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CreateMaterialFromJson(const std::string& assetName, Material*& materialPtr, const WeaponAttachment& attachment) const
|
||||||
|
{
|
||||||
|
auto* material = static_cast<XAssetInfo<Material>*>(m_manager.LoadDependency(ASSET_TYPE_MATERIAL, assetName));
|
||||||
|
if (!material)
|
||||||
|
{
|
||||||
|
PrintError(attachment, std::format("Could not find material {}", assetName));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
m_dependencies.push_back(material);
|
||||||
|
materialPtr = material->Asset();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CreateFxFromJson(const std::string& assetName, FxEffectDef*& fxPtr, const WeaponAttachment& attachment) const
|
||||||
|
{
|
||||||
|
auto* fx = static_cast<XAssetInfo<FxEffectDef>*>(m_manager.LoadDependency(ASSET_TYPE_FX, assetName));
|
||||||
|
if (!fx)
|
||||||
|
{
|
||||||
|
PrintError(attachment, std::format("Could not find fx {}", assetName));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
m_dependencies.push_back(fx);
|
||||||
|
fxPtr = fx->Asset();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CreateSoundFromJson(const std::string& assetName, SndAliasCustom& sndAliasCustom, const WeaponAttachment& attachment) const
|
||||||
|
{
|
||||||
|
auto sound = m_manager.LoadIndirectAssetReference(ASSET_TYPE_SOUND, assetName);
|
||||||
|
m_indirect_asset_references.push_back(std::move(sound));
|
||||||
|
sndAliasCustom.name = static_cast<snd_alias_list_name*>(m_memory.Alloc(sizeof(snd_alias_list_name)));
|
||||||
|
sndAliasCustom.name->soundName = m_memory.Dup(assetName.c_str());
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CreateXModelFromJson(const std::string& assetName, XModel*& xmodelPtr, const WeaponAttachment& attachment) const
|
||||||
|
{
|
||||||
|
auto* xmodel = static_cast<XAssetInfo<XModel>*>(m_manager.LoadDependency(ASSET_TYPE_XMODEL, assetName));
|
||||||
|
if (!xmodel)
|
||||||
|
{
|
||||||
|
PrintError(attachment, std::format("Could not find xmodel {}", assetName));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
m_dependencies.push_back(xmodel);
|
||||||
|
xmodelPtr = xmodel->Asset();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CreateXModelArrayFromJson(const std::vector<std::string>& jXmodelArray,
|
||||||
|
XModel**& xmodelArray,
|
||||||
|
const char* propertyName,
|
||||||
|
size_t propertyCount,
|
||||||
|
const WeaponAttachment& attachment) const
|
||||||
|
{
|
||||||
|
if (!jXmodelArray.empty())
|
||||||
|
{
|
||||||
|
const auto arraySize = jXmodelArray.size();
|
||||||
|
if (arraySize > propertyCount)
|
||||||
|
{
|
||||||
|
PrintError(attachment, std::format("{} size cannot exceed {}", propertyName, propertyCount));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
xmodelArray = static_cast<XModel**>(m_memory.Alloc(sizeof(void*) * propertyCount));
|
||||||
|
memset(xmodelArray, 0, sizeof(void*) * propertyCount);
|
||||||
|
|
||||||
|
for (auto i = 0u; i < arraySize; i++)
|
||||||
|
{
|
||||||
|
if (!CreateXModelFromJson(jXmodelArray[i], xmodelArray[i], attachment))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
xmodelArray = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CreateAttAmmoGeneralFromJson(const JsonAttAmmoGeneral& jAmmoGeneral, AttAmmoGeneral& ammoGeneral, const WeaponAttachment& attachment) const
|
||||||
|
{
|
||||||
|
ammoGeneral.penetrateType = jAmmoGeneral.penetrateType;
|
||||||
|
ammoGeneral.penetrateMultiplier = jAmmoGeneral.penetrateMultiplier;
|
||||||
|
ammoGeneral.impactType = jAmmoGeneral.impactType;
|
||||||
|
ammoGeneral.fireType = jAmmoGeneral.fireType;
|
||||||
|
|
||||||
|
if (jAmmoGeneral.tracerType)
|
||||||
|
{
|
||||||
|
if (!CreateTracerFromJson(jAmmoGeneral.tracerType.value(), ammoGeneral.tracerType, attachment))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
ammoGeneral.tracerType = nullptr;
|
||||||
|
|
||||||
|
ammoGeneral.rifleBullet = jAmmoGeneral.rifleBullet;
|
||||||
|
ammoGeneral.armorPiercing = jAmmoGeneral.armorPiercing;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool CreateAttSightFromJson(const JsonAttSight& jSight, AttSight& sight, const WeaponAttachment& attachment)
|
||||||
|
{
|
||||||
|
sight.aimDownSight = jSight.aimDownSight;
|
||||||
|
sight.adsFire = jSight.adsFire;
|
||||||
|
sight.rechamberWhileAds = jSight.rechamberWhileAds;
|
||||||
|
sight.noAdsWhenMagEmpty = jSight.noAdsWhenMagEmpty;
|
||||||
|
sight.canHoldBreath = jSight.canHoldBreath;
|
||||||
|
sight.canVariableZoom = jSight.canVariableZoom;
|
||||||
|
sight.hideRailWithThisScope = jSight.hideRailWithThisScope;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool CreateAttReloadFromJson(const JsonAttReload& jReload, AttReload& reload, const WeaponAttachment& attachment)
|
||||||
|
{
|
||||||
|
reload.noPartialReload = jReload.noPartialReload;
|
||||||
|
reload.segmentedReload = jReload.segmentedReload;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool CreateAttAddOnsFromJson(const JsonAttAddOns& jAddOns, AttAddOns& addOns, const WeaponAttachment& attachment)
|
||||||
|
{
|
||||||
|
addOns.motionTracker = jAddOns.motionTracker;
|
||||||
|
addOns.silenced = jAddOns.silenced;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CreateAttGeneralFromJson(const JsonAttGeneral& jGeneral, AttGeneral& general, const WeaponAttachment& attachment) const
|
||||||
|
{
|
||||||
|
general.boltAction = jGeneral.boltAction;
|
||||||
|
general.inheritsPerks = jGeneral.inheritsPerks;
|
||||||
|
general.enemyCrosshairRange = jGeneral.enemyCrosshairRange;
|
||||||
|
|
||||||
|
if (jGeneral.reticleCenter)
|
||||||
|
{
|
||||||
|
if (!CreateMaterialFromJson(jGeneral.reticleCenter.value(), general.reticleCenter, attachment))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
general.reticleCenter = nullptr;
|
||||||
|
|
||||||
|
if (jGeneral.reticleSide)
|
||||||
|
{
|
||||||
|
if (!CreateMaterialFromJson(jGeneral.reticleSide.value(), general.reticleSide, attachment))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
general.reticleSide = nullptr;
|
||||||
|
|
||||||
|
general.reticleCenterSize = jGeneral.reticleCenterSize;
|
||||||
|
general.reticleSideSize = jGeneral.reticleSideSize;
|
||||||
|
general.moveSpeedScale = jGeneral.moveSpeedScale;
|
||||||
|
general.adsMoveSpeedScale = jGeneral.adsMoveSpeedScale;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool CreateAttAimAssistFromJson(const JsonAttAimAssist& jAimAssist, AttAimAssist& aimAssist, const WeaponAttachment& attachment)
|
||||||
|
{
|
||||||
|
aimAssist.autoAimRange = jAimAssist.autoAimRange;
|
||||||
|
aimAssist.aimAssistRange = jAimAssist.aimAssistRange;
|
||||||
|
aimAssist.aimAssistRangeAds = jAimAssist.aimAssistRangeAds;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool CreateAttAmmunitionFromJson(const JsonAttAmmunition& jAmmunition, AttAmmunition& ammunition, const WeaponAttachment& attachment)
|
||||||
|
{
|
||||||
|
ammunition.maxAmmo = jAmmunition.maxAmmo;
|
||||||
|
ammunition.startAmmo = jAmmunition.startAmmo;
|
||||||
|
ammunition.clipSize = jAmmunition.clipSize;
|
||||||
|
ammunition.shotCount = jAmmunition.shotCount;
|
||||||
|
ammunition.reloadAmmoAdd = jAmmunition.reloadAmmoAdd;
|
||||||
|
ammunition.reloadStartAdd = jAmmunition.reloadStartAdd;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool CreateAttDamageFromJson(const JsonAttDamage& jDamage, AttDamage& damage, const WeaponAttachment& attachment)
|
||||||
|
{
|
||||||
|
damage.damage = jDamage.damage;
|
||||||
|
damage.minDamage = jDamage.minDamage;
|
||||||
|
damage.meleeDamage = jDamage.meleeDamage;
|
||||||
|
damage.maxDamageRange = jDamage.maxDamageRange;
|
||||||
|
damage.minDamageRange = jDamage.minDamageRange;
|
||||||
|
damage.playerDamage = jDamage.playerDamage;
|
||||||
|
damage.minPlayerDamage = jDamage.minPlayerDamage;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
CreateAttLocationDamageFromJson(const JsonAttLocationDamage& jLocationDamage, AttLocationDamage& locationDamage, const WeaponAttachment& attachment)
|
||||||
|
{
|
||||||
|
locationDamage.locNone = jLocationDamage.locNone;
|
||||||
|
locationDamage.locHelmet = jLocationDamage.locHelmet;
|
||||||
|
locationDamage.locHead = jLocationDamage.locHead;
|
||||||
|
locationDamage.locNeck = jLocationDamage.locNeck;
|
||||||
|
locationDamage.locTorsoUpper = jLocationDamage.locTorsoUpper;
|
||||||
|
locationDamage.locTorsoLower = jLocationDamage.locTorsoLower;
|
||||||
|
locationDamage.locRightArmUpper = jLocationDamage.locRightArmUpper;
|
||||||
|
locationDamage.locRightArmLower = jLocationDamage.locRightArmLower;
|
||||||
|
locationDamage.locRightHand = jLocationDamage.locRightHand;
|
||||||
|
locationDamage.locLeftArmUpper = jLocationDamage.locLeftArmUpper;
|
||||||
|
locationDamage.locLeftArmLower = jLocationDamage.locLeftArmLower;
|
||||||
|
locationDamage.locLeftHand = jLocationDamage.locLeftHand;
|
||||||
|
locationDamage.locRightLegUpper = jLocationDamage.locRightLegUpper;
|
||||||
|
locationDamage.locRightLegLower = jLocationDamage.locRightLegLower;
|
||||||
|
locationDamage.locRightFoot = jLocationDamage.locRightFoot;
|
||||||
|
locationDamage.locLeftLegUpper = jLocationDamage.locLeftLegUpper;
|
||||||
|
locationDamage.locLeftLegLower = jLocationDamage.locLeftLegLower;
|
||||||
|
locationDamage.locLeftFoot = jLocationDamage.locLeftFoot;
|
||||||
|
locationDamage.locGun = jLocationDamage.locGun;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool CreateAttIdleSettingsFromJson(const JsonAttIdleSettings& jIdleSettings, AttIdleSettings& idleSettings, const WeaponAttachment& attachment)
|
||||||
|
{
|
||||||
|
idleSettings.hipIdleAmount = jIdleSettings.hipIdleAmount;
|
||||||
|
idleSettings.hipIdleSpeed = jIdleSettings.hipIdleSpeed;
|
||||||
|
idleSettings.idleCrouchFactor = jIdleSettings.idleCrouchFactor;
|
||||||
|
idleSettings.idleProneFactor = jIdleSettings.idleProneFactor;
|
||||||
|
idleSettings.adsIdleLerpStartTime = jIdleSettings.adsIdleLerpStartTime;
|
||||||
|
idleSettings.adsIdleLerpTime = jIdleSettings.adsIdleLerpTime;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool CreateAttADSSettingsFromJson(const JsonAttADSSettings& jAdsSettings, AttADSSettings& adsSettings, const WeaponAttachment& attachment)
|
||||||
|
{
|
||||||
|
adsSettings.adsSpread = jAdsSettings.adsSpread;
|
||||||
|
adsSettings.adsAimPitch = jAdsSettings.adsAimPitch;
|
||||||
|
adsSettings.adsTransInTime = jAdsSettings.adsTransInTime;
|
||||||
|
adsSettings.adsTransOutTime = jAdsSettings.adsTransOutTime;
|
||||||
|
adsSettings.adsReloadTransTime = jAdsSettings.adsReloadTransTime;
|
||||||
|
adsSettings.adsCrosshairInFrac = jAdsSettings.adsCrosshairInFrac;
|
||||||
|
adsSettings.adsCrosshairOutFrac = jAdsSettings.adsCrosshairOutFrac;
|
||||||
|
adsSettings.adsZoomFov = jAdsSettings.adsZoomFov;
|
||||||
|
adsSettings.adsZoomInFrac = jAdsSettings.adsZoomInFrac;
|
||||||
|
adsSettings.adsZoomOutFrac = jAdsSettings.adsZoomOutFrac;
|
||||||
|
adsSettings.adsBobFactor = jAdsSettings.adsBobFactor;
|
||||||
|
adsSettings.adsViewBobMult = jAdsSettings.adsViewBobMult;
|
||||||
|
adsSettings.adsViewErrorMin = jAdsSettings.adsViewErrorMin;
|
||||||
|
adsSettings.adsViewErrorMax = jAdsSettings.adsViewErrorMax;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool CreateAttHipSpreadFromJson(const JsonAttHipSpread& jHipSpread, AttHipSpread& hipSpread, const WeaponAttachment& attachment)
|
||||||
|
{
|
||||||
|
hipSpread.hipSpreadStandMin = jHipSpread.hipSpreadStandMin;
|
||||||
|
hipSpread.hipSpreadDuckedMin = jHipSpread.hipSpreadDuckedMin;
|
||||||
|
hipSpread.hipSpreadProneMin = jHipSpread.hipSpreadProneMin;
|
||||||
|
hipSpread.hipSpreadMax = jHipSpread.hipSpreadMax;
|
||||||
|
hipSpread.hipSpreadDuckedMax = jHipSpread.hipSpreadDuckedMax;
|
||||||
|
hipSpread.hipSpreadProneMax = jHipSpread.hipSpreadProneMax;
|
||||||
|
hipSpread.hipSpreadFireAdd = jHipSpread.hipSpreadFireAdd;
|
||||||
|
hipSpread.hipSpreadTurnAdd = jHipSpread.hipSpreadTurnAdd;
|
||||||
|
hipSpread.hipSpreadMoveAdd = jHipSpread.hipSpreadMoveAdd;
|
||||||
|
hipSpread.hipSpreadDecayRate = jHipSpread.hipSpreadDecayRate;
|
||||||
|
hipSpread.hipSpreadDuckedDecay = jHipSpread.hipSpreadDuckedDecay;
|
||||||
|
hipSpread.hipSpreadProneDecay = jHipSpread.hipSpreadProneDecay;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool CreateAttGunKickFromJson(const JsonAttGunKick& jGunKick, AttGunKick& gunKick, const WeaponAttachment& attachment)
|
||||||
|
{
|
||||||
|
gunKick.hipGunKickReducedKickBullets = jGunKick.hipGunKickReducedKickBullets;
|
||||||
|
gunKick.hipGunKickReducedKickPercent = jGunKick.hipGunKickReducedKickPercent;
|
||||||
|
gunKick.hipGunKickPitchMin = jGunKick.hipGunKickPitchMin;
|
||||||
|
gunKick.hipGunKickPitchMax = jGunKick.hipGunKickPitchMax;
|
||||||
|
gunKick.hipGunKickYawMin = jGunKick.hipGunKickYawMin;
|
||||||
|
gunKick.hipGunKickYawMax = jGunKick.hipGunKickYawMax;
|
||||||
|
gunKick.hipGunKickAccel = jGunKick.hipGunKickAccel;
|
||||||
|
gunKick.hipGunKickSpeedMax = jGunKick.hipGunKickSpeedMax;
|
||||||
|
gunKick.hipGunKickSpeedDecay = jGunKick.hipGunKickSpeedDecay;
|
||||||
|
gunKick.hipGunKickStaticDecay = jGunKick.hipGunKickStaticDecay;
|
||||||
|
gunKick.adsGunKickReducedKickBullets = jGunKick.adsGunKickReducedKickBullets;
|
||||||
|
gunKick.adsGunKickReducedKickPercent = jGunKick.adsGunKickReducedKickPercent;
|
||||||
|
gunKick.adsGunKickPitchMin = jGunKick.adsGunKickPitchMin;
|
||||||
|
gunKick.adsGunKickPitchMax = jGunKick.adsGunKickPitchMax;
|
||||||
|
gunKick.adsGunKickYawMin = jGunKick.adsGunKickYawMin;
|
||||||
|
gunKick.adsGunKickYawMax = jGunKick.adsGunKickYawMax;
|
||||||
|
gunKick.adsGunKickAccel = jGunKick.adsGunKickAccel;
|
||||||
|
gunKick.adsGunKickSpeedMax = jGunKick.adsGunKickSpeedMax;
|
||||||
|
gunKick.adsGunKickSpeedDecay = jGunKick.adsGunKickSpeedDecay;
|
||||||
|
gunKick.adsGunKickStaticDecay = jGunKick.adsGunKickStaticDecay;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool CreateAttViewKickFromJson(const JsonAttViewKick& jViewKick, AttViewKick& viewKick, const WeaponAttachment& attachment)
|
||||||
|
{
|
||||||
|
viewKick.hipViewKickPitchMin = jViewKick.hipViewKickPitchMin;
|
||||||
|
viewKick.hipViewKickPitchMax = jViewKick.hipViewKickPitchMax;
|
||||||
|
viewKick.hipViewKickYawMin = jViewKick.hipViewKickYawMin;
|
||||||
|
viewKick.hipViewKickYawMax = jViewKick.hipViewKickYawMax;
|
||||||
|
viewKick.hipViewKickCenterSpeed = jViewKick.hipViewKickCenterSpeed;
|
||||||
|
viewKick.adsViewKickPitchMin = jViewKick.adsViewKickPitchMin;
|
||||||
|
viewKick.adsViewKickPitchMax = jViewKick.adsViewKickPitchMax;
|
||||||
|
viewKick.adsViewKickYawMin = jViewKick.adsViewKickYawMin;
|
||||||
|
viewKick.adsViewKickYawMax = jViewKick.adsViewKickYawMax;
|
||||||
|
viewKick.adsViewKickCenterSpeed = jViewKick.adsViewKickCenterSpeed;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CreateAttADSOverlayFromJson(const JsonAttADSOverlay& jAdsOverlay, AttADSOverlay& adsOverlay, const WeaponAttachment& attachment) const
|
||||||
|
{
|
||||||
|
if (jAdsOverlay.shader)
|
||||||
|
{
|
||||||
|
if (!CreateMaterialFromJson(jAdsOverlay.shader.value(), adsOverlay.overlay.shader, attachment))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
adsOverlay.overlay.shader = nullptr;
|
||||||
|
|
||||||
|
if (jAdsOverlay.shaderLowRes)
|
||||||
|
{
|
||||||
|
if (!CreateMaterialFromJson(jAdsOverlay.shaderLowRes.value(), adsOverlay.overlay.shaderLowRes, attachment))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
adsOverlay.overlay.shaderLowRes = nullptr;
|
||||||
|
|
||||||
|
if (jAdsOverlay.shaderEMP)
|
||||||
|
{
|
||||||
|
if (!CreateMaterialFromJson(jAdsOverlay.shaderEMP.value(), adsOverlay.overlay.shaderEMP, attachment))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
adsOverlay.overlay.shaderEMP = nullptr;
|
||||||
|
|
||||||
|
if (jAdsOverlay.shaderEMPLowRes)
|
||||||
|
{
|
||||||
|
if (!CreateMaterialFromJson(jAdsOverlay.shaderEMPLowRes.value(), adsOverlay.overlay.shaderEMPLowRes, attachment))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
adsOverlay.overlay.shaderEMPLowRes = nullptr;
|
||||||
|
|
||||||
|
adsOverlay.overlay.reticle = jAdsOverlay.reticle;
|
||||||
|
adsOverlay.overlay.width = jAdsOverlay.width;
|
||||||
|
adsOverlay.overlay.height = jAdsOverlay.height;
|
||||||
|
adsOverlay.overlay.widthSplitscreen = jAdsOverlay.widthSplitscreen;
|
||||||
|
adsOverlay.overlay.heightSplitscreen = jAdsOverlay.heightSplitscreen;
|
||||||
|
adsOverlay.thermalScope = jAdsOverlay.thermalScope;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CreateAttUIFromJson(const JsonAttUI& jUi, AttUI& ui, const WeaponAttachment& attachment) const
|
||||||
|
{
|
||||||
|
if (jUi.dpadIcon)
|
||||||
|
{
|
||||||
|
if (!CreateMaterialFromJson(jUi.dpadIcon.value(), ui.dpadIcon, attachment))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
ui.dpadIcon = nullptr;
|
||||||
|
|
||||||
|
if (jUi.ammoCounterIcon)
|
||||||
|
{
|
||||||
|
if (!CreateMaterialFromJson(jUi.ammoCounterIcon.value(), ui.ammoCounterIcon, attachment))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
ui.ammoCounterIcon = nullptr;
|
||||||
|
|
||||||
|
ui.dpadIconRatio = jUi.dpadIconRatio;
|
||||||
|
ui.ammoCounterIconRatio = jUi.ammoCounterIconRatio;
|
||||||
|
ui.ammoCounterClip = jUi.ammoCounterClip;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CreateAttRumblesFromJson(const JsonAttRumbles& jRumbles, AttRumbles& rumbles, const WeaponAttachment& attachment) const
|
||||||
|
{
|
||||||
|
if (jRumbles.fireRumble)
|
||||||
|
rumbles.fireRumble = m_memory.Dup(jRumbles.fireRumble.value().c_str());
|
||||||
|
else
|
||||||
|
rumbles.fireRumble = nullptr;
|
||||||
|
|
||||||
|
if (jRumbles.meleeImpactRumble)
|
||||||
|
rumbles.meleeImpactRumble = m_memory.Dup(jRumbles.meleeImpactRumble.value().c_str());
|
||||||
|
else
|
||||||
|
rumbles.meleeImpactRumble = nullptr;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CreateAttProjectileFromJson(const JsonAttProjectile& jProjectile, AttProjectile& projectile, const WeaponAttachment& attachment) const
|
||||||
|
{
|
||||||
|
projectile.explosionRadius = jProjectile.explosionRadius;
|
||||||
|
projectile.explosionInnerDamage = jProjectile.explosionInnerDamage;
|
||||||
|
projectile.explosionOuterDamage = jProjectile.explosionOuterDamage;
|
||||||
|
projectile.damageConeAngle = jProjectile.damageConeAngle;
|
||||||
|
projectile.projectileSpeed = jProjectile.projectileSpeed;
|
||||||
|
projectile.projectileSpeedUp = jProjectile.projectileSpeedUp;
|
||||||
|
projectile.projectileActivateDist = jProjectile.projectileActivateDist;
|
||||||
|
projectile.projectileLifetime = jProjectile.projectileLifetime;
|
||||||
|
|
||||||
|
if (jProjectile.projectileModel)
|
||||||
|
{
|
||||||
|
if (!CreateXModelFromJson(jProjectile.projectileModel.value(), projectile.projectileModel, attachment))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
projectile.projectileModel = nullptr;
|
||||||
|
|
||||||
|
projectile.projExplosionType = jProjectile.projExplosionType;
|
||||||
|
|
||||||
|
if (jProjectile.projExplosionEffect)
|
||||||
|
{
|
||||||
|
if (!CreateFxFromJson(jProjectile.projExplosionEffect.value(), projectile.projExplosionEffect, attachment))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
projectile.projExplosionEffect = nullptr;
|
||||||
|
|
||||||
|
projectile.projExplosionEffectForceNormalUp = jProjectile.projExplosionEffectForceNormalUp;
|
||||||
|
|
||||||
|
if (jProjectile.projExplosionSound)
|
||||||
|
{
|
||||||
|
if (!CreateSoundFromJson(jProjectile.projExplosionSound.value(), projectile.projExplosionSound, attachment))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
projectile.projExplosionSound.name = nullptr;
|
||||||
|
|
||||||
|
if (jProjectile.projDudEffect)
|
||||||
|
{
|
||||||
|
if (!CreateFxFromJson(jProjectile.projDudEffect.value(), projectile.projDudEffect, attachment))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
projectile.projDudEffect = nullptr;
|
||||||
|
|
||||||
|
if (jProjectile.projDudSound)
|
||||||
|
{
|
||||||
|
if (!CreateSoundFromJson(jProjectile.projDudSound.value(), projectile.projDudSound, attachment))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
projectile.projDudSound.name = nullptr;
|
||||||
|
|
||||||
|
projectile.projImpactExplode = jProjectile.projImpactExplode;
|
||||||
|
projectile.destabilizationRateTime = jProjectile.destabilizationRateTime;
|
||||||
|
projectile.destabilizationCurvatureMax = jProjectile.destabilizationCurvatureMax;
|
||||||
|
projectile.destabilizeDistance = jProjectile.destabilizeDistance;
|
||||||
|
|
||||||
|
if (jProjectile.projTrailEffect)
|
||||||
|
{
|
||||||
|
if (!CreateFxFromJson(jProjectile.projTrailEffect.value(), projectile.projTrailEffect, attachment))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
projectile.projTrailEffect = nullptr;
|
||||||
|
|
||||||
|
projectile.projIgnitionDelay = jProjectile.projIgnitionDelay;
|
||||||
|
|
||||||
|
if (jProjectile.projIgnitionEffect)
|
||||||
|
{
|
||||||
|
if (!CreateFxFromJson(jProjectile.projIgnitionEffect.value(), projectile.projIgnitionEffect, attachment))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
projectile.projIgnitionEffect = nullptr;
|
||||||
|
|
||||||
|
if (jProjectile.projIgnitionSound)
|
||||||
|
{
|
||||||
|
if (!CreateSoundFromJson(jProjectile.projIgnitionSound.value(), projectile.projIgnitionSound, attachment))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
projectile.projIgnitionSound.name = nullptr;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::istream& m_stream;
|
||||||
|
MemoryManager& m_memory;
|
||||||
|
IAssetLoadingManager& m_manager;
|
||||||
|
std::vector<XAssetInfoGeneric*>& m_dependencies;
|
||||||
|
std::vector<IndirectAssetReference>& m_indirect_asset_references;
|
||||||
|
};
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
namespace IW5
|
||||||
|
{
|
||||||
|
bool LoadWeaponAttachmentAsJson(std::istream& stream,
|
||||||
|
WeaponAttachment& attachment,
|
||||||
|
MemoryManager* memory,
|
||||||
|
IAssetLoadingManager* manager,
|
||||||
|
std::vector<XAssetInfoGeneric*>& dependencies,
|
||||||
|
std::vector<IndirectAssetReference>& indirectAssetReferences)
|
||||||
|
{
|
||||||
|
const JsonLoader loader(stream, *memory, *manager, dependencies, indirectAssetReferences);
|
||||||
|
|
||||||
|
return loader.Load(attachment);
|
||||||
|
}
|
||||||
|
} // namespace IW5
|
17
src/ObjLoading/Game/IW5/Weapon/JsonWeaponAttachmentLoader.h
Normal file
17
src/ObjLoading/Game/IW5/Weapon/JsonWeaponAttachmentLoader.h
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "AssetLoading/IAssetLoadingManager.h"
|
||||||
|
#include "Game/IW5/IW5.h"
|
||||||
|
#include "Utils/MemoryManager.h"
|
||||||
|
|
||||||
|
#include <istream>
|
||||||
|
|
||||||
|
namespace IW5
|
||||||
|
{
|
||||||
|
bool LoadWeaponAttachmentAsJson(std::istream& stream,
|
||||||
|
WeaponAttachment& attachment,
|
||||||
|
MemoryManager* memory,
|
||||||
|
IAssetLoadingManager* manager,
|
||||||
|
std::vector<XAssetInfoGeneric*>& dependencies,
|
||||||
|
std::vector<IndirectAssetReference>& indirectAssetReferences);
|
||||||
|
} // namespace IW5
|
@ -81,8 +81,8 @@ namespace T6
|
|||||||
|
|
||||||
_NODISCARD bool ConvertNotetrackSoundMap(const cspField_t& field, const std::string& value)
|
_NODISCARD bool ConvertNotetrackSoundMap(const cspField_t& field, const std::string& value)
|
||||||
{
|
{
|
||||||
std::vector<std::pair<std::string, std::string>> pairs;
|
std::vector<std::array<std::string, 2>> pairs;
|
||||||
if (!ParseAsPairs(value, pairs))
|
if (!ParseAsArray(value, pairs))
|
||||||
{
|
{
|
||||||
std::cerr << "Failed to parse notetracksoundmap as pairs\n";
|
std::cerr << "Failed to parse notetracksoundmap as pairs\n";
|
||||||
return false;
|
return false;
|
||||||
@ -106,10 +106,10 @@ namespace T6
|
|||||||
for (; currentEntryNum < pairs.size(); currentEntryNum++)
|
for (; currentEntryNum < pairs.size(); currentEntryNum++)
|
||||||
{
|
{
|
||||||
const auto& currentValue = pairs[currentEntryNum];
|
const auto& currentValue = pairs[currentEntryNum];
|
||||||
const auto keyScriptString = !currentValue.first.empty() ? m_zone_script_strings.AddOrGetScriptString(currentValue.first)
|
const auto keyScriptString = !currentValue[0].empty() ? m_zone_script_strings.AddOrGetScriptString(currentValue[0])
|
||||||
: m_zone_script_strings.AddOrGetScriptString(nullptr);
|
: m_zone_script_strings.AddOrGetScriptString(nullptr);
|
||||||
const auto valueScriptString = !currentValue.second.empty() ? m_zone_script_strings.AddOrGetScriptString(currentValue.second)
|
const auto valueScriptString = !currentValue[1].empty() ? m_zone_script_strings.AddOrGetScriptString(currentValue[1])
|
||||||
: m_zone_script_strings.AddOrGetScriptString(nullptr);
|
: m_zone_script_strings.AddOrGetScriptString(nullptr);
|
||||||
|
|
||||||
keys[currentEntryNum] = keyScriptString;
|
keys[currentEntryNum] = keyScriptString;
|
||||||
m_used_script_string_list.emplace(keyScriptString);
|
m_used_script_string_list.emplace(keyScriptString);
|
||||||
|
@ -41,68 +41,6 @@ bool InfoStringToStructConverterBase::ParseAsArray(const std::string& value, std
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool InfoStringToStructConverterBase::ParseAsPairs(const std::string& value, std::vector<std::pair<std::string, std::string>>& valueArray)
|
|
||||||
{
|
|
||||||
std::string key;
|
|
||||||
auto isKey = true;
|
|
||||||
|
|
||||||
for (auto ci = 0u; ci < value.size(); ci++)
|
|
||||||
{
|
|
||||||
auto c = value[ci];
|
|
||||||
|
|
||||||
if (c == '\r' && ci + 1 < value.size() && value[ci + 1] == '\n')
|
|
||||||
c = value[++ci];
|
|
||||||
|
|
||||||
if (c == '\n' && !isKey)
|
|
||||||
{
|
|
||||||
std::cout << "Expected value but got new line\n";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isspace(c))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
int separator;
|
|
||||||
const auto startPos = ci;
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
ci++;
|
|
||||||
if (ci >= value.size())
|
|
||||||
{
|
|
||||||
separator = EOF;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
c = value[ci];
|
|
||||||
if (c == '\r' && ci + 1 < value.size() && value[ci + 1] == '\n')
|
|
||||||
c = value[++ci];
|
|
||||||
if (isspace(c))
|
|
||||||
{
|
|
||||||
separator = static_cast<int>(static_cast<unsigned char>(c));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isKey)
|
|
||||||
{
|
|
||||||
if (separator == '\n' || separator == EOF)
|
|
||||||
{
|
|
||||||
std::cout << "Expected value but got new line\n";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
key = std::string(value, startPos, ci - startPos);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
auto parsedValue = std::string(value, startPos, ci - startPos);
|
|
||||||
valueArray.emplace_back(std::make_pair(std::move(key), std::move(parsedValue)));
|
|
||||||
key = std::string();
|
|
||||||
}
|
|
||||||
isKey = !isKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool InfoStringToStructConverterBase::ConvertString(const std::string& value, const size_t offset)
|
bool InfoStringToStructConverterBase::ConvertString(const std::string& value, const size_t offset)
|
||||||
{
|
{
|
||||||
*reinterpret_cast<const char**>(reinterpret_cast<uintptr_t>(m_structure) + offset) = m_memory->Dup(value.c_str());
|
*reinterpret_cast<const char**>(reinterpret_cast<uintptr_t>(m_structure) + offset) = m_memory->Dup(value.c_str());
|
||||||
|
@ -6,6 +6,8 @@
|
|||||||
#include "Utils/MemoryManager.h"
|
#include "Utils/MemoryManager.h"
|
||||||
#include "Zone/ZoneScriptStrings.h"
|
#include "Zone/ZoneScriptStrings.h"
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <iostream>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
@ -22,7 +24,82 @@ protected:
|
|||||||
void* m_structure;
|
void* m_structure;
|
||||||
|
|
||||||
static bool ParseAsArray(const std::string& value, std::vector<std::string>& valueArray);
|
static bool ParseAsArray(const std::string& value, std::vector<std::string>& valueArray);
|
||||||
static bool ParseAsPairs(const std::string& value, std::vector<std::pair<std::string, std::string>>& valueArray);
|
|
||||||
|
template<size_t ARRAY_SIZE> static bool ParseAsArray(const std::string& value, std::vector<std::array<std::string, ARRAY_SIZE>>& valueArray)
|
||||||
|
{
|
||||||
|
static_assert(ARRAY_SIZE >= 1);
|
||||||
|
|
||||||
|
std::array<std::string, ARRAY_SIZE> currentEntry;
|
||||||
|
auto currentEntryOffset = 0u;
|
||||||
|
|
||||||
|
for (auto ci = 0u; ci < value.size(); ci++)
|
||||||
|
{
|
||||||
|
auto c = value[ci];
|
||||||
|
|
||||||
|
if (c == '\r' && ci + 1 < value.size() && value[ci + 1] == '\n')
|
||||||
|
c = value[++ci];
|
||||||
|
|
||||||
|
if (c == '\n' && currentEntryOffset != ARRAY_SIZE)
|
||||||
|
{
|
||||||
|
std::cerr << "Expected value but got new line\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isspace(c))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
int separator;
|
||||||
|
const auto startPos = ci;
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
ci++;
|
||||||
|
|
||||||
|
// If value ends we use EOF as separator
|
||||||
|
if (ci >= value.size())
|
||||||
|
{
|
||||||
|
separator = EOF;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
c = value[ci];
|
||||||
|
|
||||||
|
// Skip \r from \r\n
|
||||||
|
if (c == '\r' && ci + 1 < value.size() && value[ci + 1] == '\n')
|
||||||
|
c = value[++ci];
|
||||||
|
|
||||||
|
// Newline is considered space
|
||||||
|
if (isspace(c))
|
||||||
|
{
|
||||||
|
separator = static_cast<int>(static_cast<unsigned char>(c));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto isNextEntrySeparator = separator == '\n' || separator == EOF;
|
||||||
|
const auto isLastEntry = currentEntryOffset >= (ARRAY_SIZE - 1);
|
||||||
|
if (isNextEntrySeparator != isLastEntry)
|
||||||
|
{
|
||||||
|
std::cout << "Expected " << ARRAY_SIZE << " values but got new line\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentEntry[currentEntryOffset++] = std::string(value, startPos, ci - startPos);
|
||||||
|
|
||||||
|
if (isLastEntry)
|
||||||
|
{
|
||||||
|
valueArray.emplace_back(std::move(currentEntry));
|
||||||
|
currentEntryOffset = 0u;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentEntryOffset > 0)
|
||||||
|
{
|
||||||
|
std::cout << "Expected " << ARRAY_SIZE << " values but got new line\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool ConvertString(const std::string& value, size_t offset);
|
bool ConvertString(const std::string& value, size_t offset);
|
||||||
bool ConvertStringBuffer(const std::string& value, size_t offset, size_t bufferSize);
|
bool ConvertStringBuffer(const std::string& value, size_t offset, size_t bufferSize);
|
||||||
|
@ -427,7 +427,7 @@ namespace IW5
|
|||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
bool first = true;
|
bool first = true;
|
||||||
|
|
||||||
for (auto i = 0u; i < m_weapon->weapCompleteDef.numFXOverrides; i++)
|
for (auto i = 0u; i < m_weapon->weapCompleteDef.numFxOverrides; i++)
|
||||||
{
|
{
|
||||||
const auto& fxOverride = m_weapon->weapCompleteDef.fxOverrides[i];
|
const auto& fxOverride = m_weapon->weapCompleteDef.fxOverrides[i];
|
||||||
|
|
||||||
@ -455,15 +455,15 @@ namespace IW5
|
|||||||
if (fxOverride.fxType < FXTYPE_COUNT)
|
if (fxOverride.fxType < FXTYPE_COUNT)
|
||||||
ss << fxOverrideTypeNames[fxOverride.fxType] << ' ';
|
ss << fxOverrideTypeNames[fxOverride.fxType] << ' ';
|
||||||
|
|
||||||
if (fxOverride.overrideFX && fxOverride.overrideFX->name && fxOverride.overrideFX->name[0])
|
if (fxOverride.overrideFx && fxOverride.overrideFx->name && fxOverride.overrideFx->name[0])
|
||||||
ss << fxOverride.overrideFX->name;
|
ss << fxOverride.overrideFx->name;
|
||||||
else
|
else
|
||||||
ss << "none";
|
ss << "none";
|
||||||
|
|
||||||
ss << ' ';
|
ss << ' ';
|
||||||
|
|
||||||
if (fxOverride.altmodeFX && fxOverride.altmodeFX->name && fxOverride.altmodeFX->name[0])
|
if (fxOverride.altmodeFx && fxOverride.altmodeFx->name && fxOverride.altmodeFx->name[0])
|
||||||
ss << fxOverride.altmodeFX->name;
|
ss << fxOverride.altmodeFx->name;
|
||||||
else
|
else
|
||||||
ss << "none";
|
ss << "none";
|
||||||
}
|
}
|
||||||
@ -688,6 +688,8 @@ bool AssetDumperWeapon::ShouldDump(XAssetInfo<WeaponCompleteDef>* asset)
|
|||||||
|
|
||||||
void AssetDumperWeapon::DumpAsset(AssetDumpingContext& context, XAssetInfo<WeaponCompleteDef>* asset)
|
void AssetDumperWeapon::DumpAsset(AssetDumpingContext& context, XAssetInfo<WeaponCompleteDef>* asset)
|
||||||
{
|
{
|
||||||
|
// TODO: only dump infostring fields when non-default
|
||||||
|
|
||||||
// Only dump raw when no gdt available
|
// Only dump raw when no gdt available
|
||||||
if (context.m_gdt)
|
if (context.m_gdt)
|
||||||
{
|
{
|
||||||
|
@ -68,9 +68,9 @@ namespace
|
|||||||
jAttachment.weaponType = attachment.weaponType;
|
jAttachment.weaponType = attachment.weaponType;
|
||||||
jAttachment.weapClass = attachment.weapClass;
|
jAttachment.weapClass = attachment.weapClass;
|
||||||
|
|
||||||
CONVERT_XMODEL_ARRAY(worldModels, 16u)
|
CONVERT_XMODEL_ARRAY(worldModels, ATTACHMENT_WORLD_MODEL_COUNT)
|
||||||
CONVERT_XMODEL_ARRAY(viewModels, 16u)
|
CONVERT_XMODEL_ARRAY(viewModels, ATTACHMENT_VIEW_MODEL_COUNT)
|
||||||
CONVERT_XMODEL_ARRAY(reticleViewModels, 8u)
|
CONVERT_XMODEL_ARRAY(reticleViewModels, ATTACHMENT_RETICLE_VIEW_MODEL_COUNT)
|
||||||
|
|
||||||
CONVERT_ATTRIBUTE(JsonAttAmmoGeneral, ammoGeneral)
|
CONVERT_ATTRIBUTE(JsonAttAmmoGeneral, ammoGeneral)
|
||||||
CONVERT_ATTRIBUTE(JsonAttSight, sight)
|
CONVERT_ATTRIBUTE(JsonAttSight, sight)
|
||||||
|
@ -25,7 +25,7 @@ set count animOverrides numAnimOverrides;
|
|||||||
set reusable soundOverrides;
|
set reusable soundOverrides;
|
||||||
set count soundOverrides numSoundOverrides;
|
set count soundOverrides numSoundOverrides;
|
||||||
set reusable fxOverrides;
|
set reusable fxOverrides;
|
||||||
set count fxOverrides numFXOverrides;
|
set count fxOverrides numFxOverrides;
|
||||||
set reusable reloadOverrides;
|
set reusable reloadOverrides;
|
||||||
set count reloadOverrides numReloadStateTimerOverrides;
|
set count reloadOverrides numReloadStateTimerOverrides;
|
||||||
set reusable notetrackOverrides;
|
set reusable notetrackOverrides;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user