2
0
mirror of https://github.com/Laupetin/OpenAssetTools.git synced 2025-08-30 21:53:15 +00:00

refactor: streamline IW5 asset loading

This commit is contained in:
Jan Laupetin
2025-08-05 00:23:06 +02:00
parent b5c9567389
commit 81a67151b5
32 changed files with 868 additions and 929 deletions

View File

@@ -1,6 +1,7 @@
#include "LoaderImageIW5.h"
#include "Game/IW5/IW5.h"
#include "Image/ImageCommon.h"
#include "Image/IwiLoader.h"
#include <cstring>
@@ -9,6 +10,7 @@
#include <sstream>
using namespace IW5;
using namespace ::image;
namespace
{
@@ -25,7 +27,7 @@ namespace
AssetCreationResult CreateAsset(const std::string& assetName, AssetCreationContext& context) override
{
const auto fileName = std::format("images/{}.iwi", assetName);
const auto fileName = GetFileNameForAsset(assetName, ".iwi");
const auto file = m_search_path.Open(fileName);
if (!file.IsOpen())
return AssetCreationResult::NoAction();
@@ -60,10 +62,10 @@ namespace
};
} // namespace
namespace IW5
namespace IW5::image
{
std::unique_ptr<AssetCreator<AssetImage>> CreateImageLoader(MemoryManager& memory, ISearchPath& searchPath)
std::unique_ptr<AssetCreator<AssetImage>> CreateLoader(MemoryManager& memory, ISearchPath& searchPath)
{
return std::make_unique<ImageLoader>(memory, searchPath);
}
} // namespace IW5
} // namespace IW5::image

View File

@@ -7,7 +7,7 @@
#include <memory>
namespace IW5
namespace IW5::image
{
std::unique_ptr<AssetCreator<AssetImage>> CreateImageLoader(MemoryManager& memory, ISearchPath& searchPath);
} // namespace IW5
std::unique_ptr<AssetCreator<AssetImage>> CreateLoader(MemoryManager& memory, ISearchPath& searchPath);
} // namespace IW5::image

View File

@@ -1,131 +0,0 @@
#include "JsonLeaderboardDefLoader.h"
#include "Game/IW5/CommonIW5.h"
#include "Game/IW5/Leaderboard/JsonLeaderboardDef.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)
: m_stream(stream),
m_memory(memory)
{
}
bool Load(LeaderboardDef& leaderboardDef) const
{
try
{
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 != "leaderboard" || version != 1u)
{
std::cerr << std::format("Tried to load leaderboard \"{}\" but did not find expected type leaderboard of version 1\n", leaderboardDef.name);
return false;
}
const auto jLeaderboard = jRoot.get<JsonLeaderboardDef>();
return CreateLeaderboardFromJson(jLeaderboard, leaderboardDef);
}
catch (const json::exception& e)
{
std::cerr << std::format("Failed to parse json of leaderboard: {}\n", e.what());
}
return false;
}
private:
static bool CreateTrackTypeFlagsFromJson(const JsonLeaderboardDef& jLeaderboardDef, int& trackTypeFlags)
{
for (const auto trackType : jLeaderboardDef.trackTypes)
trackTypeFlags |= 1 << trackType;
return true;
}
bool CreateColumnDefFromJson(const JsonColumnDef& jColumn, LbColumnDef& lbColumnDef, LeaderboardDef& leaderboardDef) const
{
lbColumnDef.name = m_memory.Dup(jColumn.name.c_str());
lbColumnDef.id = jColumn.colId;
lbColumnDef.propertyId = jColumn.propertyId.value_or(0);
lbColumnDef.hidden = jColumn.hidden.value_or(false);
if (jColumn.statName)
lbColumnDef.statName = m_memory.Dup(jColumn.statName->c_str());
else
lbColumnDef.statName = nullptr;
lbColumnDef.type = jColumn.type;
lbColumnDef.precision = jColumn.precision.value_or(0);
lbColumnDef.agg = jColumn.aggregationFunction;
lbColumnDef.uiCalColX = jColumn.uiCalColX.value_or(0);
lbColumnDef.uiCalColY = jColumn.uiCalColY.value_or(0);
return true;
}
bool CreateLeaderboardFromJson(const JsonLeaderboardDef& jLeaderboardDef, LeaderboardDef& leaderboardDef) const
{
leaderboardDef.id = jLeaderboardDef.id;
leaderboardDef.xpColId = jLeaderboardDef.xpColId.value_or(-1);
leaderboardDef.prestigeColId = jLeaderboardDef.prestigeColId.value_or(-1);
if (!jLeaderboardDef.columns.empty())
{
leaderboardDef.columnCount = static_cast<int>(jLeaderboardDef.columns.size());
leaderboardDef.columns = m_memory.Alloc<LbColumnDef>(leaderboardDef.columnCount);
for (auto i = 0; i < leaderboardDef.columnCount; i++)
{
if (!CreateColumnDefFromJson(jLeaderboardDef.columns[i], leaderboardDef.columns[i], leaderboardDef))
return false;
}
}
else
{
leaderboardDef.columnCount = 0;
leaderboardDef.columns = nullptr;
}
leaderboardDef.updateType = jLeaderboardDef.updateType;
if (!CreateTrackTypeFlagsFromJson(jLeaderboardDef, leaderboardDef.trackTypes))
return false;
return true;
}
std::istream& m_stream;
MemoryManager& m_memory;
};
} // namespace
namespace IW5
{
bool LoadLeaderboardAsJson(std::istream& stream, LeaderboardDef& leaderboard, MemoryManager& memory)
{
const JsonLoader loader(stream, memory);
return loader.Load(leaderboard);
}
} // namespace IW5

