Custom weapon cac stuff + fixes
This commit is contained in:
parent
d5cbad5c81
commit
f01aa5ba59
336
data/cdata/scripts/mp_patches/custom_weapons.gsc
Normal file
336
data/cdata/scripts/mp_patches/custom_weapons.gsc
Normal file
@ -0,0 +1,336 @@
|
|||||||
|
main()
|
||||||
|
{
|
||||||
|
replacefunc(maps\mp\gametypes\_class::isvalidprimary, ::isvalidprimary);
|
||||||
|
replacefunc(maps\mp\gametypes\_class::isvalidsecondary, ::isvalidsecondary);
|
||||||
|
replacefunc(maps\mp\gametypes\_class::isvalidweapon, ::isvalidweapon);
|
||||||
|
replacefunc(maps\mp\gametypes\_class::buildweaponname, ::buildweaponname);
|
||||||
|
replacefunc(maps\mp\gametypes\_weapons::watchweaponchange, ::watchweaponchange);
|
||||||
|
}
|
||||||
|
|
||||||
|
find_in_table(csv, weap)
|
||||||
|
{
|
||||||
|
rows = tablelookuprownum(csv);
|
||||||
|
for (i = 0; i < rows; i++)
|
||||||
|
{
|
||||||
|
if (tablelookup(csv, i, 0) == weap)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
is_custom_weapon(weap)
|
||||||
|
{
|
||||||
|
return find_in_table("mp/customweapons.csv", weap);
|
||||||
|
}
|
||||||
|
|
||||||
|
watchweaponchange()
|
||||||
|
{
|
||||||
|
self endon("death");
|
||||||
|
self endon("disconnect");
|
||||||
|
self endon("faux_spawn");
|
||||||
|
thread maps\mp\gametypes\_weapons::watchstartweaponchange();
|
||||||
|
self.lastdroppableweapon = self.currentweaponatspawn;
|
||||||
|
self.hitsthismag = [];
|
||||||
|
var_0 = self getcurrentweapon();
|
||||||
|
|
||||||
|
if (maps\mp\_utility::iscacprimaryweapon(var_0) && !isdefined(self.hitsthismag[var_0]))
|
||||||
|
{
|
||||||
|
self.hitsthismag[var_0] = weaponclipsize(var_0);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.bothbarrels = undefined;
|
||||||
|
|
||||||
|
if (issubstr(var_0, "ranger"))
|
||||||
|
{
|
||||||
|
thread maps\mp\gametypes\_weapons::watchrangerusage(var_0);
|
||||||
|
}
|
||||||
|
|
||||||
|
var_1 = 1;
|
||||||
|
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
if (!var_1)
|
||||||
|
{
|
||||||
|
self waittill("weapon_change");
|
||||||
|
}
|
||||||
|
|
||||||
|
var_1 = 0;
|
||||||
|
var_0 = self getcurrentweapon();
|
||||||
|
|
||||||
|
if (var_0 == "none")
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var_2 = getweaponattachments(var_0);
|
||||||
|
self.has_opticsthermal = 0;
|
||||||
|
self.has_target_enhancer = 0;
|
||||||
|
self.has_stock = 0;
|
||||||
|
self.has_laser = 0;
|
||||||
|
|
||||||
|
if (isdefined(var_2))
|
||||||
|
{
|
||||||
|
foreach (var_4 in var_2)
|
||||||
|
{
|
||||||
|
if (var_4 == "opticstargetenhancer")
|
||||||
|
{
|
||||||
|
self.has_target_enhancer = 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (var_4 == "stock")
|
||||||
|
{
|
||||||
|
self.has_stock = 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (var_4 == "lasersight")
|
||||||
|
{
|
||||||
|
self.has_laser = 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (issubstr(var_4, "opticsthermal"))
|
||||||
|
{
|
||||||
|
self.has_opticsthermal = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (maps\mp\_utility::isbombsiteweapon(var_0))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var_6 = maps\mp\_utility::getweaponnametokens(var_0);
|
||||||
|
self.bothbarrels = undefined;
|
||||||
|
|
||||||
|
if (issubstr(var_0, "ranger"))
|
||||||
|
{
|
||||||
|
thread maps\mp\gametypes\_weapons::watchrangerusage(var_0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (var_6[0] == "alt")
|
||||||
|
{
|
||||||
|
var_7 = getsubstr(var_0, 4);
|
||||||
|
var_0 = var_7;
|
||||||
|
var_6 = maps\mp\_utility::getweaponnametokens(var_0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (var_0 != "none" && var_6[0] != "iw5" && var_6[0] != "iw6" && var_6[0] != "h1" && var_6[0] != "h2")
|
||||||
|
{
|
||||||
|
if (maps\mp\_utility::iscacprimaryweapon(var_0) && !isdefined(self.hitsthismag[var_0 + "_mp"]))
|
||||||
|
{
|
||||||
|
self.hitsthismag[var_0 + "_mp"] = weaponclipsize(var_0 + "_mp");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (var_0 != "none" && (var_6[0] == "iw5" || var_6[0] == "iw6" || var_6[0] == "h1" || var_6[0] == "h2"))
|
||||||
|
{
|
||||||
|
if (maps\mp\_utility::iscacprimaryweapon(var_0) && !isdefined(self.hitsthismag[var_0]))
|
||||||
|
{
|
||||||
|
self.hitsthismag[var_0] = weaponclipsize(var_0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (maps\mp\gametypes\_weapons::maydropweapon(var_0))
|
||||||
|
{
|
||||||
|
self.lastdroppableweapon = var_0;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.changingweapon = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buildweaponname(var_0, var_1, var_2, var_3, var_4, var_5)
|
||||||
|
{
|
||||||
|
if (!isdefined(var_0) || var_0 == "none" || var_0 == "")
|
||||||
|
{
|
||||||
|
return var_0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isdefined(level.lettertonumber))
|
||||||
|
{
|
||||||
|
level.lettertonumber = maps\mp\gametypes\_class::makeletterstonumbers();
|
||||||
|
}
|
||||||
|
|
||||||
|
var_6 = "";
|
||||||
|
|
||||||
|
if (issubstr(var_0, "iw5_") || issubstr(var_0, "h1_") || issubstr(var_0, "h2_"))
|
||||||
|
{
|
||||||
|
var_7 = var_0 + "_mp";
|
||||||
|
var_8 = var_0.size;
|
||||||
|
|
||||||
|
if (issubstr(var_0, "h1_") || issubstr(var_0, "h2_"))
|
||||||
|
{
|
||||||
|
var_6 = getsubstr(var_0, 3, var_8);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var_6 = getsubstr(var_0, 4, var_8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var_7 = var_0;
|
||||||
|
var_6 = var_0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (var_7 == "h1_junsho_mp")
|
||||||
|
{
|
||||||
|
var_1 = "akimbohidden";
|
||||||
|
}
|
||||||
|
|
||||||
|
var_9 = isdefined(var_1) && var_1 != "none";
|
||||||
|
var_10 = isdefined(var_2) && var_2 != "none";
|
||||||
|
|
||||||
|
if (!var_10)
|
||||||
|
{
|
||||||
|
var_11 = tablelookuprownum("mp/furniturekits/base.csv", 0, var_7);
|
||||||
|
|
||||||
|
if (var_11 >= 0)
|
||||||
|
{
|
||||||
|
var_2 = "base";
|
||||||
|
var_10 = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (issubstr(var_0, "h2_"))
|
||||||
|
{
|
||||||
|
if (var_9)
|
||||||
|
{
|
||||||
|
var_7 += "_" + var_1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (var_9 || var_10)
|
||||||
|
{
|
||||||
|
if (!var_9)
|
||||||
|
var_1 = "none";
|
||||||
|
|
||||||
|
if (!var_10)
|
||||||
|
var_2 = "base";
|
||||||
|
|
||||||
|
var_7 += ("_a#" + var_1);
|
||||||
|
var_7 += ("_f#" + var_2);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (issubstr(var_7, "iw5_") || issubstr(var_7, "h1_") || issubstr(var_7, "h2_"))
|
||||||
|
{
|
||||||
|
var_7 = maps\mp\gametypes\_class::buildweaponnamereticle(var_7, var_4);
|
||||||
|
var_7 = maps\mp\gametypes\_class::buildweaponnameemblem(var_7, var_5);
|
||||||
|
var_7 = maps\mp\gametypes\_class::buildweaponnamecamo(var_7, var_3);
|
||||||
|
return var_7;
|
||||||
|
}
|
||||||
|
else if (!isvalidweapon(var_7 + "_mp"))
|
||||||
|
{
|
||||||
|
return var_0 + "_mp";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var_7 = maps\mp\gametypes\_class::buildweaponnamereticle(var_7, var_4);
|
||||||
|
var_7 = maps\mp\gametypes\_class::buildweaponnameemblem(var_7, var_5);
|
||||||
|
var_7 = maps\mp\gametypes\_class::buildweaponnamecamo(var_7, var_3);
|
||||||
|
return var_7 + "_mp";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
isvalidweapon(var_0, var_1)
|
||||||
|
{
|
||||||
|
if (!isdefined(level.weaponrefs))
|
||||||
|
{
|
||||||
|
level.weaponrefs = [];
|
||||||
|
|
||||||
|
foreach (var_3 in level.weaponlist)
|
||||||
|
{
|
||||||
|
level.weaponrefs[var_3] = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isdefined(level.weaponrefs[var_0]))
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
isvalidsecondary(var_0, var_1)
|
||||||
|
{
|
||||||
|
if (is_custom_weapon(var_0))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (var_0)
|
||||||
|
{
|
||||||
|
case "none":
|
||||||
|
case "h1_beretta":
|
||||||
|
case "h1_colt45":
|
||||||
|
case "h1_deserteagle":
|
||||||
|
case "h1_deserteagle55":
|
||||||
|
case "h1_usp":
|
||||||
|
case "h1_janpst":
|
||||||
|
case "h1_aprpst":
|
||||||
|
case "h1_augpst":
|
||||||
|
case "h1_rpg":
|
||||||
|
return 1;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
isvalidprimary(var_0, var_1)
|
||||||
|
{
|
||||||
|
if (is_custom_weapon(var_0))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (var_0)
|
||||||
|
{
|
||||||
|
case "h1_ak47":
|
||||||
|
case "h1_g3":
|
||||||
|
case "h1_g36c":
|
||||||
|
case "h1_m14":
|
||||||
|
case "h1_m16":
|
||||||
|
case "h1_m4":
|
||||||
|
case "h1_mp44":
|
||||||
|
case "h1_xmlar":
|
||||||
|
case "h1_aprast":
|
||||||
|
case "h1_augast":
|
||||||
|
case "h1_ak74u":
|
||||||
|
case "h1_mp5":
|
||||||
|
case "h1_p90":
|
||||||
|
case "h1_skorpion":
|
||||||
|
case "h1_uzi":
|
||||||
|
case "h1_febsmg":
|
||||||
|
case "h1_aprsmg":
|
||||||
|
case "h1_augsmg":
|
||||||
|
case "h1_m1014":
|
||||||
|
case "h1_winchester1200":
|
||||||
|
case "h1_kam12":
|
||||||
|
case "h1_junsho":
|
||||||
|
case "h1_m60e4":
|
||||||
|
case "h1_rpd":
|
||||||
|
case "h1_saw":
|
||||||
|
case "h1_feblmg":
|
||||||
|
case "h1_junlmg":
|
||||||
|
case "h1_barrett":
|
||||||
|
case "h1_dragunov":
|
||||||
|
case "h1_m21":
|
||||||
|
case "h1_m40a3":
|
||||||
|
case "h1_remington700":
|
||||||
|
case "h1_febsnp":
|
||||||
|
case "h1_junsnp":
|
||||||
|
return 1;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
26
data/cdata/ui_scripts/custom_weapons/__init__.lua
Normal file
26
data/cdata/ui_scripts/custom_weapons/__init__.lua
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
if (not Engine.InFrontend()) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local cols = {
|
||||||
|
name = 0,
|
||||||
|
class = 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
local csv = "mp/customWeapons.csv"
|
||||||
|
local rows = Engine.TableGetRowCount(csv)
|
||||||
|
for i = 0, rows do
|
||||||
|
local weap = Engine.TableLookupByRow(csv, i, cols.name)
|
||||||
|
local class = Engine.TableLookupByRow(csv, i, cols.class)
|
||||||
|
if (type(Cac.Weapons.Primary[class]) == "table") then
|
||||||
|
table.insert(Cac.Weapons.Primary[class], {
|
||||||
|
weap,
|
||||||
|
0
|
||||||
|
})
|
||||||
|
elseif (type(Cac.Weapons.Secondary[class]) == "table") then
|
||||||
|
table.insert(Cac.Weapons.Secondary[class], {
|
||||||
|
weap,
|
||||||
|
0
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
@ -346,6 +346,8 @@ namespace fastfiles
|
|||||||
|
|
||||||
// code_pre_gfx
|
// code_pre_gfx
|
||||||
|
|
||||||
|
try_load_zone("h1_mod_code_pre_gfx", true);
|
||||||
|
|
||||||
game::DB_LoadXAssets(data.data(), static_cast<std::uint32_t>(data.size()), syncMode);
|
game::DB_LoadXAssets(data.data(), static_cast<std::uint32_t>(data.size()), syncMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,6 +82,27 @@ namespace gsc
|
|||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <size_t rva>
|
||||||
|
void safe_func()
|
||||||
|
{
|
||||||
|
static utils::hook::detour hook;
|
||||||
|
|
||||||
|
const auto stub = []()
|
||||||
|
{
|
||||||
|
__try
|
||||||
|
{
|
||||||
|
hook.invoke<void>();
|
||||||
|
}
|
||||||
|
__except (EXCEPTION_EXECUTE_HANDLER)
|
||||||
|
{
|
||||||
|
game::Scr_ErrorInternal();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto ptr = rva + 0_b;
|
||||||
|
hook.create(ptr, stub);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<std::pair<std::string, std::string>> find_function(const char* pos)
|
std::optional<std::pair<std::string, std::string>> find_function(const char* pos)
|
||||||
@ -111,6 +132,8 @@ namespace gsc
|
|||||||
utils::hook::call(SELECT_VALUE(0x3BD626_b, 0x504606_b), unknown_function_stub); // CompileError (LinkFile)
|
utils::hook::call(SELECT_VALUE(0x3BD626_b, 0x504606_b), unknown_function_stub); // CompileError (LinkFile)
|
||||||
utils::hook::call(SELECT_VALUE(0x3BD672_b, 0x504652_b), unknown_function_stub); // ^
|
utils::hook::call(SELECT_VALUE(0x3BD672_b, 0x504652_b), unknown_function_stub); // ^
|
||||||
utils::hook::call(SELECT_VALUE(0x3BD75A_b, 0x50473A_b), find_variable_stub); // Scr_EmitFunction
|
utils::hook::call(SELECT_VALUE(0x3BD75A_b, 0x50473A_b), find_variable_stub); // Scr_EmitFunction
|
||||||
|
|
||||||
|
safe_func<0xBA7A0>(); // fix vlobby cac crash
|
||||||
}
|
}
|
||||||
|
|
||||||
void pre_destroy() override
|
void pre_destroy() override
|
||||||
|
@ -109,18 +109,20 @@ namespace gsc
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool force_load = false;
|
||||||
|
|
||||||
game::ScriptFile* load_custom_script(const char* file_name, const std::string& real_name)
|
game::ScriptFile* load_custom_script(const char* file_name, const std::string& real_name)
|
||||||
{
|
{
|
||||||
if (game::VirtualLobby_Loaded())
|
|
||||||
{
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (loaded_scripts.contains(real_name))
|
if (loaded_scripts.contains(real_name))
|
||||||
{
|
{
|
||||||
return loaded_scripts[real_name];
|
return loaded_scripts[real_name];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (game::VirtualLobby_Loaded() && !force_load)
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
std::string source_buffer;
|
std::string source_buffer;
|
||||||
if (!read_scriptfile(real_name + ".gsc", &source_buffer) || source_buffer.empty())
|
if (!read_scriptfile(real_name + ".gsc", &source_buffer) || source_buffer.empty())
|
||||||
{
|
{
|
||||||
@ -283,21 +285,27 @@ namespace gsc
|
|||||||
{
|
{
|
||||||
utils::hook::invoke<void>(SELECT_VALUE(0x2B9DA0_b, 0x18BC00_b), a1, a2);
|
utils::hook::invoke<void>(SELECT_VALUE(0x2B9DA0_b, 0x18BC00_b), a1, a2);
|
||||||
|
|
||||||
if (game::VirtualLobby_Loaded())
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const auto& path : filesystem::get_search_paths())
|
for (const auto& path : filesystem::get_search_paths())
|
||||||
{
|
{
|
||||||
load_scripts(path, "scripts/");
|
|
||||||
if (game::environment::is_sp())
|
if (game::environment::is_sp())
|
||||||
{
|
{
|
||||||
load_scripts(path, "scripts/sp/");
|
load_scripts(path, "scripts/sp/");
|
||||||
|
load_scripts(path, "scripts/");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
load_scripts(path, "scripts/mp/");
|
if (!game::VirtualLobby_Loaded())
|
||||||
|
{
|
||||||
|
load_scripts(path, "scripts/mp/");
|
||||||
|
load_scripts(path, "scripts/");
|
||||||
|
}
|
||||||
|
|
||||||
|
force_load = true;
|
||||||
|
const auto _0 = gsl::finally([&]
|
||||||
|
{
|
||||||
|
force_load = false;
|
||||||
|
});
|
||||||
|
load_scripts(path, "scripts/mp_patches/");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -97,20 +97,16 @@ namespace scripting
|
|||||||
game::G_LogPrintf("InitGame\n");
|
game::G_LogPrintf("InitGame\n");
|
||||||
|
|
||||||
lua::engine::start();
|
lua::engine::start();
|
||||||
|
|
||||||
gsc::load_main_handles();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gsc::load_main_handles();
|
||||||
|
|
||||||
g_load_structs_hook.invoke<void>();
|
g_load_structs_hook.invoke<void>();
|
||||||
}
|
}
|
||||||
|
|
||||||
void scr_load_level_stub()
|
void scr_load_level_stub()
|
||||||
{
|
{
|
||||||
if (!game::VirtualLobby_Loaded())
|
gsc::load_init_handles();
|
||||||
{
|
|
||||||
gsc::load_init_handles();
|
|
||||||
}
|
|
||||||
|
|
||||||
scr_load_level_hook.invoke<void>();
|
scr_load_level_hook.invoke<void>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -379,7 +379,26 @@ namespace ui_scripting
|
|||||||
|
|
||||||
setup_functions();
|
setup_functions();
|
||||||
|
|
||||||
lua["print"] = function(reinterpret_cast<game::hks::lua_function>(SELECT_VALUE(0x93490_b, 0x209EB0_b)));
|
lua["print"] = [](const variadic_args& va)
|
||||||
|
{
|
||||||
|
std::string buffer{};
|
||||||
|
const auto to_string = get_globals()["tostring"];
|
||||||
|
|
||||||
|
for (auto i = 0; i < va.size(); i++)
|
||||||
|
{
|
||||||
|
const auto& arg = va[i];
|
||||||
|
const auto str = to_string(arg)[0].as<std::string>();
|
||||||
|
buffer.append(str);
|
||||||
|
|
||||||
|
if (i < va.size() - 1)
|
||||||
|
{
|
||||||
|
buffer.append("\t");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console::info("%s\n", buffer.data());
|
||||||
|
};
|
||||||
|
|
||||||
lua["table"]["unpack"] = lua["unpack"];
|
lua["table"]["unpack"] = lua["unpack"];
|
||||||
lua["luiglobals"] = lua;
|
lua["luiglobals"] = lua;
|
||||||
|
|
||||||
|
@ -116,6 +116,122 @@ namespace weapon
|
|||||||
{
|
{
|
||||||
set_weapon_field<bool>(weapon_name, field, value);
|
set_weapon_field<bool>(weapon_name, field, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int compare_hash(const void* a, const void* b)
|
||||||
|
{
|
||||||
|
const auto hash_a = reinterpret_cast<game::DDLHash*>(
|
||||||
|
reinterpret_cast<size_t>(a))->hash;
|
||||||
|
const auto hash_b = reinterpret_cast<game::DDLHash*>(
|
||||||
|
reinterpret_cast<size_t>(b))->hash;
|
||||||
|
|
||||||
|
if (hash_a < hash_b)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
else if (hash_a > hash_b)
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
utils::memory::allocator ddl_allocator;
|
||||||
|
|
||||||
|
std::vector<const char*> get_stringtable_entries(const std::string& name)
|
||||||
|
{
|
||||||
|
std::vector<const char*> entries;
|
||||||
|
|
||||||
|
const auto string_table = game::DB_FindXAssetHeader(
|
||||||
|
game::ASSET_TYPE_STRINGTABLE, name.data(), false).stringTable;
|
||||||
|
|
||||||
|
if (string_table == nullptr)
|
||||||
|
{
|
||||||
|
return entries;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto row = 0; row < string_table->rowCount; row++)
|
||||||
|
{
|
||||||
|
if (string_table->columnCount <= 0)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto index = (row * string_table->columnCount);
|
||||||
|
const auto weapon = string_table->values[index].string;
|
||||||
|
entries.push_back(ddl_allocator.duplicate_string(weapon));
|
||||||
|
}
|
||||||
|
|
||||||
|
return entries;
|
||||||
|
}
|
||||||
|
|
||||||
|
void add_entries_to_enum(game::DDLEnum* enum_, const std::vector<const char*> entries)
|
||||||
|
{
|
||||||
|
const auto new_size = enum_->memberCount + entries.size();
|
||||||
|
const auto members = ddl_allocator.allocate_array<const char*>(new_size);
|
||||||
|
const auto hash_list = ddl_allocator.allocate_array<game::DDLHash>(new_size);
|
||||||
|
|
||||||
|
std::memcpy(members, enum_->members, 8 * enum_->memberCount);
|
||||||
|
std::memcpy(hash_list, enum_->hashTable.list, 8 * enum_->hashTable.count);
|
||||||
|
|
||||||
|
for (auto i = 0; i < entries.size(); i++)
|
||||||
|
{
|
||||||
|
const auto hash = utils::hook::invoke<unsigned int>(0x794FB0_b, entries[i], 0);
|
||||||
|
const auto index = enum_->memberCount + i;
|
||||||
|
hash_list[index].index = index;
|
||||||
|
hash_list[index].hash = hash;
|
||||||
|
members[index] = entries[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
std::qsort(hash_list, new_size, sizeof(game::DDLHash), compare_hash);
|
||||||
|
|
||||||
|
enum_->members = members;
|
||||||
|
enum_->hashTable.list = hash_list;
|
||||||
|
enum_->memberCount = static_cast<int>(new_size);
|
||||||
|
enum_->hashTable.count = static_cast<int>(new_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void load_ddl_asset_stub(game::DDLRoot** asset)
|
||||||
|
{
|
||||||
|
static std::unordered_set<void*> modified_enums;
|
||||||
|
|
||||||
|
const auto root = *asset;
|
||||||
|
if (!root->ddlDef)
|
||||||
|
{
|
||||||
|
return utils::hook::invoke<void>(0x39BE20_b, root);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto ddl_def = root->ddlDef;
|
||||||
|
while (ddl_def)
|
||||||
|
{
|
||||||
|
for (auto i = 0; i < ddl_def->enumCount; i++)
|
||||||
|
{
|
||||||
|
const auto enum_ = &ddl_def->enumList[i];
|
||||||
|
if (modified_enums.contains(enum_))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((enum_->name == "WeaponStats"s || enum_->name == "Weapon"s))
|
||||||
|
{
|
||||||
|
static const auto weapons = get_stringtable_entries("mp/customWeapons.csv");
|
||||||
|
add_entries_to_enum(enum_, weapons);
|
||||||
|
modified_enums.insert(enum_);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (enum_->name == "AttachmentBase"s)
|
||||||
|
{
|
||||||
|
static const auto attachments = get_stringtable_entries("mp/customAttachments.csv");
|
||||||
|
add_entries_to_enum(enum_, attachments);
|
||||||
|
modified_enums.insert(enum_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ddl_def = ddl_def->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
utils::hook::invoke<void>(0x39BE20_b, asset);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class component final : public component_interface
|
class component final : public component_interface
|
||||||
@ -137,6 +253,8 @@ namespace weapon
|
|||||||
|
|
||||||
// patch attachment configstring so it will create if not found
|
// patch attachment configstring so it will create if not found
|
||||||
utils::hook::call(0x41C595_b, g_find_config_string_index_stub);
|
utils::hook::call(0x41C595_b, g_find_config_string_index_stub);
|
||||||
|
|
||||||
|
utils::hook::call(0x36B4D4_b, load_ddl_asset_stub);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
|
@ -1600,6 +1600,81 @@ namespace game
|
|||||||
const char* name;
|
const char* name;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct DDLMember
|
||||||
|
{
|
||||||
|
const char* name;
|
||||||
|
int index;
|
||||||
|
void* parent;
|
||||||
|
int bitSize;
|
||||||
|
int limitSize;
|
||||||
|
int offset;
|
||||||
|
int type;
|
||||||
|
int externalIndex;
|
||||||
|
unsigned int rangeLimit;
|
||||||
|
unsigned int serverDelta;
|
||||||
|
unsigned int clientDelta;
|
||||||
|
int arraySize;
|
||||||
|
int enumIndex;
|
||||||
|
int permission;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DDLHash
|
||||||
|
{
|
||||||
|
unsigned int hash;
|
||||||
|
int index;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DDLHashTable
|
||||||
|
{
|
||||||
|
DDLHash* list;
|
||||||
|
int count;
|
||||||
|
int max;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DDLStruct
|
||||||
|
{
|
||||||
|
const char* name;
|
||||||
|
int bitSize;
|
||||||
|
int memberCount;
|
||||||
|
DDLMember* members;
|
||||||
|
DDLHashTable hashTableUpper;
|
||||||
|
DDLHashTable hashTableLower;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DDLEnum
|
||||||
|
{
|
||||||
|
const char* name;
|
||||||
|
int memberCount;
|
||||||
|
const char** members;
|
||||||
|
DDLHashTable hashTable;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DDLDef
|
||||||
|
{
|
||||||
|
char* name;
|
||||||
|
unsigned short version;
|
||||||
|
unsigned int checksum;
|
||||||
|
unsigned char flags;
|
||||||
|
int bitSize;
|
||||||
|
int byteSize;
|
||||||
|
DDLStruct* structList;
|
||||||
|
int structCount;
|
||||||
|
DDLEnum* enumList;
|
||||||
|
int enumCount;
|
||||||
|
DDLDef* next;
|
||||||
|
int headerBitSize;
|
||||||
|
int headerByteSize;
|
||||||
|
int reserveSize;
|
||||||
|
int userFlagsSize;
|
||||||
|
bool paddingUsed;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DDLRoot
|
||||||
|
{
|
||||||
|
const char* name;
|
||||||
|
DDLDef* ddlDef;
|
||||||
|
};
|
||||||
|
|
||||||
union XAssetHeader
|
union XAssetHeader
|
||||||
{
|
{
|
||||||
void* data;
|
void* data;
|
||||||
|
Loading…
Reference in New Issue
Block a user