Custom weapon cac stuff + fixes

This commit is contained in:
fed 2023-01-27 00:26:05 +01:00
parent d5cbad5c81
commit f01aa5ba59
9 changed files with 623 additions and 20 deletions

View 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;
}

View 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

View File

@ -346,6 +346,8 @@ namespace fastfiles
// 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);
}

View File

@ -82,6 +82,27 @@ namespace gsc
}
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)
@ -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(0x3BD672_b, 0x504652_b), unknown_function_stub); // ^
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

View File

@ -109,18 +109,20 @@ namespace gsc
return false;
}
bool force_load = false;
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))
{
return loaded_scripts[real_name];
}
if (game::VirtualLobby_Loaded() && !force_load)
{
return nullptr;
}
std::string source_buffer;
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);
if (game::VirtualLobby_Loaded())
{
return;
}
for (const auto& path : filesystem::get_search_paths())
{
load_scripts(path, "scripts/");
if (game::environment::is_sp())
{
load_scripts(path, "scripts/sp/");
load_scripts(path, "scripts/");
}
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/");
}
}
}

View File

@ -97,20 +97,16 @@ namespace scripting
game::G_LogPrintf("InitGame\n");
lua::engine::start();
gsc::load_main_handles();
}
gsc::load_main_handles();
g_load_structs_hook.invoke<void>();
}
void scr_load_level_stub()
{
if (!game::VirtualLobby_Loaded())
{
gsc::load_init_handles();
}
gsc::load_init_handles();
scr_load_level_hook.invoke<void>();
}

View File

@ -379,7 +379,26 @@ namespace ui_scripting
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["luiglobals"] = lua;

View File

@ -116,6 +116,122 @@ namespace weapon
{
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
@ -137,6 +253,8 @@ namespace weapon
// patch attachment configstring so it will create if not found
utils::hook::call(0x41C595_b, g_find_config_string_index_stub);
utils::hook::call(0x36B4D4_b, load_ddl_asset_stub);
}
#ifdef DEBUG

View File

@ -1600,6 +1600,81 @@ namespace game
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
{
void* data;