View File

@@ -1,11 +0,0 @@
#pragma once
#include "Game/IW5/IW5.h"
#include "Utils/MemoryManager.h"
#include <istream>
namespace IW5
{
bool LoadLeaderboardAsJson(std::istream& stream, LeaderboardDef& leaderboard, MemoryManager& memory);
} // namespace IW5

View File

@@ -1,16 +1,127 @@
#include "LoaderLeaderboardIW5.h"
#include "Game/IW5/IW5.h"
#include "JsonLeaderboardDefLoader.h"
#include "Game/IW5/Leaderboard/JsonLeaderboardDef.h"
#include "Leaderboard/LeaderboardCommon.h"
#include <cstring>
#include <format>
#include <iostream>
#include <nlohmann/json.hpp>
using namespace nlohmann;
using namespace IW5;
using namespace ::leaderboard;
namespace
{
class JsonLoader
{
public:
JsonLoader(std::istream& stream, MemoryManager& memory)
: m_stream(stream),
m_memory(memory)
{
}
bool Load(LeaderboardDef& leaderboardDef) const
{
try
{
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 != "leaderboard" || version != 1u)
{
std::cerr << std::format("Tried to load leaderboard \"{}\" but did not find expected type leaderboard of version 1\n", leaderboardDef.name);
return false;
}
const auto jLeaderboard = jRoot.get<JsonLeaderboardDef>();
return CreateLeaderboardFromJson(jLeaderboard, leaderboardDef);
}
catch (const json::exception& e)
{
std::cerr << std::format("Failed to parse json of leaderboard: {}\n", e.what());
}
return false;
}
private:
static bool CreateTrackTypeFlagsFromJson(const JsonLeaderboardDef& jLeaderboardDef, int& trackTypeFlags)
{
for (const auto trackType : jLeaderboardDef.trackTypes)
trackTypeFlags |= 1 << trackType;
return true;
}
bool CreateColumnDefFromJson(const JsonColumnDef& jColumn, LbColumnDef& lbColumnDef, LeaderboardDef& leaderboardDef) const
{
lbColumnDef.name = m_memory.Dup(jColumn.name.c_str());
lbColumnDef.id = jColumn.colId;
lbColumnDef.propertyId = jColumn.propertyId.value_or(0);
lbColumnDef.hidden = jColumn.hidden.value_or(false);
if (jColumn.statName)
lbColumnDef.statName = m_memory.Dup(jColumn.statName->c_str());
else
lbColumnDef.statName = nullptr;
lbColumnDef.type = jColumn.type;
lbColumnDef.precision = jColumn.precision.value_or(0);
lbColumnDef.agg = jColumn.aggregationFunction;
lbColumnDef.uiCalColX = jColumn.uiCalColX.value_or(0);
lbColumnDef.uiCalColY = jColumn.uiCalColY.value_or(0);
return true;
}
bool CreateLeaderboardFromJson(const JsonLeaderboardDef& jLeaderboardDef, LeaderboardDef& leaderboardDef) const
{
leaderboardDef.id = jLeaderboardDef.id;
leaderboardDef.xpColId = jLeaderboardDef.xpColId.value_or(-1);
leaderboardDef.prestigeColId = jLeaderboardDef.prestigeColId.value_or(-1);
if (!jLeaderboardDef.columns.empty())
{
leaderboardDef.columnCount = static_cast<int>(jLeaderboardDef.columns.size());
leaderboardDef.columns = m_memory.Alloc<LbColumnDef>(leaderboardDef.columnCount);
for (auto i = 0; i < leaderboardDef.columnCount; i++)
{
if (!CreateColumnDefFromJson(jLeaderboardDef.columns[i], leaderboardDef.columns[i], leaderboardDef))
return false;
}
}
else
{
leaderboardDef.columnCount = 0;
leaderboardDef.columns = nullptr;
}
leaderboardDef.updateType = jLeaderboardDef.updateType;
if (!CreateTrackTypeFlagsFromJson(jLeaderboardDef, leaderboardDef.trackTypes))
return false;
return true;
}
std::istream& m_stream;
MemoryManager& m_memory;
};
class LeaderboardLoader final : public AssetCreator<AssetLeaderboard>
{
public:
@@ -22,14 +133,15 @@ namespace
AssetCreationResult CreateAsset(const std::string& assetName, AssetCreationContext& context) override
{
const auto file = m_search_path.Open(std::format("leaderboards/{}.json", assetName));
const auto file = m_search_path.Open(GetJsonFileNameForAsset(assetName));
if (!file.IsOpen())
return AssetCreationResult::NoAction();
auto* leaderboardDef = m_memory.Alloc<LeaderboardDef>();
leaderboardDef->name = m_memory.Dup(assetName.c_str());
if (!LoadLeaderboardAsJson(*file.m_stream, *leaderboardDef, m_memory))
const JsonLoader loader(*file.m_stream, m_memory);
if (!loader.Load(*leaderboardDef))
{
std::cerr << std::format("Failed to load leaderboard \"{}\"\n", assetName);
return AssetCreationResult::Failure();
@@ -44,10 +156,10 @@ namespace
};
} // namespace
namespace IW5
namespace IW5::leaderboard
{
std::unique_ptr<AssetCreator<AssetLeaderboard>> CreateLeaderboardLoader(MemoryManager& memory, ISearchPath& searchPath)
std::unique_ptr<AssetCreator<AssetLeaderboard>> CreateLoader(MemoryManager& memory, ISearchPath& searchPath)
{
return std::make_unique<LeaderboardLoader>(memory, searchPath);
}
} // namespace IW5
} // namespace IW5::leaderboard

