mirror of
https://github.com/Laupetin/OpenAssetTools.git
synced 2026-07-02 22:08:11 +00:00
feat: T4 weapon loading and dumping (#869)
* feat: T4 weapon loading and dumping * chore: adjust naming of weapon enum values * chore: fix typo in type names * chore: rename t4 weapon loader files * feat: dump and load gdt weapons for t4 * chore: weapon loaders to use similar logic --------- Co-authored-by: Jan Laupetin <jan@laupetin.net>
This commit is contained in:
@@ -303,15 +303,16 @@ namespace
|
||||
return strcmp(weapon.szInternalName, "defaultweapon") == 0 || strcmp(weapon.szInternalName, "defaultweapon_mp") == 0;
|
||||
}
|
||||
|
||||
snd_alias_list_name* SetDefaultSound(const char* name, MemoryManager& memory)
|
||||
snd_alias_list_name* SetDefaultSound(const char* name, MemoryManager& memory, AssetCreationContext& context, AssetRegistration<AssetWeapon>& registration)
|
||||
{
|
||||
auto* aliasListName = memory.Alloc<snd_alias_list_name>();
|
||||
aliasListName->soundName = name;
|
||||
registration.AddIndirectAssetReference(context.LoadIndirectAssetReference<AssetSound>(name));
|
||||
|
||||
return aliasListName;
|
||||
}
|
||||
|
||||
void SetWeaponDefaults(WeaponDef& weapon, MemoryManager& memory)
|
||||
void SetWeaponDefaults(WeaponDef& weapon, MemoryManager& memory, AssetCreationContext& context, AssetRegistration<AssetWeapon>& registration)
|
||||
{
|
||||
if (IsDefaultWeapon(weapon))
|
||||
return;
|
||||
@@ -321,15 +322,15 @@ namespace
|
||||
if (!weapon.worldLastShotEjectEffect)
|
||||
weapon.worldLastShotEjectEffect = weapon.worldShellEjectEffect;
|
||||
if (!weapon.raiseSound.name)
|
||||
SetDefaultSound("weap_raise", memory);
|
||||
SetDefaultSound("weap_raise", memory, context, registration);
|
||||
if (!weapon.putawaySound.name)
|
||||
SetDefaultSound("weap_putaway", memory);
|
||||
SetDefaultSound("weap_putaway", memory, context, registration);
|
||||
if (!weapon.pickupSound.name)
|
||||
SetDefaultSound("weap_pickup", memory);
|
||||
SetDefaultSound("weap_pickup", memory, context, registration);
|
||||
if (!weapon.ammoPickupSound.name)
|
||||
SetDefaultSound("weap_ammo_pickup", memory);
|
||||
SetDefaultSound("weap_ammo_pickup", memory, context, registration);
|
||||
if (!weapon.emptyFireSound.name)
|
||||
SetDefaultSound("weap_dryfire_smg_npc", memory);
|
||||
SetDefaultSound("weap_dryfire_smg_npc", memory, context, registration);
|
||||
}
|
||||
|
||||
void SetupTransitionTimes(WeaponDef& weapon)
|
||||
@@ -356,13 +357,13 @@ namespace
|
||||
weapon.fMinDamageRange = 999999.12f; // oddly specific number, no clue
|
||||
}
|
||||
|
||||
void CheckCrosshairValues(WeaponDef& weapon)
|
||||
void CheckCrosshairValues(const WeaponDef& weapon)
|
||||
{
|
||||
if (weapon.enemyCrosshairRange > 15000.0f)
|
||||
con::warn("Weapon {}: Enemy crosshair ranges should be less than 15000", weapon.szInternalName);
|
||||
}
|
||||
|
||||
void CheckProjectileValues(WeaponDef& weapon)
|
||||
void CheckProjectileValues(const WeaponDef& weapon)
|
||||
{
|
||||
if (weapon.weapType != WEAPTYPE_PROJECTILE)
|
||||
return;
|
||||
@@ -399,8 +400,6 @@ namespace weapon
|
||||
AssetCreationResult InfoStringLoaderIW3::CreateAsset(const std::string& assetName, const InfoString& infoString, AssetCreationContext& context) const
|
||||
{
|
||||
auto* weaponDef = m_memory.Alloc<WeaponDef>();
|
||||
memset(weaponDef, 0, sizeof(WeaponDef));
|
||||
|
||||
InitWeaponDef(*weaponDef);
|
||||
weaponDef->szInternalName = m_memory.Dup(assetName.c_str());
|
||||
|
||||
@@ -420,7 +419,7 @@ namespace weapon
|
||||
return AssetCreationResult::Failure();
|
||||
}
|
||||
|
||||
SetWeaponDefaults(*weaponDef, m_memory);
|
||||
SetWeaponDefaults(*weaponDef, m_memory, context, registration);
|
||||
SetupTransitionTimes(*weaponDef);
|
||||
CheckWeaponDamageRanges(*weaponDef);
|
||||
CheckCrosshairValues(*weaponDef);
|
||||
|
||||
@@ -0,0 +1,164 @@
|
||||
#include "InfoStringToStructConverter.h"
|
||||
|
||||
#include "Utils/Logging/Log.h"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
using namespace T4;
|
||||
|
||||
InfoStringToStructConverter::InfoStringToStructConverter(const InfoString& infoString,
|
||||
void* structure,
|
||||
ZoneScriptStrings& zoneScriptStrings,
|
||||
MemoryManager& memory,
|
||||
AssetCreationContext& context,
|
||||
GenericAssetRegistration& registration,
|
||||
const cspField_t* fields,
|
||||
const size_t fieldCount)
|
||||
: InfoStringToStructConverterBase(infoString, structure, zoneScriptStrings, memory, context, registration),
|
||||
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 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<FxEffectDef**>(reinterpret_cast<uintptr_t>(m_structure) + field.iOffset) = nullptr;
|
||||
return true;
|
||||
}
|
||||
|
||||
auto* fx = m_context.LoadDependency<AssetFx>(value);
|
||||
if (fx == nullptr)
|
||||
{
|
||||
con::error("Failed to load fx asset \"{}\"", value);
|
||||
return false;
|
||||
}
|
||||
|
||||
m_registration.AddDependency(fx);
|
||||
*reinterpret_cast<FxEffectDef**>(reinterpret_cast<uintptr_t>(m_structure) + field.iOffset) = fx->Asset();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
case CSPFT_XMODEL:
|
||||
{
|
||||
if (value.empty())
|
||||
{
|
||||
*reinterpret_cast<XModel**>(reinterpret_cast<uintptr_t>(m_structure) + field.iOffset) = nullptr;
|
||||
return true;
|
||||
}
|
||||
|
||||
auto* xmodel = m_context.LoadDependency<AssetXModel>(value);
|
||||
if (xmodel == nullptr)
|
||||
{
|
||||
con::error("Failed to load xmodel asset \"{}\"", value);
|
||||
return false;
|
||||
}
|
||||
|
||||
m_registration.AddDependency(xmodel);
|
||||
*reinterpret_cast<XModel**>(reinterpret_cast<uintptr_t>(m_structure) + field.iOffset) = xmodel->Asset();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
case CSPFT_MATERIAL:
|
||||
{
|
||||
if (value.empty())
|
||||
{
|
||||
*reinterpret_cast<Material**>(reinterpret_cast<uintptr_t>(m_structure) + field.iOffset) = nullptr;
|
||||
return true;
|
||||
}
|
||||
|
||||
auto* material = m_context.LoadDependency<AssetMaterial>(value);
|
||||
if (material == nullptr)
|
||||
{
|
||||
con::error("Failed to load material asset \"{}\"", value);
|
||||
return false;
|
||||
}
|
||||
|
||||
m_registration.AddDependency(material);
|
||||
*reinterpret_cast<Material**>(reinterpret_cast<uintptr_t>(m_structure) + field.iOffset) = material->Asset();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
case CSPFT_SOUND:
|
||||
{
|
||||
auto* sound = reinterpret_cast<SndAliasCustom*>(reinterpret_cast<uintptr_t>(m_structure) + field.iOffset);
|
||||
if (value.empty())
|
||||
{
|
||||
sound->name = nullptr;
|
||||
return true;
|
||||
}
|
||||
|
||||
auto* name = m_memory.Alloc<snd_alias_list_name>();
|
||||
name->soundName = m_memory.Dup(value.c_str());
|
||||
sound->name = name;
|
||||
|
||||
m_registration.AddIndirectAssetReference(m_context.LoadIndirectAssetReference<AssetSound>(value));
|
||||
return true;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include "Game/T4/T4.h"
|
||||
#include "InfoString/InfoStringToStructConverterBase.h"
|
||||
|
||||
namespace T4
|
||||
{
|
||||
class InfoStringToStructConverter : public InfoStringToStructConverterBase
|
||||
{
|
||||
public:
|
||||
InfoStringToStructConverter(const InfoString& infoString,
|
||||
void* structure,
|
||||
ZoneScriptStrings& zoneScriptStrings,
|
||||
MemoryManager& memory,
|
||||
AssetCreationContext& context,
|
||||
GenericAssetRegistration& registration,
|
||||
const cspField_t* fields,
|
||||
size_t fieldCount);
|
||||
bool Convert() override;
|
||||
|
||||
protected:
|
||||
virtual bool ConvertExtensionField(const cspField_t& field, const std::string& value) = 0;
|
||||
bool ConvertBaseField(const cspField_t& field, const std::string& value);
|
||||
|
||||
const cspField_t* m_fields;
|
||||
size_t m_field_count;
|
||||
};
|
||||
} // namespace T4
|
||||
@@ -3,10 +3,14 @@
|
||||
#include "Asset/GlobalAssetPoolsLoader.h"
|
||||
#include "Game/T4/AssetMarkerT4.h"
|
||||
#include "Game/T4/T4.h"
|
||||
#include "Game/T4/Weapon/AccuracyGraphLoaderT4.h"
|
||||
#include "Game/T4/XAnim/XAnimLoaderT4.h"
|
||||
#include "Localize/AssetLoaderLocalizeT4.h"
|
||||
#include "Maps/MapEntsLoaderT4.h"
|
||||
#include "RawFile/AssetLoaderRawFileT4.h"
|
||||
#include "Weapon/FlameTableLoaderT4.h"
|
||||
#include "Weapon/WeaponGdtLoaderT4.h"
|
||||
#include "Weapon/WeaponRawLoaderT4.h"
|
||||
|
||||
using namespace T4;
|
||||
|
||||
@@ -92,6 +96,11 @@ namespace
|
||||
collection.AddAssetCreator(localize::CreateLoaderT4(memory, searchPath, zone));
|
||||
collection.AddAssetCreator(map_ents::CreateLoaderT4(memory, searchPath));
|
||||
collection.AddAssetCreator(raw_file::CreateLoaderT4(memory, searchPath));
|
||||
collection.AddAssetCreator(weapon::CreateRawLoaderT4(memory, searchPath, zone));
|
||||
collection.AddAssetCreator(weapon::CreateGdtLoaderT4(memory, searchPath, gdt, zone));
|
||||
|
||||
collection.AddSubAssetCreator(weapon::CreateAccuracyGraphLoaderT4(memory, searchPath));
|
||||
collection.AddSubAssetCreator(weapon::CreateFlameTableLoaderT4(memory, searchPath, zone));
|
||||
}
|
||||
} // namespace
|
||||
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
#include "FlameTableLoaderT4.h"
|
||||
|
||||
#include "Game/T4/InfoString/InfoStringToStructConverter.h"
|
||||
#include "Game/T4/ObjConstantsT4.h"
|
||||
#include "Game/T4/T4.h"
|
||||
#include "Game/T4/Weapon/FlameTableFields.h"
|
||||
#include "Weapon/WeaponCommon.h"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
using namespace T4;
|
||||
|
||||
namespace
|
||||
{
|
||||
class InfoStringToFlameTableConverter final : public InfoStringToStructConverter
|
||||
{
|
||||
protected:
|
||||
bool ConvertExtensionField(const cspField_t& field, const std::string& value) override
|
||||
{
|
||||
assert(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
public:
|
||||
InfoStringToFlameTableConverter(const InfoString& infoString,
|
||||
FlameTable& flameTable,
|
||||
ZoneScriptStrings& zoneScriptStrings,
|
||||
MemoryManager& memory,
|
||||
AssetCreationContext& context,
|
||||
AssetRegistration<SubAssetFlameTable>& registration,
|
||||
const cspField_t* fields,
|
||||
const size_t fieldCount)
|
||||
: InfoStringToStructConverter(infoString, &flameTable, zoneScriptStrings, memory, context, registration, fields, fieldCount)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class FlameTableLoaderT4 final : public SubAssetCreator<SubAssetFlameTable>
|
||||
{
|
||||
public:
|
||||
FlameTableLoaderT4(MemoryManager& memory, ISearchPath& searchPath, Zone& zone)
|
||||
: m_memory(memory),
|
||||
m_search_path(searchPath),
|
||||
m_zone(zone)
|
||||
{
|
||||
}
|
||||
|
||||
AssetCreationResult CreateSubAsset(const std::string& assetName, AssetCreationContext& context) override
|
||||
{
|
||||
const auto fileName = weapon::GetFileNameForFlameTable(assetName);
|
||||
const auto file = m_search_path.Open(fileName);
|
||||
if (!file.IsOpen())
|
||||
return AssetCreationResult::NoAction();
|
||||
|
||||
InfoString infoString;
|
||||
if (!infoString.FromStream(INFO_STRING_PREFIX_FLAME_TABLE, *file.m_stream))
|
||||
{
|
||||
con::error("Could not parse as info string file: \"{}\"", fileName);
|
||||
return AssetCreationResult::Failure();
|
||||
}
|
||||
|
||||
auto* flameTable = m_memory.Alloc<FlameTable>();
|
||||
AssetRegistration<SubAssetFlameTable> registration(assetName, flameTable);
|
||||
InfoStringToFlameTableConverter converter(
|
||||
infoString, *flameTable, m_zone.m_script_strings, m_memory, context, registration, flameTableFields, std::extent_v<decltype(flameTableFields)>);
|
||||
if (!converter.Convert())
|
||||
{
|
||||
con::error("Failed to parse flame table: \"{}\"", assetName);
|
||||
return AssetCreationResult::Failure();
|
||||
}
|
||||
|
||||
flameTable->name = m_memory.Dup(assetName.c_str());
|
||||
|
||||
return AssetCreationResult::Success(context.AddSubAsset(std::move(registration)));
|
||||
}
|
||||
|
||||
private:
|
||||
MemoryManager& m_memory;
|
||||
ISearchPath& m_search_path;
|
||||
Zone& m_zone;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
namespace weapon
|
||||
{
|
||||
std::unique_ptr<ISubAssetCreator> CreateFlameTableLoaderT4(MemoryManager& memory, ISearchPath& searchPath, Zone& zone)
|
||||
{
|
||||
return std::make_unique<FlameTableLoaderT4>(memory, searchPath, zone);
|
||||
}
|
||||
} // namespace weapon
|
||||
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "Asset/IAssetCreator.h"
|
||||
#include "SearchPath/ISearchPath.h"
|
||||
#include "Utils/MemoryManager.h"
|
||||
#include "Zone/Zone.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace weapon
|
||||
{
|
||||
std::unique_ptr<ISubAssetCreator> CreateFlameTableLoaderT4(MemoryManager& memory, ISearchPath& searchPath, Zone& zone);
|
||||
} // namespace weapon
|
||||
@@ -0,0 +1,52 @@
|
||||
#include "WeaponGdtLoaderT4.h"
|
||||
|
||||
#include "Game/T4/ObjConstantsT4.h"
|
||||
#include "Game/T4/T4.h"
|
||||
#include "InfoString/InfoString.h"
|
||||
#include "Utils/Logging/Log.h"
|
||||
#include "WeaponInfoStringLoaderT4.h"
|
||||
|
||||
#include <format>
|
||||
|
||||
using namespace T4;
|
||||
|
||||
namespace
|
||||
{
|
||||
class GdtLoaderWeapon final : public AssetCreator<AssetWeapon>
|
||||
{
|
||||
public:
|
||||
GdtLoaderWeapon(MemoryManager& memory, ISearchPath& searchPath, IGdtQueryable& gdt, Zone& zone)
|
||||
: m_gdt(gdt),
|
||||
m_info_string_loader(memory, searchPath, zone)
|
||||
{
|
||||
}
|
||||
|
||||
AssetCreationResult CreateAsset(const std::string& assetName, AssetCreationContext& context) override
|
||||
{
|
||||
const auto* gdtEntry = m_gdt.GetGdtEntryByGdfAndName(GDF_FILENAME_WEAPON, assetName);
|
||||
if (gdtEntry == nullptr)
|
||||
return AssetCreationResult::NoAction();
|
||||
|
||||
InfoString infoString;
|
||||
if (!infoString.FromGdtProperties(*gdtEntry))
|
||||
{
|
||||
con::error("Failed to read weapon gdt entry: \"{}\"", assetName);
|
||||
return AssetCreationResult::Failure();
|
||||
}
|
||||
|
||||
return m_info_string_loader.CreateAsset(assetName, infoString, context);
|
||||
}
|
||||
|
||||
private:
|
||||
IGdtQueryable& m_gdt;
|
||||
weapon::InfoStringLoaderT4 m_info_string_loader;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
namespace weapon
|
||||
{
|
||||
std::unique_ptr<AssetCreator<AssetWeapon>> CreateGdtLoaderT4(MemoryManager& memory, ISearchPath& searchPath, IGdtQueryable& gdt, Zone& zone)
|
||||
{
|
||||
return std::make_unique<GdtLoaderWeapon>(memory, searchPath, gdt, zone);
|
||||
}
|
||||
} // namespace weapon
|
||||
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include "Asset/IAssetCreator.h"
|
||||
#include "Game/T4/T4.h"
|
||||
#include "Gdt/IGdtQueryable.h"
|
||||
#include "SearchPath/ISearchPath.h"
|
||||
#include "Utils/MemoryManager.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace weapon
|
||||
{
|
||||
std::unique_ptr<AssetCreator<T4::AssetWeapon>> CreateGdtLoaderT4(MemoryManager& memory, ISearchPath& searchPath, IGdtQueryable& gdt, Zone& zone);
|
||||
} // namespace weapon
|
||||
@@ -0,0 +1,491 @@
|
||||
#include "WeaponInfoStringLoaderT4.h"
|
||||
|
||||
#include "Game/T4/InfoString/InfoStringToStructConverter.h"
|
||||
#include "Game/T4/T4.h"
|
||||
#include "Game/T4/Weapon/WeaponFields.h"
|
||||
#include "Game/T4/Weapon/WeaponStrings.h"
|
||||
#include "Utils/Logging/Log.h"
|
||||
#include "Utils/StringUtils.h"
|
||||
#include "Weapon/WeaponCommon.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include <sstream>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
using namespace T4;
|
||||
|
||||
namespace
|
||||
{
|
||||
class InfoStringToWeaponConverter final : public InfoStringToStructConverter
|
||||
{
|
||||
bool ConvertHideTags(const cspField_t& field, const std::string& value)
|
||||
{
|
||||
std::istringstream ss(value);
|
||||
std::vector<std::string> tags;
|
||||
|
||||
std::string tag;
|
||||
while (ss >> tag)
|
||||
tags.emplace_back(std::move(tag));
|
||||
|
||||
if (tags.size() > std::extent_v<decltype(WeaponDef::hideTags)>)
|
||||
{
|
||||
con::error("Cannot have more than {} hide tags!", std::extent_v<decltype(WeaponDef::hideTags)>);
|
||||
return false;
|
||||
}
|
||||
|
||||
auto* hideTags = reinterpret_cast<scr_string_t*>(reinterpret_cast<uintptr_t>(m_structure) + field.iOffset);
|
||||
|
||||
if (tags.size() < std::extent_v<decltype(WeaponDef::hideTags)>)
|
||||
m_registration.AddScriptString(m_zone_script_strings.AddOrGetScriptString(nullptr));
|
||||
|
||||
auto currentHideTag = 0u;
|
||||
for (; currentHideTag < tags.size(); currentHideTag++)
|
||||
{
|
||||
auto currentValue = tags[currentHideTag];
|
||||
utils::MakeStringLowerCase(currentValue);
|
||||
|
||||
const auto scrString =
|
||||
!currentValue.empty() ? m_zone_script_strings.AddOrGetScriptString(currentValue) : m_zone_script_strings.AddOrGetScriptString(nullptr);
|
||||
hideTags[currentHideTag] = scrString;
|
||||
m_registration.AddScriptString(scrString);
|
||||
}
|
||||
|
||||
for (; currentHideTag < std::extent_v<decltype(WeaponDef::hideTags)>; currentHideTag++)
|
||||
hideTags[currentHideTag] = m_zone_script_strings.GetScriptString(nullptr);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool ConvertBounceSounds(const cspField_t& field, const std::string& value) const
|
||||
{
|
||||
auto** bounceSound = reinterpret_cast<SndAliasCustom**>(reinterpret_cast<uintptr_t>(m_structure) + field.iOffset);
|
||||
if (value.empty())
|
||||
{
|
||||
*bounceSound = nullptr;
|
||||
return true;
|
||||
}
|
||||
|
||||
static_assert(std::extent_v<decltype(bounceSoundSuffixes)> == std::extent_v<decltype(WeaponDef::parallelBounce)>);
|
||||
*bounceSound = m_memory.Alloc<SndAliasCustom>(std::extent_v<decltype(bounceSoundSuffixes)>);
|
||||
for (auto i = 0u; i < std::extent_v<decltype(bounceSoundSuffixes)>; i++)
|
||||
{
|
||||
const auto currentBounceSound = value + bounceSoundSuffixes[i];
|
||||
|
||||
(*bounceSound)[i].name = m_memory.Alloc<snd_alias_list_name>();
|
||||
(*bounceSound)[i].name->soundName = m_memory.Dup(currentBounceSound.c_str());
|
||||
m_registration.AddIndirectAssetReference(m_context.LoadIndirectAssetReference<AssetSound>(currentBounceSound));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool ConvertNotetrackSoundMap(const cspField_t& field, const std::string& value)
|
||||
{
|
||||
std::istringstream ss(value);
|
||||
std::vector<std::string> tokens;
|
||||
|
||||
std::string token;
|
||||
while (ss >> token)
|
||||
tokens.emplace_back(std::move(token));
|
||||
|
||||
if (tokens.size() % 2 != 0)
|
||||
{
|
||||
con::warn("Notetrack-to-Sound: Weapon '{}' has bad entry; notetrack '{}' doesn't have a corresponding sound.",
|
||||
*reinterpret_cast<const char**>(m_structure),
|
||||
tokens.back());
|
||||
tokens.pop_back();
|
||||
}
|
||||
|
||||
const auto pairCount = tokens.size() / 2;
|
||||
if (pairCount > std::extent_v<decltype(WeaponDef::notetrackSoundMapKeys)>)
|
||||
{
|
||||
con::error("Cannot have more than {} notetracksoundmap entries!", std::extent_v<decltype(WeaponDef::notetrackSoundMapKeys)>);
|
||||
return false;
|
||||
}
|
||||
|
||||
auto* keys = reinterpret_cast<scr_string_t*>(reinterpret_cast<uintptr_t>(m_structure) + field.iOffset);
|
||||
auto* values = &keys[std::extent_v<decltype(WeaponDef::notetrackSoundMapKeys)>];
|
||||
auto currentEntryNum = 0u;
|
||||
|
||||
if (pairCount < std::extent_v<decltype(WeaponDef::notetrackSoundMapKeys)>)
|
||||
m_registration.AddScriptString(m_zone_script_strings.AddOrGetScriptString(nullptr));
|
||||
|
||||
for (; currentEntryNum < pairCount; currentEntryNum++)
|
||||
{
|
||||
auto key = tokens[currentEntryNum * 2];
|
||||
auto sound = tokens[currentEntryNum * 2 + 1];
|
||||
|
||||
if (key.size() >= 63)
|
||||
{
|
||||
con::error("Notetrack-to-sound: keyname \"{}\" is too long (length {}/{}).", key, key.size(), 63);
|
||||
return false;
|
||||
}
|
||||
|
||||
utils::MakeStringLowerCase(key);
|
||||
utils::MakeStringLowerCase(sound);
|
||||
|
||||
const auto keyScriptString =
|
||||
!key.empty() ? m_zone_script_strings.AddOrGetScriptString(key) : m_zone_script_strings.AddOrGetScriptString(nullptr);
|
||||
const auto valueScriptString =
|
||||
!sound.empty() ? m_zone_script_strings.AddOrGetScriptString(sound) : m_zone_script_strings.AddOrGetScriptString(nullptr);
|
||||
|
||||
keys[currentEntryNum] = keyScriptString;
|
||||
m_registration.AddScriptString(keyScriptString);
|
||||
|
||||
values[currentEntryNum] = valueScriptString;
|
||||
m_registration.AddScriptString(valueScriptString);
|
||||
}
|
||||
|
||||
for (; currentEntryNum < std::extent_v<decltype(WeaponDef::notetrackSoundMapKeys)>; 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())
|
||||
m_registration.AddIndirectAssetReference(m_context.LoadIndirectAssetReference<AssetXAnim>(value));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected:
|
||||
bool ConvertExtensionField(const cspField_t& field, const std::string& value) override
|
||||
{
|
||||
switch (static_cast<weapFieldType_t>(field.iFieldType))
|
||||
{
|
||||
case WFT_WEAPONTYPE:
|
||||
return ConvertEnumInt(field.szName, value, field.iOffset, szWeapTypeNames, std::extent_v<decltype(szWeapTypeNames)>);
|
||||
|
||||
case WFT_WEAPONCLASS:
|
||||
return ConvertEnumInt(field.szName, value, field.iOffset, szWeapClassNames, std::extent_v<decltype(szWeapClassNames)>);
|
||||
|
||||
case WFT_OVERLAYRETICLE:
|
||||
return ConvertEnumInt(field.szName, value, field.iOffset, szWeapOverlayReticleNames, std::extent_v<decltype(szWeapOverlayReticleNames)>);
|
||||
|
||||
case WFT_PENETRATE_TYPE:
|
||||
return ConvertEnumInt(field.szName, value, field.iOffset, penetrateTypeNames, std::extent_v<decltype(penetrateTypeNames)>);
|
||||
|
||||
case WFT_IMPACT_TYPE:
|
||||
return ConvertEnumInt(field.szName, value, field.iOffset, impactTypeNames, std::extent_v<decltype(impactTypeNames)>);
|
||||
|
||||
case WFT_STANCE:
|
||||
return ConvertEnumInt(field.szName, value, field.iOffset, szWeapStanceNames, std::extent_v<decltype(szWeapStanceNames)>);
|
||||
|
||||
case WFT_PROJ_EXPLOSION:
|
||||
return ConvertEnumInt(field.szName, value, field.iOffset, szProjectileExplosionNames, std::extent_v<decltype(szProjectileExplosionNames)>);
|
||||
|
||||
case WFT_OFFHAND_CLASS:
|
||||
return ConvertEnumInt(field.szName, value, field.iOffset, offhandClassNames, std::extent_v<decltype(offhandClassNames)>);
|
||||
|
||||
case WFT_ANIMTYPE:
|
||||
return ConvertEnumInt(field.szName, value, field.iOffset, playerAnimTypeNames, std::extent_v<decltype(playerAnimTypeNames)>);
|
||||
|
||||
case WFT_ACTIVE_RETICLE_TYPE:
|
||||
return ConvertEnumInt(field.szName, value, field.iOffset, activeReticleNames, std::extent_v<decltype(activeReticleNames)>);
|
||||
|
||||
case WFT_GUIDED_MISSILE_TYPE:
|
||||
return ConvertEnumInt(field.szName, value, field.iOffset, guidedMissileNames, std::extent_v<decltype(guidedMissileNames)>);
|
||||
|
||||
case WFT_BOUNCE_SOUND:
|
||||
return ConvertBounceSounds(field, value);
|
||||
|
||||
case WFT_STICKINESS:
|
||||
return ConvertEnumInt(field.szName, value, field.iOffset, stickinessNames, std::extent_v<decltype(stickinessNames)>);
|
||||
|
||||
case WFT_OVERLAYINTERFACE:
|
||||
return ConvertEnumInt(field.szName, value, field.iOffset, overlayInterfaceNames, std::extent_v<decltype(overlayInterfaceNames)>);
|
||||
|
||||
case WFT_INVENTORYTYPE:
|
||||
return ConvertEnumInt(field.szName, value, field.iOffset, szWeapInventoryTypeNames, std::extent_v<decltype(szWeapInventoryTypeNames)>);
|
||||
|
||||
case WFT_FIRETYPE:
|
||||
return ConvertEnumInt(field.szName, value, field.iOffset, szWeapFireTypeNames, std::extent_v<decltype(szWeapFireTypeNames)>);
|
||||
|
||||
case WFT_CLIPTYPE:
|
||||
return ConvertEnumInt(field.szName, value, field.iOffset, szWeapClipTypeNames, std::extent_v<decltype(szWeapClipTypeNames)>);
|
||||
|
||||
case WFT_AMMOCOUNTER_CLIPTYPE:
|
||||
return ConvertEnumInt(field.szName, value, field.iOffset, ammoCounterClipNames, std::extent_v<decltype(ammoCounterClipNames)>);
|
||||
|
||||
case WFT_ICONRATIO_HUD:
|
||||
case WFT_ICONRATIO_AMMOCOUNTER:
|
||||
case WFT_ICONRATIO_KILL:
|
||||
case WFT_ICONRATIO_DPAD:
|
||||
return ConvertEnumInt(field.szName, value, field.iOffset, weapIconRatioNames, std::extent_v<decltype(weapIconRatioNames)>);
|
||||
|
||||
case WFT_HIDETAGS:
|
||||
return ConvertHideTags(field, value);
|
||||
|
||||
case WFT_NOTETRACKSOUNDMAP:
|
||||
return ConvertNotetrackSoundMap(field, value);
|
||||
|
||||
case WFT_ANIM_NAME:
|
||||
return ConvertAnimName(field, value);
|
||||
|
||||
default:
|
||||
assert(false);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
InfoStringToWeaponConverter(const InfoString& infoString,
|
||||
WeaponDef& weaponDef,
|
||||
ZoneScriptStrings& zoneScriptStrings,
|
||||
MemoryManager& memory,
|
||||
AssetCreationContext& context,
|
||||
AssetRegistration<AssetWeapon>& registration,
|
||||
const cspField_t* fields,
|
||||
const size_t fieldCount)
|
||||
: InfoStringToStructConverter(infoString, &weaponDef, zoneScriptStrings, memory, context, registration, fields, fieldCount)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
void InitWeaponDef(WeaponDef& weapon)
|
||||
{
|
||||
for (const auto& field : weapon_fields)
|
||||
{
|
||||
if (field.iFieldType != CSPFT_STRING && field.iFieldType != WFT_ANIM_NAME)
|
||||
continue;
|
||||
|
||||
*reinterpret_cast<const char**>(reinterpret_cast<char*>(&weapon) + field.iOffset) = "";
|
||||
}
|
||||
|
||||
weapon.szXAnims[WEAP_ANIM_ROOT] = "";
|
||||
}
|
||||
|
||||
bool LoadAccuracyGraph(const std::string& graphName,
|
||||
vec2_t*& originalGraphKnots,
|
||||
int& originalGraphKnotCount,
|
||||
vec2_t*& graphKnots,
|
||||
int& graphKnotCount,
|
||||
AssetCreationContext& context)
|
||||
{
|
||||
auto* accuracyGraphAsset = context.LoadSubAsset<SubAssetAccuracyGraph>(graphName);
|
||||
if (!accuracyGraphAsset)
|
||||
return false;
|
||||
|
||||
const auto* accuracyGraph = accuracyGraphAsset->Asset();
|
||||
|
||||
assert(accuracyGraphAsset->m_dependencies.empty());
|
||||
assert(accuracyGraphAsset->m_used_script_strings.empty());
|
||||
assert(accuracyGraphAsset->m_indirect_asset_references.empty());
|
||||
|
||||
originalGraphKnots = accuracyGraph->graphKnots;
|
||||
originalGraphKnotCount = accuracyGraph->graphKnotCount;
|
||||
|
||||
graphKnots = accuracyGraph->graphKnots;
|
||||
graphKnotCount = accuracyGraph->graphKnotCount;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LoadAccuracyGraphs(WeaponDef& weapon, AssetCreationContext& context)
|
||||
{
|
||||
if (weapon.aiVsAiAccuracyGraphName && weapon.aiVsAiAccuracyGraphName[0])
|
||||
{
|
||||
if (!LoadAccuracyGraph(weapon::GetAssetNameForAiVsAiAccuracyGraph(weapon.aiVsAiAccuracyGraphName),
|
||||
weapon.originalAiVsAiAccuracyGraphKnots,
|
||||
weapon.originalAiVsAiAccuracyGraphKnotCount,
|
||||
weapon.aiVsAiAccuracyGraphKnots,
|
||||
weapon.aiVsAiAccuracyGraphKnotCount,
|
||||
context))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (weapon.aiVsPlayerAccuracyGraphName && weapon.aiVsPlayerAccuracyGraphName[0])
|
||||
{
|
||||
if (!LoadAccuracyGraph(weapon::GetAssetNameForAiVsPlayerAccuracyGraph(weapon.aiVsPlayerAccuracyGraphName),
|
||||
weapon.originalAiVsPlayerAccuracyGraphKnots,
|
||||
weapon.originalAiVsPlayerAccuracyGraphKnotCount,
|
||||
weapon.aiVsPlayerAccuracyGraphKnots,
|
||||
weapon.aiVsPlayerAccuracyGraphKnotCount,
|
||||
context))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LoadFlameTable(const char* flameTableName, FlameTable*& flameTablePtr, AssetRegistration<AssetWeapon>& registration, AssetCreationContext& context)
|
||||
{
|
||||
flameTablePtr = nullptr;
|
||||
if (!flameTableName || flameTableName[0] == '\0')
|
||||
return true;
|
||||
|
||||
auto* flameTableAsset = context.LoadSubAsset<SubAssetFlameTable>(flameTableName);
|
||||
if (!flameTableAsset)
|
||||
return false;
|
||||
|
||||
for (auto* dependency : flameTableAsset->m_dependencies)
|
||||
registration.AddDependency(dependency);
|
||||
|
||||
assert(flameTableAsset->m_used_script_strings.empty());
|
||||
for (const auto& indirectReference : flameTableAsset->m_indirect_asset_references)
|
||||
registration.AddIndirectAssetReference(indirectReference);
|
||||
|
||||
flameTablePtr = flameTableAsset->Asset();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LoadFlameTables(WeaponDef& weapon, AssetRegistration<AssetWeapon>& registration, AssetCreationContext& context)
|
||||
{
|
||||
return LoadFlameTable(weapon.flameTableFirstPerson, weapon.flameTableFirstPersonPtr, registration, context)
|
||||
&& LoadFlameTable(weapon.flameTableThirdPerson, weapon.flameTableThirdPersonPtr, registration, context);
|
||||
}
|
||||
|
||||
bool IsDefaultWeapon(const WeaponDef& weapon)
|
||||
{
|
||||
return strcmp(weapon.szInternalName, "defaultweapon") == 0 || strcmp(weapon.szInternalName, "defaultweapon_mp") == 0;
|
||||
}
|
||||
|
||||
snd_alias_list_name* SetDefaultSound(const char* name, MemoryManager& memory, AssetCreationContext& context, AssetRegistration<AssetWeapon>& registration)
|
||||
{
|
||||
auto* aliasListName = memory.Alloc<snd_alias_list_name>();
|
||||
aliasListName->soundName = name;
|
||||
registration.AddIndirectAssetReference(context.LoadIndirectAssetReference<AssetSound>(name));
|
||||
|
||||
return aliasListName;
|
||||
}
|
||||
|
||||
void SetWeaponDefaults(WeaponDef& weapon, MemoryManager& memory, AssetCreationContext& context, AssetRegistration<AssetWeapon>& registration)
|
||||
{
|
||||
if (IsDefaultWeapon(weapon))
|
||||
return;
|
||||
|
||||
if (!weapon.viewLastShotEjectEffect)
|
||||
weapon.viewLastShotEjectEffect = weapon.viewShellEjectEffect;
|
||||
if (!weapon.worldLastShotEjectEffect)
|
||||
weapon.worldLastShotEjectEffect = weapon.worldShellEjectEffect;
|
||||
if (!weapon.raiseSound.name)
|
||||
SetDefaultSound("weap_raise", memory, context, registration);
|
||||
if (!weapon.putawaySound.name)
|
||||
SetDefaultSound("weap_putaway", memory, context, registration);
|
||||
if (!weapon.pickupSound.name)
|
||||
SetDefaultSound("weap_pickup", memory, context, registration);
|
||||
if (!weapon.ammoPickupSound.name)
|
||||
SetDefaultSound("weap_ammo_pickup", memory, context, registration);
|
||||
if (!weapon.emptyFireSound.name)
|
||||
SetDefaultSound("player_out_of_ammo", memory, context, registration);
|
||||
}
|
||||
|
||||
void SetupTransitionTimes(WeaponDef& weapon)
|
||||
{
|
||||
if (weapon.iAdsTransInTime <= 0)
|
||||
weapon.fOOPosAnimLength[0] = 1.0f / 300.0f;
|
||||
else
|
||||
weapon.fOOPosAnimLength[0] = 1.0f / static_cast<float>(weapon.iAdsTransInTime);
|
||||
|
||||
if (weapon.iAdsTransOutTime <= 0)
|
||||
weapon.fOOPosAnimLength[1] = 1.0f / 500.0f;
|
||||
else
|
||||
weapon.fOOPosAnimLength[1] = 1.0f / static_cast<float>(weapon.iAdsTransOutTime);
|
||||
}
|
||||
|
||||
void CheckWeaponDamageRanges(WeaponDef& weapon)
|
||||
{
|
||||
if (strcmp(weapon.szInternalName, "none") == 0)
|
||||
return;
|
||||
|
||||
if (weapon.fMaxDamageRange <= 0.0f)
|
||||
weapon.fMaxDamageRange = 999999.0f;
|
||||
if (weapon.fMinDamageRange <= 0.0f)
|
||||
weapon.fMinDamageRange = 999999.12f;
|
||||
}
|
||||
|
||||
void CheckCrosshairValues(const WeaponDef& weapon)
|
||||
{
|
||||
if (weapon.enemyCrosshairRange > 15000.0f)
|
||||
con::warn("Weapon {}: Enemy crosshair ranges should be less than 15000", weapon.szInternalName);
|
||||
}
|
||||
|
||||
void CheckProjectileValues(const WeaponDef& weapon)
|
||||
{
|
||||
if (weapon.weapType != WEAPTYPE_PROJECTILE)
|
||||
return;
|
||||
|
||||
if (weapon.iProjectileSpeed <= 0)
|
||||
con::warn("Weapon {}: Projectile speed must be greater than 0.0", weapon.szDisplayName);
|
||||
|
||||
if (weapon.destabilizationCurvatureMax >= 1000000000.0f || weapon.destabilizationCurvatureMax < 0.0f)
|
||||
con::warn("Weapon {}: Destabilization angle must be between 0 and 45 degrees", weapon.szDisplayName);
|
||||
|
||||
if (weapon.destabilizationRateTime < 0.0f)
|
||||
con::warn("Weapon {}: Destabilization rate time must be non-negative", weapon.szDisplayName);
|
||||
}
|
||||
|
||||
void CheckSharedAmmoValues(const WeaponDef& weapon)
|
||||
{
|
||||
if (weapon.szAmmoName)
|
||||
utils::MakeStringLowerCase(const_cast<char*>(weapon.szAmmoName));
|
||||
|
||||
if (weapon.szClipName)
|
||||
utils::MakeStringLowerCase(const_cast<char*>(weapon.szClipName));
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace weapon
|
||||
{
|
||||
InfoStringLoaderT4::InfoStringLoaderT4(MemoryManager& memory, ISearchPath& searchPath, Zone& zone)
|
||||
: m_memory(memory),
|
||||
m_search_path(searchPath),
|
||||
m_zone(zone)
|
||||
{
|
||||
}
|
||||
|
||||
AssetCreationResult InfoStringLoaderT4::CreateAsset(const std::string& assetName, const InfoString& infoString, AssetCreationContext& context) const
|
||||
{
|
||||
auto* weaponDef = m_memory.Alloc<WeaponDef>();
|
||||
InitWeaponDef(*weaponDef);
|
||||
weaponDef->szInternalName = m_memory.Dup(assetName.c_str());
|
||||
|
||||
AssetRegistration<AssetWeapon> registration(assetName, weaponDef);
|
||||
|
||||
InfoStringToWeaponConverter converter(
|
||||
infoString, *weaponDef, m_zone.m_script_strings, m_memory, context, registration, weapon_fields, std::extent_v<decltype(weapon_fields)>);
|
||||
if (!converter.Convert())
|
||||
{
|
||||
con::error("Failed to parse weapon: \"{}\"", assetName);
|
||||
return AssetCreationResult::Failure();
|
||||
}
|
||||
|
||||
if (!LoadAccuracyGraphs(*weaponDef, context))
|
||||
{
|
||||
con::error("Failed to load accuracy tables of weapon: \"{}\"", assetName);
|
||||
return AssetCreationResult::Failure();
|
||||
}
|
||||
|
||||
if (!LoadFlameTables(*weaponDef, registration, context))
|
||||
{
|
||||
con::error("Failed to load flame tables of weapon: \"{}\"", assetName);
|
||||
return AssetCreationResult::Failure();
|
||||
}
|
||||
|
||||
SetWeaponDefaults(*weaponDef, m_memory, context, registration);
|
||||
SetupTransitionTimes(*weaponDef);
|
||||
CheckWeaponDamageRanges(*weaponDef);
|
||||
CheckCrosshairValues(*weaponDef);
|
||||
CheckProjectileValues(*weaponDef);
|
||||
CheckSharedAmmoValues(*weaponDef);
|
||||
|
||||
return AssetCreationResult::Success(context.AddAsset(std::move(registration)));
|
||||
}
|
||||
} // namespace weapon
|
||||
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include "Asset/AssetCreationContext.h"
|
||||
#include "Asset/AssetCreationResult.h"
|
||||
#include "InfoString/InfoString.h"
|
||||
#include "SearchPath/ISearchPath.h"
|
||||
#include "Utils/MemoryManager.h"
|
||||
#include "Zone/Zone.h"
|
||||
|
||||
namespace weapon
|
||||
{
|
||||
class InfoStringLoaderT4
|
||||
{
|
||||
public:
|
||||
InfoStringLoaderT4(MemoryManager& memory, ISearchPath& searchPath, Zone& zone);
|
||||
|
||||
AssetCreationResult CreateAsset(const std::string& assetName, const InfoString& infoString, AssetCreationContext& context) const;
|
||||
|
||||
private:
|
||||
MemoryManager& m_memory;
|
||||
ISearchPath& m_search_path;
|
||||
Zone& m_zone;
|
||||
};
|
||||
} // namespace weapon
|
||||
@@ -0,0 +1,52 @@
|
||||
#include "WeaponRawLoaderT4.h"
|
||||
|
||||
#include "Game/T4/ObjConstantsT4.h"
|
||||
#include "Game/T4/T4.h"
|
||||
#include "InfoString/InfoString.h"
|
||||
#include "Utils/Logging/Log.h"
|
||||
#include "Weapon/WeaponCommon.h"
|
||||
#include "WeaponInfoStringLoaderT4.h"
|
||||
|
||||
using namespace T4;
|
||||
|
||||
namespace
|
||||
{
|
||||
class RawLoaderWeapon final : public AssetCreator<AssetWeapon>
|
||||
{
|
||||
public:
|
||||
RawLoaderWeapon(MemoryManager& memory, ISearchPath& searchPath, Zone& zone)
|
||||
: m_search_path(searchPath),
|
||||
m_info_string_loader(memory, searchPath, zone)
|
||||
{
|
||||
}
|
||||
|
||||
AssetCreationResult CreateAsset(const std::string& assetName, AssetCreationContext& context) override
|
||||
{
|
||||
const auto fileName = weapon::GetFileNameForAssetName(assetName);
|
||||
const auto file = m_search_path.Open(fileName);
|
||||
if (!file.IsOpen())
|
||||
return AssetCreationResult::NoAction();
|
||||
|
||||
InfoString infoString;
|
||||
if (!infoString.FromStream(INFO_STRING_PREFIX_WEAPON, *file.m_stream))
|
||||
{
|
||||
con::error("Could not parse as info string file: \"{}\"", fileName);
|
||||
return AssetCreationResult::Failure();
|
||||
}
|
||||
|
||||
return m_info_string_loader.CreateAsset(assetName, infoString, context);
|
||||
}
|
||||
|
||||
private:
|
||||
ISearchPath& m_search_path;
|
||||
weapon::InfoStringLoaderT4 m_info_string_loader;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
namespace weapon
|
||||
{
|
||||
std::unique_ptr<AssetCreator<AssetWeapon>> CreateRawLoaderT4(MemoryManager& memory, ISearchPath& searchPath, Zone& zone)
|
||||
{
|
||||
return std::make_unique<RawLoaderWeapon>(memory, searchPath, zone);
|
||||
}
|
||||
} // namespace weapon
|
||||
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "Asset/IAssetCreator.h"
|
||||
#include "Game/T4/T4.h"
|
||||
#include "SearchPath/ISearchPath.h"
|
||||
#include "Utils/MemoryManager.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace weapon
|
||||
{
|
||||
std::unique_ptr<AssetCreator<T4::AssetWeapon>> CreateRawLoaderT4(MemoryManager& memory, ISearchPath& searchPath, Zone& zone);
|
||||
} // namespace weapon
|
||||
@@ -395,7 +395,7 @@ namespace
|
||||
con::warn("Weapon {}: Enemy crosshair ranges should be less than 15000", weapon.weapVariantDef.szInternalName);
|
||||
}
|
||||
|
||||
void CheckProjectileValues(WeaponFullDef& weapon)
|
||||
void CheckProjectileValues(const WeaponFullDef& weapon)
|
||||
{
|
||||
if (weapon.weapDef.weapType != WEAPTYPE_PROJECTILE)
|
||||
return;
|
||||
|
||||
@@ -544,13 +544,13 @@ namespace
|
||||
weapon.weapDef.damageRange[5] = 999999.12f; // oddly specific number, no clue
|
||||
}
|
||||
|
||||
void CheckCrosshairValues(WeaponFullDef& weapon)
|
||||
void CheckCrosshairValues(const WeaponFullDef& weapon)
|
||||
{
|
||||
if (weapon.weapDef.enemyCrosshairRange > 15000.0f)
|
||||
con::warn("Weapon {}: Enemy crosshair ranges should be less than 15000", weapon.weapVariantDef.szInternalName);
|
||||
}
|
||||
|
||||
void CheckProjectileValues(WeaponFullDef& weapon)
|
||||
void CheckProjectileValues(const WeaponFullDef& weapon)
|
||||
{
|
||||
if (weapon.weapDef.weapType != WEAPTYPE_PROJECTILE)
|
||||
return;
|
||||
@@ -724,7 +724,7 @@ namespace weapon
|
||||
{
|
||||
}
|
||||
|
||||
AssetCreationResult InfoStringLoaderT6::CreateAsset(const std::string& assetName, const InfoString& infoString, AssetCreationContext& context)
|
||||
AssetCreationResult InfoStringLoaderT6::CreateAsset(const std::string& assetName, const InfoString& infoString, AssetCreationContext& context) const
|
||||
{
|
||||
auto* weaponFullDef = m_memory.Alloc<WeaponFullDef>();
|
||||
weaponFullDef->weapVariantDef.szInternalName = m_memory.Dup(assetName.c_str());
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace weapon
|
||||
public:
|
||||
InfoStringLoaderT6(MemoryManager& memory, ISearchPath& searchPath, Zone& zone);
|
||||
|
||||
AssetCreationResult CreateAsset(const std::string& assetName, const InfoString& infoString, AssetCreationContext& context);
|
||||
AssetCreationResult CreateAsset(const std::string& assetName, const InfoString& infoString, AssetCreationContext& context) const;
|
||||
|
||||
private:
|
||||
MemoryManager& m_memory;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#options GAME(IW3, IW4, IW5, T5, T6)
|
||||
#options GAME(IW3, IW4, IW5, T4, T5, T6)
|
||||
|
||||
#filename "Game/" + GAME + "/Weapon/AccuracyGraphLoader" + GAME + ".cpp"
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#options GAME (IW3, IW4, IW5, T5, T6)
|
||||
#options GAME(IW3, IW4, IW5, T4, T5, T6)
|
||||
|
||||
#filename "Game/" + GAME + "/Weapon/AccuracyGraphLoader" + GAME + ".h"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user