View File

@@ -7,7 +7,7 @@
#include <memory>
namespace IW5
namespace IW5::leaderboard
{
std::unique_ptr<AssetCreator<AssetLeaderboard>> CreateLeaderboardLoader(MemoryManager& memory, ISearchPath& searchPath);
} // namespace IW5
std::unique_ptr<AssetCreator<AssetLeaderboard>> CreateLoader(MemoryManager& memory, ISearchPath& searchPath);
} // namespace IW5::leaderboard

View File

@@ -35,10 +35,10 @@ namespace
};
} // namespace
namespace IW5
namespace IW5::localize
{
std::unique_ptr<AssetCreator<AssetLocalize>> CreateLocalizeLoader(MemoryManager& memory, ISearchPath& searchPath, Zone& zone)
std::unique_ptr<AssetCreator<AssetLocalize>> CreateLoader(MemoryManager& memory, ISearchPath& searchPath, Zone& zone)
{
return std::make_unique<LocalizeLoader>(memory, searchPath, zone);
}
} // namespace IW5
} // namespace IW5::localize

View File

@@ -8,7 +8,7 @@
#include <memory>
namespace IW5
namespace IW5::localize
{
std::unique_ptr<AssetCreator<AssetLocalize>> CreateLocalizeLoader(MemoryManager& memory, ISearchPath& searchPath, Zone& zone);
} // namespace IW5
std::unique_ptr<AssetCreator<AssetLocalize>> CreateLoader(MemoryManager& memory, ISearchPath& searchPath, Zone& zone);
} // namespace IW5::localize

View File

@@ -8,6 +8,7 @@
#include <iostream>
using namespace IW5;
using namespace ::material;
namespace
{
@@ -22,7 +23,7 @@ namespace
AssetCreationResult CreateAsset(const std::string& assetName, AssetCreationContext& context) override
{
const auto file = m_search_path.Open(material::GetFileNameForAssetName(assetName));
const auto file = m_search_path.Open(GetFileNameForAssetName(assetName));
if (!file.IsOpen())
return AssetCreationResult::NoAction();
@@ -45,10 +46,10 @@ namespace
};
} // namespace
namespace IW5
namespace IW5::material
{
std::unique_ptr<AssetCreator<AssetMaterial>> CreateMaterialLoader(MemoryManager& memory, ISearchPath& searchPath)
std::unique_ptr<AssetCreator<AssetMaterial>> CreateLoader(MemoryManager& memory, ISearchPath& searchPath)
{
return std::make_unique<MaterialLoader>(memory, searchPath);
}
} // namespace IW5
} // namespace IW5::material

View File

@@ -7,7 +7,7 @@
#include <memory>
namespace IW5
namespace IW5::material
{
std::unique_ptr<AssetCreator<AssetMaterial>> CreateMaterialLoader(MemoryManager& memory, ISearchPath& searchPath);
} // namespace IW5
std::unique_ptr<AssetCreator<AssetMaterial>> CreateLoader(MemoryManager& memory, ISearchPath& searchPath);
} // namespace IW5::material

View File

@@ -11,6 +11,7 @@
#include <iostream>
using namespace IW5;
using namespace ::menu;
namespace
{
@@ -28,7 +29,7 @@ namespace
std::vector<menuDef_t*> menus;
AssetRegistration<AssetMenuList> registration(assetName);
auto& zoneState = context.GetZoneAssetCreationState<menu::MenuAssetZoneState>();
auto& zoneState = context.GetZoneAssetCreationState<MenuAssetZoneState>();
auto& conversionState = context.GetZoneAssetCreationState<MenuConversionZoneState>();
std::deque<std::string> menuLoadQueue;
@@ -80,7 +81,7 @@ namespace
private:
bool LoadMenuFileFromQueue(const std::string& menuFilePath,
AssetCreationContext& context,
menu::MenuAssetZoneState& zoneState,
MenuAssetZoneState& zoneState,
MenuConversionZoneState& conversionState,
std::vector<menuDef_t*>& menus,
AssetRegistration<AssetMenuList>& registration) const
@@ -121,8 +122,8 @@ namespace
bool ProcessParsedResults(const std::string& fileName,
AssetCreationContext& context,
menu::ParsingResult& parsingResult,
menu::MenuAssetZoneState& zoneState,
ParsingResult& parsingResult,
MenuAssetZoneState& zoneState,
MenuConversionZoneState& conversionState,
std::vector<menuDef_t*>& menus,
AssetRegistration<AssetMenuList>& registration) const
@@ -196,10 +197,9 @@ namespace
menuList.menus = nullptr;
}
std::unique_ptr<menu::ParsingResult>
ParseMenuFile(std::istream& stream, const std::string& menuFileName, const menu::MenuAssetZoneState& zoneState) const
std::unique_ptr<ParsingResult> ParseMenuFile(std::istream& stream, const std::string& menuFileName, const MenuAssetZoneState& zoneState) const
{
menu::MenuFileReader reader(stream, menuFileName, menu::FeatureLevel::IW5, m_search_path);
MenuFileReader reader(stream, menuFileName, FeatureLevel::IW5, m_search_path);
reader.IncludeZoneState(zoneState);
reader.SetPermissiveMode(ObjLoading::Configuration.MenuPermissiveParsing);
@@ -212,10 +212,10 @@ namespace
};
} // namespace
namespace IW5
namespace IW5::menu
{
std::unique_ptr<AssetCreator<AssetMenuList>> CreateMenuListLoader(MemoryManager& memory, ISearchPath& searchPath)
{
return std::make_unique<MenuListLoader>(memory, searchPath);
}
} // namespace IW5
} // namespace IW5::menu

View File

@@ -7,7 +7,7 @@
#include <memory>
namespace IW5
namespace IW5::menu
{
std::unique_ptr<AssetCreator<AssetMenuList>> CreateMenuListLoader(MemoryManager& memory, ISearchPath& searchPath);
} // namespace IW5
} // namespace IW5::menu

View File

@@ -14,7 +14,7 @@ namespace IW5
IMenuConverter() = default;
virtual ~IMenuConverter() = default;
virtual void ConvertMenu(const menu::CommonMenuDef& commonMenu, menuDef_t& menu, AssetRegistration<AssetMenu>& registration) = 0;
virtual void ConvertMenu(const ::menu::CommonMenuDef& commonMenu, menuDef_t& menu, AssetRegistration<AssetMenu>& registration) = 0;
static std::unique_ptr<IMenuConverter> Create(bool disableOptimizations, ISearchPath& searchPath, MemoryManager& memory, AssetCreationContext& context);
};

View File

@@ -126,12 +126,12 @@ namespace
// collection.AddAssetCreator(std::make_unique<AssetLoaderXAnim>(memory));
// collection.AddAssetCreator(std::make_unique<AssetLoaderXModelSurfs>(memory));
collection.AddAssetCreator(xmodel::CreateXModelLoader(memory, searchPath, zone));
collection.AddAssetCreator(CreateMaterialLoader(memory, searchPath));
collection.AddAssetCreator(material::CreateLoader(memory, searchPath));
// collection.AddAssetCreator(std::make_unique<AssetLoaderPixelShader>(memory));
// collection.AddAssetCreator(std::make_unique<AssetLoaderVertexShader>(memory));
// collection.AddAssetCreator(std::make_unique<AssetLoaderVertexDecl>(memory));
// collection.AddAssetCreator(std::make_unique<AssetLoaderTechniqueSet>(memory));
collection.AddAssetCreator(CreateImageLoader(memory, searchPath));
collection.AddAssetCreator(image::CreateLoader(memory, searchPath));
// collection.AddAssetCreator(std::make_unique<AssetLoaderSound>(memory));
// collection.AddAssetCreator(std::make_unique<AssetLoaderSoundCurve>(memory));
// collection.AddAssetCreator(std::make_unique<AssetLoaderLoadedSound>(memory));
@@ -145,19 +145,19 @@ namespace
// collection.AddAssetCreator(std::make_unique<AssetLoaderGfxWorld>(memory));
// collection.AddAssetCreator(std::make_unique<AssetLoaderLightDef>(memory));
// collection.AddAssetCreator(std::make_unique<AssetLoaderFont>(memory));
collection.AddAssetCreator(CreateMenuListLoader(memory, searchPath));
collection.AddAssetCreator(menu::CreateMenuListLoader(memory, searchPath));
// collection.AddAssetCreator(std::make_unique<AssetLoaderMenu>(memory));
collection.AddAssetCreator(CreateLocalizeLoader(memory, searchPath, zone));
collection.AddAssetCreator(CreateAttachmentLoader(memory, searchPath));
collection.AddAssetCreator(CreateRawWeaponLoader(memory, searchPath, zone));
collection.AddAssetCreator(CreateGdtWeaponLoader(memory, searchPath, gdt, zone));
collection.AddAssetCreator(localize::CreateLoader(memory, searchPath, zone));
collection.AddAssetCreator(attachment::CreateLoader(memory, searchPath));
collection.AddAssetCreator(weapon::CreateRawLoader(memory, searchPath, zone));
collection.AddAssetCreator(weapon::CreateGdtLoader(memory, searchPath, gdt, zone));
// collection.AddAssetCreator(std::make_unique<AssetLoaderFx>(memory));
// collection.AddAssetCreator(std::make_unique<AssetLoaderImpactFx>(memory));
// collection.AddAssetCreator(std::make_unique<AssetLoaderSurfaceFx>(memory));
collection.AddAssetCreator(CreateRawFileLoader(memory, searchPath));
collection.AddAssetCreator(CreateScriptLoader(memory, searchPath));
collection.AddAssetCreator(CreateStringTableLoader(memory, searchPath));
collection.AddAssetCreator(CreateLeaderboardLoader(memory, searchPath));
collection.AddAssetCreator(raw_file::CreateLoader(memory, searchPath));
collection.AddAssetCreator(script::CreateLoader(memory, searchPath));
collection.AddAssetCreator(string_table::CreateLoader(memory, searchPath));
collection.AddAssetCreator(leaderboard::CreateLoader(memory, searchPath));
// collection.AddAssetCreator(std::make_unique<AssetLoaderStructuredDataDef>(memory));
// collection.AddAssetCreator(std::make_unique<AssetLoaderTracer>(memory));
// collection.AddAssetCreator(std::make_unique<AssetLoaderVehicle>(memory));

View File

@@ -81,10 +81,10 @@ namespace
};
} // namespace
namespace IW5
namespace IW5::raw_file
{
std::unique_ptr<AssetCreator<AssetRawFile>> CreateRawFileLoader(MemoryManager& memory, ISearchPath& searchPath)
std::unique_ptr<AssetCreator<AssetRawFile>> CreateLoader(MemoryManager& memory, ISearchPath& searchPath)
{
return std::make_unique<RawFileLoader>(memory, searchPath);
}
} // namespace IW5
} // namespace IW5::raw_file

View File

@@ -7,7 +7,7 @@
#include <memory>
namespace IW5
namespace IW5::raw_file
{
std::unique_ptr<AssetCreator<AssetRawFile>> CreateRawFileLoader(MemoryManager& memory, ISearchPath& searchPath);
} // namespace IW5
std::unique_ptr<AssetCreator<AssetRawFile>> CreateLoader(MemoryManager& memory, ISearchPath& searchPath);
} // namespace IW5::raw_file

View File

@@ -79,10 +79,10 @@ namespace
};
} // namespace
namespace IW5
namespace IW5::script
{
std::unique_ptr<AssetCreator<AssetScript>> CreateScriptLoader(MemoryManager& memory, ISearchPath& searchPath)
std::unique_ptr<AssetCreator<AssetScript>> CreateLoader(MemoryManager& memory, ISearchPath& searchPath)
{
return std::make_unique<ScriptLoader>(memory, searchPath);
}
} // namespace IW5
} // namespace IW5::script

View File

@@ -7,7 +7,7 @@
#include <memory>
namespace IW5
namespace IW5::script
{
std::unique_ptr<AssetCreator<AssetScript>> CreateScriptLoader(MemoryManager& memory, ISearchPath& searchPath);
} // namespace IW5
std::unique_ptr<AssetCreator<AssetScript>> CreateLoader(MemoryManager& memory, ISearchPath& searchPath);
} // namespace IW5::script

View File

@@ -10,6 +10,7 @@
#include <cstring>
using namespace IW5;
using namespace ::string_table;
namespace
{
@@ -28,7 +29,7 @@ namespace
if (!file.IsOpen())
return AssetCreationResult::NoAction();
string_table::StringTableLoaderV2<StringTable, Common::StringTable_HashString> loader;
StringTableLoaderV2<StringTable, Common::StringTable_HashString> loader;
auto* stringTable = loader.LoadFromStream(assetName, m_memory, *file.m_stream);
return AssetCreationResult::Success(context.AddAsset<AssetStringTable>(assetName, stringTable));
@@ -40,10 +41,10 @@ namespace
};
} // namespace
namespace IW5
namespace IW5::string_table
{
std::unique_ptr<AssetCreator<AssetStringTable>> CreateStringTableLoader(MemoryManager& memory, ISearchPath& searchPath)
std::unique_ptr<AssetCreator<AssetStringTable>> CreateLoader(MemoryManager& memory, ISearchPath& searchPath)
{
return std::make_unique<StringTableLoader>(memory, searchPath);
}
} // namespace IW5
} // namespace IW5::string_table

View File

@@ -7,7 +7,7 @@
#include <memory>
namespace IW5
namespace IW5::string_table
{
std::unique_ptr<AssetCreator<AssetStringTable>> CreateStringTableLoader(MemoryManager& memory, ISearchPath& searchPath);
} // namespace IW5
std::unique_ptr<AssetCreator<AssetStringTable>> CreateLoader(MemoryManager& memory, ISearchPath& searchPath);
} // namespace IW5::string_table

View File

@@ -40,14 +40,14 @@ namespace
private:
IGdtQueryable& m_gdt;
InfoStringLoaderWeapon m_info_string_loader;
IW5::weapon::InfoStringLoader m_info_string_loader;
};
} // namespace
namespace IW5
namespace IW5::weapon
{
std::unique_ptr<AssetCreator<AssetWeapon>> CreateGdtWeaponLoader(MemoryManager& memory, ISearchPath& searchPath, IGdtQueryable& gdt, Zone& zone)
std::unique_ptr<AssetCreator<AssetWeapon>> CreateGdtLoader(MemoryManager& memory, ISearchPath& searchPath, IGdtQueryable& gdt, Zone& zone)
{
return std::make_unique<GdtLoaderWeapon>(memory, searchPath, gdt, zone);
}
} // namespace IW5
} // namespace IW5::weapon

View File

@@ -8,7 +8,7 @@
#include <memory>
namespace IW5
namespace IW5::weapon
{
std::unique_ptr<AssetCreator<AssetWeapon>> CreateGdtWeaponLoader(MemoryManager& memory, ISearchPath& searchPath, IGdtQueryable& gdt, Zone& zone);
} // namespace IW5
std::unique_ptr<AssetCreator<AssetWeapon>> CreateGdtLoader(MemoryManager& memory, ISearchPath& searchPath, IGdtQueryable& gdt, Zone& zone);
} // namespace IW5::weapon

View File

@@ -856,33 +856,36 @@ namespace
}
} // namespace
InfoStringLoaderWeapon::InfoStringLoaderWeapon(MemoryManager& memory, ISearchPath& searchPath, Zone& zone)
: m_memory(memory),
m_search_path(searchPath),
m_zone(zone)
namespace IW5::weapon
{
}
AssetCreationResult InfoStringLoaderWeapon::CreateAsset(const std::string& assetName, const InfoString& infoString, AssetCreationContext& context) const
{
auto* weaponFullDef = m_memory.Alloc<WeaponFullDef>();
InitWeaponFullDef(*weaponFullDef);
weaponFullDef->weapCompleteDef.szInternalName = m_memory.Dup(assetName.c_str());
AssetRegistration<AssetWeapon> registration(assetName, &weaponFullDef->weapCompleteDef);
InfoStringToWeaponConverter converter(
infoString, *weaponFullDef, m_zone.m_script_strings, m_memory, context, registration, weapon_fields, std::extent_v<decltype(weapon_fields)>);
if (!converter.Convert())
InfoStringLoader::InfoStringLoader(MemoryManager& memory, ISearchPath& searchPath, Zone& zone)
: m_memory(memory),
m_search_path(searchPath),
m_zone(zone)
{
std::cerr << std::format("Failed to parse weapon: \"{}\"\n", assetName);
return AssetCreationResult::Failure();
}
CalculateWeaponFields(*weaponFullDef, m_memory);
AssetCreationResult InfoStringLoader::CreateAsset(const std::string& assetName, const InfoString& infoString, AssetCreationContext& context) const
{
auto* weaponFullDef = m_memory.Alloc<WeaponFullDef>();
LoadAccuracyGraphs(*weaponFullDef, m_memory, m_search_path, context);
InitWeaponFullDef(*weaponFullDef);
weaponFullDef->weapCompleteDef.szInternalName = m_memory.Dup(assetName.c_str());
return AssetCreationResult::Success(context.AddAsset(std::move(registration)));
}
AssetRegistration<AssetWeapon> registration(assetName, &weaponFullDef->weapCompleteDef);
InfoStringToWeaponConverter converter(
infoString, *weaponFullDef, m_zone.m_script_strings, m_memory, context, registration, weapon_fields, std::extent_v<decltype(weapon_fields)>);
if (!converter.Convert())
{
std::cerr << std::format("Failed to parse weapon: \"{}\"\n", assetName);
return AssetCreationResult::Failure();
}
CalculateWeaponFields(*weaponFullDef, m_memory);
LoadAccuracyGraphs(*weaponFullDef, m_memory, m_search_path, context);
return AssetCreationResult::Success(context.AddAsset(std::move(registration)));
}
} // namespace IW5::weapon

View File

@@ -4,12 +4,12 @@
#include "Asset/AssetCreationResult.h"
#include "InfoString/InfoString.h"
namespace IW5
namespace IW5::weapon
{
class InfoStringLoaderWeapon
class InfoStringLoader
{
public:
InfoStringLoaderWeapon(MemoryManager& memory, ISearchPath& searchPath, Zone& zone);
InfoStringLoader(MemoryManager& memory, ISearchPath& searchPath, Zone& zone);
AssetCreationResult CreateAsset(const std::string& assetName, const InfoString& infoString, AssetCreationContext& context) const;
@@ -18,4 +18,4 @@ namespace IW5
ISearchPath& m_search_path;
Zone& m_zone;
};
} // namespace IW5
} // namespace IW5::weapon

View File

@@ -1,645 +0,0 @@
#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, AssetCreationContext& context, AssetRegistration<AssetAttachment>& registration)
: m_stream(stream),
m_memory(memory),
m_context(context),
m_registration(registration)
{
}
bool Load(WeaponAttachment& attachment) const
{
try
{
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);
}
catch (const json::exception& e)
{
std::cerr << std::format("Failed to parse json of attachment: {}\n", e.what());
}
return false;
}
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 = m_memory.Alloc<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 = m_context.LoadDependency<AssetTracer>(assetName);
if (!tracer)
{
PrintError(attachment, std::format("Could not find tracer {}", assetName));
return false;
}
m_registration.AddDependency(tracer);
tracerPtr = tracer->Asset();
return true;
}
bool CreateMaterialFromJson(const std::string& assetName, Material*& materialPtr, const WeaponAttachment& attachment) const
{
auto* material = m_context.LoadDependency<AssetMaterial>(assetName);
if (!material)
{
PrintError(attachment, std::format("Could not find material {}", assetName));
return false;
}
m_registration.AddDependency(material);
materialPtr = material->Asset();
return true;
}
bool CreateFxFromJson(const std::string& assetName, FxEffectDef*& fxPtr, const WeaponAttachment& attachment) const
{
auto* fx = m_context.LoadDependency<AssetFx>(assetName);
if (!fx)
{
PrintError(attachment, std::format("Could not find fx {}", assetName));
return false;
}
m_registration.AddDependency(fx);
fxPtr = fx->Asset();
return true;
}
bool CreateSoundFromJson(const std::string& assetName, SndAliasCustom& sndAliasCustom, const WeaponAttachment& attachment) const
{
m_registration.AddIndirectAssetReference(m_context.LoadIndirectAssetReference<AssetSound>(assetName));
sndAliasCustom.name = m_memory.Alloc<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 = m_context.LoadDependency<AssetXModel>(assetName);
if (!xmodel)
{
PrintError(attachment, std::format("Could not find xmodel {}", assetName));
return false;
}
m_registration.AddDependency(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 = m_memory.Alloc<XModel*>(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;
AssetCreationContext& m_context;
AssetRegistration<AssetAttachment>& m_registration;
};
} // namespace
namespace IW5
{
bool LoadWeaponAttachmentAsJson(std::istream& stream,
WeaponAttachment& attachment,
MemoryManager& memory,
AssetCreationContext& context,
AssetRegistration<AssetAttachment>& registration)
{
const JsonLoader loader(stream, memory, context, registration);
return loader.Load(attachment);
}
} // namespace IW5

View File

@@ -1,17 +0,0 @@
#pragma once
#include "Asset/AssetCreationContext.h"
#include "Asset/AssetRegistration.h"
#include "Game/IW5/IW5.h"
#include "Utils/MemoryManager.h"
#include <istream>
namespace IW5
{
bool LoadWeaponAttachmentAsJson(std::istream& stream,
WeaponAttachment& attachment,
MemoryManager& memory,
AssetCreationContext& context,
AssetRegistration<AssetAttachment>& registration);
} // namespace IW5

View File

@@ -1,16 +1,637 @@
#include "LoaderAttachmentIW5.h"
#include "Game/IW5/IW5.h"
#include "JsonWeaponAttachmentLoader.h"
#include "Game/IW5/Weapon/JsonWeaponAttachment.h"
#include "Weapon/AttachmentCommon.h"
#include <cstring>
#include <format>
#include <iostream>
#include <nlohmann/json.hpp>
using namespace nlohmann;
using namespace IW5;
using namespace ::attachment;
namespace
{
class JsonLoader
{
public:
JsonLoader(std::istream& stream, MemoryManager& memory, AssetCreationContext& context, AssetRegistration<AssetAttachment>& registration)
: m_stream(stream),
m_memory(memory),
m_context(context),
m_registration(registration)
{
}
bool Load(WeaponAttachment& attachment) const
{
try
{
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);
}
catch (const json::exception& e)
{
std::cerr << std::format("Failed to parse json of attachment: {}\n", e.what());
}
return false;
}
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 = m_memory.Alloc<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 = m_context.LoadDependency<AssetTracer>(assetName);
if (!tracer)
{
PrintError(attachment, std::format("Could not find tracer {}", assetName));
return false;
}
m_registration.AddDependency(tracer);
tracerPtr = tracer->Asset();
return true;
}
bool CreateMaterialFromJson(const std::string& assetName, Material*& materialPtr, const WeaponAttachment& attachment) const
{
auto* material = m_context.LoadDependency<AssetMaterial>(assetName);
if (!material)
{
PrintError(attachment, std::format("Could not find material {}", assetName));
return false;
}
m_registration.AddDependency(material);
materialPtr = material->Asset();
return true;
}
bool CreateFxFromJson(const std::string& assetName, FxEffectDef*& fxPtr, const WeaponAttachment& attachment) const
{
auto* fx = m_context.LoadDependency<AssetFx>(assetName);
if (!fx)
{
PrintError(attachment, std::format("Could not find fx {}", assetName));
return false;
}
m_registration.AddDependency(fx);
fxPtr = fx->Asset();
return true;
}
bool CreateSoundFromJson(const std::string& assetName, SndAliasCustom& sndAliasCustom, const WeaponAttachment& attachment) const
{
m_registration.AddIndirectAssetReference(m_context.LoadIndirectAssetReference<AssetSound>(assetName));
sndAliasCustom.name = m_memory.Alloc<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 = m_context.LoadDependency<AssetXModel>(assetName);
if (!xmodel)
{
PrintError(attachment, std::format("Could not find xmodel {}", assetName));
return false;
}
m_registration.AddDependency(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 = m_memory.Alloc<XModel*>(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;
AssetCreationContext& m_context;
AssetRegistration<AssetAttachment>& m_registration;
};
class AttachmentLoader final : public AssetCreator<AssetAttachment>
{
public:
@@ -22,7 +643,7 @@ namespace
AssetCreationResult CreateAsset(const std::string& assetName, AssetCreationContext& context) override
{
const auto file = m_search_path.Open(std::format("attachment/{}.json", assetName));
const auto file = m_search_path.Open(GetJsonFileNameForAssetName(assetName));
if (!file.IsOpen())
return AssetCreationResult::NoAction();
@@ -30,7 +651,8 @@ namespace
attachment->szInternalName = m_memory.Dup(assetName.c_str());
AssetRegistration<AssetAttachment> registration(assetName, attachment);
if (!LoadWeaponAttachmentAsJson(*file.m_stream, *attachment, m_memory, context, registration))
const JsonLoader loader(*file.m_stream, m_memory, context, registration);
if (!loader.Load(*attachment))
{
std::cerr << std::format("Failed to load attachment \"{}\"\n", assetName);
return AssetCreationResult::Failure();
@@ -45,10 +667,10 @@ namespace
};
} // namespace
namespace IW5
namespace IW5::attachment
{
std::unique_ptr<AssetCreator<AssetAttachment>> CreateAttachmentLoader(MemoryManager& memory, ISearchPath& searchPath)
std::unique_ptr<AssetCreator<AssetAttachment>> CreateLoader(MemoryManager& memory, ISearchPath& searchPath)
{
return std::make_unique<AttachmentLoader>(memory, searchPath);
}
} // namespace IW5
} // namespace IW5::attachment

View File

@@ -7,7 +7,7 @@
#include <memory>
namespace IW5
namespace IW5::attachment
{
std::unique_ptr<AssetCreator<AssetAttachment>> CreateAttachmentLoader(MemoryManager& memory, ISearchPath& searchPath);
} // namespace IW5
std::unique_ptr<AssetCreator<AssetAttachment>> CreateLoader(MemoryManager& memory, ISearchPath& searchPath);
} // namespace IW5::attachment

View File

@@ -4,12 +4,14 @@
#include "Game/IW5/ObjConstantsIW5.h"
#include "InfoString/InfoString.h"
#include "InfoStringLoaderWeaponIW5.h"
#include "Weapon/WeaponCommon.h"
#include <cstring>
#include <format>
#include <iostream>
using namespace IW5;
using namespace ::weapon;
namespace
{
@@ -24,7 +26,7 @@ namespace
AssetCreationResult CreateAsset(const std::string& assetName, AssetCreationContext& context) override
{
const auto fileName = std::format("weapons/{}", assetName);
const auto fileName = GetFileNameForAssetName(assetName);
const auto file = m_search_path.Open(fileName);
if (!file.IsOpen())
return AssetCreationResult::NoAction();
@@ -41,14 +43,14 @@ namespace
private:
ISearchPath& m_search_path;
InfoStringLoaderWeapon m_info_string_loader;
IW5::weapon::InfoStringLoader m_info_string_loader;
};
} // namespace
namespace IW5
namespace IW5::weapon
{
std::unique_ptr<AssetCreator<AssetWeapon>> CreateRawWeaponLoader(MemoryManager& memory, ISearchPath& searchPath, Zone& zone)
std::unique_ptr<AssetCreator<AssetWeapon>> CreateRawLoader(MemoryManager& memory, ISearchPath& searchPath, Zone& zone)
{
return std::make_unique<RawLoaderWeapon>(memory, searchPath, zone);
}
} // namespace IW5
} // namespace IW5::weapon

View File

@@ -7,7 +7,7 @@
#include <memory>
namespace IW5
namespace IW5::weapon
{
std::unique_ptr<AssetCreator<AssetWeapon>> CreateRawWeaponLoader(MemoryManager& memory, ISearchPath& searchPath, Zone& zone);
} // namespace IW5
std::unique_ptr<AssetCreator<AssetWeapon>> CreateRawLoader(MemoryManager& memory, ISearchPath& searchPath, Zone& zone);
} // namespace IW5::weapon

View File

@@ -26,7 +26,7 @@ namespace
IgnoredAssetLookup ignoredAssetLookup;
AssetCreationContext context(zone, &creatorCollection, &ignoredAssetLookup);
auto loader = CreateStringTableLoader(memory, searchPath);
auto loader = string_table::CreateLoader(memory, searchPath);
auto result = loader->CreateAsset("mp/cooltable.csv", context);
REQUIRE(result.HasBeenSuccessful());

View File

@@ -313,7 +313,7 @@ namespace
GivenImage("me_metal_rusty02_col", context, memory);
GivenTechset("wc_l_sm_r0c0n0s0", context, memory);
auto loader = CreateMaterialLoader(memory, searchPath);
auto loader = material::CreateLoader(memory, searchPath);
auto result = loader->CreateAsset("wc/me_metal_rust_02", context);
REQUIRE(result.HasBeenSuccessful());