From b082b6933c92a58a2393efc7f47b54b9be9e83a1 Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Wed, 24 Sep 2025 00:58:03 +1200 Subject: [PATCH 001/134] Alpine compat --- common/include/common/config/GameConfig.h | 1 + common/src/config/GameConfig.cpp | 1 + game_patch/graphics/gr.cpp | 31 +++- game_patch/misc/player.cpp | 11 ++ game_patch/misc/save_restore.cpp | 22 ++- game_patch/multi/multi.h | 18 ++ game_patch/multi/network.cpp | 203 +++++++++++++++++++--- game_patch/multi/server.cpp | 12 ++ game_patch/object/entity.cpp | 9 +- game_patch/object/weapon.cpp | 44 +++++ game_patch/rf/object.h | 2 +- 11 files changed, 313 insertions(+), 41 deletions(-) diff --git a/common/include/common/config/GameConfig.h b/common/include/common/config/GameConfig.h index 8a78539e2..674acb715 100644 --- a/common/include/common/config/GameConfig.h +++ b/common/include/common/config/GameConfig.h @@ -67,6 +67,7 @@ struct GameConfig // Audio CfgVar level_sound_volume = 1.0f; CfgVar eax_sound = true; + CfgVar play_hit_sounds = true; // Multiplayer static const char default_rf_tracker[]; diff --git a/common/src/config/GameConfig.cpp b/common/src/config/GameConfig.cpp index 90d118919..dde105671 100644 --- a/common/src/config/GameConfig.cpp +++ b/common/src/config/GameConfig.cpp @@ -167,6 +167,7 @@ bool GameConfig::visit_vars(T&& visitor, bool is_save) result &= visitor(dash_faction_key, "Scoreboard Animations", scoreboard_anim); result &= visitor(dash_faction_key, "Spectate Mode Minimal UI", spectate_mode_minimal_ui); result &= visitor(dash_faction_key, "Level Sound Volume", level_sound_volume); + result &= visitor(dash_faction_key, "Play Hit Sounds", play_hit_sounds); result &= visitor(dash_faction_key, "Allow Overwriting Game Files", allow_overwrite_game_files); result &= visitor(dash_faction_key, "Version", dash_faction_version); result &= visitor(dash_faction_key, "Swap Assault Rifle Controls", swap_assault_rifle_controls); diff --git a/game_patch/graphics/gr.cpp b/game_patch/graphics/gr.cpp index 49c49067e..b686be824 100644 --- a/game_patch/graphics/gr.cpp +++ b/game_patch/graphics/gr.cpp @@ -82,10 +82,20 @@ float gr_scale_world_fov(float horizontal_fov = 90.0f) horizontal_fov = gr_scale_fov_hor_plus(horizontal_fov); } - const auto& server_info_opt = get_df_server_info(); - if (server_info_opt && server_info_opt.value().max_fov) { - horizontal_fov = std::min(horizontal_fov, server_info_opt.value().max_fov.value()); + const auto& df_server_info = get_df_server_info(); + const auto& af_server_info = get_af_server_info(); + if (df_server_info.has_value()) { + const DashFactionServerInfo& server_info = df_server_info.value(); + if (server_info.max_fov.has_value()) { + horizontal_fov = std::min(horizontal_fov, server_info.max_fov.value()); + } + } else if (af_server_info.has_value()) { + const AlpineFactionServerInfo& server_info = af_server_info.value(); + if (server_info.max_fov.has_value()) { + horizontal_fov = std::min(horizontal_fov, server_info.max_fov.value()); + } } + return horizontal_fov; } @@ -123,9 +133,18 @@ ConsoleCommand2 fov_cmd{ } rf::console::print("Horizontal FOV: {:.2f}", gr_scale_world_fov()); - const auto& server_info_opt = get_df_server_info(); - if (server_info_opt && server_info_opt.value().max_fov) { - rf::console::print("Server FOV limit: {:.2f}", server_info_opt.value().max_fov.value()); + const auto& df_server_info = get_df_server_info(); + const auto& af_server_info = get_af_server_info(); + if (df_server_info.has_value()) { + const DashFactionServerInfo& server_info = df_server_info.value(); + if (server_info.max_fov.has_value()) { + rf::console::print("Server FOV limit: {:.2f}", server_info.max_fov.value()); + } + } else if (af_server_info.has_value()) { + const AlpineFactionServerInfo& server_info = af_server_info.value(); + if (server_info.max_fov.has_value()) { + rf::console::print("Server FOV limit: {:.2f}", server_info.max_fov.value()); + } } }, "Sets horizontal FOV (field of view) in degrees. Use 0 to enable automatic FOV scaling.", diff --git a/game_patch/misc/player.cpp b/game_patch/misc/player.cpp index a85cf07c7..39955303e 100644 --- a/game_patch/misc/player.cpp +++ b/game_patch/misc/player.cpp @@ -269,6 +269,16 @@ ConsoleCommand2 damage_screen_flash_cmd{ "Toggle damage screen flash effect", }; +ConsoleCommand2 hit_sounds_cmd{ + "hit_sounds", + [] { + g_game_config.play_hit_sounds = !g_game_config.play_hit_sounds; + rf::console::print("Hit sounds is {}", g_game_config.play_hit_sounds ? "enabled" : "disabled"); + }, + "Toggle whether to play a sound, when you hit players in multiplayer, if enabled by a server", + "hit_sounds", +}; + CallHook player_cockpit_vmesh_render_hook{ 0x004A7907, [](rf::VMesh *vmesh, rf::Vector3 *pos, rf::Matrix3 *orient, void *params) { @@ -356,4 +366,5 @@ void player_do_patch() // Commands damage_screen_flash_cmd.register_cmd(); + hit_sounds_cmd.register_cmd(); } diff --git a/game_patch/misc/save_restore.cpp b/game_patch/misc/save_restore.cpp index 8b5dd7a00..ba0a80a03 100644 --- a/game_patch/misc/save_restore.cpp +++ b/game_patch/misc/save_restore.cpp @@ -253,13 +253,25 @@ CodeInjection corpse_deserialize_all_obj_create_patch{ }, }; +bool server_saving_enabled() { + const bool df_saving_enabled = get_df_server_info() + .transform([] (const DashFactionServerInfo& server_info) { + return server_info.saving_enabled; + }) + .value_or(false); + const bool af_saving_enabled = get_af_server_info() + .transform([] (const AlpineFactionServerInfo& server_info) { + return server_info.saving_enabled; + }) + .value_or(false); + return rf::is_multi && !rf::is_server && (df_saving_enabled || af_saving_enabled); +} + FunHook quick_save_hook{ 0x004B6160, []() { quick_save_hook.call_target(); - bool server_side_saving_enabled = rf::is_multi && !rf::is_server && get_df_server_info() - && get_df_server_info().value().saving_enabled; - if (server_side_saving_enabled) { + if (server_saving_enabled()) { send_chat_line_packet("/save", nullptr); } }, @@ -269,9 +281,7 @@ FunHook quick_load_hook{ 0x004B6180, []() { quick_load_hook.call_target(); - bool server_side_saving_enabled = rf::is_multi && !rf::is_server && get_df_server_info() - && get_df_server_info().value().saving_enabled; - if (server_side_saving_enabled) { + if (server_saving_enabled()) { send_chat_line_packet("/load", nullptr); } }, diff --git a/game_patch/multi/multi.h b/game_patch/multi/multi.h index 4dd347cd7..4f563e35c 100644 --- a/game_patch/multi/multi.h +++ b/game_patch/multi/multi.h @@ -81,12 +81,30 @@ struct DashFactionServerInfo std::optional max_fov; }; +struct AlpineFactionServerInfo { + uint8_t version_major = 0; + uint8_t version_minor = 0; + bool saving_enabled = false; + std::optional max_fov{}; + bool allow_fb_mesh = false; + bool allow_lmap = false; + bool allow_no_ss = false; + bool no_player_collide = false; + bool allow_no_mf = false; + bool click_limit = false; + std::optional semi_auto_cooldown{}; + bool unlimited_fps = false; + bool gaussian_spread = false; + bool location_pinging = false; +}; + void multi_level_download_update(); void multi_do_patch(); void multi_after_full_game_init(); void multi_init_player(rf::Player* player); void send_chat_line_packet(const char* msg, rf::Player* target, rf::Player* sender = nullptr, bool is_team_msg = false); const std::optional& get_df_server_info(); +const std::optional& get_af_server_info(); void multi_level_download_do_frame(); void multi_level_download_abort(); void multi_ban_apply_patch(); diff --git a/game_patch/multi/network.cpp b/game_patch/multi/network.cpp index efc97656e..191db9e9d 100644 --- a/game_patch/multi/network.cpp +++ b/game_patch/multi/network.cpp @@ -36,6 +36,7 @@ #include "../rf/os/timer.h" #include "../rf/geometry.h" #include "../rf/level.h" +#include "../rf/sound/sound.h" #include "../misc/misc.h" #include "../misc/player.h" #include "../object/object.h" @@ -185,6 +186,7 @@ enum packet_type : uint8_t { sound = 0x34, team_score = 0x35, glass_kill = 0x36, + af_damage_notify = 0x52, }; // client -> server @@ -256,10 +258,12 @@ std::array g_client_side_packet_whitelist{ sound, team_score, glass_kill, + af_damage_notify, }; // clang-format on -std::optional g_df_server_info; +std::optional g_df_server_info{}; +std::optional g_af_server_info{}; CodeInjection process_game_packet_whitelist_filter{ 0x0047918D, @@ -702,6 +706,33 @@ struct DashFactionJoinAcceptPacketExt template<> struct EnableEnumBitwiseOperators : std::true_type {}; +struct AlpineFactionJoinAcceptPacketExt { + uint32_t af_signature = ALPINE_FACTION_SIGNATURE; + uint8_t version_major = 1; + uint8_t version_minor = 1; + + enum class Flags : uint32_t { + none = 0, + saving_enabled = 1 << 0, + max_fov = 1 << 1, + allow_fb_mesh = 1 << 2, + allow_lmap = 1 << 3, + allow_no_ss = 1 << 4, + no_player_collide = 1 << 5, + allow_no_mf = 1 << 6, + click_limit = 1 << 7, + unlimited_fps = 1 << 8, + gaussian_spread = 1 << 9, + location_pinging = 1 << 10, + } flags = Flags::none; + + float max_fov{}; + int semi_auto_cooldown{}; +}; + +template<> +struct EnableEnumBitwiseOperators : std::true_type {}; + CallHook send_join_req_packet_hook{ 0x0047ABFB, [](const rf::NetAddr* addr, std::byte* data, size_t len) { @@ -728,33 +759,80 @@ CallHook send_join_accept_packet_ho }, }; +bool try_process_df_join_accept_tail(const std::byte* const tail) { + DashFactionJoinAcceptPacketExt ext{}; + std::copy(tail, tail + sizeof(ext), reinterpret_cast(&ext)); + xlog::debug("Checking for join_accept DF extension: {:#08x}", ext.df_signature); + if (ext.df_signature != DASH_FACTION_SIGNATURE) { + return false; + } + xlog::debug( + "Got DF server info: {} {} {:#x}", + ext.version_major, + ext.version_minor, + static_cast(ext.flags) + ); + DashFactionServerInfo server_info{}; + server_info.version_major = ext.version_major; + server_info.version_minor = ext.version_minor; + using Flags = DashFactionJoinAcceptPacketExt::Flags; + server_info.saving_enabled = !!(ext.flags & Flags::saving_enabled); + constexpr const float default_fov = 90.f; + if (!!(ext.flags & Flags::max_fov) && ext.max_fov >= default_fov) { + server_info.max_fov = std::optional{ext.max_fov}; + } + g_df_server_info = std::optional{server_info}; + return true; +} + +bool try_process_af_join_accept_tail(const std::byte* const tail) { + AlpineFactionJoinAcceptPacketExt ext{}; + std::copy(tail, tail + sizeof(ext), reinterpret_cast(&ext)); + xlog::debug("Checking for join_accept AF extension: {:#08x}", ext.af_signature); + if (ext.af_signature != ALPINE_FACTION_SIGNATURE) { + return false; + } + xlog::debug( + "Got AF server info: {} {} {:#x}", + ext.version_major, + ext.version_minor, + static_cast(ext.flags) + ); + AlpineFactionServerInfo server_info{}; + server_info.version_major = ext.version_major; + server_info.version_minor = ext.version_minor; + using Flags = AlpineFactionJoinAcceptPacketExt::Flags; + server_info.saving_enabled = !!(ext.flags & Flags::saving_enabled); + server_info.allow_fb_mesh = !!(ext.flags & Flags::allow_fb_mesh); + server_info.allow_lmap = !!(ext.flags & Flags::allow_lmap); + server_info.allow_no_ss = !!(ext.flags & Flags::allow_no_ss); + server_info.no_player_collide = !!(ext.flags & Flags::no_player_collide); + server_info.allow_no_mf = !!(ext.flags & Flags::allow_no_mf); + server_info.click_limit = !!(ext.flags & Flags::click_limit); + server_info.unlimited_fps = !!(ext.flags & Flags::unlimited_fps); + server_info.gaussian_spread = !!(ext.flags & Flags::gaussian_spread); + server_info.location_pinging = !!(ext.flags & Flags::location_pinging); + constexpr const float default_fov = 90.f; + if (!!(ext.flags & Flags::max_fov) && ext.max_fov >= default_fov) { + server_info.max_fov = std::optional{ext.max_fov}; + } + if (server_info.click_limit) { + server_info.semi_auto_cooldown = std::optional{ext.semi_auto_cooldown}; + } + g_af_server_info = std::optional{server_info}; + return true; +} + CodeInjection process_join_accept_injection{ 0x0047A979, - [](auto& regs) { - std::byte* packet = regs.ebp; - auto ext_offset = regs.esi + 5; - DashFactionJoinAcceptPacketExt ext_data; - std::copy(packet + ext_offset, packet + ext_offset + sizeof(DashFactionJoinAcceptPacketExt), - reinterpret_cast(&ext_data)); - xlog::debug("Checking for join_accept DF extension: {:08X}", ext_data.df_signature); - if (ext_data.df_signature == DASH_FACTION_SIGNATURE) { - DashFactionServerInfo server_info; - server_info.version_major = ext_data.version_major; - server_info.version_minor = ext_data.version_minor; - xlog::debug("Got DF server info: {} {} {}", ext_data.version_major, ext_data.version_minor, - static_cast(ext_data.flags)); - server_info.saving_enabled = !!(ext_data.flags & DashFactionJoinAcceptPacketExt::Flags::saving_enabled); - - constexpr float default_fov = 90.0f; - if (!!(ext_data.flags & DashFactionJoinAcceptPacketExt::Flags::max_fov) && ext_data.max_fov >= default_fov) { - server_info.max_fov = ext_data.max_fov; - } - g_df_server_info = std::optional{server_info}; + [] (const auto& regs) { + const std::byte* const packet = regs.ebp; + const int len = regs.esi + 5; + const std::byte* const tail = packet + len; + if (!try_process_df_join_accept_tail(tail)) { + try_process_af_join_accept_tail(tail); } - else { - g_df_server_info.reset(); - } - }, + } }; CodeInjection process_join_accept_send_game_info_req_injection{ @@ -917,6 +995,7 @@ FunHook multi_stop_hook{ []() { // Clear server info when leaving g_df_server_info.reset(); + g_af_server_info.reset(); multi_stop_hook.call_target(); if (rf::local_player) { reset_player_additional_data(rf::local_player); @@ -929,6 +1008,10 @@ const std::optional& get_df_server_info() return g_df_server_info; } +const std::optional& get_af_server_info() { + return g_af_server_info; +} + void send_chat_line_packet(const char* msg, rf::Player* target, rf::Player* sender, bool is_team_msg) { if (!rf::is_server && sender == nullptr) { @@ -1049,10 +1132,78 @@ void __fastcall multi_io_stats_add_new(void *this_, int edx, int size, bool is_s FunHook multi_io_stats_add_hook{0x0047CAC0, multi_io_stats_add_new}; +#pragma pack(push, 1) + +struct af_damage_notify_packet { + RF_GamePacketHeader header; + uint8_t player_id; + uint16_t damage; + uint8_t flags; +}; + +#pragma pack(pop) + +void af_process_damage_notify_packet(const void* const data, const size_t len, const rf::NetAddr& addr) { + // Receive: client <- server + if (!rf::is_multi || rf::is_server || rf::is_dedicated_server) { + return; + } + + af_damage_notify_packet damage_notify_packet{}; + if (len < sizeof(damage_notify_packet)) { + return; + } + + std::memcpy(&damage_notify_packet, data, sizeof(damage_notify_packet)); + + const rf::Player* const player = rf::multi_find_player_by_id(damage_notify_packet.player_id); + if (!player) { + return; + } + + const rf::Entity* const entity = rf::entity_from_handle(player->entity_handle); + if (!entity) { + return; + } + + const bool died = static_cast(damage_notify_packet.flags & 0x01); + if (g_game_config.play_hit_sounds) { + rf::snd_play(29, 0, 0.f, 1.f); + } +} + +bool af_process_packet( + const void* const data, + const int len, + const rf::NetAddr& addr, + const rf::Player* const player +) { + RF_GamePacketHeader header{}; + if (len < static_cast(sizeof(header))) { + return false; + } + + std::memcpy(&header, data, sizeof(header)); + const packet_type packet_ty = static_cast(header.type); + + switch (packet_ty) { + case packet_type::af_damage_notify: { + af_process_damage_notify_packet(data, len, addr); + break; + } + default: { + return false; + } + } + return true; +} + static void process_custom_packet([[maybe_unused]] void* data, [[maybe_unused]] int len, [[maybe_unused]] const rf::NetAddr& addr, [[maybe_unused]] rf::Player* player) { - pf_process_packet(data, len, addr, player); + if (!pf_process_packet(data, len, addr, player)) { + af_process_packet(data, len, addr, player); + } } CodeInjection multi_io_process_packets_injection{ diff --git a/game_patch/multi/server.cpp b/game_patch/multi/server.cpp index 30e13ad04..ee67bf897 100644 --- a/game_patch/multi/server.cpp +++ b/game_patch/multi/server.cpp @@ -713,6 +713,16 @@ CodeInjection multi_level_init_injection{ }, }; +CallHook player_create_entity_entity_create_hook{ + 0x004A41D3, + [] (int type, const char* name, int parent_handle, const rf::Vector3& pos, rf::Matrix3& orient, int create_flags, int mp_character) { + if (get_af_server_info().has_value() && get_af_server_info().value().no_player_collide) { + create_flags |= 0x4; + } + return player_create_entity_entity_create_hook.call_target(type, name, parent_handle, pos, orient, create_flags, mp_character); + } +}; + void server_init() { // Override rcon command whitelist @@ -781,6 +791,8 @@ void server_init() // Shuffle rotation when the last map in the list is loaded multi_level_init_injection.install(); + + player_create_entity_entity_create_hook.install(); } void server_do_frame() diff --git a/game_patch/object/entity.cpp b/game_patch/object/entity.cpp index dd5bfe27a..bf631a37d 100644 --- a/game_patch/object/entity.cpp +++ b/game_patch/object/entity.cpp @@ -14,6 +14,7 @@ #include "../rf/debris.h" #include "../rf/misc.h" #include "../main/main.h" +#include "../multi/multi.h" rf::Timestamp g_player_jump_timestamp; @@ -278,8 +279,12 @@ static FunHook entity_damage_hook{ CallHook entity_update_muzzle_flash_light_hook{ 0x0041E814, - [](rf::Entity& ep) { - if (g_game_config.muzzle_flash) { + [] (rf::Entity& ep) { + const bool server_force_muzzle_flash = rf::is_multi + && !rf::is_server + && get_af_server_info() + && !get_af_server_info()->allow_no_mf; + if (g_game_config.muzzle_flash || server_force_muzzle_flash) { entity_update_muzzle_flash_light_hook.call_target(ep); } }, diff --git a/game_patch/object/weapon.cpp b/game_patch/object/weapon.cpp index 55d89ed5d..44ad58a2b 100644 --- a/game_patch/object/weapon.cpp +++ b/game_patch/object/weapon.cpp @@ -126,6 +126,47 @@ CallHook weapon_hit_wall_obj_apply_r }, }; +bool server_click_limit() { + if (rf::is_multi + && get_af_server_info().has_value() + && get_af_server_info().value().click_limit) { + return true; + } + return false; +} + +std::optional server_fire_wait_override() { + return get_af_server_info().and_then([] (const AlpineFactionServerInfo& server_info) { + return server_info.semi_auto_cooldown; + }); +} + +CodeInjection player_fire_primary_weapon_semi_auto_patch{ + 0x004A50BB, + [] (auto& regs) { + const rf::Entity* const entity = regs.esi; + if (server_click_limit() && !entity->ai.next_fire_primary.elapsed()) { + regs.eip = 0x004A58B8; + } + }, +}; + +CodeInjection entity_fire_primary_weapon_semi_auto_patch{ + 0x004259B8, + [] (auto& regs) { + // HACKFIX: Override fire wait for stock semi auto weapons. + BaseCodeInjection::Reg& fire_wait = regs.eax; + const int weapon_type = regs.ebx; + const rf::Entity* const entity = regs.esi; + if (server_fire_wait_override().has_value() + && rf::obj_is_player(entity) + && rf::weapon_is_semi_automatic(weapon_type) + && fire_wait == 500) { + fire_wait = server_fire_wait_override().value(); + } + }, +}; + void apply_weapon_patches() { // Fix crashes caused by too many records in weapons.tbl file @@ -157,4 +198,7 @@ void apply_weapon_patches() // Fix rockets not making damage after hitting a detail brush weapon_hit_wall_obj_apply_radius_damage_hook.install(); + + player_fire_primary_weapon_semi_auto_patch.install(); + entity_fire_primary_weapon_semi_auto_patch.install(); } diff --git a/game_patch/rf/object.h b/game_patch/rf/object.h index 3e14722c8..3f031324f 100644 --- a/game_patch/rf/object.h +++ b/game_patch/rf/object.h @@ -127,7 +127,7 @@ namespace rf static auto& obj_flag_dead = addr_as_ref(0x0048AB40); static auto& obj_find_root_bone_pos = addr_as_ref(0x0048AC70); static auto& obj_update_liquid_status = addr_as_ref(0x00486C30); - static auto& obj_is_player = addr_as_ref(0x004895D0); + static auto& obj_is_player = addr_as_ref(0x004895D0); static auto& obj_hide = addr_as_ref(0x0048A570); static auto& obj_light_free = addr_as_ref(0x0048B370); From 13782a3cef4ab54b6a0f0370e3de5bc4a517daa3 Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Wed, 24 Sep 2025 01:18:02 +1200 Subject: [PATCH 002/134] Update server.cpp --- game_patch/multi/server.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/game_patch/multi/server.cpp b/game_patch/multi/server.cpp index ee67bf897..503b3b612 100644 --- a/game_patch/multi/server.cpp +++ b/game_patch/multi/server.cpp @@ -715,7 +715,7 @@ CodeInjection multi_level_init_injection{ CallHook player_create_entity_entity_create_hook{ 0x004A41D3, - [] (int type, const char* name, int parent_handle, const rf::Vector3& pos, rf::Matrix3& orient, int create_flags, int mp_character) { + [] (const int type, const char* const name, const int parent_handle, const rf::Vector3& pos, rf::Matrix3& orient, int create_flags, const int mp_character) { if (get_af_server_info().has_value() && get_af_server_info().value().no_player_collide) { create_flags |= 0x4; } From 7d7b2d5d9cc9deb0220fce16f95f758840b05a9e Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Wed, 24 Sep 2025 01:24:35 +1200 Subject: [PATCH 003/134] Update CMakeLists.txt --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8eab5a415..f172aae15 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ -# CMake 3.15 is required for CMAKE_MSVC_RUNTIME_LIBRARY. -cmake_minimum_required(VERSION 3.15) +# CMake 3.20 is required for C++23. +cmake_minimum_required(VERSION 3.20) project(DashFaction) if(NOT "${CMAKE_SIZEOF_VOID_P}" STREQUAL "4") From 75958f1b8dcf7b4f4486fb0161d52e544f4ed776 Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Wed, 24 Sep 2025 01:25:09 +1200 Subject: [PATCH 004/134] Update CMakeLists.txt --- game_patch/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/game_patch/CMakeLists.txt b/game_patch/CMakeLists.txt index ec918654a..2f87e760c 100644 --- a/game_patch/CMakeLists.txt +++ b/game_patch/CMakeLists.txt @@ -172,7 +172,7 @@ set(SRCS source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${SRCS}) add_library(DashFaction SHARED ${SRCS}) -target_compile_features(DashFaction PUBLIC cxx_std_20) +target_compile_features(DashFaction PRIVATE cxx_std_23) set_target_properties(DashFaction PROPERTIES PREFIX "" CXX_EXTENSIONS NO From dad1b0d17df89e85e683b60b433220ed1038df8f Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Wed, 24 Sep 2025 01:32:59 +1200 Subject: [PATCH 005/134] Update network.cpp --- game_patch/multi/network.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/game_patch/multi/network.cpp b/game_patch/multi/network.cpp index 191db9e9d..398128c68 100644 --- a/game_patch/multi/network.cpp +++ b/game_patch/multi/network.cpp @@ -624,14 +624,14 @@ struct df_sign_packet_ext uint8_t version_minor = VERSION_MINOR; }; -struct af_join_req_tail_v2 +struct AFJoinReqV2 { uint32_t signature = ALPINE_FACTION_SIGNATURE; uint8_t version_major = 1; uint8_t version_minor = 1; uint8_t version_patch = 0; uint8_t version_type = VERSION_TYPE; - uint32_t max_rfl_version = 301; + uint32_t max_rfl_version = 200; uint32_t flags = 0; }; @@ -666,7 +666,7 @@ std::pair, size_t> extend_packet_with_df_signature( std::pair, size_t> extend_packet_with_af_join_req_tail(std::byte* data, size_t len) { - af_join_req_tail_v2 ext{}; + AFJoinReqV2 ext{}; return extend_packet(data, len, ext); } From 74a52728ce9b40ca3bc0c23fa80554ea3e739c13 Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Wed, 24 Sep 2025 05:01:38 +1200 Subject: [PATCH 006/134] Update network.cpp --- game_patch/multi/network.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/game_patch/multi/network.cpp b/game_patch/multi/network.cpp index 398128c68..81a9b9bce 100644 --- a/game_patch/multi/network.cpp +++ b/game_patch/multi/network.cpp @@ -827,8 +827,8 @@ CodeInjection process_join_accept_injection{ 0x0047A979, [] (const auto& regs) { const std::byte* const packet = regs.ebp; - const int len = regs.esi + 5; - const std::byte* const tail = packet + len; + const int tail_offset = regs.esi + 5; + const std::byte* const tail = packet + tail_offset; if (!try_process_df_join_accept_tail(tail)) { try_process_af_join_accept_tail(tail); } From 97e144c47b91497ec371237f490132bc7a411619 Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Wed, 24 Sep 2025 16:43:51 +1200 Subject: [PATCH 007/134] Update gr.cpp --- game_patch/graphics/gr.cpp | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/game_patch/graphics/gr.cpp b/game_patch/graphics/gr.cpp index b686be824..5b92056a6 100644 --- a/game_patch/graphics/gr.cpp +++ b/game_patch/graphics/gr.cpp @@ -82,14 +82,12 @@ float gr_scale_world_fov(float horizontal_fov = 90.0f) horizontal_fov = gr_scale_fov_hor_plus(horizontal_fov); } - const auto& df_server_info = get_df_server_info(); - const auto& af_server_info = get_af_server_info(); - if (df_server_info.has_value()) { + if (const auto& df_server_info = get_df_server_info(); df_server_info.has_value()) { const DashFactionServerInfo& server_info = df_server_info.value(); if (server_info.max_fov.has_value()) { horizontal_fov = std::min(horizontal_fov, server_info.max_fov.value()); } - } else if (af_server_info.has_value()) { + } else if (const auto& af_server_info = get_af_server_info(); af_server_info.has_value()) { const AlpineFactionServerInfo& server_info = af_server_info.value(); if (server_info.max_fov.has_value()) { horizontal_fov = std::min(horizontal_fov, server_info.max_fov.value()); @@ -132,15 +130,13 @@ ConsoleCommand2 fov_cmd{ g_game_config.save(); } rf::console::print("Horizontal FOV: {:.2f}", gr_scale_world_fov()); - - const auto& df_server_info = get_df_server_info(); - const auto& af_server_info = get_af_server_info(); - if (df_server_info.has_value()) { + + if (const auto& df_server_info = get_df_server_info(); df_server_info.has_value()) { const DashFactionServerInfo& server_info = df_server_info.value(); if (server_info.max_fov.has_value()) { rf::console::print("Server FOV limit: {:.2f}", server_info.max_fov.value()); } - } else if (af_server_info.has_value()) { + } else if (const auto& af_server_info = get_af_server_info(); af_server_info.has_value()) { const AlpineFactionServerInfo& server_info = af_server_info.value(); if (server_info.max_fov.has_value()) { rf::console::print("Server FOV limit: {:.2f}", server_info.max_fov.value()); From 08d827bcfd5fa8d2cddebeb1796890589b9f7fb9 Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Wed, 24 Sep 2025 17:40:25 +1200 Subject: [PATCH 008/134] Update gr.cpp --- game_patch/graphics/gr.cpp | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/game_patch/graphics/gr.cpp b/game_patch/graphics/gr.cpp index 5b92056a6..dcb833729 100644 --- a/game_patch/graphics/gr.cpp +++ b/game_patch/graphics/gr.cpp @@ -82,15 +82,15 @@ float gr_scale_world_fov(float horizontal_fov = 90.0f) horizontal_fov = gr_scale_fov_hor_plus(horizontal_fov); } - if (const auto& df_server_info = get_df_server_info(); df_server_info.has_value()) { - const DashFactionServerInfo& server_info = df_server_info.value(); - if (server_info.max_fov.has_value()) { - horizontal_fov = std::min(horizontal_fov, server_info.max_fov.value()); + if (get_df_server_info().has_value()) { + const DashFactionServerInfo& df_server_info = get_df_server_info().value(); + if (df_server_info.max_fov.has_value()) { + rf::console::print("Server FOV limit: {:.2f}", df_server_info.max_fov.value()); } - } else if (const auto& af_server_info = get_af_server_info(); af_server_info.has_value()) { - const AlpineFactionServerInfo& server_info = af_server_info.value(); - if (server_info.max_fov.has_value()) { - horizontal_fov = std::min(horizontal_fov, server_info.max_fov.value()); + } else if (get_af_server_info().has_value()) { + const AlpineFactionServerInfo& af_server_info = get_af_server_info().value(); + if (af_server_info.max_fov.has_value()) { + rf::console::print("Server FOV limit: {:.2f}", af_server_info.max_fov.value()); } } @@ -131,15 +131,15 @@ ConsoleCommand2 fov_cmd{ } rf::console::print("Horizontal FOV: {:.2f}", gr_scale_world_fov()); - if (const auto& df_server_info = get_df_server_info(); df_server_info.has_value()) { - const DashFactionServerInfo& server_info = df_server_info.value(); - if (server_info.max_fov.has_value()) { - rf::console::print("Server FOV limit: {:.2f}", server_info.max_fov.value()); + if (get_df_server_info().has_value()) { + const DashFactionServerInfo& df_server_info = get_df_server_info().value(); + if (df_server_info.max_fov.has_value()) { + rf::console::print("Server FOV limit: {:.2f}", df_server_info.max_fov.value()); } - } else if (const auto& af_server_info = get_af_server_info(); af_server_info.has_value()) { - const AlpineFactionServerInfo& server_info = af_server_info.value(); - if (server_info.max_fov.has_value()) { - rf::console::print("Server FOV limit: {:.2f}", server_info.max_fov.value()); + } else if (get_af_server_info().has_value()) { + const AlpineFactionServerInfo& af_server_info = get_af_server_info().value(); + if (af_server_info.max_fov.has_value()) { + rf::console::print("Server FOV limit: {:.2f}", af_server_info.max_fov.value()); } } }, From 77896f31a34afb1f03f7dbbdd49d5364b5cba7ec Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Wed, 24 Sep 2025 17:42:26 +1200 Subject: [PATCH 009/134] Update save_restore.cpp --- game_patch/misc/save_restore.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/game_patch/misc/save_restore.cpp b/game_patch/misc/save_restore.cpp index ba0a80a03..aef3894c8 100644 --- a/game_patch/misc/save_restore.cpp +++ b/game_patch/misc/save_restore.cpp @@ -255,13 +255,13 @@ CodeInjection corpse_deserialize_all_obj_create_patch{ bool server_saving_enabled() { const bool df_saving_enabled = get_df_server_info() - .transform([] (const DashFactionServerInfo& server_info) { - return server_info.saving_enabled; + .transform([] (const DashFactionServerInfo& df_server_info) { + return df_server_info.saving_enabled; }) .value_or(false); const bool af_saving_enabled = get_af_server_info() - .transform([] (const AlpineFactionServerInfo& server_info) { - return server_info.saving_enabled; + .transform([] (const AlpineFactionServerInfo& af_server_info) { + return af_server_info.saving_enabled; }) .value_or(false); return rf::is_multi && !rf::is_server && (df_saving_enabled || af_saving_enabled); From 8b6489018e2f2d04a9b345e01b1f588c23a429e8 Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Wed, 24 Sep 2025 17:57:24 +1200 Subject: [PATCH 010/134] Update network.cpp --- game_patch/multi/network.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/game_patch/multi/network.cpp b/game_patch/multi/network.cpp index 81a9b9bce..640e6b96d 100644 --- a/game_patch/multi/network.cpp +++ b/game_patch/multi/network.cpp @@ -761,7 +761,7 @@ CallHook send_join_accept_packet_ho bool try_process_df_join_accept_tail(const std::byte* const tail) { DashFactionJoinAcceptPacketExt ext{}; - std::copy(tail, tail + sizeof(ext), reinterpret_cast(&ext)); + std::memcpy(&ext, tail, sizeof(ext)); xlog::debug("Checking for join_accept DF extension: {:#08x}", ext.df_signature); if (ext.df_signature != DASH_FACTION_SIGNATURE) { return false; @@ -781,13 +781,13 @@ bool try_process_df_join_accept_tail(const std::byte* const tail) { if (!!(ext.flags & Flags::max_fov) && ext.max_fov >= default_fov) { server_info.max_fov = std::optional{ext.max_fov}; } - g_df_server_info = std::optional{server_info}; + g_df_server_info.emplace(server_info); return true; } bool try_process_af_join_accept_tail(const std::byte* const tail) { AlpineFactionJoinAcceptPacketExt ext{}; - std::copy(tail, tail + sizeof(ext), reinterpret_cast(&ext)); + std::memcpy(&ext, tail, sizeof(ext)); xlog::debug("Checking for join_accept AF extension: {:#08x}", ext.af_signature); if (ext.af_signature != ALPINE_FACTION_SIGNATURE) { return false; @@ -819,7 +819,7 @@ bool try_process_af_join_accept_tail(const std::byte* const tail) { if (server_info.click_limit) { server_info.semi_auto_cooldown = std::optional{ext.semi_auto_cooldown}; } - g_af_server_info = std::optional{server_info}; + g_af_server_info.emplace(server_info); return true; } @@ -827,8 +827,8 @@ CodeInjection process_join_accept_injection{ 0x0047A979, [] (const auto& regs) { const std::byte* const packet = regs.ebp; - const int tail_offset = regs.esi + 5; - const std::byte* const tail = packet + tail_offset; + const int ext_offset = regs.esi + 5; + const std::byte* const tail = packet + ext_offset; if (!try_process_df_join_accept_tail(tail)) { try_process_af_join_accept_tail(tail); } From 3e1095be0ac70f632806baca4ca92c300de4195d Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Wed, 24 Sep 2025 18:03:44 +1200 Subject: [PATCH 011/134] Update weapon.cpp --- game_patch/object/weapon.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/game_patch/object/weapon.cpp b/game_patch/object/weapon.cpp index 44ad58a2b..2840e4987 100644 --- a/game_patch/object/weapon.cpp +++ b/game_patch/object/weapon.cpp @@ -136,8 +136,8 @@ bool server_click_limit() { } std::optional server_fire_wait_override() { - return get_af_server_info().and_then([] (const AlpineFactionServerInfo& server_info) { - return server_info.semi_auto_cooldown; + return get_af_server_info().and_then([] (const AlpineFactionServerInfo& af_server_info) { + return af_server_info.semi_auto_cooldown; }); } From f301672e84d4db6354263c18f1f06496c32f7f89 Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Thu, 25 Sep 2025 01:06:27 +1200 Subject: [PATCH 012/134] Update player.cpp --- game_patch/misc/player.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/game_patch/misc/player.cpp b/game_patch/misc/player.cpp index 39955303e..c0412c658 100644 --- a/game_patch/misc/player.cpp +++ b/game_patch/misc/player.cpp @@ -273,6 +273,7 @@ ConsoleCommand2 hit_sounds_cmd{ "hit_sounds", [] { g_game_config.play_hit_sounds = !g_game_config.play_hit_sounds; + g_game_config.save(); rf::console::print("Hit sounds is {}", g_game_config.play_hit_sounds ? "enabled" : "disabled"); }, "Toggle whether to play a sound, when you hit players in multiplayer, if enabled by a server", From 66e3f0fba5241b2f8860e0410617e31ca67591e3 Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Thu, 25 Sep 2025 01:09:22 +1200 Subject: [PATCH 013/134] Update camera.cpp --- game_patch/misc/camera.cpp | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/game_patch/misc/camera.cpp b/game_patch/misc/camera.cpp index eb22810a5..1c42d4e26 100644 --- a/game_patch/misc/camera.cpp +++ b/game_patch/misc/camera.cpp @@ -20,6 +20,34 @@ FunHook camera_update_shake_hook{ }, }; +CallHook entity_fire_primary_weapon_camera_shake_hook{ + 0x00426C79, + [] (rf::Camera* const cp, const float amplitude, const float time_seconds) { + const bool server_force_weapon_shake = rf::is_multi + && !rf::is_server + && get_af_server_info() + && !get_af_server_info()->allow_no_ss; + if (g_game_config.weapon_shake || server_force_weapon_shake) { + entity_fire_primary_weapon_camera_shake_hook.call_target(cp, amplitude, time_seconds); + } + }, +}; + +ConsoleCommand2 weapon_shake_cmd{ + "weapon_shake", + [] { + g_game_config.weapon_shake = !g_game_config.weapon_shake; + g_game_config.save(); + rf::console::print( + "Weapon shake is {}", + g_game_config.weapon_shake + ? "enabled" + : "disabled [multiplayer servers may force weapon shake]" + ); + }, + "Toggle camera shake from weapon fire [mutiplayer servers may force weapon shake]", +}; + void camera_do_patch() { // Fix crash when executing camera2 command in main menu From fe19cb113c4ebaf6569f5e50f3014c503b41c24d Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Thu, 25 Sep 2025 01:09:55 +1200 Subject: [PATCH 014/134] Update GameConfig.h --- common/include/common/config/GameConfig.h | 1 + 1 file changed, 1 insertion(+) diff --git a/common/include/common/config/GameConfig.h b/common/include/common/config/GameConfig.h index 674acb715..122b4c45f 100644 --- a/common/include/common/config/GameConfig.h +++ b/common/include/common/config/GameConfig.h @@ -58,6 +58,7 @@ struct GameConfig CfgVar muzzle_flash = true; CfgVar glares = true; CfgVar show_enemy_bullets = true; + CfgVar weapon_shake = true; static constexpr float min_fov = 75.0f; static constexpr float max_fov = 160.0f; From f8aedec8636ad828190c1698a014c47ccfcd403a Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Thu, 25 Sep 2025 01:10:40 +1200 Subject: [PATCH 015/134] Update GameConfig.cpp --- common/src/config/GameConfig.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/common/src/config/GameConfig.cpp b/common/src/config/GameConfig.cpp index dde105671..067133f2d 100644 --- a/common/src/config/GameConfig.cpp +++ b/common/src/config/GameConfig.cpp @@ -175,6 +175,7 @@ bool GameConfig::visit_vars(T&& visitor, bool is_save) result &= visitor(dash_faction_key, "Glares", glares); result &= visitor(dash_faction_key, "Linear Pitch", linear_pitch); result &= visitor(dash_faction_key, "Show Enemy Bullets", show_enemy_bullets); + result &= visitor(dash_faction_key, "Weapon Shake", weapon_shake); result &= visitor(dash_faction_key, "Keep Launcher Open", keep_launcher_open); result &= visitor(dash_faction_key, "Skip Cutscene Control", skip_cutscene_ctrl); result &= visitor(dash_faction_key, "Damage Screen Flash", damage_screen_flash); From dd43b7bfdd849b34d6270981bfc721d1f5511a60 Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Thu, 25 Sep 2025 01:17:12 +1200 Subject: [PATCH 016/134] Update camera.cpp --- game_patch/misc/camera.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/game_patch/misc/camera.cpp b/game_patch/misc/camera.cpp index 1c42d4e26..285127912 100644 --- a/game_patch/misc/camera.cpp +++ b/game_patch/misc/camera.cpp @@ -1,7 +1,11 @@ #include +#include #include #include "../rf/player/player.h" #include "../rf/os/frametime.h" +#include "../os/console.h" +#include "../main/main.h" +#include "../multi/multi.h" constexpr auto screen_shake_fps = 150.0f; From 8b7523dc77f2bfdb28cb8a6d057afab1a6026997 Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Thu, 25 Sep 2025 01:49:39 +1200 Subject: [PATCH 017/134] Update CHANGELOG.md --- docs/CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 9b661e006..73220edb0 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -6,6 +6,8 @@ Version 1.9.2 (not released yet) [@is-this-c](https://github.com/is-this-c) - Fix a potential crash after a client quits a game if Directd3D 11 is enabled - Improve compatibility with Alpine Faction servers +- Add `hit_sounds` cmd +- Add `weapon_shake` cmd Version 1.9.1 (released 2025-07-05) -------------------------------- From 460579c0481cb74d6599b20699d259ed7a81de75 Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Thu, 25 Sep 2025 01:55:32 +1200 Subject: [PATCH 018/134] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index d67bc8248..9a9ae4a46 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,8 @@ Name | Description `debug_event_msg` | toggle tracking of event messages in console `gibs` | toggle gibs (characters exploding into parts) `muzzle_flash` | toggle muzzle flash +`hit_sounds` | toggle whether to play a sound, when you hit players in multiplayer, if enabled by a server +`weapon_shake` | toggle camera shake from weapon fire [mutiplayer servers may force weapon shake] ### Server commands From 470b4fd14a072c87f601f062617547685abc3592 Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Thu, 25 Sep 2025 01:56:25 +1200 Subject: [PATCH 019/134] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9a9ae4a46..dd3fb5cf2 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,7 @@ Name | Description `gibs` | toggle gibs (characters exploding into parts) `muzzle_flash` | toggle muzzle flash `hit_sounds` | toggle whether to play a sound, when you hit players in multiplayer, if enabled by a server -`weapon_shake` | toggle camera shake from weapon fire [mutiplayer servers may force weapon shake] +`weapon_shake` | toggle camera shake from weapon fire [multiplayer servers may force weapon shake] ### Server commands From 4440cde15c4c6958c8fd48ff5837c8764dbf08ce Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Thu, 25 Sep 2025 01:57:19 +1200 Subject: [PATCH 020/134] Update camera.cpp --- game_patch/misc/camera.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/game_patch/misc/camera.cpp b/game_patch/misc/camera.cpp index 285127912..192032f81 100644 --- a/game_patch/misc/camera.cpp +++ b/game_patch/misc/camera.cpp @@ -49,7 +49,7 @@ ConsoleCommand2 weapon_shake_cmd{ : "disabled [multiplayer servers may force weapon shake]" ); }, - "Toggle camera shake from weapon fire [mutiplayer servers may force weapon shake]", + "Toggle camera shake from weapon fire [multiplayer servers may force weapon shake]", }; void camera_do_patch() From 34eab5eaf7f693c4dc4ad18f81f094687a272b59 Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Thu, 25 Sep 2025 17:22:18 +1200 Subject: [PATCH 021/134] Update network.cpp --- game_patch/multi/network.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/game_patch/multi/network.cpp b/game_patch/multi/network.cpp index 640e6b96d..161792c6f 100644 --- a/game_patch/multi/network.cpp +++ b/game_patch/multi/network.cpp @@ -1168,7 +1168,9 @@ void af_process_damage_notify_packet(const void* const data, const size_t len, c const bool died = static_cast(damage_notify_packet.flags & 0x01); if (g_game_config.play_hit_sounds) { - rf::snd_play(29, 0, 0.f, 1.f); + constexpr const int hit_sound_id = 29; + static const int kill_sound_id = rf::snd_get_handle("af_killsound1.wav", 5.f, 1.f, 1.f); + rf::snd_play(died ? kill_sound_id : hit_sound_id, 0, 0.f, 1.f); } } From 307dd7e9e25f144befdef9b90f3613e12c1be926 Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Thu, 25 Sep 2025 17:23:42 +1200 Subject: [PATCH 022/134] Add files via upload --- resources/audio/af_hitsound1.wav | Bin 0 -> 2786 bytes resources/audio/af_killsound1.wav | Bin 0 -> 34906 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 resources/audio/af_hitsound1.wav create mode 100644 resources/audio/af_killsound1.wav diff --git a/resources/audio/af_hitsound1.wav b/resources/audio/af_hitsound1.wav new file mode 100644 index 0000000000000000000000000000000000000000..953aed44c6e86b76bb99324f53908e4092f5d675 GIT binary patch literal 2786 zcmeIydobJC0tfJ45=4j=jUa73g9ag9p=&AP)l^ZV9zj#D8nrRH!?3&UcDFm*>aZ%s zQlwTX_2`snYbCAq7#$(&mBk{p36jVNja|}6BE7%Po&EdH+`0el{eJ#9XXczaGv_;J zPTbk>@MQx4NDNL0yFx3pF$Mqt0ziS#ZU8Xug#tQ&F_4j6Qb>V<3;*!Mg$2ik#r^js zcoIB)@B~l1ho6Tp-h+tuA^v?9wD{a=a}V~9oav!UprqrUp`PiKx`qlq*SL=yH>bXc+x%TSO%71)N9lmKN&x{BwdnP z8(14u*MPqKnu_;1xpdX7{7Ri zeTL0$Ww+MV)YTZ>GrIQ^{U^F5!;*2U=T=X`M8ZVjX5l7HpQg`rVmh%%EK(RHjB+FQ zMlP{{Siq<8X`LCJ8Kp6$F)$*Gc*^3G1x-m)UR=7kl=C|0wWLkb_MrAb?W@XHmCN^+ z?~gIY7}Y)1J;R(~PM9Q2@>1`m9^HZNkQAI0Jdrq&Xq{u7W0P-_@Bf+qXAS8M>8D~& z#iSBbiT;-Umi8)pRn3UmlM)ZNkDkyM{l?_1?twN|}WO=u!Ck$OqJ&75XVqBv0u z(}n4NV*iPKX<%s}E1ne(&w^(K<^|?W=1%7Fs647mv`aLJNFo|w3^3E`X*FyWwhA8w zJ%vBzGPsOye*EUg*R`)}Q=3zpYnU}m6-UK+B6=dC!l#NVjNmZ*mue2tppkK8l@9oK0K@ ziDSjFpOZc(wGr9~CKwY;Iiwt7A~liL&DG7xf06%E!K`3j@4DU<4ifYVdiw_Z276|D zW+-BcSb8WuEHW!Hvv#p|!3JOhFk}o_?}FY1DhPKDcTN?k3gqE=cq>aQ%PMFU6e5Gj z{1*KdMPs6|tbwe7c4j-%2L${K{9HC#Haf?fark5QrJMI(riHZdZ1{nWw-~U^`$?d=&pY_dIuHXl1CDRm=Ko;I9G2 zpkffq!E)*r>lU4)PEv}7qG>$Zcr?#8&o-VAPw?>f@W%(^gZZGKfS`bJuW>JuBgt{e zY{_g5wg%(v@^C@@cPZypp;Dk8gHW-;&rdA@9hy(Nidee5(R*VzlvfZ-X%-zl1H7*(# zngh*2=Y-CQGUGC1HB1e=^#0QO{;mEkxlk?~Umjl`Sr}Qc0`Y(6|K7gZzN!^##kRY) zyF?9969bQd-#58$;)rp?T(!AsBeR#;e}(-Di?&1CAx|Pt&Y8`bA&rnmw_vwm`v?06 z?z`^0AOEa78+SG;g_U3{c0#vxxAkI4vE+yCAGWvlw)WOEYnpkTd7Y!kqe!$V+B6HD zh2~grEQ~CTEGgy`^O<8a$HYg(N0jZMjS? zqwY|5(7Whe$)02{{2=@Qs)cHcp~cX9diV4?5uJ#4hVKl8NFj0))MeOZIFFb|oP?i* zC+Q^V1V92HE!q~Xm&Qxes%lkTKe&Ey9&{5_q$*OCsms(K-xpa+)>eF|_>cu;0Sb^Q S#1ygtYykP7|L1?#z`p=F?r#2Y%DwvO zc0|eUnWq~w^VAQWnCUx@H#X&7>#lXz?TC;)Z!t+wa?ea+5-5;T;)wVufK+UY0IC2` zwk!fwcf)=4P?8E&g*yA}v(LA`{cX(shadlV?jI5QkFmeL{C9uZ(a6aL=96#E~sm+Ze=c>7%v{=b4@=x-O^T|n?e#3AeZ ziyMsn)T9pHHh5j_>ip`fpy%SneOKJ3t?v(G5YPY#PspVq}yT7`#8tG zUC+myZ`U5qG(;v7J3hr`LJYw!D>|}R`}E5g(qfDuzweXw1p3LrU zzELeuH3~youV(f8n(00Tsf)>O+Loy>g;n2>7hf6HA9Tf=(FgBA*KE9^?3b{6I#6V&189)GdVOY zmj++PWAw*L{q64lO+CPBQ`)dCGdVMXir-MVcXEW&Of|@%rd)EJH{}NLn$8=H=|$Oz z|El~B=XPf5F7KVl0OBzT?P2Vr*fBo26k>2`Np|9r#=HAi6L8}B}Ge=uvcM>pd2C!^&^iF`%( z8N_Q<79| zOE;CY|2dpcXe_v*vWTt1Tx7%4LGb;s;b6zUf_utcq&s`JCzOLZ@DD+fh z@{`*atm;%buui;QRqFDIwzLteKjTVC1HLJo#wm=`q~pH2aAh-e7#aFZ*^XL|CXGn; zlv|H)L{1VOElD|89j>RF$+CX{leJifM0vGzj>fL$BU}C}AIrynW=H8JvgG+@XjP*6 zSY566Gp1S|q_C-ba))Gh)Z&G1>zY1TCobI&ulecEqP)3^e zbo=NhW~dZ)wnEt${SAZE3{DSvmnF(&?cQvNbnEXoBikm^8xKzDb?eVqGG%s#>Su zOYTU=BA4Miz=ENli5=UyQU?cVk%2`8XqDCx7v%p1JC42r$;r_ z7^L+@3zu|Q_NZq;=1;YYOjF7X(q>908N_b)BSYXcVowZp`x#tz_6H5cut;Ar z&6?r6j@K)EJusRc?lfn#={j*!i+qi7EWvQaBej8+grOFX953xl4K$+pif>PR=&~?n z+kx~2wu&&{iF~*&(BDW~JU*Hw7BsX+GD{JvEoH(Gp>sUl&y_;X7Kb?2!69Wugw>Ug zJ;#~+dxne7#5mTs;iHYeX7#Cjm|mK7Vw|beiLqSlyEX&ilso)et2(*;CVg*fHXeC7BUhTc|AZsdV;ROMKv@>iq)sD#Sq>2u(yFQ4Jj-MbbZ1^O} z<&y@+?kqOItFCL2uQ0T))}r;#W(N$q`YKYN6*MQ@vTj|4+;w{1vfis(UR; z>qT0VS>_H=P4oWV-Ya$28(WQ9wy|XuN_)ln-A18QUn_2vwu;3yI25)UdyQJLzSV#a zw~Ruzv)w2Z4|^wiH#G`n_;9UMC>FjyWb50-HMps`)~Ib4w;Nj}xT#p$E1~VipLA42 zPK_(dhtDKz4xN?6IV>t&^oetPAS~YI$7O$9oQ^~z;yJlFBG@o6nxSs7`3dm zZPf3wMdNeB%G!H&cLQ12p+@Hd-TlSpo5-&cZ}ZdPK*E;@pM}B02rq~I^SvR!kYRlO zo}FOi(=ePsBr*@f_s{o+JnswitTY|=&wX!$z??kp0~1Dlfw&JYj6|bgq6@u;MMJ|8 zxF!%6=EFiHIwy?F<6&_Y1!Np%hr=Y(#bT=IqRJ=$q{{4XjKk92c75yL zhrC_#q^;S(@Lu~sy||JF*M z_U!cUH`TX$uGF(@_XD$2w!r)J`UYFnYdVt_n6t{!9D9MoW7uU54`Dy{`D0?j|8@jy zlZZyc(YP=T)`6jc2ldhsBGL&7{C2x0U5IM~%UANTo$xbFgog_@VW#H=Y$t=TgF0^^#hP12;aSp#l@XZMT?3kw!geGS~xDp-5v!xFn)?rt~k+TCoSWNnp-IVOvD zuz?Mn6_a{|ZpKYz&qUbxW#908O?)^o7o5F~(%pQ76MRAffsSlQzRn{5bzQ>|B)@?E)K^#iPFY@Nh`D%?Bhg zj?Z&s0-xiD!Bp}EpMTyzFT(1+OEFFCfBOS+r5tkT#`%LRQ9F z6CUf{hOkDQ*M!gQ7iD|fS}X4rJL~45!DQ{nrd39dbt_A+8+*k=uo*=6`j*vmDr?O+ zH0px%&Dz!< z8}urwnanEcm~4(cH0$LvWvqam$)IiCxjVHUsXd!4&)dX^CTHFrBRKLMewY()VNq^4 zM9gy+WIWD~^Zo@f;hPV+BEEMxQWkC?mm0T(B4})F55B9xvIUaT;7#`)KAx9hqIAV^U#{4lMA>QU(GVC>^ z*9e!0=Q{U3q77?oN2fJuSBfHG#C;SP7 z9tN4%203E7vcl^Xu^4&{Zc@=f*Z1Bg?&Q%&>UTKO)DCW&sWMjQVilH7Wsc6{u{3@j zMK<{JrjV7C9sIWlBRd@T3_A|fKbL6uCW-DdZ3(rCwBj&?5$==c*wl(ZH@PKg1n`># z(cUASGf@gX9IM|#o9X~=Hm_PowxQA=Bg*GXvl4!~5mu`|h8zUvFPXytH`{~9C|v9F znuAn+woF`axKQ`J(U0k~OjY(w(|2N08^Ieb5kp8Q?z~qb05a)=kitRMV4X%t3f*;h zc-bI-*o>E4v~~*?U_M1v$Mo+YPs5z2q`Yd5*V~JFlf_boa44fqZ(U7e&zo{Ca1!~9 zUgBE{F|DYXDiM4B$$OzsqlfrZcD$pj4HsiO^AStgs9ar9A*XVvOCk^ofNlx2BC&0> z9=z}Wl=JMwe=jiimCplA`#Nwg)l2QrveM~e%RTtkR5wT(W82*&wb3FAs{sR zoJdKB2Y}f~LXrVVAJ{3>dmHa(lf^f?#7{H+Jz*m4%Zj59;_ z+XoW>dY)hp&~VVrn>yAN zf*;E@U$@_*&lMf+{6dL%qpfW4qr2*c!Z^I<*s}i^(oy+Of;XS60~Mh{E`U$w{ZAv- zw?W`sT#(|XMXJ?vp97RpOAZQ4YT~gNyc4Rq*d4Hk)xNHYMc1Xgx+iq@Co>B-7<)ws=Vx@_94rw0j-lP17oBt`#Pi-$QE!VwRFM4CRThk z!u>!kUhWcO+H8>wYTT0Yf?$BrfxJY8wx{fd`pd z@f{PMdRfRO|?Aksi>t0B)dA8?rUH+LSGZ!e9CswM_}k~ zYvNwyu?W=Cs{arVYOy`iou6%x^!{uq+?rbSqq|oQpfjs-VHMX;ub1|5j z1Nv#K?0XF4%v9+z)4Hz3ia++|)ohfmsvD77d$KrRKN@W(VdYgl%Q1_fwNz=CX-=gh z#~3g=_B(X-Jm%ZD?5?V5U+rLQ!&f9JJ(lsG1m~Zn2o4g(L93lCxw@^HWsHL9;&}2(`Gwh@HOLIM)TB?dak&xpt5b{St`b`|%TywR6^4pr0E3EH;^QaG0{$ z+qibrMr1qlMrQulRCTcTnIav7f!;rt60WjGpb=GaTH_!hXGNh?9x-Xw96vekQ0 zDihukJ-f+lc#El z!XC_>X$CZ;+D{Z_xTa0o_&=dksl{<@yN~v9PwL)xt@c}$8+H6z1}h3T)94VB_0|~@ zxPbu@HIpW8#zr#eQ;hi-9r_y&r(j|;L=62FyG!=%J{ev7kBk}21Ue8-(HFd=b2L`k z{TuVg7I7sXErwrxsQi*=^U`CH-hJKhm&j{SJrMrp39F?VM^ar`LNto#!t4BRc5ZfE z7JkVg8*|<-y~R2Ez+Idzwa?o=NTz{0zcm26B`_l%Ow9x!4nR@i0-FJ@Yn_-WW9#wY zgIHsMeWcLL3TzmBTImGXdy~0~G&Yqv$C^Qyg&qN4^cN0ZtHf3XcUAeN4*+pC!aY&Y z0wM>1;^)2s-<`}lEE2-66;L!6qclDnDfmj~-XN6Ko@?j)z_*qb>JBIgzD*sLiw(w9c;NF*vSCN=^*-DHNf8Cta#4Y^9k+%#y5WG!p zaLf!X^DMk0MnmA9DG}c_RV{vud4Xe#?Cqz}blvpm_;2y^M_ew#?hlxfcG|7YYN2Bw zzuno3H6mFSsmkQCU`K)8E&J$)lOIgmn5P+A7wA6trtgkC4Y}CoX{xI@Yl@OgnZ~(G z`|&+-pz7-oGn-7-*S!}xMoqOFVE@M=3$aDrCSRC98Na}$HZWG>%orNSZqPgP-5X3b zm>vJl1i2vE3V8%)KfeBPK012E7~LV|p|OG5I!2p44Ne;4CNLjj>MX0^- zSuz+v=4DB!pPtBsS0h84T=^D6q;;P~xO2YDZ4b#D-0H$!x(Fz#lFYg&lhY$7aL)8G zr=Y&D6S^B1xi4)Ao?Yda93sbza2YZGKxdL22Qt0o;SP4aEOzi96d8uu)}(f0`>fXt z7v~3>T+RoaaW-5aN1Mv~0F_WW*%1xdsjnD zzqbj5Byg>|d^`vavOU;s-PBzjeDr>V#wR}-D}`%+IHLo^fC|U5{Jo>2v#cp?U=-=C z1h>TFovWEZ9sh|o-z62DE57Z8Fe^!`LeH8J+H^J;>f=8 zzD!kRJDDZDO?5>EkhrPV&h~K0U=2wXUm@!L80tlPe-3Iss5CWWS^rQ~TJ^gR*qo|6 z01qv3tjIiA1ZZ8IDWC0P!8G2DP*0@BY!)0jULJPiQ||*IFXd$F$fGNn3&%*>hawZ& zg_T=>Hlqmzabg84`smN-ObYmS9AL`}T*|}&>i{t9QS;h4M)cfQ3YWVNC$rNGekmtd z#7j+ogWG<_p_JWZE=p;caY*pur{E@oc#6DDek)wtpV^+b9KrlTp*>Xz-}R5|N|_Yf zD@3KS-k10(!m}i?q*uGH53(>B`vbMiy3M_U#FRgLbGmoCi5<_NeL!g>i$0lJUV6>u5|ubI%F1S{jwri+84 z)%dn}gSno4y5hN%)_f?9uf%y(-r`TEWskDVd^h3*jQSi z2buiU@^q2-iI%AQPBueUa&pLHD%rRoIn5{aafFHg3`U|NOzt&2$pZxi66A$b=OETlTCzr{+5!z_7rE3&n^Fe-4j`$!JWv1Os+K4aYIClR(HXgZ zgE{gtSZn^P=2#lufzCjVg3MWi7(?u4Vj$2Ue_--;@=_&H5Qp{`Y@XT-TcHiYNhq6< z=Y4OX`5zp!7f-*VPosqD@bOU<_`6%-)4UiHqtcv+N4Sd^L|PX|B#Bd)FX9N`ao=z# zbQ|}pp#&aPPKjJXI>+-#IUxEM`~hElR*cDUpDQHa!2GIz9G_JKzQ|m7Tn^0&k?;k7 zYk_m|d`b#%;g~BRMCM$P(AxqK8ButqJZ|!8a~vh*K?0u>&T+m7o{*-+_5weQNh#!F zc?j0@tn>$t8$|7^D4|6sh{Ov2YRd%Ye?(hS`VkbQ)7$`JGi^bMqG!?wNRBi|VQ6KN9@O<~Hc@2W%9D>}&wb7*YoENr z@Hu;cWmnj4#o7pJ4~r~!r!~N{A0a1iZ@TN~ed~;lHV~6X^_1E69>?@YZp1CIClQnJ zbei@0$c9;Cy_@JeiT;G{ax3{C08X|kja^1`3qiE~60u2Bbo$Wd*w=VndzHev`X!Cn zlCFLx(6`VLvSLR2uMn9=fZ8+4=(iG}T>_z0(?1a6b`@trZ{0Kp8~l}Btdm6FAe2N7 zFSlU&Jz_LJH+m6asip&A$(Em)rM;;}{F3Utr8Yl2Y!U1QS zVKy!~WNZNg7V;MEOOhf=Nur1rITs>At!Xqak$(0vPb~5QER-Z%W=wMMXcjpT7jK>& zBjU~Y)M){w;Cftb1{h!Re&{&NJW#&&G5c59vp|O;VS^j}Gu~KWNQgNZ2QA1eG9dGN zNwd#HGgYGDAN^-(L#}@^x*f>gA8PQcCS{r%=(x$0mF#CPOtgUwV2p+y0}bpc;(O+$ z4E8BHX|gq}n5Ih1sxe@jX}zo2t6UxL7~bXPF$$*93z%C}K=n+gl>d!6V46jA5}t{+ z?>G%Mr_~ybCA(pk(oC*$XtFElDPS^h4RzD*B3f@ayX-pKv9UdQpv5%!LiMDD3fc+V zPFr=mkOT6(U{V?M1!6R|go^pUF!0qLpSsfXm-X;gn=Yp64scuze;G6z+zWt*?`h;A z2K!8npz_i-NGQ&m?1oKcJgeL>2BZBDqiqn6oa6z;9Qy0`uNHwjq?ch3PSabG3;Cgv z{};kZ#5Mdc0M%aa;%D?J$X8MD9@G^(_oueU8`Uehz#8_dj_qNTK3e4N!pISh%6D8yN4 z0PE=N7AoEwGF;mrU{Kd_r!M92y8a4_baX;AGD+a1mX#b1q(+(O*xV{s^BSAp^>Dr4 zi*B)}xMs{75QqgXf)gR>EDC%7619?^ey^D_Nv@g1amDv09*NBch;ZmOHY|~0tmlT@ zv>Z-^7JM!-wBU~l)3SeoPe`YH_^{;i5s{QXIw(ZqgMMG+79W-SWIp0|ghSK*VZS;n zioSTl5$103xXQ(G(ud7+E>#F97X%0Ii@2u!gAsC^hxi-`ox`OlKP-jggAnz_Z)AP} zBPDzs3(aB?drs=ZBtL%|$x`ziODg7qx2@zC5h-DDIP3G_3GOyeswjb;C_B@3MBNRq zC)s`E~DD4kF7Q7?#=)$95zQa{4w$_bJkR~?MClC zFkeN|ihL4hH1AW~wx{%J{RflxnxAv^eXEc%4w#1j`$$QJ-CrU~)vzu0Rn7c@WirfT zRMWdf;zJgq@2bRn!j)3y6UI(~D2DZ=dxS(uDL6{jh*r6a{-T2IN!=Z04SQkqKxDO8wCw4a-P}+E|BxLrn`>jomBwSPv^i7iQR+?l0NCL7#7s_WN#c}`OIQ9PI7 zUcrt0>t-`&K1Zk{pJ|S5V0|aBgYW*I<0W^=T(;;+eT8|7iaGact7DF)E6-VYR+sIr zST!qGdd})~=GZW|Q3rB8Vf%n?$g9Z7>NWOHsIkFp*ieUDHtKb2)$Xubsn*q(8J4ZS zz|QF0w$%7%_K+x*yw6QD%}{!+c8qcwRYThbwW1%|R?eni2Sqe<(ZlV#@A6+)-0Rq% zOuB|jxnK_9?=9pKRB^f)QC#P2Q1qZP6)ua-%M{qH>uRRLSR(0@K*roa+=Kly(%&g^A0&-ya?p6jJR~fkNo?&9=(V^+-6*er$CS!@ zbtALEZWy%^y>2$_W)3@nH&BovSJ~Gz{!?Txp)PRmK)^Gl5h_zJeFgQg?xAfrc)L%; z5&+^VO8U9g+}0_oIiS-S=0kHxn*lz|THsDW9fc>l)K{j}K__(vDs1~66#tU-Ne<+B z4c9h(kOwNi(pP% zt-|Cdn;|Kn!bTj~am@@yTerEmpP0j=vY0@TNiuwIvU{=dY|5O@5u??2K+r(kz;B4d zLU^1X9us|-3TUE|;?5#bB7J-!ax{*!ab%z&)%ctGoS#NXwJc=QHN}tt9_bIG37d(e!v*-d671D)B z<)PpS@s*(yLI#ZhN|Im3y_wLNK4hvUHe_>Pan#Xc>0QWJ>bz6uR-s(3WJ8#144nS{k8~I&O_&*# z>SvoNQ}9AmA{au~K7Y;9>MvtLJ~h+6X!@>WK0VAid}tg!a&zWG^l|{wc3`3OcZ^dQ z8$tanK8wOi05p%>X-rgz%XZ{C5kO%TPq1&|WGFUIBxZ(XJSh$G*evG4u_PatWa263WX0+o%TD%^|9BTM&@mx>6G2FXJnIdp=5toMLI2o4{{?oFYPyz~b z(Sl?0wn|&bU|QVX zINQh7f=d7wQ5HAA9g4rm)hWH44L+{cj-bCQU=C2{SB=auTOu_EJwK?|RRGHd zlh#kbN@rMGdz~>dtKGxg`kqxUHxAkIM!k5qOm&-E-ScU^W78RPkEYAo|Ao3MjgC=V zcd~}=rZi)nvb4i(_O5-%==HL*X$tB>$i=15GxS&JT<5_4g3(UX>^rEtMFNIe1+IP^>+3NK7;2kiZ;?Z~>I;LZ=Za!dK8 zVD)iG06w;z0#Ls{xfEl`V?CG__8dGzAP1`{g@2pMC|MVZj;$c=7AvymVh;CKW>yHd zi_w57QS~$0+#&FcLXo(l-)4}rnY1zHnMac>@n&UeHOlpQfIMsgPh;L@%MFBRhxT`F zBhbJXb)O~vsDx@RsB>)y68<}2^NzqQ*e!w5LI*2c4J%djP5A1mK4(^c0F22ywr_ zyP1h)Xh{Gf8A_bRVFoyPLkeX-JJB2i^)41FU!iVRvPA!{HCi*aa2KC! zMz?w9BA1!kAfd#b@t=ScR;C=FbYc3Slfw*rZZd7|10VejhzPVVM-4t>{saknjKcDP z9_21vPxFk6U6$O04Qwx7L6>kct^6JkRf?|CotSp;DnJ+PJsi)_Oymx%2i)qO%tsDo z->;CHY*t4k;@|l50d_@V2!2gz{DHO9bo&e!+eEJ;LZ^eo&$+jHTJTf_t3}jb@goGk z!nrF1eG((EtDb(IRqa0>vyw)PDxqJXw=jJF>S(`hUaDSh@UP_KmH^DTQ(*Io6E=ql z%b`mrk7PQ@e8WQBb>}ZwB8QCtpnZ+Svlrba*%3USg%5q~onT|$$XrKa8Gi{-rTP?F z4Pw?a)|xGYPKaLy-3Hr&Vm{y({h}51Jdm>T@uQ#)70w~QvIGqP%#uCO7b9A zpyU`3TXO0C2t(%l15l%hP>;AyjC}yAk9;3+R0p9GxS79+h?)UyAyDSMgAMR#e~g*c zD{q(q$o|@)4Vl{KYjfx!#l@QK*LnrumoVL&WXZeey4mU`y?;iOY<|Ncu0{MkhVF1D znw*tn59q?Ix$lNYwW$#Ik#YhmTGvs)V4-8^nB#WvAE3s8G^T8}pt!TbO%jdtjP(ud zrh2Qx#q#3IG4$}tn#%@8&ed%$=6xNGy)T@Aauj!+&+sF%wIl^02?)~98uiJ{6DBA@ z!9v2)^h`h_-b3vix7Z|@5$O}+1SguXp=vQ zVNGb<(6uCURA$fPO^-tNpSKqp`Gh7tN-2oY1vMRw<=-lQ_2?hzXV zOk7Xe58eLI(lEzcOFjn2%PF>rP1=3kb!AVh3AZvBWk&XZM;c^#)g0y!Vf`y&9`es| zw6D@GLr-zov7T%Ox(w5=l&+PJDC0J;a%LzE6=IZ2j5#R4xgZ?l?#a*qkm0{`q2_hq z+|bmwa%q;`_ZCBqKL*h)4K!_&OA-xbzneB$N5CUI7NRy zcKVPZLBkH>HUDcmmwz2yL})}$tO*kz$Lry-|E@emK%?5E$q)6hY0l~cPNW2W!dt{p zGrsNe?1Lz$aNsn5V1^3O$xLJ`;{KHak$5L%iVX2ei&>WFBW`E0?x6F)vVl&!B+!Un zfyTVxsgcddZ}H&fTtW1tW5|!4v1W-kX-(B}XLCdoQS$eoBvCi|;Gm22QL@f6B-2oc z@wD$pEpA(W-c;(5ftHl>cg#>`ni0OkSUaWTv<%%f?50XXi;!Xr7l}6>g&Em-??ap@3! z1nP=%AB)C5_B%QDS2FD{J-P~lMeJI|WvkRP#Hhv-FH+OQ0Fw34Nnj0UY_i)(>ivwM z+(GpidN$SJH1dZCx9Le@|9zEf8`L7&p;hH0#3aqU(;e7p{m68cnKQV9QRr{sMYL}{ z44+2laUsnG`&n(Q`;AR0`4*FuevZ5$JnfmdsH3ZPJ2QIa{=?b`_odEA4x=+B+~Hrx z@SU0SRjl|^^n|6|e?+tw`xi72GVjmOr!;g8++uX(A$bz<6~-OgPkXN854;n}%8JMH ztop)>KVup1!!$e7yL$mO2P7hILU`1IKi$o|7~>zXbaFRCUnlNKuVZBXio37L%&>oC zS!I8Py(&Cpu1tMYy%Z!jvA1wC=e<_habL$Cv6s{b`Jsu{&pCIXb#E-#Z`Nnn&md?L zOWY_?HQtiGHE9p|5WRuEW$CfqXP)4=reEXxbS8*>L{pJ=`urG~w-(I<(=)a6(ddlp zKDwkl%3pKuub`WDyWve$+D=9O2r?dX{~AM7^`D{kig)Y)JC-moLa4g?BNn3F8`yqk zV#uMPpP3K4N@^3MWa$p)j^Mv~ofUQLZ%hn=;eGr?i2aPC62_fLbV@g`m~G>#=UW@- zuy1MbCI{3Y_Pwu&pd8|ejQ15x*jC*C6VWmDevrP=sJ@E)dGsR;Qe29(%th@R#BwVF z#ba9dhd44vki$fmK#v)l_!rbo+V?%3a9X^C{CA)m4QW4Zd}D)%51P!TmVHhTHc3vw z#IB)ejyq4tx!I8i-g*q!IyStF1v%_d&>1PGZKRN?`4223X`8R{{tVNd*NH`gETaEZ6}!^Jzu4VbtTlis56}sW2lXL|aw)cL zHB|FUHzOY)*sJqHoyarCgx#;Rqxp7sDs`Yap3uk`&i3 zzA$-^EJ809C!OX^XrrsM!L;ww3to+Ly6UZ~ha{?`48V-3P1Kn6iwL~~;)*g?LMNJ{ z9vi7(bQ)eLDk7?<}RG68ZrkHx&U0 z3vLQqi2X?UG{9Cozr`7S%DSkc;0}5$NG%5cPZ>htW^;jFc@SL%HkG+0G^xEo3Rm~w z5y|o}tH+=^+w7)6R{>l>LwB|pGnjoA>JAj>72;=L3-|n@-9`UzYYepbc&Dmx_HhQs zQ#vG|^;7nLB^snjJ^U`OEYgb_23xs(3rc8ai8*j-T#utt}4tQ5F0Z3y532E zPGS#&|7TPV)BTpMWKPit=x&O}?<+qvvFaj+ZLpFH+vCUnj383=A#7q7{W?J1pRrt> z$`$QYL%a$-glOzD9~HA6Osq!gt-sR!+v4l{;jIw!M`VQX{gX(^;L!67N)-Z99|JG( zi%qOM=Y7Z=hKt{hYT@_sQ9ZCO4;^tOmG1X|Fa>wQi^$(WlPbuKk@+(Vx3EPGTz2YX z-*X-{gEsWp{s7_n6m*K>?==V(dryPSk<*m#0%qkBbGEGi1d$ElTGa*pG*!T*=H}II zAj7|f6CX;R1FW0u9LRv*9*weC8VyL*<~4~-qevpvn8#Q`z$%bkb!tubB=5ztR@7$)N)# zn`E9bcCvJI-i+k%*IT}{bfyik-FC2ODoq^Xo_jLow|3v_0wXwyISh8kv)P9^;Z~;h-GvhL)EDy`E&H&28g~T_F9v))R}vJS^``(It*&e$rXB%0#dmrd{7TA8*j5Vd+g_!= zMJen@9Fx%R%rFkU`BRewBemPV<9L#l*ndM~N%rTGhcvZC7Dx4KW*^J4k8oPq*gxZq zEaO+upvGUq2FptDVf70yyHiR+75OX9+?42xrv55;43=zTjcay4ZI3NCfy;!ZMhp&T z2;3NHJkgoaYUZ~Rx^H}^7)L=WvSdzSBwHFm`^5i-0fM6+NFPYtA2{G;c9r0dZG(w| zV05e>HKw}%lkKX;&a13TGvBa|bhKzT1-Iuj{i?g))>1$~?nwkXc4D~TNuk~AMtB)d znj|e;*S`s(xJI7oZ4s0Dw=Cn=9{d!YH5paf)x3+%&CuVY!3Mgv3DVtIb#}-kwD5aa zlbM*V?k>{($Wm&5MqIekUX<{o2`Tz>;R-&kDX%s0;-cpfV#53p9W=jX=m`FQD@>sD z0Lp?^<$5JRGtxH_gW=FP9!m2ABy`~pw75)?d4gD8uToRee^sc3#_kn6v3+z6RyJ{E z1+ygPh~clT@Gpq53=fQerck6O4N^LA))mfH*Y_u~pxt~+4}XZc^S-;tvyVPy^}thn zbeV$`eG@vjSzG^m{eJqg}f#yG^d6;Qm=n<-FnLH16v zgt~&W9NG`AhkuFFty@~JdjfQpJ+Y#`Uc)fo#9q%-+SHm@tx_<*SY7G;A zS2o3owyblv3H<6WWn*x3CCtf(XE}rpT?z`9#@+j`FZ*B3D+Llcq=FBTGrFP$CQps6 z3f*Z&qdyQ$GRfd*cuGfmL^O2Qb%T}mF!(tJ=-kiwVS~uuvT)FOuVP-3y+3LGt+k&t z@Vj`*^ihaeLM$P-V+geuYE%(Kn=N-b>>Tu%2(&$YK~4H#klCN9N4vXke26!M2~EB? zOA`~n#4Lv=pVYDMkXm#dVv-i>oVA)t`OO-d%*@pT?$0DG{w7Bv8-mi@G2BO7fxK3!uCM8t5o#h&BAmrmq%t zKcZLV&XIdJ1@3U5f~>3!E&DZs2Ou|G!-AXKpG5XxCKvDEZY=@aSX0Wzog`LQHu@=0 zUiHzqnuFi(;4+}YU^TF07eBv03mW`;l6i}6j_HvNS8zYRB^)!&+K0^A_e>WGpLu^BApJd0+R8*FVc|9MxdXjIaR_- z?Vn}P2N?YbZTsj4!P2ez(TEYznlDwoOPuXz(o)v{Dg3m8u!oPP`LA(y$6N5*jseqW z$j@6`n(yq7rR7pT4T41FDek%B&5{jrCI^Otrjz=h=U%wRcXubV{xtC_E^qKBd*lpg z_CWLrJx(A`=w5{NWnbvxBl-={Z*Qtv)LBuoa^2xxk%48y7423Rh{)cLM^shA)%!gJ zwEZSqRgBvP?DcXHY^iKS=qj_BvfHCa*I`MB-aE7PK~z!Thd#`pHr#EF>?(JC^s^Ct z-geM>EH_A3Ls{7kzuAzqTQ6{RNBLAhk0y&}8GSN4TnbEP@NIYzE<1;wxOMoM66hP# zd?#_f5&4tDt4lgS9R}7B$!#j^V8gtr`HRt4cVl(G=cB1&d;_w1`F-3Cef$94=<;p4 zN#oFG>)=!5WcmzGveGfAwDIYo2Q>FL-X}Dvd3fqd1XPPPW)Auo?*YGG~Ds zU53^NMoU@5u|887tl?@|bPB%x;1@V||7!g-(q}BZtR}V7Xbj!QQoKo|xbTjAqUmoZ zUy!J@z_l!-R8jpF_eTYV%r6&N5#iU%q>bHt##<8mt&GAsVoXRpd)w{s>9gK_Mt_HW z!m%=Wk(We`L;nTgV`jA8r(l7GF!N{5luGBOjH%^8hVZT=yttYU+vmJ+7BsF(ujxgq zEYvS_+B5%Od*9aG#+6=qstTkoK;2NFlt=_n1&~rtECL09l%`{mxR7eknzTuPQdS;j z#UiE5yv&-Iq&xF44{0T@leU$-PGZqLkBKGFotZVWViBO+ot18hK!K9mEs+2Upxm}e zTnYfyz5s=q4dt@(2j;E0mQ4zm0_s$qbG~nXd+!e#p&=P+jRBw5m^7K=y6un1{~rE5 z+jivt(hIm+Of2h!nrJ`S5PR)cwAo$S(TS~7wIMGgG_T?8E!sg}PRkbajSd~ghnTm( ztWWA8KEyx%&7!Y<%x(x}s(C(_jbXMGj~y4I|d$dBEq#qROpa=Pme7i|9)(L4Lf(GcOpYT@$q zX+yuxeJKdBOw}vK(uRX3X<1z~$Q$}4i_EM`Jr~?^pf4-Q*c0`bF=UY#Bf^g4?}bLf z$X+n%JFV{8U8+HFb2TUZRy9&z z>OCf%#Ifev1<*;#E$W#@++poCaetzg-Ar<0spm%w(6shFF91P@?X%)AH6rhB;_QGm|}LA|1!Y zNUOzhvl1=4?lYG=>f8QqCy)*wGVZJ|XLD=NQ%%_52i0Q^D)aeBZOD@jK>)WEE6s6L zcVVq)JK2ggC%c_s+PBL%v{(+zeYH5-1v5NUJH=OGCH#H1l7eL7LAbI4^OhHi$NFtB;&=U{*?1%3coHkmKBFfp@!~n;`KSnB-htoct}K;ZEFE0;IT&_p zMQ2$04O~+2`D4Y|-chtPyJ;JxgbW50HzWw~h6;`*R#+O^MNG<5Tow51Av2FF0)wyU zrn67aXT!ydp69|H1r?ztOnZ0(Lv)fNS%tmWuPii1`t+N+^O1ArLEMad(T#ldxR^L?LJ(bz>*guGQ)Jba)%u#lT%Z`{{&{DxPvz02p$kB3I^iEoZCBr|E zV2O8pU$=#R;_xV#mrP^6>f4BOr%7H#>fM1v{af1)J}@E?m>Amkp$wXL^#GDL~X zO*%^C+xkjaG}M!HCdUs@9J%!xUoUOMfuQAy-1)ExULZ%_EYmDU{g50tkaj#}1b*Uv zr`C(ESM4NT0RfZfvWMKF=QX_;{den@1<0R2^rlHoJzR&@iQ5Y7O1a8)=y=KW1M)T{ zJP?Y@+~X)oyiH#yQ_voC!VUGzbL%1e+sL8}($rfd?J16Bj0q!hD{`jAXXES>xw2M2 z8UG}gQ$yfz5fS}_X>61Ba; zGVF^b%f?~8O0|(-CDt29T-6_k{KL3Jq=2sE{qnJ-Z{zlX*q=8>lwTTEu$^+uuD(6zqn?{;o`M z2TtPTE z@ji|VVZQUnIo%i^&GES@f1JzvE$ze@T&Io`rxApCK{{TZQ(i>YW8ZXwnMrfVt%r)2+yxcm4{3;8gzN|7>2PA9syp|N1+kIY;DNTO zV9UInFsp&BRN3XI2pMFqf-cw2b2@bM939Kbm=s&GT;RB z=b8X?9QHA>SPyp%Lc;YUH?_7EMuiPn!fQNM zs6ix?AL~iew`CaXZK%aa`Sf&Cxy5*3BdDCH28-e5v8aKp9=vyOsgYv1wexH{x~|m5 zXHr0SDLCDlUmnn)9*JEkTx^@DrZO59hsOTKd(dtwd05 zs3+A=V3nbL9P~yuY_E&rJK|`IFD%S*YPKc>f?QNbOUp9xQ7 zKBPYmyw3b-_%~2My=z25A77R^?ybAjAfSc zWYzq57tqg?yA#WkyEd*6Z_aVd^|XMeH1 zkd7aME0x4?FmN|&^h7mTgrYEz^*^P(xH^7E;Z5CNlP>Sb?}XVSM8lf{V~g_S0TtEa z+v6O3CTxR{JN(p%qRL<;4nhh?zp5?v#6Tyw9j||hD5cTHN{NN^7;YKUW&$dbOy>6= ztM7{DbG396z69mS<_HF{c#*5f+A0E7W4Xzkq!3jDKL&tvdO+a-S{>LB%?SHH1G(AS zF;sm@V;6Q3Ip4{)x18!9<8`@w4ncn!2Al|l0Zz6Tw2>ZJsDvK*yl_B`JUa>)sS@ll z!uOPuhH5EnXCgfyE-~tDkwr=!N6@vi1o~4!UiX=jkPhyhW2>)LA_sUfTtk4amL|I` z41_gj(xcf#@ig*>o;0x)dMG)zL^I)xJ7Dcc9nu%JmLPK6UxmQO255$DdU5{?_lIF& zV8U#4xju<4vN+X+L#eMs9j8JSOec1K#r9|9V*fb#4yOYg_!_8UxGzS3q#CRB5l2P6 z4c--e9U=eLmMF}ryjX8E=8nU)|)KU;*yaAkKD3Db){U<7l5dlpOIY|2? zM%U%3cyBxT5dszk7J+2e$5lPI#omyNJa*Eg<&EV%7de8r?;)dBR~ypAb7ay8MhYty zoFkWX>6tXM5jRIXRam)lznp*!)CKUQB)LCRkl`SxhD3nd7jUUgL&{5>ed+b*H<}60 zi(odf2Zmgtj#?AEVps--G~k58r|}d|Vi^!8f*C*r?pvXOWX91%_}ahr%K9W8$}W~& zf@Ng-_HkkBdV{Kc3KVb;Xo6Bd=hYMxoT?M~$=w!8YGnx}y<7_}lJ^ADP%}1C{=Cs5 zJve#ozfqCjhW`;&m!j{fe*^lZW0z<=7e|q3lw#k(E(NAyw<9!*#{m( z>%-m_rXw~y<0Y9-c}fRcDTDT&BW*P|#WBIcf9=Cy=BlF*-AS>Mcm-F<7WwH>68;R; zSazgGsTms80*KkcvvmI`wrNZpsdus5ICqJ+?Y%_>oBeX{a)^I~~)3&A)V6 z4QwO^3j93w zFD;1X!Qez<-I4Vbp6Lw*b?2V*m5osSEp7)&%^CwkCTy{lL_hvTM>b~CK*VHVC^TYD zECd>?cMvLX(k2!BMBSdkrgCrcWF?q&)u>4K`?qR+&e54EBjbCulCmTYW*zdt9u}{0*yOac%S5+ej!gC!vM$c$d629lwU1^Z zdoZ|3G_Wki%K%Okkrtil%*yAy0P2LU1v#ZE4GJ z26FKgcqMVn<&Dsx(}PH2lI_f7!o^_(RpnXKk^sF7V{>e}dTlISwZS@A8{_C{jKYYg zvILx^8qFm5A#XlZ8{!Q$Z3E!>1fC~wtip!?8Inx@^exeZJTj^;<=Ydj;M*`8Rk`E{BdNfz?(tu?uX+$A<&&VH&(cMd{ev^vsU98MjyE#*)$QA$h=9au1fa z0XN2sa~zOj5X#vNxkoQlr`MUL_oRM}4cj%kCpLa5^YMzP&Ysx<`-P;VrPB>=l!k0U zrn1iOR&Vy|xilqzhctQenm`ox7V$|D5316iCXHF@N>E3w0)kkFw{c3EOu}n_=msTn z=fnm%JWm;lQyNoi#?W~w*|)%%C$GgppQA1<6P2qCi}h-f6W*a`I%|Yp&;^Y@<2*&g zaLQu7)rt#aT%Nl8c$HA29CLZ& z;eLa;kRin(St3t~K(Sh~_-R7QhGijgEhBKZ?X}4WA^(W9Mz>=3X~cT# zQy!fFG#ru3iEa8sAw|dTGc&gp^OQhji$0*ZoxuCP9{#mRU36%2Mj9;uz(A!90$LYH z16zPOPgdk@@X9eadc!g-Wt5XuufDq1u_Mw)B`T>ab>BDpYK}ULZ zXRjU8!_OGDvP{Nd1Cj-ndQwv?RNX-GVUH#NMo>WdP(x+h>vCxdur+W38>cTf2AOLb zK0Xucis|ZBW2MP&P~M0kKjc1FKoR03QjPN55*e&bA|Y=W$Je zq>ny8M05!j5@iYoc38v*G|wW#+b*Y3*>PiuarXCD_Gt4(Or*6d!DjP)BHILqz+5~4 z!KqKyd?aYM`JBJgD6sk~`|{>ZZXd}~?;cma%F`=N7nGCmUZ>e7*PGvQ7+y_!I#{Jm zaJ5KtjXzSGbZzI`6;+>QFCn=~QO}9k?SQ$=OQvTNCRcKKx;O=A)Gcx=O^2Yi8$+raZz~`!iSBLb$V1 z>u~9q35*rgArvxh2^__xLp&`H&1;h%(kh+Yq&-9MD1%8)Mt++XuoI%TA8aS@(cPA} zvOFkO1-H*(nbLg+bg%+fHNb7!WNpa3qrN*RXq6Imyk@yiIrbqZcfg-8Rrfzdn3%wt z09ePxzEE>w@sy_>yMw~+^u}@@YjCVS!e&uDv!kk=;3)Ppb=%)R9n1jnMRoik1%@8# zbhO=2^GGX&twsE;%e&w!OurXCXvx*+hcx?9jNBXmNItVQTDZwl-Abq*E*%>!CmO>O zLjl-^bCq)cAO%>95=7)0F9&_(0h8@h<|6Fz~i)I3s%6 zla0vn9f41g=b$!bJKqwyjVPS^Y=;%dH!M9{_Njb7WaE;mPj&^BBsPo^jBaLJ0Buv?7j{9iGSom7AF8|-Z3 zd> zcuR)cMpbUc&0)k|t;t8Oz}9jdqz`q|huga8!zw*U92}h`v}$O3vht&$U`xG=CMjy} zYH)wzcAP-jXMe0dl-xgFC@$72GZ}PUSfHtV%yTSyd*D@s^Tvv&mXX#8`S3SwPitYF z<{-IF*j;wiX>9_%)TH|)S)uL==8t%N&4N)uTo=AVCMniVqg-&0uG^p%+@lE{tfJpexR%+}-~;XZ|7nbrCIJX54s2vdyl!-nyC_B2P$0Mn$sA0=6=fG|@k zGJja{WhN)mB67wT!RA0&P#$Zv=9$QyF|Tgj3-^FUuIMhdGk+WKIXHdA5lf#p=1TPO zYA6%&9#b2k`>4L5_~_A86v-Gl8>^isdMt;9W+6T1upwsZQN01|(H}vg|GpG>5X@Zl z7Ldv3+7~ME9J;v30I^(bI^MYGr7@uP7MB)lHtz9C9lM$v3$hCV*duVMF5L0=hZdU7 zrvG>aD-}B|>x0BRo5YO)IC*jujeOR~f3j|Wxq$C2b2hoO#tsLq6tMRJ35rDaoIU!c=QW z@XobHX2$&0@b`}qQI%gm*T+pjccb^ z%9mDOKSczU8t%u0eP1_4KAPyIvYl!2mT@XbNqM3F6R<$_pO_Xpj__nL>q8!WE6BA4 zZ9)50R}5|`2LgXvDETS@A%P7QA3x(}K5iG6WBPq@DN|y2MUE(m1T*Cf!>m4XUh*fV#+>n(Uy>I?BhFA94IXAwp|~>XJQwm`5yLQ~ z5@%POVP{;C#^^==IiK^Yu&CJlWKlgQ!D{NFh^Hn)=OhY#CeQ`T$Dp@vMaY=?vC{Je z|6DDf@3-fT@v?IHfpk}Pe*mr{3&SFKuASiF%(G{!H|I;8pYrZ?zDuz?o=4O({j5&q zs1rJWhtzfW+0=-=>0LJ*91L!rk$RKeZ0$m|Po7b*vZ*tUOUWD&X#`}yQ zk4)Qo)O1$frVk5&JIr4DRn2#wa-^+$mZ_1uLb32UTxKZurv(0Mm?c8pW+$-tJd`{` zGWiaA{Zv-@)1(b@5bVm(g^F5$%k%l@$BG{3j|l|MnF0vgF1{7Y`#{it9JA*7``5L^O&NfoGv-MEQCY8 ztLNhAjvxy>@J`88M6-V z7Y#=Z-6S5?GmS=#Z`3q(-6upU+U1=1j{e)w^p_i@KDYB#v$@G1iq8;XaQEvE^IS&z z8dmrE7ybU8bpZPbwoX5z0_BU_#&0x@N1U0~izIKH;n88~q@Vv2a4PuL8h@Swo+;MI z$x8n#s$A(d<$J_kh@l`tVisjbzX_tdlGj9x60<*-RBl!x$_-^aI4la0>+nC2R$D0w z0`MC&dLYaxv#?nYFN+D036wv6NKLd8?Hl1#Xx8VyMy|@kVQw|JpX>wpU)YJW$51Vx zZaB}Y$|8xVwq+7htKr>Q9=-?(iDF#!{z^5jai`E}M;}w$i_M64Sfpd@FsgxDv)=QVjjRjwVo0|KR44>-SNyzOzew81>UHXs9qO*a{+!E2-_=hxfxt`@ zLeMB1UThgVT;d^>Ssc1n3j}9pDi9b zC)-rvJb~}b6rp342tSr2E7Ta7RF6{D?82`kl2B|RA;NsAD(3}%bj~*vk6m>}R+JH3 z-XVsMDeZ)PQuqvpGh|$#QhYdi%5NAj*O|wowNLlfO zlYbEHkL%2T0kYx$t+p>4@%}{CmbejkMUfHC>CskTtXO>$0d;$cAL}#T_aNBhkm1di zj=pQ0YT71_q;g9534Ml~r)<=>Y>`tb!7tD;eeAGxA&$!$Ql~GL}vX{G8NXZd6@*cDh!34=&P|fg&r`<#jvQ5F~JB~mxK}Y9a8)CHw&d>T>N3jXya%!bX9U@;LKco1pP#1pC4#4=5KCTO;$k)}trf3$9m&qpO z(1nchxsK)?d#zq2PQN8csuub?3SNGj;!_>`yV4a8lcM0M3wnw*613eQARfpW`5A>I z4Mm$FxOolgDas**_7|I!iG)XZTk>?4jX4g8_9Fisk1l5;U(+m#Kc~13Dtv?BGSNTh zJxUpqAaPd~IcDO)Dw%{`4oaFW4@!6=?x7HMlOIv=;a3DELCp#m@ig)ALu%`$a7m^U zWJ<8bv1EbbH_MO6%-{ecVJg9&AM_m>8@$>tpAS(=A4P=N@LS?V1s$R==m{}GMpS0m zn38FEXU%N8(p%t>u@rFACsqvYLut}b^y!s7-uN`iV$*0k! zN*EI;S^r&Od~haL248j-KpO1SE-7@FT$9n)<)(0Ma3+%6#+i7IsYZmq$fJVrABio_ zV6Nfx4UDB)fe(zky4t!4JgvRRza}#(z76Pm%3jN!N6$&FqrOXO6-S28jd&&;d~jahqD)z=PN?z{YU%TY~Pe#cR$y=GiOnSjhe6p#_2S;`1|v|S5i z#Dlh6o#n5{ZMB~GnZ^xL+!IhL#Qv^DY^#*ZOGWQdJUP%r{-Q9Vk~t(@Kc5pKgZ2kk z!nC?e9+n6*$2DWQb(TL&6J8mTupJh2_M(3O@Sxl*65-fvXwfNoIQ9c&cT)XiFn98! z1D`KWa{{Kx685Y66)(I>3^pS^Ydmf84T{&;_p!bo@3VJcJQd9FKQqitLPjvwRpD=> zz=qsdJFMEvzHMgavl}EL+d_@dUu)eG|48R;DeyipX+mH(!pd{C#rRtmnz2mM6nE z#M&^}QTf+oI+!zm&_suvyA)>#y6}(*+~M9gxN7j8pNH|U{Fg?z%-uI?n&ZCp$l&zQ zwk_~HnIk{bYh?n;43m7mCOEwnS%0gidn;?!c*vEY*2rn}O%f%Qifz;Eu{ANGjE9|9 z{eOVAW@u~T3*K*y|3O%a2X_~`-=+}gX zXkoMk8}tT4+cR{o!m>CwXpV*MR!4Ha6=eI-!xz#FxELO+ZJ)yYhg6=!@NvUlV0qOH@LQU_y%UT zGfc;^D{S_glir~aeU-OGv+SQ?!^R~wD~`Mut-V@-JJ=}9d;Ld-i3T{dsh=!a_o;6x zUcBZ7grsKsc%I)T2Tk;1dtbMVAQ~m;qZugG-FNs85i<63XtLGIW5|1m3HgUQn};DK ztBt;GU>nK^yT)!A9+kBwr{UW4vfMU#S~^6x9;>JEdF5G4Cb9c1aBcyZFfp_?YIQZa zME$jasaFI(ooZYYMiNwnxB^Fu#QS#;atN6Bwm3?@w!l4t44pw;cJYflU|n9W2h$ zH@XAXgA~3cZX^)76majcLHTW`$$zt2)+P5x$!)Ye3V^A#Wj`n{@1~SSSbKVr>s(A>iwQ z0TL;m4?KWOYm`>e=7NTq&Y`OYHs)A;Wa5KFZQQ%BZuZ=eI~WJ$bSXZ|XxE z7D8i$10f!5xP;1*>3rCVwyxIb->js#2%634t36cq)f_Y?5UC)tO~<$accgd@I}3~o zK!i?~&v6ubXsbM>Tl9)Ik zODEBk06km6U(jAnd^7@qTxs?fAV0*5=Lfv*F}6LQQJzr~SdFCP;4#5-+MSMro~)_2 zoj|3sJ}@Y;{ZFA}u(MjXoq=d4abQD51`JA6NYU^lR#*cZwG}#Gya2nTFIoftaZ0T& znh~&)ytD$IG+UYSl4doCc0v@& z2SkZ#!Zv)H6lIb)Rj$?S%M3-r(JF5Wd|SY>3?;azOF=^MGGPpZ5Ss9aamb>u1yLhq z2~opPypUb9y0d|DSBJof+~#c%o%f2g(<_wLiQshdA?ygI?rd=;WQl@IGcjW5l-j5? zDAjOszuq#ksbhg6*m?^K z4Z#``Dxngl>C0&s%r3$ZU87J)U}PPYdxsuwfC~(v8hD*$xo`9-bOhuiwxAlmVmqow z4%<9xhUn6Zu1Q=gp!+fLL12wIs?pM--9VkFhDw#JOFV_q=Ei`}m%;E3WM|Rn8r2G4 z%@$~>+Y$@r7g{w(^AW`Pc^L(Mmz*Ybf-XxI2E8z%MrHj4KKEb0XfyC~5S+&GBZ zieMgBl?E_=To#^QFhaZ!pIVyaj)JAQiIar7#4`Xo?O1nG{Q*H2OjssRNpcbh2_>`{ z2;%(4yxAxy@D%2j33Xz;PIH}L6HL5-rl5G=ifMkdH%QCC)GKPkj}@*U7sI2FwIx7t zZ6dCK1gi0dYqXle z#I9l~s!JXt){yG^0}2*{c0Z*A%S5x`UI)d1{+I~`h0h#8tugN(TzjlpU_sS!XF(wX z=q`()z?)FbLJA)SLqJpeXdb2(!ab*ZCxq4>`)U))P;Ujztx+%-VRSmE;gt%{#YJMke<|U-<#NOXD|GOI6&dFZBt4D_?n`+J z1`BWU=qfr8;9Q*+x+N;;=-iGreL4+gT*JpP!pkc@7<31KFkj>4hrnh+M%Qdu?K`oP z>#7A9U$zvUcnLLFHDJRA!Zg8aKwiPEPmz{@+oUBJVw1*wY2kJdEIb~B@&GRs4Aj>V z5aNXfp2P#XNQgiL+&qJ0@(8E}3u_X5p9ZhS<8X&E&~*U6@HE~u_{^7&6TA%H0AFPA zCQQY_-vqz$Chy?A_%~ko^2wL~qw$Uk9(?&UUS^1ccMg8=rk95zn8`1!4nAY>Gk9)r z5d6G+7GK~W|E30ii2@~J_78rrM*hps;7wQ)Fu@0p^Ej7(dFr44_{Wd>=YQe%KYhsH jk$-ihfAxZ2|M}|?`1J_2)CUys25#}W8{!c4J> literal 0 HcmV?d00001 From 3a036bb19da00b201e30cfd24d7ff9080f64cf43 Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Thu, 25 Sep 2025 17:28:42 +1200 Subject: [PATCH 023/134] Update CMakeLists.txt --- resources/CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/resources/CMakeLists.txt b/resources/CMakeLists.txt index 355e34032..fd26558d7 100644 --- a/resources/CMakeLists.txt +++ b/resources/CMakeLists.txt @@ -95,6 +95,8 @@ add_packfile(dashfaction.vpp meshes/meatchunk4.v3m meshes/meatchunk5.v3m + audio/af_hitsound1.wav + audio/af_killsound1.wav # Dummy audio files to silence warnings audio/fp_shotgun_reload.wav "audio/Laser loop.wav" From e735be73fd3f97e5bda436814570eb3c6aac7a82 Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Thu, 25 Sep 2025 17:30:01 +1200 Subject: [PATCH 024/134] Update sound.h --- game_patch/rf/sound/sound.h | 1 + 1 file changed, 1 insertion(+) diff --git a/game_patch/rf/sound/sound.h b/game_patch/rf/sound/sound.h index 750a27320..172801b6f 100644 --- a/game_patch/rf/sound/sound.h +++ b/game_patch/rf/sound/sound.h @@ -103,4 +103,5 @@ namespace rf static auto snd_pc_load_hint = addr_as_ref(0x00543760); static auto snd_pc_is_ds3d_enabled = addr_as_ref(0x00544750); static auto snd_pc_calculate_pan = addr_as_ref(0x00543EA0); + static auto snd_get_handle = addr_as_ref(0x005054B0); } From 710cea3a6bb05000977cb004a06a9c1a255a8075 Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Thu, 25 Sep 2025 17:36:51 +1200 Subject: [PATCH 025/134] Update CHANGELOG.md --- docs/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 73220edb0..ce1da6e5e 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -6,6 +6,7 @@ Version 1.9.2 (not released yet) [@is-this-c](https://github.com/is-this-c) - Fix a potential crash after a client quits a game if Directd3D 11 is enabled - Improve compatibility with Alpine Faction servers +- Add `af_hitsound1.wav` and `af_killsound1.wav` from [@GooberRF](https://github.com/GooberRF) - Add `hit_sounds` cmd - Add `weapon_shake` cmd @@ -20,7 +21,6 @@ Version 1.9.1 (released 2025-07-05) [@is-this-c](https://github.com/is-this-c) - Fix Alpine Faction disguise option -- Build improvements Version 1.9.0 (released 2025-04-06) -------------------------------- From d3e1926ed2e5fa772a321073e843be1569923a17 Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Thu, 25 Sep 2025 17:40:56 +1200 Subject: [PATCH 026/134] Rename resources/audio/Laser loop.wav to resources/audio/dummy/Laser loop.wav --- resources/audio/{ => dummy}/Laser loop.wav | Bin 1 file changed, 0 insertions(+), 0 deletions(-) rename resources/audio/{ => dummy}/Laser loop.wav (100%) diff --git a/resources/audio/Laser loop.wav b/resources/audio/dummy/Laser loop.wav similarity index 100% rename from resources/audio/Laser loop.wav rename to resources/audio/dummy/Laser loop.wav From 0807f0c2a87b96d51f47b341be9a75bbb5834b90 Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Thu, 25 Sep 2025 17:41:17 +1200 Subject: [PATCH 027/134] Rename resources/audio/fp_shotgun_reload.wav to resources/audio/dummy/fp_shotgun_reload.wav --- resources/audio/{ => dummy}/fp_shotgun_reload.wav | Bin 1 file changed, 0 insertions(+), 0 deletions(-) rename resources/audio/{ => dummy}/fp_shotgun_reload.wav (100%) diff --git a/resources/audio/fp_shotgun_reload.wav b/resources/audio/dummy/fp_shotgun_reload.wav similarity index 100% rename from resources/audio/fp_shotgun_reload.wav rename to resources/audio/dummy/fp_shotgun_reload.wav From 2cb3215aae3db8550a0d581b05269ff1b2d65fe0 Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Thu, 25 Sep 2025 17:41:47 +1200 Subject: [PATCH 028/134] Rename resources/images/bigboom.vbm to resources/images/dummy/bigboom.vbm --- resources/images/bigboom.vbm | Bin 34 -> 0 bytes resources/images/dummy/bigboom.vbm | Bin 0 -> 35 bytes 2 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 resources/images/bigboom.vbm create mode 100644 resources/images/dummy/bigboom.vbm diff --git a/resources/images/bigboom.vbm b/resources/images/bigboom.vbm deleted file mode 100644 index 8956ddb478cfccff10a525144bd35ab025b1952f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 34 YcmdNZOUh+rU|@h@CL}hH$&jiE05D_%2><{9 diff --git a/resources/images/dummy/bigboom.vbm b/resources/images/dummy/bigboom.vbm new file mode 100644 index 0000000000000000000000000000000000000000..e6d360c16f6c2760a20af1ce10ffbaac7521fdf4 GIT binary patch literal 35 ZcmdNZOUh+rU|@h@CL}hH$&jka1pqR40uulL literal 0 HcmV?d00001 From 6b8c72a7fe6b5a4c1183870b0a0d10327b4ac4e4 Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Thu, 25 Sep 2025 17:43:18 +1200 Subject: [PATCH 029/134] Update CMakeLists.txt --- resources/CMakeLists.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/resources/CMakeLists.txt b/resources/CMakeLists.txt index fd26558d7..2d6465372 100644 --- a/resources/CMakeLists.txt +++ b/resources/CMakeLists.txt @@ -75,7 +75,7 @@ add_packfile(dashfaction.vpp images/pls_airlockmat01-mip2.tga images/consolebutton2A.tga images/gore1.tga # https://www.filterforge.com/filters/7459.html - images/bigboom.vbm # Dummy bitmap to silence warning + images/dummmy/bigboom.vbm # Dummy bitmap to silence warning fonts/biggerfont.vf fonts/regularfont.ttf @@ -98,8 +98,8 @@ add_packfile(dashfaction.vpp audio/af_hitsound1.wav audio/af_killsound1.wav # Dummy audio files to silence warnings - audio/fp_shotgun_reload.wav - "audio/Laser loop.wav" + audio/dummy/fp_shotgun_reload.wav + "audio/dummy/Laser loop.wav" standard_vs:${CMAKE_BINARY_DIR}/shaders/standard_vs.bin character_vs:${CMAKE_BINARY_DIR}/shaders/character_vs.bin From 51da624b8b7b55692d5aece389f40d467542e9e3 Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Thu, 25 Sep 2025 17:52:23 +1200 Subject: [PATCH 030/134] Update CMakeLists.txt --- resources/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/CMakeLists.txt b/resources/CMakeLists.txt index 2d6465372..64a6f09eb 100644 --- a/resources/CMakeLists.txt +++ b/resources/CMakeLists.txt @@ -75,7 +75,7 @@ add_packfile(dashfaction.vpp images/pls_airlockmat01-mip2.tga images/consolebutton2A.tga images/gore1.tga # https://www.filterforge.com/filters/7459.html - images/dummmy/bigboom.vbm # Dummy bitmap to silence warning + images/dummy/bigboom.vbm # Dummy bitmap to silence warning fonts/biggerfont.vf fonts/regularfont.ttf From 95d65f1a3b874ee6f6f08e9ba079e0e160809c3d Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Thu, 25 Sep 2025 20:09:55 +1200 Subject: [PATCH 031/134] Update network.cpp --- game_patch/multi/network.cpp | 56 ++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/game_patch/multi/network.cpp b/game_patch/multi/network.cpp index 161792c6f..e716d94d5 100644 --- a/game_patch/multi/network.cpp +++ b/game_patch/multi/network.cpp @@ -835,6 +835,60 @@ CodeInjection process_join_accept_injection{ } }; +ConsoleCommand2 dbg_server_flags_cmd{ + "dbg_server_flags", + [] { + if (g_df_server_info.has_value()) { + const DashFactionServerInfo& df_server_info = g_df_server_info.value(); + rf::console::print("===================="); + rf::console::print( + "Dash Faction {}.{}", + df_server_info.version_major, + df_server_info.version_minor + ); + rf::console::print("===================="); + rf::console::print("saving_enabled: {}", df_server_info.saving_enabled); + if (df_server_info.max_fov.has_value()) { + rf::console::print("max_fov: Some({})", df_server_info.max_fov.value()); + } else { + rf::console::print("max_fov: None"); + } + } else if (g_af_server_info.has_value()) { + const AlpineFactionServerInfo& af_server_info = g_af_server_info.value(); + rf::console::print("===================="); + rf::console::print( + "Alpine Faction {}.{}", + af_server_info.version_major, + af_server_info.version_minor + ); + rf::console::print("===================="); + rf::console::print("saving_enabled: {}", af_server_info.saving_enabled); + if (af_server_info.max_fov.has_value()) { + rf::console::print("max_fov: Some({})", af_server_info.max_fov.value()); + } else { + rf::console::print("max_fov: None"); + } + rf::console::print("allow_fb_mesh: {}", af_server_info.allow_fb_mesh); + rf::console::print("allow_lmap: {}", af_server_info.allow_lmap); + rf::console::print("allow_no_ss: {}", af_server_info.allow_no_ss); + rf::console::print("no_player_collide: {}", af_server_info.no_player_collide); + rf::console::print("allow_no_mf: {}", af_server_info.allow_no_mf); + rf::console::print("click_limit: {}", af_server_info.click_limit); + if (af_server_info.semi_auto_cooldown.has_value()) { + rf::console::print( + "semi_auto_cooldown: Some({})", + af_server_info.semi_auto_cooldown.value() + ); + } else { + rf::console::print("semi_auto_cooldown: None"); + } + rf::console::print("unlimited_fps: {}", af_server_info.unlimited_fps); + rf::console::print("gaussian_spread: {}", af_server_info.gaussian_spread); + rf::console::print("location_pinging: {}", af_server_info.location_pinging); + } + } +}; + CodeInjection process_join_accept_send_game_info_req_injection{ 0x0047AA00, [](auto& regs) { @@ -1408,4 +1462,6 @@ void network_init() // Ignore browsers when calculating player count for info requests game_info_num_players_hook.install(); + + dbg_server_flags_cmd.register_cmd(); } From 6a038d7fee48cf88b3001ab4dbc21996ced08637 Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Thu, 25 Sep 2025 20:12:09 +1200 Subject: [PATCH 032/134] Update CHANGELOG.md --- docs/CHANGELOG.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index ce1da6e5e..79021b374 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -7,8 +7,9 @@ Version 1.9.2 (not released yet) - Fix a potential crash after a client quits a game if Directd3D 11 is enabled - Improve compatibility with Alpine Faction servers - Add `af_hitsound1.wav` and `af_killsound1.wav` from [@GooberRF](https://github.com/GooberRF) -- Add `hit_sounds` cmd -- Add `weapon_shake` cmd +- Add `hit_sounds` command +- Add `weapon_shake` command +- Add `dbg_server_flags` command Version 1.9.1 (released 2025-07-05) -------------------------------- From e2fda8f7ef2eb78414c405458122aa93536a82aa Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Sat, 27 Sep 2025 17:12:10 +1200 Subject: [PATCH 033/134] Update network.cpp --- game_patch/multi/network.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/game_patch/multi/network.cpp b/game_patch/multi/network.cpp index e716d94d5..eb1b1e2c2 100644 --- a/game_patch/multi/network.cpp +++ b/game_patch/multi/network.cpp @@ -835,8 +835,8 @@ CodeInjection process_join_accept_injection{ } }; -ConsoleCommand2 dbg_server_flags_cmd{ - "dbg_server_flags", +ConsoleCommand2 dbg_remote_flags_cmd{ + "dbg_remote_flags", [] { if (g_df_server_info.has_value()) { const DashFactionServerInfo& df_server_info = g_df_server_info.value(); @@ -1463,5 +1463,5 @@ void network_init() // Ignore browsers when calculating player count for info requests game_info_num_players_hook.install(); - dbg_server_flags_cmd.register_cmd(); + dbg_remote_flags_cmd.register_cmd(); } From e1bb9c32a71b76ccdab603b7da8c44078ce505a4 Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Sun, 28 Sep 2025 04:13:42 +1300 Subject: [PATCH 034/134] Update save_restore.cpp --- game_patch/misc/save_restore.cpp | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/game_patch/misc/save_restore.cpp b/game_patch/misc/save_restore.cpp index aef3894c8..b46904684 100644 --- a/game_patch/misc/save_restore.cpp +++ b/game_patch/misc/save_restore.cpp @@ -254,16 +254,10 @@ CodeInjection corpse_deserialize_all_obj_create_patch{ }; bool server_saving_enabled() { - const bool df_saving_enabled = get_df_server_info() - .transform([] (const DashFactionServerInfo& df_server_info) { - return df_server_info.saving_enabled; - }) - .value_or(false); - const bool af_saving_enabled = get_af_server_info() - .transform([] (const AlpineFactionServerInfo& af_server_info) { - return af_server_info.saving_enabled; - }) - .value_or(false); + const bool df_saving_enabled = get_df_server_info().has_value() + && get_df_server_info().value().saving_enabled; + const bool af_saving_enabled = get_af_server_info().has_value() + && get_af_server_info().value().saving_enabled; return rf::is_multi && !rf::is_server && (df_saving_enabled || af_saving_enabled); } From f6f9a89b10489b19a0578f2d965bc2fbd7afc488 Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Sun, 28 Sep 2025 16:17:56 +1300 Subject: [PATCH 035/134] Update CHANGELOG.md --- docs/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 79021b374..2af38d795 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -9,7 +9,7 @@ Version 1.9.2 (not released yet) - Add `af_hitsound1.wav` and `af_killsound1.wav` from [@GooberRF](https://github.com/GooberRF) - Add `hit_sounds` command - Add `weapon_shake` command -- Add `dbg_server_flags` command +- Add `dbg_remote_flags` command Version 1.9.1 (released 2025-07-05) -------------------------------- From 97b328adb0fc49da06074b73daf3d5a6e5afcc34 Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Sun, 28 Sep 2025 17:04:50 +1300 Subject: [PATCH 036/134] Update licensing-info.txt --- resources/licensing-info.txt | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/resources/licensing-info.txt b/resources/licensing-info.txt index 1274e7332..1e9d23d93 100644 --- a/resources/licensing-info.txt +++ b/resources/licensing-info.txt @@ -18,24 +18,38 @@ during each Dash Faction release process by the owner of the project. Dash Faction uses some open-source third-party components. Their licenses can be found below. +--------------------------------- +Assets +--------------------------------- + +natalie : +* af_hitsound1.wav + Source: https://github.com/GooberRF/alpinefaction + +soneproject: +* af_killsound1.wav + Title: ecofuture3 + Source: https://freesound.org/s/346425 + License: https://creativecommons.org/licenses/by/3.0 + --------------------------------- Contributors --------------------------------- Chris "Goober" Parsons: -* code contributions -* HUD textures for the big mode -* symbol glyphs in ttf fonts -* launcher header +* Code contributions +* Textures for `bighud` +* Symbol glyphs in TTF fonts +* Launcher header natalie : -* code contributions +* Code contributions is-this-c: -* code contributions +* Code contributions Mystyle: -* setup code improvements +* Setup code improvements ############################################################################### ## subhook From f61afe574bda93ef717fbd88b3aae9cff50175a3 Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Sun, 28 Sep 2025 17:12:45 +1300 Subject: [PATCH 037/134] Update CHANGELOG.md --- docs/CHANGELOG.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 2af38d795..f5c20d001 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -6,7 +6,7 @@ Version 1.9.2 (not released yet) [@is-this-c](https://github.com/is-this-c) - Fix a potential crash after a client quits a game if Directd3D 11 is enabled - Improve compatibility with Alpine Faction servers -- Add `af_hitsound1.wav` and `af_killsound1.wav` from [@GooberRF](https://github.com/GooberRF) +- Add `af_hitsound1.wav` and `af_killsound1.wav` from [Alpine Faction](https://github.com/GooberRF/alpinefaction) - Add `hit_sounds` command - Add `weapon_shake` command - Add `dbg_remote_flags` command @@ -14,10 +14,10 @@ Version 1.9.2 (not released yet) Version 1.9.1 (released 2025-07-05) -------------------------------- [@rafalh](https://github.com/rafalh) -- Fix crash when loading levels in version 300 with `AF_Teleport_Player` or `Clone_Entity` events +- Fix a crash when loading levels in version 300 with `AF_Teleport_Player` or `Clone_Entity` events - Add basic handling for `AF_Teleport_Player` event - Add a new editor help file that includes information from the original red.hlp included with the game and the newer red.chm (RED help 1.1) published online -- Add option to ignore level version +- Add an option to ignore level version - Fix warnings in main menu caused by missing files (e.g. bigboom.vbm) [@is-this-c](https://github.com/is-this-c) @@ -68,9 +68,9 @@ Version 1.9.0 (released 2025-04-06) - Fix items being too dim in multi-player (RF bug) - Fix clutters and items being too bright when static mesh lighting was enabled - Add level property which enables full lightmaps depth (enabled by default in new maps) -- Add option to disguise as Alpine Faction to avoids AF advertisements and allow playing in servers that reject non-AF players -- Enable loading RFL version 300 -- Log a warning when RFL uses unsupported event class +- Add an option to disguise as Alpine Faction to avoid AF advertisements and allow playing in servers that reject non-AF players +- Enable loading partially supported RFL version 300 +- Log a warning when RFL uses an unsupported event class - Add support for high resolution GeoMod textures - Add gibs support based on RF PS2 demo (use `gibs` command to enable) - Add muzzle flash option in the launcher From 96a8cc7d0e20a75119c2c827707ccad2ababb672 Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Sun, 28 Sep 2025 17:13:52 +1300 Subject: [PATCH 038/134] Update CHANGELOG.md --- docs/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index f5c20d001..b2ad631ed 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -6,7 +6,7 @@ Version 1.9.2 (not released yet) [@is-this-c](https://github.com/is-this-c) - Fix a potential crash after a client quits a game if Directd3D 11 is enabled - Improve compatibility with Alpine Faction servers -- Add `af_hitsound1.wav` and `af_killsound1.wav` from [Alpine Faction](https://github.com/GooberRF/alpinefaction) +- Add `af_hitsound1.wav` and `af_killsound1.wav` from Alpine Faction - Add `hit_sounds` command - Add `weapon_shake` command - Add `dbg_remote_flags` command From 550a9bbf5873f2e6d534e126914a79b863d1c3d6 Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Sun, 28 Sep 2025 17:32:19 +1300 Subject: [PATCH 039/134] Update camera.cpp --- game_patch/misc/camera.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/game_patch/misc/camera.cpp b/game_patch/misc/camera.cpp index 192032f81..006906448 100644 --- a/game_patch/misc/camera.cpp +++ b/game_patch/misc/camera.cpp @@ -27,11 +27,9 @@ FunHook camera_update_shake_hook{ CallHook entity_fire_primary_weapon_camera_shake_hook{ 0x00426C79, [] (rf::Camera* const cp, const float amplitude, const float time_seconds) { - const bool server_force_weapon_shake = rf::is_multi - && !rf::is_server - && get_af_server_info() - && !get_af_server_info()->allow_no_ss; - if (g_game_config.weapon_shake || server_force_weapon_shake) { + const bool remote_force_weapon_shake = get_af_server_info().has_value() + && !get_af_server_info().value().allow_no_ss; + if (g_game_config.weapon_shake || remote_force_weapon_shake) { entity_fire_primary_weapon_camera_shake_hook.call_target(cp, amplitude, time_seconds); } }, From 123da354498b6725c00f31c930531a343a665163 Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Sun, 28 Sep 2025 17:35:38 +1300 Subject: [PATCH 040/134] Update save_restore.cpp --- game_patch/misc/save_restore.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/game_patch/misc/save_restore.cpp b/game_patch/misc/save_restore.cpp index b46904684..314cba56b 100644 --- a/game_patch/misc/save_restore.cpp +++ b/game_patch/misc/save_restore.cpp @@ -253,19 +253,19 @@ CodeInjection corpse_deserialize_all_obj_create_patch{ }, }; -bool server_saving_enabled() { +bool remote_saving_enabled() { const bool df_saving_enabled = get_df_server_info().has_value() && get_df_server_info().value().saving_enabled; const bool af_saving_enabled = get_af_server_info().has_value() && get_af_server_info().value().saving_enabled; - return rf::is_multi && !rf::is_server && (df_saving_enabled || af_saving_enabled); + return df_saving_enabled || af_saving_enabled; } FunHook quick_save_hook{ 0x004B6160, []() { quick_save_hook.call_target(); - if (server_saving_enabled()) { + if (remote_saving_enabled()) { send_chat_line_packet("/save", nullptr); } }, @@ -275,7 +275,7 @@ FunHook quick_load_hook{ 0x004B6180, []() { quick_load_hook.call_target(); - if (server_saving_enabled()) { + if (remote_saving_enabled()) { send_chat_line_packet("/load", nullptr); } }, From 305898578ea6f5e7273ca7da623f14987093f0b8 Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Sun, 28 Sep 2025 17:37:46 +1300 Subject: [PATCH 041/134] Update entity.cpp --- game_patch/object/entity.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/game_patch/object/entity.cpp b/game_patch/object/entity.cpp index bf631a37d..7867e615f 100644 --- a/game_patch/object/entity.cpp +++ b/game_patch/object/entity.cpp @@ -280,11 +280,9 @@ static FunHook entity_damage_hook{ CallHook entity_update_muzzle_flash_light_hook{ 0x0041E814, [] (rf::Entity& ep) { - const bool server_force_muzzle_flash = rf::is_multi - && !rf::is_server - && get_af_server_info() - && !get_af_server_info()->allow_no_mf; - if (g_game_config.muzzle_flash || server_force_muzzle_flash) { + const bool remote_force_muzzle_flash = get_af_server_info().has_value() + && !get_af_server_info().value().allow_no_mf; + if (g_game_config.muzzle_flash || remote_force_muzzle_flash) { entity_update_muzzle_flash_light_hook.call_target(ep); } }, From ad48e217745ca4539ab4035fb692f3bd57a3ee2e Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Sun, 28 Sep 2025 17:39:21 +1300 Subject: [PATCH 042/134] Update weapon.cpp --- game_patch/object/weapon.cpp | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/game_patch/object/weapon.cpp b/game_patch/object/weapon.cpp index 2840e4987..0e0e3c37c 100644 --- a/game_patch/object/weapon.cpp +++ b/game_patch/object/weapon.cpp @@ -126,16 +126,14 @@ CallHook weapon_hit_wall_obj_apply_r }, }; -bool server_click_limit() { - if (rf::is_multi - && get_af_server_info().has_value() - && get_af_server_info().value().click_limit) { +bool remote_click_limit() { + if (get_af_server_info().has_value() && get_af_server_info().value().click_limit) { return true; } return false; } -std::optional server_fire_wait_override() { +std::optional remote_fire_wait_override() { return get_af_server_info().and_then([] (const AlpineFactionServerInfo& af_server_info) { return af_server_info.semi_auto_cooldown; }); @@ -145,7 +143,7 @@ CodeInjection player_fire_primary_weapon_semi_auto_patch{ 0x004A50BB, [] (auto& regs) { const rf::Entity* const entity = regs.esi; - if (server_click_limit() && !entity->ai.next_fire_primary.elapsed()) { + if (remote_click_limit() && !entity->ai.next_fire_primary.elapsed()) { regs.eip = 0x004A58B8; } }, @@ -158,11 +156,11 @@ CodeInjection entity_fire_primary_weapon_semi_auto_patch{ BaseCodeInjection::Reg& fire_wait = regs.eax; const int weapon_type = regs.ebx; const rf::Entity* const entity = regs.esi; - if (server_fire_wait_override().has_value() + if (remote_fire_wait_override().has_value() && rf::obj_is_player(entity) && rf::weapon_is_semi_automatic(weapon_type) && fire_wait == 500) { - fire_wait = server_fire_wait_override().value(); + fire_wait = remote_fire_wait_override().value(); } }, }; From 633812f960e68e81ebeb257786a9227b7794a668 Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Sun, 28 Sep 2025 20:36:00 +1300 Subject: [PATCH 043/134] Rename variables --- game_patch/graphics/gr.cpp | 32 ++++----- game_patch/misc/camera.cpp | 4 +- game_patch/misc/save_restore.cpp | 8 +-- game_patch/multi/multi.h | 8 +-- game_patch/multi/network.cpp | 114 +++++++++++++++---------------- game_patch/multi/server.cpp | 2 +- game_patch/object/entity.cpp | 4 +- game_patch/object/weapon.cpp | 6 +- 8 files changed, 89 insertions(+), 89 deletions(-) diff --git a/game_patch/graphics/gr.cpp b/game_patch/graphics/gr.cpp index dcb833729..90a188967 100644 --- a/game_patch/graphics/gr.cpp +++ b/game_patch/graphics/gr.cpp @@ -82,15 +82,15 @@ float gr_scale_world_fov(float horizontal_fov = 90.0f) horizontal_fov = gr_scale_fov_hor_plus(horizontal_fov); } - if (get_df_server_info().has_value()) { - const DashFactionServerInfo& df_server_info = get_df_server_info().value(); - if (df_server_info.max_fov.has_value()) { - rf::console::print("Server FOV limit: {:.2f}", df_server_info.max_fov.value()); + if (get_df_remote_info().has_value()) { + const DashFactionRemoteInfo& df_remote_info = get_df_remote_info().value(); + if (df_remote_info.max_fov.has_value()) { + rf::console::print("Server FOV limit: {:.2f}", df_remote_info.max_fov.value()); } - } else if (get_af_server_info().has_value()) { - const AlpineFactionServerInfo& af_server_info = get_af_server_info().value(); - if (af_server_info.max_fov.has_value()) { - rf::console::print("Server FOV limit: {:.2f}", af_server_info.max_fov.value()); + } else if (get_af_remote_info().has_value()) { + const AlpineFactionRemoteInfo& af_remote_info = get_af_remote_info().value(); + if (af_remote_info.max_fov.has_value()) { + rf::console::print("Server FOV limit: {:.2f}", af_remote_info.max_fov.value()); } } @@ -131,15 +131,15 @@ ConsoleCommand2 fov_cmd{ } rf::console::print("Horizontal FOV: {:.2f}", gr_scale_world_fov()); - if (get_df_server_info().has_value()) { - const DashFactionServerInfo& df_server_info = get_df_server_info().value(); - if (df_server_info.max_fov.has_value()) { - rf::console::print("Server FOV limit: {:.2f}", df_server_info.max_fov.value()); + if (get_df_remote_info().has_value()) { + const DashFactionRemoteInfo& df_remote_info = get_df_remote_info().value(); + if (df_remote_info.max_fov.has_value()) { + rf::console::print("Server FOV limit: {:.2f}", df_remote_info.max_fov.value()); } - } else if (get_af_server_info().has_value()) { - const AlpineFactionServerInfo& af_server_info = get_af_server_info().value(); - if (af_server_info.max_fov.has_value()) { - rf::console::print("Server FOV limit: {:.2f}", af_server_info.max_fov.value()); + } else if (get_af_remote_info().has_value()) { + const AlpineFactionRemoteInfo& af_remote_info = get_af_remote_info().value(); + if (af_remote_info.max_fov.has_value()) { + rf::console::print("Server FOV limit: {:.2f}", af_remote_info.max_fov.value()); } } }, diff --git a/game_patch/misc/camera.cpp b/game_patch/misc/camera.cpp index 006906448..469ee438c 100644 --- a/game_patch/misc/camera.cpp +++ b/game_patch/misc/camera.cpp @@ -27,8 +27,8 @@ FunHook camera_update_shake_hook{ CallHook entity_fire_primary_weapon_camera_shake_hook{ 0x00426C79, [] (rf::Camera* const cp, const float amplitude, const float time_seconds) { - const bool remote_force_weapon_shake = get_af_server_info().has_value() - && !get_af_server_info().value().allow_no_ss; + const bool remote_force_weapon_shake = get_af_remote_info().has_value() + && !get_af_remote_info().value().allow_no_ss; if (g_game_config.weapon_shake || remote_force_weapon_shake) { entity_fire_primary_weapon_camera_shake_hook.call_target(cp, amplitude, time_seconds); } diff --git a/game_patch/misc/save_restore.cpp b/game_patch/misc/save_restore.cpp index 314cba56b..1a9cd63c3 100644 --- a/game_patch/misc/save_restore.cpp +++ b/game_patch/misc/save_restore.cpp @@ -254,10 +254,10 @@ CodeInjection corpse_deserialize_all_obj_create_patch{ }; bool remote_saving_enabled() { - const bool df_saving_enabled = get_df_server_info().has_value() - && get_df_server_info().value().saving_enabled; - const bool af_saving_enabled = get_af_server_info().has_value() - && get_af_server_info().value().saving_enabled; + const bool df_saving_enabled = get_df_remote_info().has_value() + && get_df_remote_info().value().saving_enabled; + const bool af_saving_enabled = get_af_remote_info().has_value() + && get_af_remote_info().value().saving_enabled; return df_saving_enabled || af_saving_enabled; } diff --git a/game_patch/multi/multi.h b/game_patch/multi/multi.h index 4f563e35c..5f319f2bc 100644 --- a/game_patch/multi/multi.h +++ b/game_patch/multi/multi.h @@ -73,7 +73,7 @@ struct PlayerStatsNew : rf::PlayerLevelStats } }; -struct DashFactionServerInfo +struct DashFactionRemoteInfo { uint8_t version_major = 0; uint8_t version_minor = 0; @@ -81,7 +81,7 @@ struct DashFactionServerInfo std::optional max_fov; }; -struct AlpineFactionServerInfo { +struct AlpineFactionRemoteInfo { uint8_t version_major = 0; uint8_t version_minor = 0; bool saving_enabled = false; @@ -103,8 +103,8 @@ void multi_do_patch(); void multi_after_full_game_init(); void multi_init_player(rf::Player* player); void send_chat_line_packet(const char* msg, rf::Player* target, rf::Player* sender = nullptr, bool is_team_msg = false); -const std::optional& get_df_server_info(); -const std::optional& get_af_server_info(); +const std::optional& get_df_remote_info(); +const std::optional& get_af_remote_info(); void multi_level_download_do_frame(); void multi_level_download_abort(); void multi_ban_apply_patch(); diff --git a/game_patch/multi/network.cpp b/game_patch/multi/network.cpp index eb1b1e2c2..9a2397a2b 100644 --- a/game_patch/multi/network.cpp +++ b/game_patch/multi/network.cpp @@ -262,8 +262,8 @@ std::array g_client_side_packet_whitelist{ }; // clang-format on -std::optional g_df_server_info{}; -std::optional g_af_server_info{}; +std::optional g_df_remote_info{}; +std::optional g_af_remote_info{}; CodeInjection process_game_packet_whitelist_filter{ 0x0047918D, @@ -772,16 +772,16 @@ bool try_process_df_join_accept_tail(const std::byte* const tail) { ext.version_minor, static_cast(ext.flags) ); - DashFactionServerInfo server_info{}; - server_info.version_major = ext.version_major; - server_info.version_minor = ext.version_minor; + DashFactionRemoteInfo remote_info{}; + remote_info.version_major = ext.version_major; + remote_info.version_minor = ext.version_minor; using Flags = DashFactionJoinAcceptPacketExt::Flags; - server_info.saving_enabled = !!(ext.flags & Flags::saving_enabled); + remote_info.saving_enabled = !!(ext.flags & Flags::saving_enabled); constexpr const float default_fov = 90.f; if (!!(ext.flags & Flags::max_fov) && ext.max_fov >= default_fov) { - server_info.max_fov = std::optional{ext.max_fov}; + remote_info.max_fov = std::optional{ext.max_fov}; } - g_df_server_info.emplace(server_info); + g_df_remote_info.emplace(remote_info); return true; } @@ -798,28 +798,28 @@ bool try_process_af_join_accept_tail(const std::byte* const tail) { ext.version_minor, static_cast(ext.flags) ); - AlpineFactionServerInfo server_info{}; - server_info.version_major = ext.version_major; - server_info.version_minor = ext.version_minor; + AlpineFactionRemoteInfo remote_info{}; + remote_info.version_major = ext.version_major; + remote_info.version_minor = ext.version_minor; using Flags = AlpineFactionJoinAcceptPacketExt::Flags; - server_info.saving_enabled = !!(ext.flags & Flags::saving_enabled); - server_info.allow_fb_mesh = !!(ext.flags & Flags::allow_fb_mesh); - server_info.allow_lmap = !!(ext.flags & Flags::allow_lmap); - server_info.allow_no_ss = !!(ext.flags & Flags::allow_no_ss); - server_info.no_player_collide = !!(ext.flags & Flags::no_player_collide); - server_info.allow_no_mf = !!(ext.flags & Flags::allow_no_mf); - server_info.click_limit = !!(ext.flags & Flags::click_limit); - server_info.unlimited_fps = !!(ext.flags & Flags::unlimited_fps); - server_info.gaussian_spread = !!(ext.flags & Flags::gaussian_spread); - server_info.location_pinging = !!(ext.flags & Flags::location_pinging); + remote_info.saving_enabled = !!(ext.flags & Flags::saving_enabled); + remote_info.allow_fb_mesh = !!(ext.flags & Flags::allow_fb_mesh); + remote_info.allow_lmap = !!(ext.flags & Flags::allow_lmap); + remote_info.allow_no_ss = !!(ext.flags & Flags::allow_no_ss); + remote_info.no_player_collide = !!(ext.flags & Flags::no_player_collide); + remote_info.allow_no_mf = !!(ext.flags & Flags::allow_no_mf); + remote_info.click_limit = !!(ext.flags & Flags::click_limit); + remote_info.unlimited_fps = !!(ext.flags & Flags::unlimited_fps); + remote_info.gaussian_spread = !!(ext.flags & Flags::gaussian_spread); + remote_info.location_pinging = !!(ext.flags & Flags::location_pinging); constexpr const float default_fov = 90.f; if (!!(ext.flags & Flags::max_fov) && ext.max_fov >= default_fov) { - server_info.max_fov = std::optional{ext.max_fov}; + remote_info.max_fov = std::optional{ext.max_fov}; } - if (server_info.click_limit) { - server_info.semi_auto_cooldown = std::optional{ext.semi_auto_cooldown}; + if (remote_info.click_limit) { + remote_info.semi_auto_cooldown = std::optional{ext.semi_auto_cooldown}; } - g_af_server_info.emplace(server_info); + g_af_remote_info.emplace(remote_info); return true; } @@ -838,53 +838,53 @@ CodeInjection process_join_accept_injection{ ConsoleCommand2 dbg_remote_flags_cmd{ "dbg_remote_flags", [] { - if (g_df_server_info.has_value()) { - const DashFactionServerInfo& df_server_info = g_df_server_info.value(); + if (g_df_remote_info.has_value()) { + const DashFactionRemoteInfo& df_remote_info = g_df_remote_info.value(); rf::console::print("===================="); rf::console::print( "Dash Faction {}.{}", - df_server_info.version_major, - df_server_info.version_minor + df_remote_info.version_major, + df_remote_info.version_minor ); rf::console::print("===================="); - rf::console::print("saving_enabled: {}", df_server_info.saving_enabled); - if (df_server_info.max_fov.has_value()) { - rf::console::print("max_fov: Some({})", df_server_info.max_fov.value()); + rf::console::print("saving_enabled: {}", df_remote_info.saving_enabled); + if (df_remote_info.max_fov.has_value()) { + rf::console::print("max_fov: Some({})", df_remote_info.max_fov.value()); } else { rf::console::print("max_fov: None"); } - } else if (g_af_server_info.has_value()) { - const AlpineFactionServerInfo& af_server_info = g_af_server_info.value(); + } else if (g_af_remote_info.has_value()) { + const AlpineFactionRemoteInfo& af_remote_info = g_af_remote_info.value(); rf::console::print("===================="); rf::console::print( "Alpine Faction {}.{}", - af_server_info.version_major, - af_server_info.version_minor + af_remote_info.version_major, + af_remote_info.version_minor ); rf::console::print("===================="); - rf::console::print("saving_enabled: {}", af_server_info.saving_enabled); - if (af_server_info.max_fov.has_value()) { - rf::console::print("max_fov: Some({})", af_server_info.max_fov.value()); + rf::console::print("saving_enabled: {}", af_remote_info.saving_enabled); + if (af_remote_info.max_fov.has_value()) { + rf::console::print("max_fov: Some({})", af_remote_info.max_fov.value()); } else { rf::console::print("max_fov: None"); } - rf::console::print("allow_fb_mesh: {}", af_server_info.allow_fb_mesh); - rf::console::print("allow_lmap: {}", af_server_info.allow_lmap); - rf::console::print("allow_no_ss: {}", af_server_info.allow_no_ss); - rf::console::print("no_player_collide: {}", af_server_info.no_player_collide); - rf::console::print("allow_no_mf: {}", af_server_info.allow_no_mf); - rf::console::print("click_limit: {}", af_server_info.click_limit); - if (af_server_info.semi_auto_cooldown.has_value()) { + rf::console::print("allow_fb_mesh: {}", af_remote_info.allow_fb_mesh); + rf::console::print("allow_lmap: {}", af_remote_info.allow_lmap); + rf::console::print("allow_no_ss: {}", af_remote_info.allow_no_ss); + rf::console::print("no_player_collide: {}", af_remote_info.no_player_collide); + rf::console::print("allow_no_mf: {}", af_remote_info.allow_no_mf); + rf::console::print("click_limit: {}", af_remote_info.click_limit); + if (af_remote_info.semi_auto_cooldown.has_value()) { rf::console::print( "semi_auto_cooldown: Some({})", - af_server_info.semi_auto_cooldown.value() + af_remote_info.semi_auto_cooldown.value() ); } else { rf::console::print("semi_auto_cooldown: None"); } - rf::console::print("unlimited_fps: {}", af_server_info.unlimited_fps); - rf::console::print("gaussian_spread: {}", af_server_info.gaussian_spread); - rf::console::print("location_pinging: {}", af_server_info.location_pinging); + rf::console::print("unlimited_fps: {}", af_remote_info.unlimited_fps); + rf::console::print("gaussian_spread: {}", af_remote_info.gaussian_spread); + rf::console::print("location_pinging: {}", af_remote_info.location_pinging); } } }; @@ -1048,8 +1048,8 @@ FunHook multi_stop_hook{ 0x0046E2C0, []() { // Clear server info when leaving - g_df_server_info.reset(); - g_af_server_info.reset(); + g_df_remote_info.reset(); + g_af_remote_info.reset(); multi_stop_hook.call_target(); if (rf::local_player) { reset_player_additional_data(rf::local_player); @@ -1057,13 +1057,13 @@ FunHook multi_stop_hook{ }, }; -const std::optional& get_df_server_info() +const std::optional& get_df_remote_info() { - return g_df_server_info; + return g_df_remote_info; } -const std::optional& get_af_server_info() { - return g_af_server_info; +const std::optional& get_af_remote_info() { + return g_af_remote_info; } void send_chat_line_packet(const char* msg, rf::Player* target, rf::Player* sender, bool is_team_msg) @@ -1250,7 +1250,7 @@ bool af_process_packet( default: { return false; } - } + } return true; } diff --git a/game_patch/multi/server.cpp b/game_patch/multi/server.cpp index 503b3b612..ba18da0c4 100644 --- a/game_patch/multi/server.cpp +++ b/game_patch/multi/server.cpp @@ -716,7 +716,7 @@ CodeInjection multi_level_init_injection{ CallHook player_create_entity_entity_create_hook{ 0x004A41D3, [] (const int type, const char* const name, const int parent_handle, const rf::Vector3& pos, rf::Matrix3& orient, int create_flags, const int mp_character) { - if (get_af_server_info().has_value() && get_af_server_info().value().no_player_collide) { + if (get_af_remote_info().has_value() && get_af_remote_info().value().no_player_collide) { create_flags |= 0x4; } return player_create_entity_entity_create_hook.call_target(type, name, parent_handle, pos, orient, create_flags, mp_character); diff --git a/game_patch/object/entity.cpp b/game_patch/object/entity.cpp index 7867e615f..7fa6ec216 100644 --- a/game_patch/object/entity.cpp +++ b/game_patch/object/entity.cpp @@ -280,8 +280,8 @@ static FunHook entity_damage_hook{ CallHook entity_update_muzzle_flash_light_hook{ 0x0041E814, [] (rf::Entity& ep) { - const bool remote_force_muzzle_flash = get_af_server_info().has_value() - && !get_af_server_info().value().allow_no_mf; + const bool remote_force_muzzle_flash = get_af_remote_info().has_value() + && !get_af_remote_info().value().allow_no_mf; if (g_game_config.muzzle_flash || remote_force_muzzle_flash) { entity_update_muzzle_flash_light_hook.call_target(ep); } diff --git a/game_patch/object/weapon.cpp b/game_patch/object/weapon.cpp index 0e0e3c37c..c023643a5 100644 --- a/game_patch/object/weapon.cpp +++ b/game_patch/object/weapon.cpp @@ -127,15 +127,15 @@ CallHook weapon_hit_wall_obj_apply_r }; bool remote_click_limit() { - if (get_af_server_info().has_value() && get_af_server_info().value().click_limit) { + if (get_af_remote_info().has_value() && get_af_remote_info().value().click_limit) { return true; } return false; } std::optional remote_fire_wait_override() { - return get_af_server_info().and_then([] (const AlpineFactionServerInfo& af_server_info) { - return af_server_info.semi_auto_cooldown; + return get_af_remote_info().and_then([] (const AlpineFactionRemoteInfo& af_remote_info) { + return af_remote_info.semi_auto_cooldown; }); } From c88b288acb36f1285783c1a3412bf51bf059c663 Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Sun, 28 Sep 2025 20:43:19 +1300 Subject: [PATCH 044/134] Update weapon.cpp --- game_patch/object/weapon.cpp | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/game_patch/object/weapon.cpp b/game_patch/object/weapon.cpp index c023643a5..37f78d6a2 100644 --- a/game_patch/object/weapon.cpp +++ b/game_patch/object/weapon.cpp @@ -126,13 +126,6 @@ CallHook weapon_hit_wall_obj_apply_r }, }; -bool remote_click_limit() { - if (get_af_remote_info().has_value() && get_af_remote_info().value().click_limit) { - return true; - } - return false; -} - std::optional remote_fire_wait_override() { return get_af_remote_info().and_then([] (const AlpineFactionRemoteInfo& af_remote_info) { return af_remote_info.semi_auto_cooldown; @@ -143,7 +136,9 @@ CodeInjection player_fire_primary_weapon_semi_auto_patch{ 0x004A50BB, [] (auto& regs) { const rf::Entity* const entity = regs.esi; - if (remote_click_limit() && !entity->ai.next_fire_primary.elapsed()) { + if (get_af_remote_info().has_value() + && get_af_remote_info().value().click_limit + && !entity->ai.next_fire_primary.elapsed()) { regs.eip = 0x004A58B8; } }, From 915f8022753f1936b1c9c91bb3913daa86340977 Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Sun, 28 Sep 2025 23:20:25 +1300 Subject: [PATCH 045/134] Remove hit sound --- resources/CMakeLists.txt | 1 - resources/audio/af_hitsound1.wav | Bin 2786 -> 0 bytes resources/licensing-info.txt | 4 ---- 3 files changed, 5 deletions(-) delete mode 100644 resources/audio/af_hitsound1.wav diff --git a/resources/CMakeLists.txt b/resources/CMakeLists.txt index 64a6f09eb..c9227ed02 100644 --- a/resources/CMakeLists.txt +++ b/resources/CMakeLists.txt @@ -95,7 +95,6 @@ add_packfile(dashfaction.vpp meshes/meatchunk4.v3m meshes/meatchunk5.v3m - audio/af_hitsound1.wav audio/af_killsound1.wav # Dummy audio files to silence warnings audio/dummy/fp_shotgun_reload.wav diff --git a/resources/audio/af_hitsound1.wav b/resources/audio/af_hitsound1.wav deleted file mode 100644 index 953aed44c6e86b76bb99324f53908e4092f5d675..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2786 zcmeIydobJC0tfJ45=4j=jUa73g9ag9p=&AP)l^ZV9zj#D8nrRH!?3&UcDFm*>aZ%s zQlwTX_2`snYbCAq7#$(&mBk{p36jVNja|}6BE7%Po&EdH+`0el{eJ#9XXczaGv_;J zPTbk>@MQx4NDNL0yFx3pF$Mqt0ziS#ZU8Xug#tQ&F_4j6Qb>V<3;*!Mg$2ik#r^js zcoIB)@B~l1ho6Tp-h+tuA^v?9wD{a=a}V~9oav!UprqrUp`PiKx`qlq*SL=yH>bXc+x%TSO%71)N9lmKN&x{BwdnP z8(14u*MPqKnu_;1xpdX7{7Ri zeTL0$Ww+MV)YTZ>GrIQ^{U^F5!;*2U=T=X`M8ZVjX5l7HpQg`rVmh%%EK(RHjB+FQ zMlP{{Siq<8X`LCJ8Kp6$F)$*Gc*^3G1x-m)UR=7kl=C|0wWLkb_MrAb?W@XHmCN^+ z?~gIY7}Y)1J;R(~PM9Q2@>1`m9^HZNkQAI0Jdrq&Xq{u7W0P-_@Bf+qXAS8M>8D~& z#iSBbiT;-Umi8)pRn3UmlM)ZNkDkyM{l?_1?twN|}WO=u!Ck$OqJ&75XVqBv0u z(}n4NV*iPKX<%s}E1ne(&w^(K<^|?W=1%7Fs647mv`aLJNFo|w3^3E`X*FyWwhA8w zJ%vBzGPsOye*EUg*R`)}Q=3zpYnU}m6-UK+B6=dC!l#NVjNmZ*mue2tppkK8l@9oK0K@ ziDSjFpOZc(wGr9~CKwY;Iiwt7A~liL&DG7xf06%E!K`3j@4DU<4ifYVdiw_Z276|D zW+-BcSb8WuEHW!Hvv#p|!3JOhFk}o_?}FY1DhPKDcTN?k3gqE=cq>aQ%PMFU6e5Gj z{1*KdMPs6|tbwe7c4j-%2L${K{9HC#Haf?fark5QrJMI(riHZdZ1{nWw-~U^`$?d=&pY_dIuHXl1CDRm=Ko;I9G2 zpkffq!E)*r>lU4)PEv}7qG>$Zcr?#8&o-VAPw?>f@W%(^gZZGKfS`bJuW>JuBgt{e zY{_g5wg%(v@^C@@cPZypp;Dk8gHW-;&rdA@9hy(Nidee5(R*VzlvfZ-X%-zl1H7*(# zngh*2=Y-CQGUGC1HB1e=^#0QO{;mEkxlk?~Umjl`Sr}Qc0`Y(6|K7gZzN!^##kRY) zyF?9969bQd-#58$;)rp?T(!AsBeR#;e}(-Di?&1CAx|Pt&Y8`bA&rnmw_vwm`v?06 z?z`^0AOEa78+SG;g_U3{c0#vxxAkI4vE+yCAGWvlw)WOEYnpkTd7Y!kqe!$V+B6HD zh2~grEQ~CTEGgy`^O<8a$HYg(N0jZMjS? zqwY|5(7Whe$)02{{2=@Qs)cHcp~cX9diV4?5uJ#4hVKl8NFj0))MeOZIFFb|oP?i* zC+Q^V1V92HE!q~Xm&Qxes%lkTKe&Ey9&{5_q$*OCsms(K-xpa+)>eF|_>cu;0Sb^Q S#1ygtYykP7|L1?#z`p=F?r#: -* af_hitsound1.wav - Source: https://github.com/GooberRF/alpinefaction - soneproject: * af_killsound1.wav Title: ecofuture3 From ec0eef4290094e7955a31c29c518cd9720a14a87 Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Sun, 28 Sep 2025 23:24:48 +1300 Subject: [PATCH 046/134] Update CHANGELOG.md --- docs/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index b2ad631ed..596959b78 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -6,7 +6,7 @@ Version 1.9.2 (not released yet) [@is-this-c](https://github.com/is-this-c) - Fix a potential crash after a client quits a game if Directd3D 11 is enabled - Improve compatibility with Alpine Faction servers -- Add `af_hitsound1.wav` and `af_killsound1.wav` from Alpine Faction +- Add `af_killsound1.wav` from Alpine Faction - Add `hit_sounds` command - Add `weapon_shake` command - Add `dbg_remote_flags` command From 514518ec18ef3be546dadbd35c2b399a24345740 Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Mon, 29 Sep 2025 23:37:20 +1300 Subject: [PATCH 047/134] Rename --- README.md | 2 +- common/include/common/config/GameConfig.h | 2 +- common/src/config/GameConfig.cpp | 2 +- docs/CHANGELOG.md | 2 +- game_patch/misc/camera.cpp | 18 +++++++++--------- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index dd3fb5cf2..df0cb752a 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,7 @@ Name | Description `gibs` | toggle gibs (characters exploding into parts) `muzzle_flash` | toggle muzzle flash `hit_sounds` | toggle whether to play a sound, when you hit players in multiplayer, if enabled by a server -`weapon_shake` | toggle camera shake from weapon fire [multiplayer servers may force weapon shake] +`weapon_screen_shake` | toggle camera shake from weapon fire [multiplayer servers may force weapon shake] ### Server commands diff --git a/common/include/common/config/GameConfig.h b/common/include/common/config/GameConfig.h index 122b4c45f..23f4c58eb 100644 --- a/common/include/common/config/GameConfig.h +++ b/common/include/common/config/GameConfig.h @@ -58,7 +58,7 @@ struct GameConfig CfgVar muzzle_flash = true; CfgVar glares = true; CfgVar show_enemy_bullets = true; - CfgVar weapon_shake = true; + CfgVar weapon_screen_shake = true; static constexpr float min_fov = 75.0f; static constexpr float max_fov = 160.0f; diff --git a/common/src/config/GameConfig.cpp b/common/src/config/GameConfig.cpp index 067133f2d..a75247457 100644 --- a/common/src/config/GameConfig.cpp +++ b/common/src/config/GameConfig.cpp @@ -175,7 +175,7 @@ bool GameConfig::visit_vars(T&& visitor, bool is_save) result &= visitor(dash_faction_key, "Glares", glares); result &= visitor(dash_faction_key, "Linear Pitch", linear_pitch); result &= visitor(dash_faction_key, "Show Enemy Bullets", show_enemy_bullets); - result &= visitor(dash_faction_key, "Weapon Shake", weapon_shake); + result &= visitor(dash_faction_key, "Weapon Screen Shake", weapon_screen_shake); result &= visitor(dash_faction_key, "Keep Launcher Open", keep_launcher_open); result &= visitor(dash_faction_key, "Skip Cutscene Control", skip_cutscene_ctrl); result &= visitor(dash_faction_key, "Damage Screen Flash", damage_screen_flash); diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 596959b78..94d0289d4 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -8,7 +8,7 @@ Version 1.9.2 (not released yet) - Improve compatibility with Alpine Faction servers - Add `af_killsound1.wav` from Alpine Faction - Add `hit_sounds` command -- Add `weapon_shake` command +- Add `weapon_screen_shake` command - Add `dbg_remote_flags` command Version 1.9.1 (released 2025-07-05) diff --git a/game_patch/misc/camera.cpp b/game_patch/misc/camera.cpp index 469ee438c..d9d75fad1 100644 --- a/game_patch/misc/camera.cpp +++ b/game_patch/misc/camera.cpp @@ -27,27 +27,27 @@ FunHook camera_update_shake_hook{ CallHook entity_fire_primary_weapon_camera_shake_hook{ 0x00426C79, [] (rf::Camera* const cp, const float amplitude, const float time_seconds) { - const bool remote_force_weapon_shake = get_af_remote_info().has_value() + const bool remote_force_ss = get_af_remote_info().has_value() && !get_af_remote_info().value().allow_no_ss; - if (g_game_config.weapon_shake || remote_force_weapon_shake) { + if (g_game_config.weapon_screen_shake || remote_force_ss) { entity_fire_primary_weapon_camera_shake_hook.call_target(cp, amplitude, time_seconds); } }, }; -ConsoleCommand2 weapon_shake_cmd{ - "weapon_shake", +ConsoleCommand2 weapon_screen_shake_cmd{ + "weapon_screen_shake", [] { - g_game_config.weapon_shake = !g_game_config.weapon_shake; + g_game_config.weapon_screen_shake = !g_game_config.weapon_screen_shake; g_game_config.save(); rf::console::print( - "Weapon shake is {}", - g_game_config.weapon_shake + "Weapon screen shake is {}", + g_game_config.weapon_screen_shake ? "enabled" - : "disabled [multiplayer servers may force weapon shake]" + : "disabled [multiplayer servers may force weapon screen shake]" ); }, - "Toggle camera shake from weapon fire [multiplayer servers may force weapon shake]", + "Toggle camera shake from weapon fire [multiplayer servers may force weapon screen shake]", }; void camera_do_patch() From 42208c4cb1957b2d0a7d00d2b5d5a1040325979b Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Mon, 29 Sep 2025 23:53:28 +1300 Subject: [PATCH 048/134] Rename --- game_patch/multi/network.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/game_patch/multi/network.cpp b/game_patch/multi/network.cpp index 9a2397a2b..dac012329 100644 --- a/game_patch/multi/network.cpp +++ b/game_patch/multi/network.cpp @@ -759,7 +759,7 @@ CallHook send_join_accept_packet_ho }, }; -bool try_process_df_join_accept_tail(const std::byte* const tail) { +bool parse_df_join_accept_tail(const std::byte* const tail) { DashFactionJoinAcceptPacketExt ext{}; std::memcpy(&ext, tail, sizeof(ext)); xlog::debug("Checking for join_accept DF extension: {:#08x}", ext.df_signature); @@ -779,13 +779,13 @@ bool try_process_df_join_accept_tail(const std::byte* const tail) { remote_info.saving_enabled = !!(ext.flags & Flags::saving_enabled); constexpr const float default_fov = 90.f; if (!!(ext.flags & Flags::max_fov) && ext.max_fov >= default_fov) { - remote_info.max_fov = std::optional{ext.max_fov}; + remote_info.max_fov.emplace(ext.max_fov); } g_df_remote_info.emplace(remote_info); return true; } -bool try_process_af_join_accept_tail(const std::byte* const tail) { +bool parse_af_join_accept_tail(const std::byte* const tail) { AlpineFactionJoinAcceptPacketExt ext{}; std::memcpy(&ext, tail, sizeof(ext)); xlog::debug("Checking for join_accept AF extension: {:#08x}", ext.af_signature); @@ -814,10 +814,10 @@ bool try_process_af_join_accept_tail(const std::byte* const tail) { remote_info.location_pinging = !!(ext.flags & Flags::location_pinging); constexpr const float default_fov = 90.f; if (!!(ext.flags & Flags::max_fov) && ext.max_fov >= default_fov) { - remote_info.max_fov = std::optional{ext.max_fov}; + remote_info.max_fov.emplace(ext.max_fov); } if (remote_info.click_limit) { - remote_info.semi_auto_cooldown = std::optional{ext.semi_auto_cooldown}; + remote_info.semi_auto_cooldown.emplace(ext.semi_auto_cooldown); } g_af_remote_info.emplace(remote_info); return true; @@ -829,8 +829,8 @@ CodeInjection process_join_accept_injection{ const std::byte* const packet = regs.ebp; const int ext_offset = regs.esi + 5; const std::byte* const tail = packet + ext_offset; - if (!try_process_df_join_accept_tail(tail)) { - try_process_af_join_accept_tail(tail); + if (!parse_df_join_accept_tail(tail)) { + parse_af_join_accept_tail(tail); } } }; @@ -1250,7 +1250,7 @@ bool af_process_packet( default: { return false; } - } + } return true; } From 793bf1769c84810655823dd2b68962699c7d5c15 Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Mon, 6 Oct 2025 16:58:34 +1300 Subject: [PATCH 049/134] Update sound.h --- game_patch/rf/sound/sound.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/game_patch/rf/sound/sound.h b/game_patch/rf/sound/sound.h index 172801b6f..e40d30893 100644 --- a/game_patch/rf/sound/sound.h +++ b/game_patch/rf/sound/sound.h @@ -88,7 +88,8 @@ namespace rf static auto snd_change_3d = addr_as_ref(0x005058C0); static auto snd_calculate_2d_from_3d_info = addr_as_ref(0x00505740); static auto snd_update_sounds = addr_as_ref(0x00505EC0); - + static auto snd_get_handle = addr_as_ref(0x005054B0); + static auto snd_pc_play = addr_as_ref(0x005439D0); static auto snd_pc_play_looping = addr_as_ref(0x00543A80); static auto snd_pc_play_3d = addr_as_ref(0x00544180); @@ -103,5 +104,4 @@ namespace rf static auto snd_pc_load_hint = addr_as_ref(0x00543760); static auto snd_pc_is_ds3d_enabled = addr_as_ref(0x00544750); static auto snd_pc_calculate_pan = addr_as_ref(0x00543EA0); - static auto snd_get_handle = addr_as_ref(0x005054B0); } From 9868c8caf2490afe3cf38c209f1140a31df0510c Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Mon, 6 Oct 2025 17:07:44 +1300 Subject: [PATCH 050/134] Update weapon.cpp --- game_patch/object/weapon.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/game_patch/object/weapon.cpp b/game_patch/object/weapon.cpp index 37f78d6a2..4ce3a2a4a 100644 --- a/game_patch/object/weapon.cpp +++ b/game_patch/object/weapon.cpp @@ -148,7 +148,7 @@ CodeInjection entity_fire_primary_weapon_semi_auto_patch{ 0x004259B8, [] (auto& regs) { // HACKFIX: Override fire wait for stock semi auto weapons. - BaseCodeInjection::Reg& fire_wait = regs.eax; + auto& fire_wait = regs.eax; const int weapon_type = regs.ebx; const rf::Entity* const entity = regs.esi; if (remote_fire_wait_override().has_value() From 0acd51d9e46e07e11e99606d8eb5d6dee0a2b696 Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Mon, 6 Oct 2025 17:12:59 +1300 Subject: [PATCH 051/134] Update server.cpp --- game_patch/multi/server.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/game_patch/multi/server.cpp b/game_patch/multi/server.cpp index ba18da0c4..b55042f4d 100644 --- a/game_patch/multi/server.cpp +++ b/game_patch/multi/server.cpp @@ -717,7 +717,8 @@ CallHook Date: Mon, 6 Oct 2025 17:19:29 +1300 Subject: [PATCH 052/134] Update network.cpp --- game_patch/multi/network.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/game_patch/multi/network.cpp b/game_patch/multi/network.cpp index dac012329..5b18ffc49 100644 --- a/game_patch/multi/network.cpp +++ b/game_patch/multi/network.cpp @@ -1222,7 +1222,7 @@ void af_process_damage_notify_packet(const void* const data, const size_t len, c const bool died = static_cast(damage_notify_packet.flags & 0x01); if (g_game_config.play_hit_sounds) { - constexpr const int hit_sound_id = 29; + constexpr int hit_sound_id = 29; static const int kill_sound_id = rf::snd_get_handle("af_killsound1.wav", 5.f, 1.f, 1.f); rf::snd_play(died ? kill_sound_id : hit_sound_id, 0, 0.f, 1.f); } From 813a6700ee0b3f1f0abd37ace54d14f56cde1377 Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Mon, 6 Oct 2025 17:26:42 +1300 Subject: [PATCH 053/134] Update network.cpp --- game_patch/multi/network.cpp | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/game_patch/multi/network.cpp b/game_patch/multi/network.cpp index 5b18ffc49..dd326fe9f 100644 --- a/game_patch/multi/network.cpp +++ b/game_patch/multi/network.cpp @@ -1210,16 +1210,6 @@ void af_process_damage_notify_packet(const void* const data, const size_t len, c std::memcpy(&damage_notify_packet, data, sizeof(damage_notify_packet)); - const rf::Player* const player = rf::multi_find_player_by_id(damage_notify_packet.player_id); - if (!player) { - return; - } - - const rf::Entity* const entity = rf::entity_from_handle(player->entity_handle); - if (!entity) { - return; - } - const bool died = static_cast(damage_notify_packet.flags & 0x01); if (g_game_config.play_hit_sounds) { constexpr int hit_sound_id = 29; From 02b0e424326ff5b7c5e5303105b232496884381e Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Mon, 6 Oct 2025 17:29:43 +1300 Subject: [PATCH 054/134] Update network.cpp --- game_patch/multi/network.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/game_patch/multi/network.cpp b/game_patch/multi/network.cpp index dd326fe9f..2acd1d56a 100644 --- a/game_patch/multi/network.cpp +++ b/game_patch/multi/network.cpp @@ -777,7 +777,7 @@ bool parse_df_join_accept_tail(const std::byte* const tail) { remote_info.version_minor = ext.version_minor; using Flags = DashFactionJoinAcceptPacketExt::Flags; remote_info.saving_enabled = !!(ext.flags & Flags::saving_enabled); - constexpr const float default_fov = 90.f; + constexpr float default_fov = 90.f; if (!!(ext.flags & Flags::max_fov) && ext.max_fov >= default_fov) { remote_info.max_fov.emplace(ext.max_fov); } @@ -812,7 +812,7 @@ bool parse_af_join_accept_tail(const std::byte* const tail) { remote_info.unlimited_fps = !!(ext.flags & Flags::unlimited_fps); remote_info.gaussian_spread = !!(ext.flags & Flags::gaussian_spread); remote_info.location_pinging = !!(ext.flags & Flags::location_pinging); - constexpr const float default_fov = 90.f; + constexpr float default_fov = 90.f; if (!!(ext.flags & Flags::max_fov) && ext.max_fov >= default_fov) { remote_info.max_fov.emplace(ext.max_fov); } From c2da1c29a72b9b306d905694fd386898b306ad77 Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Mon, 6 Oct 2025 17:41:26 +1300 Subject: [PATCH 055/134] Update --- game_patch/misc/camera.cpp | 30 ------------------------------ game_patch/object/entity.cpp | 29 +++++++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 30 deletions(-) diff --git a/game_patch/misc/camera.cpp b/game_patch/misc/camera.cpp index d9d75fad1..eb22810a5 100644 --- a/game_patch/misc/camera.cpp +++ b/game_patch/misc/camera.cpp @@ -1,11 +1,7 @@ #include -#include #include #include "../rf/player/player.h" #include "../rf/os/frametime.h" -#include "../os/console.h" -#include "../main/main.h" -#include "../multi/multi.h" constexpr auto screen_shake_fps = 150.0f; @@ -24,32 +20,6 @@ FunHook camera_update_shake_hook{ }, }; -CallHook entity_fire_primary_weapon_camera_shake_hook{ - 0x00426C79, - [] (rf::Camera* const cp, const float amplitude, const float time_seconds) { - const bool remote_force_ss = get_af_remote_info().has_value() - && !get_af_remote_info().value().allow_no_ss; - if (g_game_config.weapon_screen_shake || remote_force_ss) { - entity_fire_primary_weapon_camera_shake_hook.call_target(cp, amplitude, time_seconds); - } - }, -}; - -ConsoleCommand2 weapon_screen_shake_cmd{ - "weapon_screen_shake", - [] { - g_game_config.weapon_screen_shake = !g_game_config.weapon_screen_shake; - g_game_config.save(); - rf::console::print( - "Weapon screen shake is {}", - g_game_config.weapon_screen_shake - ? "enabled" - : "disabled [multiplayer servers may force weapon screen shake]" - ); - }, - "Toggle camera shake from weapon fire [multiplayer servers may force weapon screen shake]", -}; - void camera_do_patch() { // Fix crash when executing camera2 command in main menu diff --git a/game_patch/object/entity.cpp b/game_patch/object/entity.cpp index 7fa6ec216..4e1d66f31 100644 --- a/game_patch/object/entity.cpp +++ b/game_patch/object/entity.cpp @@ -277,6 +277,32 @@ static FunHook entity_damage_hook{ }, }; +CallHook entity_fire_primary_weapon_camera_shake_hook{ + 0x00426C79, + [] (rf::Camera* const cp, const float amplitude, const float time_seconds) { + const bool remote_force_ss = get_af_remote_info().has_value() + && !get_af_remote_info().value().allow_no_ss; + if (g_game_config.weapon_screen_shake || remote_force_ss) { + entity_fire_primary_weapon_camera_shake_hook.call_target(cp, amplitude, time_seconds); + } + }, +}; + +ConsoleCommand2 weapon_screen_shake_cmd{ + "weapon_screen_shake", + [] { + g_game_config.weapon_screen_shake = !g_game_config.weapon_screen_shake; + g_game_config.save(); + rf::console::print( + "Weapon screen shake is {}", + g_game_config.weapon_screen_shake + ? "enabled" + : "disabled [multiplayer servers may force weapon screen shake]" + ); + }, + "Toggle camera shake from weapon fire [multiplayer servers may force weapon screen shake]", +}; + CallHook entity_update_muzzle_flash_light_hook{ 0x0041E814, [] (rf::Entity& ep) { @@ -365,7 +391,10 @@ void entity_do_patch() // Don't create muzzle flash lights entity_update_muzzle_flash_light_hook.install(); + entity_fire_primary_weapon_camera_shake_hook.install(); + // Commands gibs_cmd.register_cmd(); muzzle_flash_cmd.register_cmd(); + weapon_screen_shake_cmd.register_cmd(); } From 25e51ddf431cbcff4d34f4441dbd3ab8d2400af5 Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Mon, 6 Oct 2025 17:43:47 +1300 Subject: [PATCH 056/134] Update gr.cpp --- game_patch/graphics/gr.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/game_patch/graphics/gr.cpp b/game_patch/graphics/gr.cpp index 90a188967..5eae1956f 100644 --- a/game_patch/graphics/gr.cpp +++ b/game_patch/graphics/gr.cpp @@ -85,12 +85,12 @@ float gr_scale_world_fov(float horizontal_fov = 90.0f) if (get_df_remote_info().has_value()) { const DashFactionRemoteInfo& df_remote_info = get_df_remote_info().value(); if (df_remote_info.max_fov.has_value()) { - rf::console::print("Server FOV limit: {:.2f}", df_remote_info.max_fov.value()); + horizontal_fov = std::min(horizontal_fov, df_remote_info.max_fov.value()); } } else if (get_af_remote_info().has_value()) { const AlpineFactionRemoteInfo& af_remote_info = get_af_remote_info().value(); if (af_remote_info.max_fov.has_value()) { - rf::console::print("Server FOV limit: {:.2f}", af_remote_info.max_fov.value()); + horizontal_fov = std::min(horizontal_fov, af_remote_info.max_fov.value()); } } From 1448b02a45c7a5f9433f7f8c37aed9e44863bbc7 Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Mon, 6 Oct 2025 20:59:54 +1300 Subject: [PATCH 057/134] Update network.cpp --- game_patch/multi/network.cpp | 42 ++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/game_patch/multi/network.cpp b/game_patch/multi/network.cpp index 2acd1d56a..271a8e9b0 100644 --- a/game_patch/multi/network.cpp +++ b/game_patch/multi/network.cpp @@ -835,8 +835,8 @@ CodeInjection process_join_accept_injection{ } }; -ConsoleCommand2 dbg_remote_flags_cmd{ - "dbg_remote_flags", +ConsoleCommand2 remote_flags_cmd{ + "remote_flags", [] { if (g_df_remote_info.has_value()) { const DashFactionRemoteInfo& df_remote_info = g_df_remote_info.value(); @@ -847,14 +847,14 @@ ConsoleCommand2 dbg_remote_flags_cmd{ df_remote_info.version_minor ); rf::console::print("===================="); - rf::console::print("saving_enabled: {}", df_remote_info.saving_enabled); + rf::console::print("Teleport: {}", df_remote_info.saving_enabled); if (df_remote_info.max_fov.has_value()) { - rf::console::print("max_fov: Some({})", df_remote_info.max_fov.value()); + rf::console::print("Max FOV: Some({})", df_remote_info.max_fov.value()); } else { - rf::console::print("max_fov: None"); + rf::console::print("Max FOV: None"); } } else if (g_af_remote_info.has_value()) { - const AlpineFactionRemoteInfo& af_remote_info = g_af_remote_info.value(); + const AlpineFactionServerInfo& af_remote_info = g_af_remote_info.value(); rf::console::print("===================="); rf::console::print( "Alpine Faction {}.{}", @@ -862,29 +862,29 @@ ConsoleCommand2 dbg_remote_flags_cmd{ af_remote_info.version_minor ); rf::console::print("===================="); - rf::console::print("saving_enabled: {}", af_remote_info.saving_enabled); + rf::console::print("Teleport: {}", af_remote_info.saving_enabled); if (af_remote_info.max_fov.has_value()) { - rf::console::print("max_fov: Some({})", af_remote_info.max_fov.value()); + rf::console::print("Max FOV: Some({})", af_remote_info.max_fov.value()); } else { - rf::console::print("max_fov: None"); + rf::console::print("Max FOV: None"); } - rf::console::print("allow_fb_mesh: {}", af_remote_info.allow_fb_mesh); - rf::console::print("allow_lmap: {}", af_remote_info.allow_lmap); - rf::console::print("allow_no_ss: {}", af_remote_info.allow_no_ss); - rf::console::print("no_player_collide: {}", af_remote_info.no_player_collide); - rf::console::print("allow_no_mf: {}", af_remote_info.allow_no_mf); - rf::console::print("click_limit: {}", af_remote_info.click_limit); + rf::console::print("Allow full-bright meshes: {}", af_remote_info.allow_fb_mesh); + rf::console::print("Allow light maps only: {}", af_remote_info.allow_lmap); + rf::console::print("Allow no screen shake: {}", af_remote_info.allow_no_ss); + rf::console::print("No player collide: {}", af_remote_info.no_player_collide); + rf::console::print("Allow no muzzle flash: {}", af_remote_info.allow_no_mf); + rf::console::print("Semiauto fire rate limit: {}", af_remote_info.click_limit); if (af_remote_info.semi_auto_cooldown.has_value()) { rf::console::print( - "semi_auto_cooldown: Some({})", + "Semiauto cool-down: Some({})", af_remote_info.semi_auto_cooldown.value() ); } else { - rf::console::print("semi_auto_cooldown: None"); + rf::console::print("Semiauto cool-down: None"); } - rf::console::print("unlimited_fps: {}", af_remote_info.unlimited_fps); - rf::console::print("gaussian_spread: {}", af_remote_info.gaussian_spread); - rf::console::print("location_pinging: {}", af_remote_info.location_pinging); + rf::console::print("Allow unlimited FPS: {}", af_remote_info.unlimited_fps); + rf::console::print("Gaussian spread: {}", af_remote_info.gaussian_spread); + rf::console::print("Ping locations: {}", af_remote_info.location_pinging); } } }; @@ -1453,5 +1453,5 @@ void network_init() // Ignore browsers when calculating player count for info requests game_info_num_players_hook.install(); - dbg_remote_flags_cmd.register_cmd(); + remote_flags_cmd.register_cmd(); } From cef8bfd1147eeb3cb3a7b8637f4cca3cd96276b8 Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Mon, 6 Oct 2025 21:21:55 +1300 Subject: [PATCH 058/134] Update network.cpp --- game_patch/multi/network.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/game_patch/multi/network.cpp b/game_patch/multi/network.cpp index 271a8e9b0..4b50a173e 100644 --- a/game_patch/multi/network.cpp +++ b/game_patch/multi/network.cpp @@ -854,7 +854,7 @@ ConsoleCommand2 remote_flags_cmd{ rf::console::print("Max FOV: None"); } } else if (g_af_remote_info.has_value()) { - const AlpineFactionServerInfo& af_remote_info = g_af_remote_info.value(); + const AlpineFactionRemoteInfo& af_remote_info = g_af_remote_info.value(); rf::console::print("===================="); rf::console::print( "Alpine Faction {}.{}", From bc92076daa2df139afbf5b3c8197ac65e3483761 Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Mon, 6 Oct 2025 21:30:33 +1300 Subject: [PATCH 059/134] Update CHANGELOG.md --- docs/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 94d0289d4..e37091661 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -9,7 +9,7 @@ Version 1.9.2 (not released yet) - Add `af_killsound1.wav` from Alpine Faction - Add `hit_sounds` command - Add `weapon_screen_shake` command -- Add `dbg_remote_flags` command +- Add `remote_flags` command Version 1.9.1 (released 2025-07-05) -------------------------------- From 319d021e6e6f8c08a0cb53fa7ace2c0f2dc3a95d Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Mon, 6 Oct 2025 21:38:51 +1300 Subject: [PATCH 060/134] Update network.cpp --- game_patch/multi/network.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/game_patch/multi/network.cpp b/game_patch/multi/network.cpp index 4b50a173e..95f5f5ddd 100644 --- a/game_patch/multi/network.cpp +++ b/game_patch/multi/network.cpp @@ -1210,7 +1210,7 @@ void af_process_damage_notify_packet(const void* const data, const size_t len, c std::memcpy(&damage_notify_packet, data, sizeof(damage_notify_packet)); - const bool died = static_cast(damage_notify_packet.flags & 0x01); + const bool died = static_cast(damage_notify_packet.flags & 0x1); if (g_game_config.play_hit_sounds) { constexpr int hit_sound_id = 29; static const int kill_sound_id = rf::snd_get_handle("af_killsound1.wav", 5.f, 1.f, 1.f); From 9f4e896b56b1938482acc8b09633a2b89cd82276 Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Mon, 6 Oct 2025 21:39:22 +1300 Subject: [PATCH 061/134] Update server.cpp --- game_patch/multi/server.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/game_patch/multi/server.cpp b/game_patch/multi/server.cpp index b55042f4d..f23660887 100644 --- a/game_patch/multi/server.cpp +++ b/game_patch/multi/server.cpp @@ -717,7 +717,7 @@ CallHook Date: Mon, 6 Oct 2025 21:40:09 +1300 Subject: [PATCH 062/134] Update server.cpp --- game_patch/multi/server.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/game_patch/multi/server.cpp b/game_patch/multi/server.cpp index f23660887..5f1afa377 100644 --- a/game_patch/multi/server.cpp +++ b/game_patch/multi/server.cpp @@ -717,8 +717,8 @@ CallHook Date: Tue, 7 Oct 2025 22:40:47 +1300 Subject: [PATCH 063/134] Update --- game_patch/misc/level.cpp | 18 +++++++++++++++++- game_patch/misc/player.cpp | 22 ++++++++++++++++++++++ game_patch/multi/network.cpp | 2 +- game_patch/object/event.cpp | 9 +++++++++ 4 files changed, 49 insertions(+), 2 deletions(-) diff --git a/game_patch/misc/level.cpp b/game_patch/misc/level.cpp index 87d5c515c..cf28054de 100644 --- a/game_patch/misc/level.cpp +++ b/game_patch/misc/level.cpp @@ -74,10 +74,23 @@ CallHook level_init_pre_console_output_hook{ }, }; +bool g_level_has_unsupported_event_classes = false; + +CodeInjection level_load_player_create_entity_injection{ + 0x0045C7D9, + [] (auto& regs) { + if (g_level_has_unsupported_event_classes) { + rf::camera_enter_fixed(rf::local_player->cam); + regs.eip = 0x0045C80F; + } + }, +}; + CodeInjection level_read_header_inj{ 0x004616FB, - []() { + [] { DashLevelProps::instance().reset(rf::level.version); + g_level_has_unsupported_event_classes = false; }, }; @@ -129,6 +142,9 @@ void level_apply_patch() // Add level name to "-- Level Initializing --" message level_init_pre_console_output_hook.install(); + // If a level has unsupported event classes, do not automatically spawn in a listen server. + level_load_player_create_entity_injection.install(); + // Load custom rfl chunks level_read_header_inj.install(); level_load_chunk_inj.install(); diff --git a/game_patch/misc/player.cpp b/game_patch/misc/player.cpp index c0412c658..a7604433f 100644 --- a/game_patch/misc/player.cpp +++ b/game_patch/misc/player.cpp @@ -303,6 +303,26 @@ CodeInjection sr_load_player_weapon_anims_injection{ }, }; +CodeInjection player_execute_action_spawn_player_injection{ + 0x004A678B, + [] (auto& regs) { + extern bool g_level_has_unsupported_event_classes; + if (g_level_has_unsupported_event_classes) { + const std::string text = std::format( + "You cannot spawn in a level that has unsupported event classes!\nRFL version: {}", + rf::level.version + ); + rf::ui::popup_message( + "Error", + text .c_str(), + nullptr, + false + ); + regs.eip = 0x004A679D; + } + }, +}; + void player_do_patch() { // general hooks @@ -368,4 +388,6 @@ void player_do_patch() // Commands damage_screen_flash_cmd.register_cmd(); hit_sounds_cmd.register_cmd(); + + player_execute_action_spawn_player_injection.install(); } diff --git a/game_patch/multi/network.cpp b/game_patch/multi/network.cpp index 95f5f5ddd..db8af770e 100644 --- a/game_patch/multi/network.cpp +++ b/game_patch/multi/network.cpp @@ -631,7 +631,7 @@ struct AFJoinReqV2 uint8_t version_minor = 1; uint8_t version_patch = 0; uint8_t version_type = VERSION_TYPE; - uint32_t max_rfl_version = 200; + uint32_t max_rfl_version = 301; uint32_t flags = 0; }; diff --git a/game_patch/object/event.cpp b/game_patch/object/event.cpp index b35cd8f47..a31a8fd1a 100644 --- a/game_patch/object/event.cpp +++ b/game_patch/object/event.cpp @@ -265,6 +265,15 @@ CodeInjection event_load_level_turn_on_injection{ FunHook event_lookup_type_hook{ 0x004BD700, [](const rf::String& name) { + const auto rf_event = std::find( + std::begin(rf::event_names), + std::end(rf::event_names), + name + ); + if (rf_event == std::end(rf::event_names)) { + extern bool g_level_has_unsupported_event_classes; + g_level_has_unsupported_event_classes = true; + } auto it = event_name_mapper.find(name.c_str()); if (!it) { xlog::warn("Unsupported event class: {}", name); From daa7d81a35747a7472e8bdb7b59bcd5513273f71 Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Tue, 7 Oct 2025 22:51:01 +1300 Subject: [PATCH 064/134] Update --- game_patch/misc/level.h | 2 ++ game_patch/misc/player.cpp | 2 +- game_patch/object/event.cpp | 13 ++++++------- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/game_patch/misc/level.h b/game_patch/misc/level.h index 98778e30a..cbca4a04d 100644 --- a/game_patch/misc/level.h +++ b/game_patch/misc/level.h @@ -27,3 +27,5 @@ struct DashLevelProps xlog::debug("lightmaps_full_depth {}", lightmaps_full_depth); } }; + +extern bool g_level_has_unsupported_event_classes; diff --git a/game_patch/misc/player.cpp b/game_patch/misc/player.cpp index a7604433f..d804dba75 100644 --- a/game_patch/misc/player.cpp +++ b/game_patch/misc/player.cpp @@ -8,6 +8,7 @@ #include "../rf/weapon.h" #include "../rf/hud.h" #include "../rf/input.h" +#include "../rf/level.h" #include "../os/console.h" #include "../main/main.h" #include "../multi/multi.h" @@ -306,7 +307,6 @@ CodeInjection sr_load_player_weapon_anims_injection{ CodeInjection player_execute_action_spawn_player_injection{ 0x004A678B, [] (auto& regs) { - extern bool g_level_has_unsupported_event_classes; if (g_level_has_unsupported_event_classes) { const std::string text = std::format( "You cannot spawn in a level that has unsupported event classes!\nRFL version: {}", diff --git a/game_patch/object/event.cpp b/game_patch/object/event.cpp index a31a8fd1a..36f57c90d 100644 --- a/game_patch/object/event.cpp +++ b/game_patch/object/event.cpp @@ -264,22 +264,21 @@ CodeInjection event_load_level_turn_on_injection{ FunHook event_lookup_type_hook{ 0x004BD700, - [](const rf::String& name) { - const auto rf_event = std::find( + [] (const rf::String& name) { + const char* const* const rf_event = std::find( std::begin(rf::event_names), std::end(rf::event_names), - name + name.c_str() ); if (rf_event == std::end(rf::event_names)) { - extern bool g_level_has_unsupported_event_classes; g_level_has_unsupported_event_classes = true; } - auto it = event_name_mapper.find(name.c_str()); - if (!it) { + std::optional id = event_name_mapper.find(name.c_str()); + if (!id) { xlog::warn("Unsupported event class: {}", name); return -1; } - return it.value(); + return id.value(); }, }; From b032b5dbb81525da5173af06d9590a62d5e60933 Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Tue, 7 Oct 2025 23:18:30 +1300 Subject: [PATCH 065/134] Remove `has_value` --- game_patch/graphics/gr.cpp | 18 +++++++++--------- game_patch/misc/save_restore.cpp | 4 ++-- game_patch/multi/network.cpp | 10 +++++----- game_patch/multi/server.cpp | 2 +- game_patch/object/entity.cpp | 4 ++-- game_patch/object/weapon.cpp | 4 ++-- 6 files changed, 21 insertions(+), 21 deletions(-) diff --git a/game_patch/graphics/gr.cpp b/game_patch/graphics/gr.cpp index 5eae1956f..d450b1517 100644 --- a/game_patch/graphics/gr.cpp +++ b/game_patch/graphics/gr.cpp @@ -82,14 +82,14 @@ float gr_scale_world_fov(float horizontal_fov = 90.0f) horizontal_fov = gr_scale_fov_hor_plus(horizontal_fov); } - if (get_df_remote_info().has_value()) { + if (get_df_remote_info()) { const DashFactionRemoteInfo& df_remote_info = get_df_remote_info().value(); - if (df_remote_info.max_fov.has_value()) { + if (df_remote_info.max_fov) { horizontal_fov = std::min(horizontal_fov, df_remote_info.max_fov.value()); } - } else if (get_af_remote_info().has_value()) { + } else if (get_af_remote_info()) { const AlpineFactionRemoteInfo& af_remote_info = get_af_remote_info().value(); - if (af_remote_info.max_fov.has_value()) { + if (af_remote_info.max_fov) { horizontal_fov = std::min(horizontal_fov, af_remote_info.max_fov.value()); } } @@ -131,14 +131,14 @@ ConsoleCommand2 fov_cmd{ } rf::console::print("Horizontal FOV: {:.2f}", gr_scale_world_fov()); - if (get_df_remote_info().has_value()) { + if (get_df_remote_info()) { const DashFactionRemoteInfo& df_remote_info = get_df_remote_info().value(); - if (df_remote_info.max_fov.has_value()) { + if (df_remote_info.max_fov) { rf::console::print("Server FOV limit: {:.2f}", df_remote_info.max_fov.value()); } - } else if (get_af_remote_info().has_value()) { + } else if (get_af_remote_info()) { const AlpineFactionRemoteInfo& af_remote_info = get_af_remote_info().value(); - if (af_remote_info.max_fov.has_value()) { + if (af_remote_info.max_fov) { rf::console::print("Server FOV limit: {:.2f}", af_remote_info.max_fov.value()); } } @@ -255,7 +255,7 @@ ConsoleCommand2 nearest_texture_filtering_cmd{ ConsoleCommand2 lod_distance_scale_cmd{ "lod_distance_scale", [](std::optional scale_opt) { - if (scale_opt.has_value()) { + if (scale_opt) { gr_lod_dist_scale = scale_opt.value(); } rf::console::print("LOD distance scale: {:.2f}", gr_lod_dist_scale); diff --git a/game_patch/misc/save_restore.cpp b/game_patch/misc/save_restore.cpp index 1a9cd63c3..d4fc2d228 100644 --- a/game_patch/misc/save_restore.cpp +++ b/game_patch/misc/save_restore.cpp @@ -254,9 +254,9 @@ CodeInjection corpse_deserialize_all_obj_create_patch{ }; bool remote_saving_enabled() { - const bool df_saving_enabled = get_df_remote_info().has_value() + const bool df_saving_enabled = get_df_remote_info() && get_df_remote_info().value().saving_enabled; - const bool af_saving_enabled = get_af_remote_info().has_value() + const bool af_saving_enabled = get_af_remote_info() && get_af_remote_info().value().saving_enabled; return df_saving_enabled || af_saving_enabled; } diff --git a/game_patch/multi/network.cpp b/game_patch/multi/network.cpp index db8af770e..f1dfe49b1 100644 --- a/game_patch/multi/network.cpp +++ b/game_patch/multi/network.cpp @@ -838,7 +838,7 @@ CodeInjection process_join_accept_injection{ ConsoleCommand2 remote_flags_cmd{ "remote_flags", [] { - if (g_df_remote_info.has_value()) { + if (g_df_remote_info) { const DashFactionRemoteInfo& df_remote_info = g_df_remote_info.value(); rf::console::print("===================="); rf::console::print( @@ -848,12 +848,12 @@ ConsoleCommand2 remote_flags_cmd{ ); rf::console::print("===================="); rf::console::print("Teleport: {}", df_remote_info.saving_enabled); - if (df_remote_info.max_fov.has_value()) { + if (df_remote_info.max_fov) { rf::console::print("Max FOV: Some({})", df_remote_info.max_fov.value()); } else { rf::console::print("Max FOV: None"); } - } else if (g_af_remote_info.has_value()) { + } else if (g_af_remote_info) { const AlpineFactionRemoteInfo& af_remote_info = g_af_remote_info.value(); rf::console::print("===================="); rf::console::print( @@ -863,7 +863,7 @@ ConsoleCommand2 remote_flags_cmd{ ); rf::console::print("===================="); rf::console::print("Teleport: {}", af_remote_info.saving_enabled); - if (af_remote_info.max_fov.has_value()) { + if (af_remote_info.max_fov) { rf::console::print("Max FOV: Some({})", af_remote_info.max_fov.value()); } else { rf::console::print("Max FOV: None"); @@ -874,7 +874,7 @@ ConsoleCommand2 remote_flags_cmd{ rf::console::print("No player collide: {}", af_remote_info.no_player_collide); rf::console::print("Allow no muzzle flash: {}", af_remote_info.allow_no_mf); rf::console::print("Semiauto fire rate limit: {}", af_remote_info.click_limit); - if (af_remote_info.semi_auto_cooldown.has_value()) { + if (af_remote_info.semi_auto_cooldown) { rf::console::print( "Semiauto cool-down: Some({})", af_remote_info.semi_auto_cooldown.value() diff --git a/game_patch/multi/server.cpp b/game_patch/multi/server.cpp index 5f1afa377..d17cbc759 100644 --- a/game_patch/multi/server.cpp +++ b/game_patch/multi/server.cpp @@ -716,7 +716,7 @@ CodeInjection multi_level_init_injection{ CallHook player_create_entity_entity_create_hook{ 0x004A41D3, [] (const int type, const char* const name, const int parent_handle, const rf::Vector3& pos, rf::Matrix3& orient, int create_flags, const int mp_character) { - if (get_af_remote_info().has_value() && get_af_remote_info().value().no_player_collide) { + if (get_af_remote_info() && get_af_remote_info().value().no_player_collide) { constexpr int NO_PLAYER_COLLIDE = 0x4; create_flags |= NO_PLAYER_COLLIDE; } diff --git a/game_patch/object/entity.cpp b/game_patch/object/entity.cpp index 4e1d66f31..4dbdb3ed4 100644 --- a/game_patch/object/entity.cpp +++ b/game_patch/object/entity.cpp @@ -280,7 +280,7 @@ static FunHook entity_damage_hook{ CallHook entity_fire_primary_weapon_camera_shake_hook{ 0x00426C79, [] (rf::Camera* const cp, const float amplitude, const float time_seconds) { - const bool remote_force_ss = get_af_remote_info().has_value() + const bool remote_force_ss = get_af_remote_info() && !get_af_remote_info().value().allow_no_ss; if (g_game_config.weapon_screen_shake || remote_force_ss) { entity_fire_primary_weapon_camera_shake_hook.call_target(cp, amplitude, time_seconds); @@ -306,7 +306,7 @@ ConsoleCommand2 weapon_screen_shake_cmd{ CallHook entity_update_muzzle_flash_light_hook{ 0x0041E814, [] (rf::Entity& ep) { - const bool remote_force_muzzle_flash = get_af_remote_info().has_value() + const bool remote_force_muzzle_flash = get_af_remote_info() && !get_af_remote_info().value().allow_no_mf; if (g_game_config.muzzle_flash || remote_force_muzzle_flash) { entity_update_muzzle_flash_light_hook.call_target(ep); diff --git a/game_patch/object/weapon.cpp b/game_patch/object/weapon.cpp index 4ce3a2a4a..008ad1e79 100644 --- a/game_patch/object/weapon.cpp +++ b/game_patch/object/weapon.cpp @@ -136,7 +136,7 @@ CodeInjection player_fire_primary_weapon_semi_auto_patch{ 0x004A50BB, [] (auto& regs) { const rf::Entity* const entity = regs.esi; - if (get_af_remote_info().has_value() + if (get_af_remote_info() && get_af_remote_info().value().click_limit && !entity->ai.next_fire_primary.elapsed()) { regs.eip = 0x004A58B8; @@ -151,7 +151,7 @@ CodeInjection entity_fire_primary_weapon_semi_auto_patch{ auto& fire_wait = regs.eax; const int weapon_type = regs.ebx; const rf::Entity* const entity = regs.esi; - if (remote_fire_wait_override().has_value() + if (remote_fire_wait_override() && rf::obj_is_player(entity) && rf::weapon_is_semi_automatic(weapon_type) && fire_wait == 500) { From adc807dbdc26460c55693b5a461180ca487e5eb3 Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Tue, 7 Oct 2025 23:22:30 +1300 Subject: [PATCH 066/134] Update --- game_patch/misc/player.cpp | 2 +- game_patch/object/event.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/game_patch/misc/player.cpp b/game_patch/misc/player.cpp index d804dba75..b591fb46d 100644 --- a/game_patch/misc/player.cpp +++ b/game_patch/misc/player.cpp @@ -8,9 +8,9 @@ #include "../rf/weapon.h" #include "../rf/hud.h" #include "../rf/input.h" -#include "../rf/level.h" #include "../os/console.h" #include "../main/main.h" +#include "../misc/level.h" #include "../multi/multi.h" #include "../hud/multi_spectate.h" #include diff --git a/game_patch/object/event.cpp b/game_patch/object/event.cpp index 36f57c90d..634aa7d8c 100644 --- a/game_patch/object/event.cpp +++ b/game_patch/object/event.cpp @@ -8,12 +8,12 @@ #include "../rf/object.h" #include "../rf/event.h" #include "../rf/entity.h" -#include "../rf/level.h" #include "../rf/multi.h" #include "../rf/file/file.h" #include "../rf/player/player.h" #include "../rf/os/console.h" #include "../os/console.h" +#include "../misc/level.h" class EventNameMapper { std::unordered_map event_name_map; From ee168666ee89820d33febd65c679ff12be62d6ad Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Tue, 7 Oct 2025 23:44:51 +1300 Subject: [PATCH 067/134] Update --- game_patch/graphics/gr.cpp | 26 ++++------- game_patch/misc/save_restore.cpp | 12 +----- game_patch/multi/multi.h | 11 ++--- game_patch/multi/network.cpp | 74 ++++++++++++++------------------ game_patch/object/event.cpp | 1 + 5 files changed, 47 insertions(+), 77 deletions(-) diff --git a/game_patch/graphics/gr.cpp b/game_patch/graphics/gr.cpp index d450b1517..6e42c3a3f 100644 --- a/game_patch/graphics/gr.cpp +++ b/game_patch/graphics/gr.cpp @@ -82,15 +82,10 @@ float gr_scale_world_fov(float horizontal_fov = 90.0f) horizontal_fov = gr_scale_fov_hor_plus(horizontal_fov); } - if (get_df_remote_info()) { - const DashFactionRemoteInfo& df_remote_info = get_df_remote_info().value(); - if (df_remote_info.max_fov) { - horizontal_fov = std::min(horizontal_fov, df_remote_info.max_fov.value()); - } - } else if (get_af_remote_info()) { - const AlpineFactionRemoteInfo& af_remote_info = get_af_remote_info().value(); - if (af_remote_info.max_fov) { - horizontal_fov = std::min(horizontal_fov, af_remote_info.max_fov.value()); + if (get_remote_info()) { + const RemoteInfo& remote_info = get_remote_info().value(); + if (remote_info.max_fov) { + horizontal_fov = std::min(horizontal_fov, remote_info.max_fov.value()); } } @@ -131,15 +126,10 @@ ConsoleCommand2 fov_cmd{ } rf::console::print("Horizontal FOV: {:.2f}", gr_scale_world_fov()); - if (get_df_remote_info()) { - const DashFactionRemoteInfo& df_remote_info = get_df_remote_info().value(); - if (df_remote_info.max_fov) { - rf::console::print("Server FOV limit: {:.2f}", df_remote_info.max_fov.value()); - } - } else if (get_af_remote_info()) { - const AlpineFactionRemoteInfo& af_remote_info = get_af_remote_info().value(); - if (af_remote_info.max_fov) { - rf::console::print("Server FOV limit: {:.2f}", af_remote_info.max_fov.value()); + if (get_remote_info()) { + const RemoteInfo& remote_info = get_remote_info().value(); + if (remote_info.max_fov) { + rf::console::print("Server FOV limit: {:.2f}", remote_info.max_fov.value()); } } }, diff --git a/game_patch/misc/save_restore.cpp b/game_patch/misc/save_restore.cpp index d4fc2d228..56a82d48f 100644 --- a/game_patch/misc/save_restore.cpp +++ b/game_patch/misc/save_restore.cpp @@ -253,19 +253,11 @@ CodeInjection corpse_deserialize_all_obj_create_patch{ }, }; -bool remote_saving_enabled() { - const bool df_saving_enabled = get_df_remote_info() - && get_df_remote_info().value().saving_enabled; - const bool af_saving_enabled = get_af_remote_info() - && get_af_remote_info().value().saving_enabled; - return df_saving_enabled || af_saving_enabled; -} - FunHook quick_save_hook{ 0x004B6160, []() { quick_save_hook.call_target(); - if (remote_saving_enabled()) { + if (get_remote_info() && get_remote_info().value().saving_enabled) { send_chat_line_packet("/save", nullptr); } }, @@ -275,7 +267,7 @@ FunHook quick_load_hook{ 0x004B6180, []() { quick_load_hook.call_target(); - if (remote_saving_enabled()) { + if (get_remote_info() && get_remote_info().value().saving_enabled) { send_chat_line_packet("/load", nullptr); } }, diff --git a/game_patch/multi/multi.h b/game_patch/multi/multi.h index 5f319f2bc..8cf6cb8fe 100644 --- a/game_patch/multi/multi.h +++ b/game_patch/multi/multi.h @@ -73,19 +73,14 @@ struct PlayerStatsNew : rf::PlayerLevelStats } }; -struct DashFactionRemoteInfo -{ +struct RemoteInfo { uint8_t version_major = 0; uint8_t version_minor = 0; bool saving_enabled = false; - std::optional max_fov; + std::optional max_fov{}; }; struct AlpineFactionRemoteInfo { - uint8_t version_major = 0; - uint8_t version_minor = 0; - bool saving_enabled = false; - std::optional max_fov{}; bool allow_fb_mesh = false; bool allow_lmap = false; bool allow_no_ss = false; @@ -103,7 +98,7 @@ void multi_do_patch(); void multi_after_full_game_init(); void multi_init_player(rf::Player* player); void send_chat_line_packet(const char* msg, rf::Player* target, rf::Player* sender = nullptr, bool is_team_msg = false); -const std::optional& get_df_remote_info(); +const std::optional& get_remote_info(); const std::optional& get_af_remote_info(); void multi_level_download_do_frame(); void multi_level_download_abort(); diff --git a/game_patch/multi/network.cpp b/game_patch/multi/network.cpp index f1dfe49b1..74c0364d0 100644 --- a/game_patch/multi/network.cpp +++ b/game_patch/multi/network.cpp @@ -262,7 +262,7 @@ std::array g_client_side_packet_whitelist{ }; // clang-format on -std::optional g_df_remote_info{}; +std::optional g_remote_info{}; std::optional g_af_remote_info{}; CodeInjection process_game_packet_whitelist_filter{ @@ -772,7 +772,7 @@ bool parse_df_join_accept_tail(const std::byte* const tail) { ext.version_minor, static_cast(ext.flags) ); - DashFactionRemoteInfo remote_info{}; + RemoteInfo remote_info{}; remote_info.version_major = ext.version_major; remote_info.version_minor = ext.version_minor; using Flags = DashFactionJoinAcceptPacketExt::Flags; @@ -781,7 +781,7 @@ bool parse_df_join_accept_tail(const std::byte* const tail) { if (!!(ext.flags & Flags::max_fov) && ext.max_fov >= default_fov) { remote_info.max_fov.emplace(ext.max_fov); } - g_df_remote_info.emplace(remote_info); + g_remote_info.emplace(remote_info); return true; } @@ -798,28 +798,31 @@ bool parse_af_join_accept_tail(const std::byte* const tail) { ext.version_minor, static_cast(ext.flags) ); - AlpineFactionRemoteInfo remote_info{}; + RemoteInfo remote_info{}; remote_info.version_major = ext.version_major; remote_info.version_minor = ext.version_minor; using Flags = AlpineFactionJoinAcceptPacketExt::Flags; remote_info.saving_enabled = !!(ext.flags & Flags::saving_enabled); - remote_info.allow_fb_mesh = !!(ext.flags & Flags::allow_fb_mesh); - remote_info.allow_lmap = !!(ext.flags & Flags::allow_lmap); - remote_info.allow_no_ss = !!(ext.flags & Flags::allow_no_ss); - remote_info.no_player_collide = !!(ext.flags & Flags::no_player_collide); - remote_info.allow_no_mf = !!(ext.flags & Flags::allow_no_mf); - remote_info.click_limit = !!(ext.flags & Flags::click_limit); - remote_info.unlimited_fps = !!(ext.flags & Flags::unlimited_fps); - remote_info.gaussian_spread = !!(ext.flags & Flags::gaussian_spread); - remote_info.location_pinging = !!(ext.flags & Flags::location_pinging); constexpr float default_fov = 90.f; if (!!(ext.flags & Flags::max_fov) && ext.max_fov >= default_fov) { remote_info.max_fov.emplace(ext.max_fov); } - if (remote_info.click_limit) { - remote_info.semi_auto_cooldown.emplace(ext.semi_auto_cooldown); + g_remote_info.emplace(remote_info); + AlpineFactionRemoteInfo af_remote_info{}; + using Flags = AlpineFactionJoinAcceptPacketExt::Flags; + af_remote_info.allow_fb_mesh = !!(ext.flags & Flags::allow_fb_mesh); + af_remote_info.allow_lmap = !!(ext.flags & Flags::allow_lmap); + af_remote_info.allow_no_ss = !!(ext.flags & Flags::allow_no_ss); + af_remote_info.no_player_collide = !!(ext.flags & Flags::no_player_collide); + af_remote_info.allow_no_mf = !!(ext.flags & Flags::allow_no_mf); + af_remote_info.click_limit = !!(ext.flags & Flags::click_limit); + af_remote_info.unlimited_fps = !!(ext.flags & Flags::unlimited_fps); + af_remote_info.gaussian_spread = !!(ext.flags & Flags::gaussian_spread); + af_remote_info.location_pinging = !!(ext.flags & Flags::location_pinging); + if (af_remote_info.click_limit) { + af_remote_info.semi_auto_cooldown.emplace(ext.semi_auto_cooldown); } - g_af_remote_info.emplace(remote_info); + g_af_remote_info.emplace(af_remote_info); return true; } @@ -838,36 +841,25 @@ CodeInjection process_join_accept_injection{ ConsoleCommand2 remote_flags_cmd{ "remote_flags", [] { - if (g_df_remote_info) { - const DashFactionRemoteInfo& df_remote_info = g_df_remote_info.value(); + if (g_remote_info) { + const RemoteInfo& remote_info = g_remote_info.value(); rf::console::print("===================="); rf::console::print( - "Dash Faction {}.{}", - df_remote_info.version_major, - df_remote_info.version_minor + "{} Faction {}.{}", + g_af_remote_info ? "Alpine" : "Dash", + remote_info.version_major, + remote_info.version_minor ); rf::console::print("===================="); - rf::console::print("Teleport: {}", df_remote_info.saving_enabled); - if (df_remote_info.max_fov) { - rf::console::print("Max FOV: Some({})", df_remote_info.max_fov.value()); + rf::console::print("Teleport: {}", remote_info.saving_enabled); + if (remote_info.max_fov) { + rf::console::print("Max FOV: Some({})", remote_info.max_fov.value()); } else { rf::console::print("Max FOV: None"); } - } else if (g_af_remote_info) { + } + if (g_af_remote_info) { const AlpineFactionRemoteInfo& af_remote_info = g_af_remote_info.value(); - rf::console::print("===================="); - rf::console::print( - "Alpine Faction {}.{}", - af_remote_info.version_major, - af_remote_info.version_minor - ); - rf::console::print("===================="); - rf::console::print("Teleport: {}", af_remote_info.saving_enabled); - if (af_remote_info.max_fov) { - rf::console::print("Max FOV: Some({})", af_remote_info.max_fov.value()); - } else { - rf::console::print("Max FOV: None"); - } rf::console::print("Allow full-bright meshes: {}", af_remote_info.allow_fb_mesh); rf::console::print("Allow light maps only: {}", af_remote_info.allow_lmap); rf::console::print("Allow no screen shake: {}", af_remote_info.allow_no_ss); @@ -1048,7 +1040,7 @@ FunHook multi_stop_hook{ 0x0046E2C0, []() { // Clear server info when leaving - g_df_remote_info.reset(); + g_remote_info.reset(); g_af_remote_info.reset(); multi_stop_hook.call_target(); if (rf::local_player) { @@ -1057,9 +1049,9 @@ FunHook multi_stop_hook{ }, }; -const std::optional& get_df_remote_info() +const std::optional& get_remote_info() { - return g_df_remote_info; + return g_remote_info; } const std::optional& get_af_remote_info() { diff --git a/game_patch/object/event.cpp b/game_patch/object/event.cpp index 634aa7d8c..fa55f2585 100644 --- a/game_patch/object/event.cpp +++ b/game_patch/object/event.cpp @@ -9,6 +9,7 @@ #include "../rf/event.h" #include "../rf/entity.h" #include "../rf/multi.h" +#include "../rf/level.h" #include "../rf/file/file.h" #include "../rf/player/player.h" #include "../rf/os/console.h" From eaad62a5f593127d8c7387a3e388e44bfce00e84 Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Tue, 7 Oct 2025 23:49:45 +1300 Subject: [PATCH 068/134] Update --- game_patch/misc/player.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/game_patch/misc/player.cpp b/game_patch/misc/player.cpp index b591fb46d..629104634 100644 --- a/game_patch/misc/player.cpp +++ b/game_patch/misc/player.cpp @@ -3,11 +3,12 @@ #include "../rf/player/camera.h" #include "../rf/entity.h" #include "../rf/multi.h" -#include "../rf/sound/sound.h" #include "../rf/bmpman.h" #include "../rf/weapon.h" #include "../rf/hud.h" #include "../rf/input.h" +#include "../rf/sound/sound.h" +#include "../rf/ui.h" #include "../os/console.h" #include "../main/main.h" #include "../misc/level.h" From cc82516524ca24b9e763e688a929a1c1774dbcc3 Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Tue, 7 Oct 2025 23:56:17 +1300 Subject: [PATCH 069/134] Update --- common/include/common/config/GameConfig.h | 2 +- common/src/config/GameConfig.cpp | 2 +- game_patch/misc/level.cpp | 1 + game_patch/misc/player.cpp | 3 ++- game_patch/multi/network.cpp | 2 +- 5 files changed, 6 insertions(+), 4 deletions(-) diff --git a/common/include/common/config/GameConfig.h b/common/include/common/config/GameConfig.h index 23f4c58eb..dea509bfe 100644 --- a/common/include/common/config/GameConfig.h +++ b/common/include/common/config/GameConfig.h @@ -68,7 +68,7 @@ struct GameConfig // Audio CfgVar level_sound_volume = 1.0f; CfgVar eax_sound = true; - CfgVar play_hit_sounds = true; + CfgVar hit_sounds = true; // Multiplayer static const char default_rf_tracker[]; diff --git a/common/src/config/GameConfig.cpp b/common/src/config/GameConfig.cpp index a75247457..9b2970952 100644 --- a/common/src/config/GameConfig.cpp +++ b/common/src/config/GameConfig.cpp @@ -167,7 +167,7 @@ bool GameConfig::visit_vars(T&& visitor, bool is_save) result &= visitor(dash_faction_key, "Scoreboard Animations", scoreboard_anim); result &= visitor(dash_faction_key, "Spectate Mode Minimal UI", spectate_mode_minimal_ui); result &= visitor(dash_faction_key, "Level Sound Volume", level_sound_volume); - result &= visitor(dash_faction_key, "Play Hit Sounds", play_hit_sounds); + result &= visitor(dash_faction_key, "Hit Sounds", hit_sounds); result &= visitor(dash_faction_key, "Allow Overwriting Game Files", allow_overwrite_game_files); result &= visitor(dash_faction_key, "Version", dash_faction_version); result &= visitor(dash_faction_key, "Swap Assault Rifle Controls", swap_assault_rifle_controls); diff --git a/game_patch/misc/level.cpp b/game_patch/misc/level.cpp index cf28054de..7ba9861d8 100644 --- a/game_patch/misc/level.cpp +++ b/game_patch/misc/level.cpp @@ -9,6 +9,7 @@ #include "../rf/level.h" #include "../rf/geometry.h" #include "../rf/file/file.h" +#include "../rf/player/camera.h" #include "level.h" #include "../main/main.h" diff --git a/game_patch/misc/player.cpp b/game_patch/misc/player.cpp index 629104634..b70f94992 100644 --- a/game_patch/misc/player.cpp +++ b/game_patch/misc/player.cpp @@ -3,12 +3,13 @@ #include "../rf/player/camera.h" #include "../rf/entity.h" #include "../rf/multi.h" +#include "../rf/sound/sound.h" #include "../rf/bmpman.h" #include "../rf/weapon.h" #include "../rf/hud.h" #include "../rf/input.h" -#include "../rf/sound/sound.h" #include "../rf/ui.h" +#include "../rf/level.h" #include "../os/console.h" #include "../main/main.h" #include "../misc/level.h" diff --git a/game_patch/multi/network.cpp b/game_patch/multi/network.cpp index 74c0364d0..492e16ba7 100644 --- a/game_patch/multi/network.cpp +++ b/game_patch/multi/network.cpp @@ -1203,7 +1203,7 @@ void af_process_damage_notify_packet(const void* const data, const size_t len, c std::memcpy(&damage_notify_packet, data, sizeof(damage_notify_packet)); const bool died = static_cast(damage_notify_packet.flags & 0x1); - if (g_game_config.play_hit_sounds) { + if (g_game_config.hit_sounds) { constexpr int hit_sound_id = 29; static const int kill_sound_id = rf::snd_get_handle("af_killsound1.wav", 5.f, 1.f, 1.f); rf::snd_play(died ? kill_sound_id : hit_sound_id, 0, 0.f, 1.f); From fec94dc4d1a68704e1bd9470fd116c608cc54d2e Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Wed, 8 Oct 2025 00:02:55 +1300 Subject: [PATCH 070/134] Update --- game_patch/misc/player.cpp | 4 ++-- game_patch/object/entity.cpp | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/game_patch/misc/player.cpp b/game_patch/misc/player.cpp index b70f94992..1aadf3644 100644 --- a/game_patch/misc/player.cpp +++ b/game_patch/misc/player.cpp @@ -275,9 +275,9 @@ ConsoleCommand2 damage_screen_flash_cmd{ ConsoleCommand2 hit_sounds_cmd{ "hit_sounds", [] { - g_game_config.play_hit_sounds = !g_game_config.play_hit_sounds; + g_game_config.hit_sounds = !g_game_config.hit_sounds; g_game_config.save(); - rf::console::print("Hit sounds is {}", g_game_config.play_hit_sounds ? "enabled" : "disabled"); + rf::console::print("Hit sounds is {}", g_game_config.hit_sounds ? "enabled" : "disabled"); }, "Toggle whether to play a sound, when you hit players in multiplayer, if enabled by a server", "hit_sounds", diff --git a/game_patch/object/entity.cpp b/game_patch/object/entity.cpp index 4dbdb3ed4..b7fd427b9 100644 --- a/game_patch/object/entity.cpp +++ b/game_patch/object/entity.cpp @@ -391,6 +391,7 @@ void entity_do_patch() // Don't create muzzle flash lights entity_update_muzzle_flash_light_hook.install(); + // Support disabling camera shake from weapon fire. entity_fire_primary_weapon_camera_shake_hook.install(); // Commands From 5d0b8189955651c8a5f28cb5895acbe931bfeabf Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Wed, 8 Oct 2025 00:09:05 +1300 Subject: [PATCH 071/134] Update --- game_patch/misc/level.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/game_patch/misc/level.cpp b/game_patch/misc/level.cpp index 7ba9861d8..4d81e9504 100644 --- a/game_patch/misc/level.cpp +++ b/game_patch/misc/level.cpp @@ -9,6 +9,7 @@ #include "../rf/level.h" #include "../rf/geometry.h" #include "../rf/file/file.h" +#include "../rf/player/player.h" #include "../rf/player/camera.h" #include "level.h" #include "../main/main.h" From 503db44afbb246b9256f40bd24aad0fdd20832ab Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Wed, 8 Oct 2025 00:24:24 +1300 Subject: [PATCH 072/134] Update --- docs/CHANGELOG.md | 1 + game_patch/object/event.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index e37091661..adcc8ac01 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -22,6 +22,7 @@ Version 1.9.1 (released 2025-07-05) [@is-this-c](https://github.com/is-this-c) - Fix Alpine Faction disguise option +- Build improvements Version 1.9.0 (released 2025-04-06) -------------------------------- diff --git a/game_patch/object/event.cpp b/game_patch/object/event.cpp index fa55f2585..7e3c5ed4c 100644 --- a/game_patch/object/event.cpp +++ b/game_patch/object/event.cpp @@ -269,7 +269,7 @@ FunHook event_lookup_type_hook{ const char* const* const rf_event = std::find( std::begin(rf::event_names), std::end(rf::event_names), - name.c_str() + name ); if (rf_event == std::end(rf::event_names)) { g_level_has_unsupported_event_classes = true; From 52721e3a20f45cd17ee9a56513e0e6645e9cacaa Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Wed, 8 Oct 2025 00:53:32 +1300 Subject: [PATCH 073/134] Update --- game_patch/object/event.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/game_patch/object/event.cpp b/game_patch/object/event.cpp index 7e3c5ed4c..9b71da63c 100644 --- a/game_patch/object/event.cpp +++ b/game_patch/object/event.cpp @@ -266,17 +266,17 @@ CodeInjection event_load_level_turn_on_injection{ FunHook event_lookup_type_hook{ 0x004BD700, [] (const rf::String& name) { - const char* const* const rf_event = std::find( + const auto event = std::find( std::begin(rf::event_names), std::end(rf::event_names), name ); - if (rf_event == std::end(rf::event_names)) { - g_level_has_unsupported_event_classes = true; - } - std::optional id = event_name_mapper.find(name.c_str()); - if (!id) { + if (event == std::end(rf::event_names)) { xlog::warn("Unsupported event class: {}", name); + g_level_has_unsupported_event_classes = true; + } + const std::optional id = event_name_mapper.find(name); + if (!id) { return -1; } return id.value(); From 7f30336ef78475ee00b9f994535421e1377092c0 Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Wed, 8 Oct 2025 01:08:03 +1300 Subject: [PATCH 074/134] Update --- game_patch/misc/player.cpp | 2 +- game_patch/multi/network.cpp | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/game_patch/misc/player.cpp b/game_patch/misc/player.cpp index 1aadf3644..e380d97fa 100644 --- a/game_patch/misc/player.cpp +++ b/game_patch/misc/player.cpp @@ -316,7 +316,7 @@ CodeInjection player_execute_action_spawn_player_injection{ ); rf::ui::popup_message( "Error", - text .c_str(), + text.c_str(), nullptr, false ); diff --git a/game_patch/multi/network.cpp b/game_patch/multi/network.cpp index 492e16ba7..496e81940 100644 --- a/game_patch/multi/network.cpp +++ b/game_patch/multi/network.cpp @@ -809,7 +809,6 @@ bool parse_af_join_accept_tail(const std::byte* const tail) { } g_remote_info.emplace(remote_info); AlpineFactionRemoteInfo af_remote_info{}; - using Flags = AlpineFactionJoinAcceptPacketExt::Flags; af_remote_info.allow_fb_mesh = !!(ext.flags & Flags::allow_fb_mesh); af_remote_info.allow_lmap = !!(ext.flags & Flags::allow_lmap); af_remote_info.allow_no_ss = !!(ext.flags & Flags::allow_no_ss); @@ -1190,7 +1189,7 @@ struct af_damage_notify_packet { #pragma pack(pop) void af_process_damage_notify_packet(const void* const data, const size_t len, const rf::NetAddr& addr) { - // Receive: client <- server + // As a client, receive from a server. if (!rf::is_multi || rf::is_server || rf::is_dedicated_server) { return; } From 79ffab3732ff646d4f3d059c23494c272dd26b92 Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Wed, 8 Oct 2025 01:18:39 +1300 Subject: [PATCH 075/134] Update --- docs/CHANGELOG.md | 10 +++++----- game_patch/multi/network.cpp | 12 ++++++------ 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index adcc8ac01..c62a839a5 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -14,10 +14,10 @@ Version 1.9.2 (not released yet) Version 1.9.1 (released 2025-07-05) -------------------------------- [@rafalh](https://github.com/rafalh) -- Fix a crash when loading levels in version 300 with `AF_Teleport_Player` or `Clone_Entity` events +- Fix crash when loading levels in version 300 with `AF_Teleport_Player` or `Clone_Entity` events - Add basic handling for `AF_Teleport_Player` event - Add a new editor help file that includes information from the original red.hlp included with the game and the newer red.chm (RED help 1.1) published online -- Add an option to ignore level version +- Add option to ignore level version - Fix warnings in main menu caused by missing files (e.g. bigboom.vbm) [@is-this-c](https://github.com/is-this-c) @@ -69,9 +69,9 @@ Version 1.9.0 (released 2025-04-06) - Fix items being too dim in multi-player (RF bug) - Fix clutters and items being too bright when static mesh lighting was enabled - Add level property which enables full lightmaps depth (enabled by default in new maps) -- Add an option to disguise as Alpine Faction to avoid AF advertisements and allow playing in servers that reject non-AF players -- Enable loading partially supported RFL version 300 -- Log a warning when RFL uses an unsupported event class +- Add option to disguise as Alpine Faction to avoids AF advertisements and allow playing in servers that reject non-AF players +- Enable loading RFL version 300 +- Log a warning when RFL uses unsupported event class - Add support for high resolution GeoMod textures - Add gibs support based on RF PS2 demo (use `gibs` command to enable) - Add muzzle flash option in the launcher diff --git a/game_patch/multi/network.cpp b/game_patch/multi/network.cpp index 496e81940..12ea009b4 100644 --- a/game_patch/multi/network.cpp +++ b/game_patch/multi/network.cpp @@ -850,7 +850,7 @@ ConsoleCommand2 remote_flags_cmd{ remote_info.version_minor ); rf::console::print("===================="); - rf::console::print("Teleport: {}", remote_info.saving_enabled); + rf::console::print("Saving: {}", remote_info.saving_enabled); if (remote_info.max_fov) { rf::console::print("Max FOV: Some({})", remote_info.max_fov.value()); } else { @@ -860,22 +860,22 @@ ConsoleCommand2 remote_flags_cmd{ if (g_af_remote_info) { const AlpineFactionRemoteInfo& af_remote_info = g_af_remote_info.value(); rf::console::print("Allow full-bright meshes: {}", af_remote_info.allow_fb_mesh); - rf::console::print("Allow light maps only: {}", af_remote_info.allow_lmap); + rf::console::print("Allow only light maps: {}", af_remote_info.allow_lmap); rf::console::print("Allow no screen shake: {}", af_remote_info.allow_no_ss); rf::console::print("No player collide: {}", af_remote_info.no_player_collide); rf::console::print("Allow no muzzle flash: {}", af_remote_info.allow_no_mf); - rf::console::print("Semiauto fire rate limit: {}", af_remote_info.click_limit); + rf::console::print("Click limit: {}", af_remote_info.click_limit); if (af_remote_info.semi_auto_cooldown) { rf::console::print( - "Semiauto cool-down: Some({})", + "Semi auto cooldown: Some({})", af_remote_info.semi_auto_cooldown.value() ); } else { - rf::console::print("Semiauto cool-down: None"); + rf::console::print("Semi auto cooldown: None"); } rf::console::print("Allow unlimited FPS: {}", af_remote_info.unlimited_fps); rf::console::print("Gaussian spread: {}", af_remote_info.gaussian_spread); - rf::console::print("Ping locations: {}", af_remote_info.location_pinging); + rf::console::print("Location pinging: {}", af_remote_info.location_pinging); } } }; From 43475b0f79f28b97e8c3b4763344cee8b669135e Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Wed, 8 Oct 2025 01:27:11 +1300 Subject: [PATCH 076/134] Update --- game_patch/multi/network.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/game_patch/multi/network.cpp b/game_patch/multi/network.cpp index 12ea009b4..7e153b9ae 100644 --- a/game_patch/multi/network.cpp +++ b/game_patch/multi/network.cpp @@ -850,7 +850,7 @@ ConsoleCommand2 remote_flags_cmd{ remote_info.version_minor ); rf::console::print("===================="); - rf::console::print("Saving: {}", remote_info.saving_enabled); + rf::console::print("Teleport: {}", remote_info.saving_enabled); if (remote_info.max_fov) { rf::console::print("Max FOV: Some({})", remote_info.max_fov.value()); } else { @@ -860,22 +860,22 @@ ConsoleCommand2 remote_flags_cmd{ if (g_af_remote_info) { const AlpineFactionRemoteInfo& af_remote_info = g_af_remote_info.value(); rf::console::print("Allow full-bright meshes: {}", af_remote_info.allow_fb_mesh); - rf::console::print("Allow only light maps: {}", af_remote_info.allow_lmap); + rf::console::print("Allow light-maps-only mode: {}", af_remote_info.allow_lmap); rf::console::print("Allow no screen shake: {}", af_remote_info.allow_no_ss); - rf::console::print("No player collide: {}", af_remote_info.no_player_collide); + rf::console::print("No collide players: {}", af_remote_info.no_player_collide); rf::console::print("Allow no muzzle flash: {}", af_remote_info.allow_no_mf); - rf::console::print("Click limit: {}", af_remote_info.click_limit); + rf::console::print("Semiauto fire rate limit: {}", af_remote_info.click_limit); if (af_remote_info.semi_auto_cooldown) { rf::console::print( - "Semi auto cooldown: Some({})", + "Semiauto cool-down: Some({})", af_remote_info.semi_auto_cooldown.value() ); } else { - rf::console::print("Semi auto cooldown: None"); + rf::console::print("Semiauto cool-down: None"); } rf::console::print("Allow unlimited FPS: {}", af_remote_info.unlimited_fps); rf::console::print("Gaussian spread: {}", af_remote_info.gaussian_spread); - rf::console::print("Location pinging: {}", af_remote_info.location_pinging); + rf::console::print("Ping locations: {}", af_remote_info.location_pinging); } } }; From 68be7b212559344a0ce72360313879d287a260da Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Wed, 8 Oct 2025 01:36:32 +1300 Subject: [PATCH 077/134] Update --- game_patch/multi/network.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/game_patch/multi/network.cpp b/game_patch/multi/network.cpp index 7e153b9ae..9c7ed899e 100644 --- a/game_patch/multi/network.cpp +++ b/game_patch/multi/network.cpp @@ -862,7 +862,7 @@ ConsoleCommand2 remote_flags_cmd{ rf::console::print("Allow full-bright meshes: {}", af_remote_info.allow_fb_mesh); rf::console::print("Allow light-maps-only mode: {}", af_remote_info.allow_lmap); rf::console::print("Allow no screen shake: {}", af_remote_info.allow_no_ss); - rf::console::print("No collide players: {}", af_remote_info.no_player_collide); + rf::console::print("No player collide: {}", af_remote_info.no_player_collide); rf::console::print("Allow no muzzle flash: {}", af_remote_info.allow_no_mf); rf::console::print("Semiauto fire rate limit: {}", af_remote_info.click_limit); if (af_remote_info.semi_auto_cooldown) { From 7fa5f676d6097bfd9168711c6814185ca948fb28 Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Wed, 8 Oct 2025 01:46:32 +1300 Subject: [PATCH 078/134] Update --- game_patch/multi/network.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/game_patch/multi/network.cpp b/game_patch/multi/network.cpp index 9c7ed899e..496e81940 100644 --- a/game_patch/multi/network.cpp +++ b/game_patch/multi/network.cpp @@ -860,7 +860,7 @@ ConsoleCommand2 remote_flags_cmd{ if (g_af_remote_info) { const AlpineFactionRemoteInfo& af_remote_info = g_af_remote_info.value(); rf::console::print("Allow full-bright meshes: {}", af_remote_info.allow_fb_mesh); - rf::console::print("Allow light-maps-only mode: {}", af_remote_info.allow_lmap); + rf::console::print("Allow light maps only: {}", af_remote_info.allow_lmap); rf::console::print("Allow no screen shake: {}", af_remote_info.allow_no_ss); rf::console::print("No player collide: {}", af_remote_info.no_player_collide); rf::console::print("Allow no muzzle flash: {}", af_remote_info.allow_no_mf); From 8031b2d1b03abbec0b5c3429b3107c6d8266503a Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Wed, 8 Oct 2025 02:02:30 +1300 Subject: [PATCH 079/134] Update --- resources/licensing-info.txt | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/resources/licensing-info.txt b/resources/licensing-info.txt index e333181d6..ff5dca6e1 100644 --- a/resources/licensing-info.txt +++ b/resources/licensing-info.txt @@ -33,19 +33,19 @@ Contributors --------------------------------- Chris "Goober" Parsons: -* Code contributions -* Textures for `bighud` -* Symbol glyphs in TTF fonts -* Launcher header +* code contributions +* HUD textures for the big mode +* symbol glyphs in ttf fonts +* launcher header natalie : -* Code contributions +* code contributions is-this-c: -* Code contributions +* code contributions Mystyle: -* Setup code improvements +* setup code improvements ############################################################################### ## subhook From 6e35b828369ed2cf39e5aa13d1f6da0a518f8d8d Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Wed, 8 Oct 2025 02:12:03 +1300 Subject: [PATCH 080/134] Update --- game_patch/graphics/gr.cpp | 16 ++--- game_patch/misc/save_restore.cpp | 4 +- game_patch/multi/multi.h | 8 +-- game_patch/multi/network.cpp | 108 +++++++++++++++---------------- game_patch/multi/server.cpp | 2 +- game_patch/object/entity.cpp | 8 +-- game_patch/object/weapon.cpp | 8 +-- 7 files changed, 77 insertions(+), 77 deletions(-) diff --git a/game_patch/graphics/gr.cpp b/game_patch/graphics/gr.cpp index 6e42c3a3f..54a6f6829 100644 --- a/game_patch/graphics/gr.cpp +++ b/game_patch/graphics/gr.cpp @@ -82,10 +82,10 @@ float gr_scale_world_fov(float horizontal_fov = 90.0f) horizontal_fov = gr_scale_fov_hor_plus(horizontal_fov); } - if (get_remote_info()) { - const RemoteInfo& remote_info = get_remote_info().value(); - if (remote_info.max_fov) { - horizontal_fov = std::min(horizontal_fov, remote_info.max_fov.value()); + if (get_remote_server_info()) { + const RemoteServerInfo& remote_server_info = get_remote_server_info().value(); + if (remote_server_info.max_fov) { + horizontal_fov = std::min(horizontal_fov, remote_server_info.max_fov.value()); } } @@ -126,10 +126,10 @@ ConsoleCommand2 fov_cmd{ } rf::console::print("Horizontal FOV: {:.2f}", gr_scale_world_fov()); - if (get_remote_info()) { - const RemoteInfo& remote_info = get_remote_info().value(); - if (remote_info.max_fov) { - rf::console::print("Server FOV limit: {:.2f}", remote_info.max_fov.value()); + if (get_remote_server_info()) { + const RemoteServerInfo& remote_server_info = get_remote_server_info().value(); + if (remote_server_info.max_fov) { + rf::console::print("Server FOV limit: {:.2f}", remote_server_info.max_fov.value()); } } }, diff --git a/game_patch/misc/save_restore.cpp b/game_patch/misc/save_restore.cpp index 56a82d48f..7e3676215 100644 --- a/game_patch/misc/save_restore.cpp +++ b/game_patch/misc/save_restore.cpp @@ -257,7 +257,7 @@ FunHook quick_save_hook{ 0x004B6160, []() { quick_save_hook.call_target(); - if (get_remote_info() && get_remote_info().value().saving_enabled) { + if (get_remote_server_info() && get_remote_server_info().value().saving_enabled) { send_chat_line_packet("/save", nullptr); } }, @@ -267,7 +267,7 @@ FunHook quick_load_hook{ 0x004B6180, []() { quick_load_hook.call_target(); - if (get_remote_info() && get_remote_info().value().saving_enabled) { + if (get_remote_server_info() && get_remote_server_info().value().saving_enabled) { send_chat_line_packet("/load", nullptr); } }, diff --git a/game_patch/multi/multi.h b/game_patch/multi/multi.h index 8cf6cb8fe..b32357bad 100644 --- a/game_patch/multi/multi.h +++ b/game_patch/multi/multi.h @@ -73,14 +73,14 @@ struct PlayerStatsNew : rf::PlayerLevelStats } }; -struct RemoteInfo { +struct RemoteServerInfo { uint8_t version_major = 0; uint8_t version_minor = 0; bool saving_enabled = false; std::optional max_fov{}; }; -struct AlpineFactionRemoteInfo { +struct AlpineFactionRemoteServerInfo { bool allow_fb_mesh = false; bool allow_lmap = false; bool allow_no_ss = false; @@ -98,8 +98,8 @@ void multi_do_patch(); void multi_after_full_game_init(); void multi_init_player(rf::Player* player); void send_chat_line_packet(const char* msg, rf::Player* target, rf::Player* sender = nullptr, bool is_team_msg = false); -const std::optional& get_remote_info(); -const std::optional& get_af_remote_info(); +const std::optional& get_remote_server_info(); +const std::optional& get_af_remote_server_info(); void multi_level_download_do_frame(); void multi_level_download_abort(); void multi_ban_apply_patch(); diff --git a/game_patch/multi/network.cpp b/game_patch/multi/network.cpp index 496e81940..39a14daeb 100644 --- a/game_patch/multi/network.cpp +++ b/game_patch/multi/network.cpp @@ -262,8 +262,8 @@ std::array g_client_side_packet_whitelist{ }; // clang-format on -std::optional g_remote_info{}; -std::optional g_af_remote_info{}; +std::optional g_remote_server_info{}; +std::optional g_af_remote_server_info{}; CodeInjection process_game_packet_whitelist_filter{ 0x0047918D, @@ -772,16 +772,16 @@ bool parse_df_join_accept_tail(const std::byte* const tail) { ext.version_minor, static_cast(ext.flags) ); - RemoteInfo remote_info{}; - remote_info.version_major = ext.version_major; - remote_info.version_minor = ext.version_minor; + RemoteServerInfo remote_server_info{}; + remote_server_info.version_major = ext.version_major; + remote_server_info.version_minor = ext.version_minor; using Flags = DashFactionJoinAcceptPacketExt::Flags; - remote_info.saving_enabled = !!(ext.flags & Flags::saving_enabled); + remote_server_info.saving_enabled = !!(ext.flags & Flags::saving_enabled); constexpr float default_fov = 90.f; if (!!(ext.flags & Flags::max_fov) && ext.max_fov >= default_fov) { - remote_info.max_fov.emplace(ext.max_fov); + remote_server_info.max_fov.emplace(ext.max_fov); } - g_remote_info.emplace(remote_info); + g_remote_server_info.emplace(remote_server_info); return true; } @@ -798,30 +798,30 @@ bool parse_af_join_accept_tail(const std::byte* const tail) { ext.version_minor, static_cast(ext.flags) ); - RemoteInfo remote_info{}; - remote_info.version_major = ext.version_major; - remote_info.version_minor = ext.version_minor; + RemoteServerInfo remote_server_info{}; + remote_server_info.version_major = ext.version_major; + remote_server_info.version_minor = ext.version_minor; using Flags = AlpineFactionJoinAcceptPacketExt::Flags; - remote_info.saving_enabled = !!(ext.flags & Flags::saving_enabled); + remote_server_info.saving_enabled = !!(ext.flags & Flags::saving_enabled); constexpr float default_fov = 90.f; if (!!(ext.flags & Flags::max_fov) && ext.max_fov >= default_fov) { - remote_info.max_fov.emplace(ext.max_fov); + remote_server_info.max_fov.emplace(ext.max_fov); } - g_remote_info.emplace(remote_info); - AlpineFactionRemoteInfo af_remote_info{}; - af_remote_info.allow_fb_mesh = !!(ext.flags & Flags::allow_fb_mesh); - af_remote_info.allow_lmap = !!(ext.flags & Flags::allow_lmap); - af_remote_info.allow_no_ss = !!(ext.flags & Flags::allow_no_ss); - af_remote_info.no_player_collide = !!(ext.flags & Flags::no_player_collide); - af_remote_info.allow_no_mf = !!(ext.flags & Flags::allow_no_mf); - af_remote_info.click_limit = !!(ext.flags & Flags::click_limit); - af_remote_info.unlimited_fps = !!(ext.flags & Flags::unlimited_fps); - af_remote_info.gaussian_spread = !!(ext.flags & Flags::gaussian_spread); - af_remote_info.location_pinging = !!(ext.flags & Flags::location_pinging); - if (af_remote_info.click_limit) { - af_remote_info.semi_auto_cooldown.emplace(ext.semi_auto_cooldown); + g_remote_server_info.emplace(remote_server_info); + AlpineFactionRemoteServerInfo af_remote_server_info{}; + af_remote_server_info.allow_fb_mesh = !!(ext.flags & Flags::allow_fb_mesh); + af_remote_server_info.allow_lmap = !!(ext.flags & Flags::allow_lmap); + af_remote_server_info.allow_no_ss = !!(ext.flags & Flags::allow_no_ss); + af_remote_server_info.no_player_collide = !!(ext.flags & Flags::no_player_collide); + af_remote_server_info.allow_no_mf = !!(ext.flags & Flags::allow_no_mf); + af_remote_server_info.click_limit = !!(ext.flags & Flags::click_limit); + af_remote_server_info.unlimited_fps = !!(ext.flags & Flags::unlimited_fps); + af_remote_server_info.gaussian_spread = !!(ext.flags & Flags::gaussian_spread); + af_remote_server_info.location_pinging = !!(ext.flags & Flags::location_pinging); + if (af_remote_server_info.click_limit) { + af_remote_server_info.semi_auto_cooldown.emplace(ext.semi_auto_cooldown); } - g_af_remote_info.emplace(af_remote_info); + g_af_remote_server_info.emplace(af_remote_server_info); return true; } @@ -840,42 +840,42 @@ CodeInjection process_join_accept_injection{ ConsoleCommand2 remote_flags_cmd{ "remote_flags", [] { - if (g_remote_info) { - const RemoteInfo& remote_info = g_remote_info.value(); + if (g_remote_server_info) { + const RemoteServerInfo& remote_server_info = g_remote_server_info.value(); rf::console::print("===================="); rf::console::print( "{} Faction {}.{}", - g_af_remote_info ? "Alpine" : "Dash", - remote_info.version_major, - remote_info.version_minor + g_af_remote_server_info ? "Alpine" : "Dash", + remote_server_info.version_major, + remote_server_info.version_minor ); rf::console::print("===================="); - rf::console::print("Teleport: {}", remote_info.saving_enabled); - if (remote_info.max_fov) { - rf::console::print("Max FOV: Some({})", remote_info.max_fov.value()); + rf::console::print("Teleport: {}", remote_server_info.saving_enabled); + if (remote_server_info.max_fov) { + rf::console::print("Max FOV: Some({})", remote_server_info.max_fov.value()); } else { rf::console::print("Max FOV: None"); } } - if (g_af_remote_info) { - const AlpineFactionRemoteInfo& af_remote_info = g_af_remote_info.value(); - rf::console::print("Allow full-bright meshes: {}", af_remote_info.allow_fb_mesh); - rf::console::print("Allow light maps only: {}", af_remote_info.allow_lmap); - rf::console::print("Allow no screen shake: {}", af_remote_info.allow_no_ss); - rf::console::print("No player collide: {}", af_remote_info.no_player_collide); - rf::console::print("Allow no muzzle flash: {}", af_remote_info.allow_no_mf); - rf::console::print("Semiauto fire rate limit: {}", af_remote_info.click_limit); - if (af_remote_info.semi_auto_cooldown) { + if (g_af_remote_server_info) { + const AlpineFactionRemoteServerInfo& af_remote_server_info = g_af_remote_server_info.value(); + rf::console::print("Allow full-bright meshes: {}", af_remote_server_info.allow_fb_mesh); + rf::console::print("Allow light maps only: {}", af_remote_server_info.allow_lmap); + rf::console::print("Allow no screen shake: {}", af_remote_server_info.allow_no_ss); + rf::console::print("No player collide: {}", af_remote_server_info.no_player_collide); + rf::console::print("Allow no muzzle flash: {}", af_remote_server_info.allow_no_mf); + rf::console::print("Semiauto fire rate limit: {}", af_remote_server_info.click_limit); + if (af_remote_server_info.semi_auto_cooldown) { rf::console::print( "Semiauto cool-down: Some({})", - af_remote_info.semi_auto_cooldown.value() + af_remote_server_info.semi_auto_cooldown.value() ); } else { rf::console::print("Semiauto cool-down: None"); } - rf::console::print("Allow unlimited FPS: {}", af_remote_info.unlimited_fps); - rf::console::print("Gaussian spread: {}", af_remote_info.gaussian_spread); - rf::console::print("Ping locations: {}", af_remote_info.location_pinging); + rf::console::print("Allow unlimited FPS: {}", af_remote_server_info.unlimited_fps); + rf::console::print("Gaussian spread: {}", af_remote_server_info.gaussian_spread); + rf::console::print("Ping locations: {}", af_remote_server_info.location_pinging); } } }; @@ -1039,8 +1039,8 @@ FunHook multi_stop_hook{ 0x0046E2C0, []() { // Clear server info when leaving - g_remote_info.reset(); - g_af_remote_info.reset(); + g_remote_server_info.reset(); + g_af_remote_server_info.reset(); multi_stop_hook.call_target(); if (rf::local_player) { reset_player_additional_data(rf::local_player); @@ -1048,13 +1048,13 @@ FunHook multi_stop_hook{ }, }; -const std::optional& get_remote_info() +const std::optional& get_remote_server_info() { - return g_remote_info; + return g_remote_server_info; } -const std::optional& get_af_remote_info() { - return g_af_remote_info; +const std::optional& get_af_remote_server_info() { + return g_af_remote_server_info; } void send_chat_line_packet(const char* msg, rf::Player* target, rf::Player* sender, bool is_team_msg) diff --git a/game_patch/multi/server.cpp b/game_patch/multi/server.cpp index d17cbc759..4edd97558 100644 --- a/game_patch/multi/server.cpp +++ b/game_patch/multi/server.cpp @@ -716,7 +716,7 @@ CodeInjection multi_level_init_injection{ CallHook player_create_entity_entity_create_hook{ 0x004A41D3, [] (const int type, const char* const name, const int parent_handle, const rf::Vector3& pos, rf::Matrix3& orient, int create_flags, const int mp_character) { - if (get_af_remote_info() && get_af_remote_info().value().no_player_collide) { + if (get_af_remote_server_info() && get_af_remote_server_info().value().no_player_collide) { constexpr int NO_PLAYER_COLLIDE = 0x4; create_flags |= NO_PLAYER_COLLIDE; } diff --git a/game_patch/object/entity.cpp b/game_patch/object/entity.cpp index b7fd427b9..5db2bdd9d 100644 --- a/game_patch/object/entity.cpp +++ b/game_patch/object/entity.cpp @@ -280,8 +280,8 @@ static FunHook entity_damage_hook{ CallHook entity_fire_primary_weapon_camera_shake_hook{ 0x00426C79, [] (rf::Camera* const cp, const float amplitude, const float time_seconds) { - const bool remote_force_ss = get_af_remote_info() - && !get_af_remote_info().value().allow_no_ss; + const bool remote_force_ss = get_af_remote_server_info() + && !get_af_remote_server_info().value().allow_no_ss; if (g_game_config.weapon_screen_shake || remote_force_ss) { entity_fire_primary_weapon_camera_shake_hook.call_target(cp, amplitude, time_seconds); } @@ -306,8 +306,8 @@ ConsoleCommand2 weapon_screen_shake_cmd{ CallHook entity_update_muzzle_flash_light_hook{ 0x0041E814, [] (rf::Entity& ep) { - const bool remote_force_muzzle_flash = get_af_remote_info() - && !get_af_remote_info().value().allow_no_mf; + const bool remote_force_muzzle_flash = get_af_remote_server_info() + && !get_af_remote_server_info().value().allow_no_mf; if (g_game_config.muzzle_flash || remote_force_muzzle_flash) { entity_update_muzzle_flash_light_hook.call_target(ep); } diff --git a/game_patch/object/weapon.cpp b/game_patch/object/weapon.cpp index 008ad1e79..126cd6ae2 100644 --- a/game_patch/object/weapon.cpp +++ b/game_patch/object/weapon.cpp @@ -127,8 +127,8 @@ CallHook weapon_hit_wall_obj_apply_r }; std::optional remote_fire_wait_override() { - return get_af_remote_info().and_then([] (const AlpineFactionRemoteInfo& af_remote_info) { - return af_remote_info.semi_auto_cooldown; + return get_af_remote_server_info().and_then([] (const AlpineFactionRemoteServerInfo& af_remote_server_info) { + return af_remote_server_info.semi_auto_cooldown; }); } @@ -136,8 +136,8 @@ CodeInjection player_fire_primary_weapon_semi_auto_patch{ 0x004A50BB, [] (auto& regs) { const rf::Entity* const entity = regs.esi; - if (get_af_remote_info() - && get_af_remote_info().value().click_limit + if (get_af_remote_server_info() + && get_af_remote_server_info().value().click_limit && !entity->ai.next_fire_primary.elapsed()) { regs.eip = 0x004A58B8; } From f0c56aa818a869ac58a32ce464f4773fe735b360 Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Wed, 8 Oct 2025 02:25:12 +1300 Subject: [PATCH 081/134] Update --- docs/CHANGELOG.md | 2 +- game_patch/multi/network.cpp | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index ad800218d..54898a10a 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -10,7 +10,7 @@ Version 1.9.2 (not released yet) - Add `af_killsound1.wav` from Alpine Faction - Add `hit_sounds` command - Add `weapon_screen_shake` command -- Add `remote_flags` command +- Add `remote_server_flags` command Version 1.9.1 (released 2025-07-05) -------------------------------- diff --git a/game_patch/multi/network.cpp b/game_patch/multi/network.cpp index 39a14daeb..1f8ad4c0d 100644 --- a/game_patch/multi/network.cpp +++ b/game_patch/multi/network.cpp @@ -837,8 +837,8 @@ CodeInjection process_join_accept_injection{ } }; -ConsoleCommand2 remote_flags_cmd{ - "remote_flags", +ConsoleCommand2 remote_server_flags_cmd{ + "remote_server_flags", [] { if (g_remote_server_info) { const RemoteServerInfo& remote_server_info = g_remote_server_info.value(); @@ -1444,5 +1444,5 @@ void network_init() // Ignore browsers when calculating player count for info requests game_info_num_players_hook.install(); - remote_flags_cmd.register_cmd(); + remote_server_flags_cmd.register_cmd(); } From 8fd9576468f9153b5d21244c7d3a63763c83ae3a Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Thu, 9 Oct 2025 19:15:06 +1300 Subject: [PATCH 082/134] Update --- game_patch/multi/multi.h | 11 +++++++++++ game_patch/multi/network.cpp | 37 +++++++++++++++++++++++------------- 2 files changed, 35 insertions(+), 13 deletions(-) diff --git a/game_patch/multi/multi.h b/game_patch/multi/multi.h index b32357bad..a5d9913cb 100644 --- a/game_patch/multi/multi.h +++ b/game_patch/multi/multi.h @@ -80,7 +80,18 @@ struct RemoteServerInfo { std::optional max_fov{}; }; +struct DashFactionRemoteServerInfo { + uint8_t version_major = 0; + uint8_t version_minor = 0; + bool saving_enabled = false; + std::optional max_fov{}; +}; + struct AlpineFactionRemoteServerInfo { + uint8_t version_major = 0; + uint8_t version_minor = 0; + bool saving_enabled = false; + std::optional max_fov{}; bool allow_fb_mesh = false; bool allow_lmap = false; bool allow_no_ss = false; diff --git a/game_patch/multi/network.cpp b/game_patch/multi/network.cpp index 1f8ad4c0d..aeaf5d51f 100644 --- a/game_patch/multi/network.cpp +++ b/game_patch/multi/network.cpp @@ -263,6 +263,7 @@ std::array g_client_side_packet_whitelist{ // clang-format on std::optional g_remote_server_info{}; +std::optional g_df_remote_server_info{}; std::optional g_af_remote_server_info{}; CodeInjection process_game_packet_whitelist_filter{ @@ -772,16 +773,22 @@ bool parse_df_join_accept_tail(const std::byte* const tail) { ext.version_minor, static_cast(ext.flags) ); - RemoteServerInfo remote_server_info{}; - remote_server_info.version_major = ext.version_major; - remote_server_info.version_minor = ext.version_minor; + DashFactionRemoteServerInfo df_remote_server_info{}; + df_remote_server_info.version_major = ext.version_major; + df_remote_server_info.version_minor = ext.version_minor; using Flags = DashFactionJoinAcceptPacketExt::Flags; - remote_server_info.saving_enabled = !!(ext.flags & Flags::saving_enabled); + df_remote_server_info.saving_enabled = !!(ext.flags & Flags::saving_enabled); constexpr float default_fov = 90.f; if (!!(ext.flags & Flags::max_fov) && ext.max_fov >= default_fov) { - remote_server_info.max_fov.emplace(ext.max_fov); + df_remote_server_info.max_fov.emplace(ext.max_fov); } - g_remote_server_info.emplace(remote_server_info); + g_df_remote_server_info.emplace(df_remote_server_info); + g_remote_server_info.emplace(RemoteServerInfo{ + .version_major = df_remote_server_info.version_major, + .version_minor = df_remote_server_info.version_minor, + .saving_enabled = df_remote_server_info.saving_enabled, + .max_fov = df_remote_server_info.max_fov, + }); return true; } @@ -798,17 +805,15 @@ bool parse_af_join_accept_tail(const std::byte* const tail) { ext.version_minor, static_cast(ext.flags) ); - RemoteServerInfo remote_server_info{}; - remote_server_info.version_major = ext.version_major; - remote_server_info.version_minor = ext.version_minor; + AlpineFactionRemoteServerInfo af_remote_server_info{}; + af_remote_server_info.version_major = ext.version_major; + af_remote_server_info.version_minor = ext.version_minor; using Flags = AlpineFactionJoinAcceptPacketExt::Flags; - remote_server_info.saving_enabled = !!(ext.flags & Flags::saving_enabled); + af_remote_server_info.saving_enabled = !!(ext.flags & Flags::saving_enabled); constexpr float default_fov = 90.f; if (!!(ext.flags & Flags::max_fov) && ext.max_fov >= default_fov) { - remote_server_info.max_fov.emplace(ext.max_fov); + af_remote_server_info.max_fov.emplace(ext.max_fov); } - g_remote_server_info.emplace(remote_server_info); - AlpineFactionRemoteServerInfo af_remote_server_info{}; af_remote_server_info.allow_fb_mesh = !!(ext.flags & Flags::allow_fb_mesh); af_remote_server_info.allow_lmap = !!(ext.flags & Flags::allow_lmap); af_remote_server_info.allow_no_ss = !!(ext.flags & Flags::allow_no_ss); @@ -822,6 +827,12 @@ bool parse_af_join_accept_tail(const std::byte* const tail) { af_remote_server_info.semi_auto_cooldown.emplace(ext.semi_auto_cooldown); } g_af_remote_server_info.emplace(af_remote_server_info); + g_remote_server_info.emplace(RemoteServerInfo{ + .version_major = af_remote_server_info.version_major, + .version_minor = af_remote_server_info.version_minor, + .saving_enabled = af_remote_server_info.saving_enabled, + .max_fov = af_remote_server_info.max_fov, + }); return true; } From 2774aa3d675270b121bb22f9c46f4fd09ada9a35 Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Thu, 9 Oct 2025 19:20:55 +1300 Subject: [PATCH 083/134] Update --- game_patch/object/event.cpp | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/game_patch/object/event.cpp b/game_patch/object/event.cpp index 9b71da63c..a5f622127 100644 --- a/game_patch/object/event.cpp +++ b/game_patch/object/event.cpp @@ -50,8 +50,6 @@ EventNameMapper event_name_mapper; const char* custom_event_names[] = { "AF_Teleport_Player", - "Clone_Entity", - "Anchor_Marker_Orient", }; enum EventType : int { @@ -61,8 +59,6 @@ enum EventType : int { Alarm = 0x2E, // There are 90 builtin events so start from 0x5A (90) AF_Teleport_Player = 0x5A, - Clone_Entity, - Anchor_Marker_Orient, }; CodeInjection switch_model_event_custom_mesh_patch{ @@ -266,17 +262,10 @@ CodeInjection event_load_level_turn_on_injection{ FunHook event_lookup_type_hook{ 0x004BD700, [] (const rf::String& name) { - const auto event = std::find( - std::begin(rf::event_names), - std::end(rf::event_names), - name - ); - if (event == std::end(rf::event_names)) { + const std::optional id = event_name_mapper.find(name); + if (!id) { xlog::warn("Unsupported event class: {}", name); g_level_has_unsupported_event_classes = true; - } - const std::optional id = event_name_mapper.find(name); - if (!id) { return -1; } return id.value(); From 40fe6a274172c9c2aa82845fe35a8d79e1966351 Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Thu, 9 Oct 2025 19:27:53 +1300 Subject: [PATCH 084/134] Update --- game_patch/object/event.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/game_patch/object/event.cpp b/game_patch/object/event.cpp index a5f622127..4d1f6a106 100644 --- a/game_patch/object/event.cpp +++ b/game_patch/object/event.cpp @@ -59,6 +59,8 @@ enum EventType : int { Alarm = 0x2E, // There are 90 builtin events so start from 0x5A (90) AF_Teleport_Player = 0x5A, + Clone_Entity, + Anchor_Marker_Orient, }; CodeInjection switch_model_event_custom_mesh_patch{ From c55b6303be6a3a965cb882fe6f75223e67c33839 Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Mon, 13 Oct 2025 17:32:14 +1300 Subject: [PATCH 085/134] Update --- game_patch/misc/save_restore.cpp | 4 +- game_patch/multi/multi.h | 34 ++++------- game_patch/multi/network.cpp | 102 +++++++++++++------------------ game_patch/multi/server.cpp | 2 +- game_patch/object/entity.cpp | 8 +-- game_patch/object/weapon.cpp | 8 +-- 6 files changed, 63 insertions(+), 95 deletions(-) diff --git a/game_patch/misc/save_restore.cpp b/game_patch/misc/save_restore.cpp index 7e3676215..ea7168b56 100644 --- a/game_patch/misc/save_restore.cpp +++ b/game_patch/misc/save_restore.cpp @@ -257,7 +257,7 @@ FunHook quick_save_hook{ 0x004B6160, []() { quick_save_hook.call_target(); - if (get_remote_server_info() && get_remote_server_info().value().saving_enabled) { + if (get_remote_server_info() && get_remote_server_info().value().saving) { send_chat_line_packet("/save", nullptr); } }, @@ -267,7 +267,7 @@ FunHook quick_load_hook{ 0x004B6180, []() { quick_load_hook.call_target(); - if (get_remote_server_info() && get_remote_server_info().value().saving_enabled) { + if (get_remote_server_info() && get_remote_server_info().value().saving) { send_chat_line_packet("/load", nullptr); } }, diff --git a/game_patch/multi/multi.h b/game_patch/multi/multi.h index a5d9913cb..f7057bf2f 100644 --- a/game_patch/multi/multi.h +++ b/game_patch/multi/multi.h @@ -74,31 +74,20 @@ struct PlayerStatsNew : rf::PlayerLevelStats }; struct RemoteServerInfo { - uint8_t version_major = 0; - uint8_t version_minor = 0; - bool saving_enabled = false; + struct { + uint8_t major = 0; + uint8_t minor = 0; + } version; + bool saving = false; std::optional max_fov{}; -}; - -struct DashFactionRemoteServerInfo { - uint8_t version_major = 0; - uint8_t version_minor = 0; - bool saving_enabled = false; - std::optional max_fov{}; -}; - -struct AlpineFactionRemoteServerInfo { - uint8_t version_major = 0; - uint8_t version_minor = 0; - bool saving_enabled = false; - std::optional max_fov{}; - bool allow_fb_mesh = false; - bool allow_lmap = false; - bool allow_no_ss = false; + bool alpine_faction = false; + bool allow_full_bright_entities = true; + bool allow_light_maps_only = false; + bool allow_no_screen_shake = true; bool no_player_collide = false; - bool allow_no_mf = false; + bool allow_no_muzzle_flash = true; bool click_limit = false; - std::optional semi_auto_cooldown{}; + std::optional semiauto_cool_down{}; bool unlimited_fps = false; bool gaussian_spread = false; bool location_pinging = false; @@ -110,7 +99,6 @@ void multi_after_full_game_init(); void multi_init_player(rf::Player* player); void send_chat_line_packet(const char* msg, rf::Player* target, rf::Player* sender = nullptr, bool is_team_msg = false); const std::optional& get_remote_server_info(); -const std::optional& get_af_remote_server_info(); void multi_level_download_do_frame(); void multi_level_download_abort(); void multi_ban_apply_patch(); diff --git a/game_patch/multi/network.cpp b/game_patch/multi/network.cpp index aeaf5d51f..5ab1e7f18 100644 --- a/game_patch/multi/network.cpp +++ b/game_patch/multi/network.cpp @@ -263,8 +263,6 @@ std::array g_client_side_packet_whitelist{ // clang-format on std::optional g_remote_server_info{}; -std::optional g_df_remote_server_info{}; -std::optional g_af_remote_server_info{}; CodeInjection process_game_packet_whitelist_filter{ 0x0047918D, @@ -773,22 +771,16 @@ bool parse_df_join_accept_tail(const std::byte* const tail) { ext.version_minor, static_cast(ext.flags) ); - DashFactionRemoteServerInfo df_remote_server_info{}; - df_remote_server_info.version_major = ext.version_major; - df_remote_server_info.version_minor = ext.version_minor; + RemoteServerInfo remote_server_info{}; + remote_server_info.version.major = ext.version_major; + remote_server_info.version.minor = ext.version_minor; using Flags = DashFactionJoinAcceptPacketExt::Flags; - df_remote_server_info.saving_enabled = !!(ext.flags & Flags::saving_enabled); + remote_server_info.saving = !!(ext.flags & Flags::saving_enabled); constexpr float default_fov = 90.f; if (!!(ext.flags & Flags::max_fov) && ext.max_fov >= default_fov) { - df_remote_server_info.max_fov.emplace(ext.max_fov); + remote_server_info.max_fov.emplace(ext.max_fov); } - g_df_remote_server_info.emplace(df_remote_server_info); - g_remote_server_info.emplace(RemoteServerInfo{ - .version_major = df_remote_server_info.version_major, - .version_minor = df_remote_server_info.version_minor, - .saving_enabled = df_remote_server_info.saving_enabled, - .max_fov = df_remote_server_info.max_fov, - }); + g_remote_server_info.emplace(remote_server_info); return true; } @@ -805,34 +797,29 @@ bool parse_af_join_accept_tail(const std::byte* const tail) { ext.version_minor, static_cast(ext.flags) ); - AlpineFactionRemoteServerInfo af_remote_server_info{}; - af_remote_server_info.version_major = ext.version_major; - af_remote_server_info.version_minor = ext.version_minor; + RemoteServerInfo remote_server_info{}; + remote_server_info.version_major = ext.version_major; + remote_server_info.version_minor = ext.version_minor; using Flags = AlpineFactionJoinAcceptPacketExt::Flags; - af_remote_server_info.saving_enabled = !!(ext.flags & Flags::saving_enabled); + remote_server_info.saving = !!(ext.flags & Flags::saving_enabled); constexpr float default_fov = 90.f; if (!!(ext.flags & Flags::max_fov) && ext.max_fov >= default_fov) { - af_remote_server_info.max_fov.emplace(ext.max_fov); + remote_server_info.max_fov.emplace(ext.max_fov); } - af_remote_server_info.allow_fb_mesh = !!(ext.flags & Flags::allow_fb_mesh); - af_remote_server_info.allow_lmap = !!(ext.flags & Flags::allow_lmap); - af_remote_server_info.allow_no_ss = !!(ext.flags & Flags::allow_no_ss); - af_remote_server_info.no_player_collide = !!(ext.flags & Flags::no_player_collide); - af_remote_server_info.allow_no_mf = !!(ext.flags & Flags::allow_no_mf); - af_remote_server_info.click_limit = !!(ext.flags & Flags::click_limit); - af_remote_server_info.unlimited_fps = !!(ext.flags & Flags::unlimited_fps); - af_remote_server_info.gaussian_spread = !!(ext.flags & Flags::gaussian_spread); - af_remote_server_info.location_pinging = !!(ext.flags & Flags::location_pinging); - if (af_remote_server_info.click_limit) { - af_remote_server_info.semi_auto_cooldown.emplace(ext.semi_auto_cooldown); + remote_server_info.alpine_faction = true; + remote_server_info.allow_full_bright_entities = !!(ext.flags & Flags::allow_fb_mesh); + remote_server_info.allow_light_maps_only = !!(ext.flags & Flags::allow_lmap); + remote_server_info.allow_no_screen_shake = !!(ext.flags & Flags::allow_no_ss); + remote_server_info.no_player_collide = !!(ext.flags & Flags::no_player_collide); + remote_server_info.allow_no_muzzle_flash = !!(ext.flags & Flags::allow_no_mf); + remote_server_info.click_limit = !!(ext.flags & Flags::click_limit); + remote_server_info.unlimited_fps = !!(ext.flags & Flags::unlimited_fps); + remote_server_info.gaussian_spread = !!(ext.flags & Flags::gaussian_spread); + remote_server_info.location_pinging = !!(ext.flags & Flags::location_pinging); + if (remote_server_info.click_limit) { + remote_server_info.semiauto_cool_down.emplace(ext.semi_auto_cooldown); } - g_af_remote_server_info.emplace(af_remote_server_info); - g_remote_server_info.emplace(RemoteServerInfo{ - .version_major = af_remote_server_info.version_major, - .version_minor = af_remote_server_info.version_minor, - .saving_enabled = af_remote_server_info.saving_enabled, - .max_fov = af_remote_server_info.max_fov, - }); + g_remote_server_info.emplace(remote_server_info); return true; } @@ -856,7 +843,7 @@ ConsoleCommand2 remote_server_flags_cmd{ rf::console::print("===================="); rf::console::print( "{} Faction {}.{}", - g_af_remote_server_info ? "Alpine" : "Dash", + remote_server_info.alpine_faction ? "Alpine" : "Dash", remote_server_info.version_major, remote_server_info.version_minor ); @@ -868,26 +855,23 @@ ConsoleCommand2 remote_server_flags_cmd{ rf::console::print("Max FOV: None"); } } - if (g_af_remote_server_info) { - const AlpineFactionRemoteServerInfo& af_remote_server_info = g_af_remote_server_info.value(); - rf::console::print("Allow full-bright meshes: {}", af_remote_server_info.allow_fb_mesh); - rf::console::print("Allow light maps only: {}", af_remote_server_info.allow_lmap); - rf::console::print("Allow no screen shake: {}", af_remote_server_info.allow_no_ss); - rf::console::print("No player collide: {}", af_remote_server_info.no_player_collide); - rf::console::print("Allow no muzzle flash: {}", af_remote_server_info.allow_no_mf); - rf::console::print("Semiauto fire rate limit: {}", af_remote_server_info.click_limit); - if (af_remote_server_info.semi_auto_cooldown) { - rf::console::print( - "Semiauto cool-down: Some({})", - af_remote_server_info.semi_auto_cooldown.value() - ); - } else { - rf::console::print("Semiauto cool-down: None"); - } - rf::console::print("Allow unlimited FPS: {}", af_remote_server_info.unlimited_fps); - rf::console::print("Gaussian spread: {}", af_remote_server_info.gaussian_spread); - rf::console::print("Ping locations: {}", af_remote_server_info.location_pinging); + rf::console::print("Allow full-bright entities: {}", remote_server_info.allow_full_bright_entities); + rf::console::print("Allow light maps only: {}", remote_server_info.allow_light_maps_only); + rf::console::print("Allow no screen shake: {}", remote_server_info.allow_no_screen_shake); + rf::console::print("No player collide: {}", remote_server_info.no_player_collide); + rf::console::print("Allow no muzzle flash: {}", remote_server_info.allow_no_muzzle_flash); + rf::console::print("Semiauto fire rate limit: {}", remote_server_info.click_limit); + if (remote_server_info.semiauto_cool_down) { + rf::console::print( + "Semiauto cool-down: Some({})", + remote_server_info.semiauto_cool_down.value() + ); + } else { + rf::console::print("Semiauto cool-down: None"); } + rf::console::print("Allow unlimited FPS: {}", remote_server_info.unlimited_fps); + rf::console::print("Gaussian spread: {}", remote_server_info.gaussian_spread); + rf::console::print("Ping locations: {}", remote_server_info.location_pinging); } }; @@ -1064,10 +1048,6 @@ const std::optional& get_remote_server_info() return g_remote_server_info; } -const std::optional& get_af_remote_server_info() { - return g_af_remote_server_info; -} - void send_chat_line_packet(const char* msg, rf::Player* target, rf::Player* sender, bool is_team_msg) { if (!rf::is_server && sender == nullptr) { diff --git a/game_patch/multi/server.cpp b/game_patch/multi/server.cpp index 4edd97558..9dbbbe8e0 100644 --- a/game_patch/multi/server.cpp +++ b/game_patch/multi/server.cpp @@ -716,7 +716,7 @@ CodeInjection multi_level_init_injection{ CallHook player_create_entity_entity_create_hook{ 0x004A41D3, [] (const int type, const char* const name, const int parent_handle, const rf::Vector3& pos, rf::Matrix3& orient, int create_flags, const int mp_character) { - if (get_af_remote_server_info() && get_af_remote_server_info().value().no_player_collide) { + if (get_remote_server_info() && get_remote_server_info().value().no_player_collide) { constexpr int NO_PLAYER_COLLIDE = 0x4; create_flags |= NO_PLAYER_COLLIDE; } diff --git a/game_patch/object/entity.cpp b/game_patch/object/entity.cpp index 5db2bdd9d..e8c64f080 100644 --- a/game_patch/object/entity.cpp +++ b/game_patch/object/entity.cpp @@ -280,8 +280,8 @@ static FunHook entity_damage_hook{ CallHook entity_fire_primary_weapon_camera_shake_hook{ 0x00426C79, [] (rf::Camera* const cp, const float amplitude, const float time_seconds) { - const bool remote_force_ss = get_af_remote_server_info() - && !get_af_remote_server_info().value().allow_no_ss; + const bool remote_force_ss = get_remote_server_info() + && !get_remote_server_info().value().allow_no_screen_shake; if (g_game_config.weapon_screen_shake || remote_force_ss) { entity_fire_primary_weapon_camera_shake_hook.call_target(cp, amplitude, time_seconds); } @@ -306,8 +306,8 @@ ConsoleCommand2 weapon_screen_shake_cmd{ CallHook entity_update_muzzle_flash_light_hook{ 0x0041E814, [] (rf::Entity& ep) { - const bool remote_force_muzzle_flash = get_af_remote_server_info() - && !get_af_remote_server_info().value().allow_no_mf; + const bool remote_force_muzzle_flash = get_remote_server_info() + && !get_remote_server_info().value().allow_no_muzzle_flash; if (g_game_config.muzzle_flash || remote_force_muzzle_flash) { entity_update_muzzle_flash_light_hook.call_target(ep); } diff --git a/game_patch/object/weapon.cpp b/game_patch/object/weapon.cpp index 126cd6ae2..112ff8381 100644 --- a/game_patch/object/weapon.cpp +++ b/game_patch/object/weapon.cpp @@ -127,8 +127,8 @@ CallHook weapon_hit_wall_obj_apply_r }; std::optional remote_fire_wait_override() { - return get_af_remote_server_info().and_then([] (const AlpineFactionRemoteServerInfo& af_remote_server_info) { - return af_remote_server_info.semi_auto_cooldown; + return get_remote_server_info().and_then([] (const RemoteServerInfo& remote_server_info) { + return remote_server_info.semiauto_cool_down; }); } @@ -136,8 +136,8 @@ CodeInjection player_fire_primary_weapon_semi_auto_patch{ 0x004A50BB, [] (auto& regs) { const rf::Entity* const entity = regs.esi; - if (get_af_remote_server_info() - && get_af_remote_server_info().value().click_limit + if (get_remote_server_info() + && get_remote_server_info().value().click_limit && !entity->ai.next_fire_primary.elapsed()) { regs.eip = 0x004A58B8; } From fefe3efef08df2fb6acb7c32ef5c90aaf0b12d01 Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Mon, 13 Oct 2025 17:37:03 +1300 Subject: [PATCH 086/134] Update --- game_patch/multi/network.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/game_patch/multi/network.cpp b/game_patch/multi/network.cpp index 5ab1e7f18..c404b287b 100644 --- a/game_patch/multi/network.cpp +++ b/game_patch/multi/network.cpp @@ -1035,7 +1035,6 @@ FunHook multi_stop_hook{ []() { // Clear server info when leaving g_remote_server_info.reset(); - g_af_remote_server_info.reset(); multi_stop_hook.call_target(); if (rf::local_player) { reset_player_additional_data(rf::local_player); From 2dea04f9f851963fce42c7e8eb749fba6cfc2171 Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Mon, 13 Oct 2025 17:39:25 +1300 Subject: [PATCH 087/134] Update --- game_patch/multi/multi.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/game_patch/multi/multi.h b/game_patch/multi/multi.h index f7057bf2f..413982757 100644 --- a/game_patch/multi/multi.h +++ b/game_patch/multi/multi.h @@ -77,7 +77,7 @@ struct RemoteServerInfo { struct { uint8_t major = 0; uint8_t minor = 0; - } version; + } version{}; bool saving = false; std::optional max_fov{}; bool alpine_faction = false; From c5f6a2901aa5aa5f9bdd3f9d7a0f3dbfe4375dec Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Mon, 13 Oct 2025 17:42:43 +1300 Subject: [PATCH 088/134] Update --- game_patch/multi/network.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/game_patch/multi/network.cpp b/game_patch/multi/network.cpp index c404b287b..bb77c8fe9 100644 --- a/game_patch/multi/network.cpp +++ b/game_patch/multi/network.cpp @@ -798,8 +798,8 @@ bool parse_af_join_accept_tail(const std::byte* const tail) { static_cast(ext.flags) ); RemoteServerInfo remote_server_info{}; - remote_server_info.version_major = ext.version_major; - remote_server_info.version_minor = ext.version_minor; + remote_server_info.version.major = ext.version_major; + remote_server_info.version.minor = ext.version_minor; using Flags = AlpineFactionJoinAcceptPacketExt::Flags; remote_server_info.saving = !!(ext.flags & Flags::saving_enabled); constexpr float default_fov = 90.f; @@ -844,8 +844,8 @@ ConsoleCommand2 remote_server_flags_cmd{ rf::console::print( "{} Faction {}.{}", remote_server_info.alpine_faction ? "Alpine" : "Dash", - remote_server_info.version_major, - remote_server_info.version_minor + remote_server_info.version.major, + remote_server_info.version.minor ); rf::console::print("===================="); rf::console::print("Teleport: {}", remote_server_info.saving_enabled); From 682d61f219149ddee335be152c8b6afc9890af30 Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Mon, 13 Oct 2025 18:10:10 +1300 Subject: [PATCH 089/134] Update --- game_patch/multi/network.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/game_patch/multi/network.cpp b/game_patch/multi/network.cpp index bb77c8fe9..0b1a40173 100644 --- a/game_patch/multi/network.cpp +++ b/game_patch/multi/network.cpp @@ -848,7 +848,7 @@ ConsoleCommand2 remote_server_flags_cmd{ remote_server_info.version.minor ); rf::console::print("===================="); - rf::console::print("Teleport: {}", remote_server_info.saving_enabled); + rf::console::print("Teleport: {}", remote_server_info.saving); if (remote_server_info.max_fov) { rf::console::print("Max FOV: Some({})", remote_server_info.max_fov.value()); } else { From a13ec992f62fa21d272e0c469a3b25e4778d6148 Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Mon, 13 Oct 2025 18:22:08 +1300 Subject: [PATCH 090/134] Update --- game_patch/multi/network.cpp | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/game_patch/multi/network.cpp b/game_patch/multi/network.cpp index 0b1a40173..09f033c8a 100644 --- a/game_patch/multi/network.cpp +++ b/game_patch/multi/network.cpp @@ -854,24 +854,24 @@ ConsoleCommand2 remote_server_flags_cmd{ } else { rf::console::print("Max FOV: None"); } + rf::console::print("Allow full-bright entities: {}", remote_server_info.allow_full_bright_entities); + rf::console::print("Allow light maps only: {}", remote_server_info.allow_light_maps_only); + rf::console::print("Allow no screen shake: {}", remote_server_info.allow_no_screen_shake); + rf::console::print("No player collide: {}", remote_server_info.no_player_collide); + rf::console::print("Allow no muzzle flash: {}", remote_server_info.allow_no_muzzle_flash); + rf::console::print("Semiauto fire rate limit: {}", remote_server_info.click_limit); + if (remote_server_info.semiauto_cool_down) { + rf::console::print( + "Semiauto cool-down: Some({})", + remote_server_info.semiauto_cool_down.value() + ); + } else { + rf::console::print("Semiauto cool-down: None"); + } + rf::console::print("Allow unlimited FPS: {}", remote_server_info.unlimited_fps); + rf::console::print("Gaussian spread: {}", remote_server_info.gaussian_spread); + rf::console::print("Ping locations: {}", remote_server_info.location_pinging); } - rf::console::print("Allow full-bright entities: {}", remote_server_info.allow_full_bright_entities); - rf::console::print("Allow light maps only: {}", remote_server_info.allow_light_maps_only); - rf::console::print("Allow no screen shake: {}", remote_server_info.allow_no_screen_shake); - rf::console::print("No player collide: {}", remote_server_info.no_player_collide); - rf::console::print("Allow no muzzle flash: {}", remote_server_info.allow_no_muzzle_flash); - rf::console::print("Semiauto fire rate limit: {}", remote_server_info.click_limit); - if (remote_server_info.semiauto_cool_down) { - rf::console::print( - "Semiauto cool-down: Some({})", - remote_server_info.semiauto_cool_down.value() - ); - } else { - rf::console::print("Semiauto cool-down: None"); - } - rf::console::print("Allow unlimited FPS: {}", remote_server_info.unlimited_fps); - rf::console::print("Gaussian spread: {}", remote_server_info.gaussian_spread); - rf::console::print("Ping locations: {}", remote_server_info.location_pinging); } }; From c82163343b4941fdb022ab77eaffe117c25351df Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Mon, 13 Oct 2025 19:14:21 +1300 Subject: [PATCH 091/134] Update --- common/include/common/config/GameConfig.h | 1 + common/src/config/GameConfig.cpp | 1 + docs/CHANGELOG.md | 1 + game_patch/graphics/d3d11/gr_d3d11_mesh.cpp | 50 ++++++++++++++------- game_patch/object/obj_light.cpp | 34 ++++++++++++++ 5 files changed, 71 insertions(+), 16 deletions(-) diff --git a/common/include/common/config/GameConfig.h b/common/include/common/config/GameConfig.h index dea509bfe..eab87273f 100644 --- a/common/include/common/config/GameConfig.h +++ b/common/include/common/config/GameConfig.h @@ -59,6 +59,7 @@ struct GameConfig CfgVar glares = true; CfgVar show_enemy_bullets = true; CfgVar weapon_screen_shake = true; + CfgVar full_bright_entities = false; static constexpr float min_fov = 75.0f; static constexpr float max_fov = 160.0f; diff --git a/common/src/config/GameConfig.cpp b/common/src/config/GameConfig.cpp index 9b2970952..30d71c809 100644 --- a/common/src/config/GameConfig.cpp +++ b/common/src/config/GameConfig.cpp @@ -176,6 +176,7 @@ bool GameConfig::visit_vars(T&& visitor, bool is_save) result &= visitor(dash_faction_key, "Linear Pitch", linear_pitch); result &= visitor(dash_faction_key, "Show Enemy Bullets", show_enemy_bullets); result &= visitor(dash_faction_key, "Weapon Screen Shake", weapon_screen_shake); + result &= visitor(dash_faction_key, "Full-Bright Entities", full_bright_entities); result &= visitor(dash_faction_key, "Keep Launcher Open", keep_launcher_open); result &= visitor(dash_faction_key, "Skip Cutscene Control", skip_cutscene_ctrl); result &= visitor(dash_faction_key, "Damage Screen Flash", damage_screen_flash); diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 54898a10a..74dce034b 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -10,6 +10,7 @@ Version 1.9.2 (not released yet) - Add `af_killsound1.wav` from Alpine Faction - Add `hit_sounds` command - Add `weapon_screen_shake` command +- Add `full_bright_entities` command - Add `remote_server_flags` command Version 1.9.1 (released 2025-07-05) diff --git a/game_patch/graphics/d3d11/gr_d3d11_mesh.cpp b/game_patch/graphics/d3d11/gr_d3d11_mesh.cpp index 7695172f3..43777f739 100644 --- a/game_patch/graphics/d3d11/gr_d3d11_mesh.cpp +++ b/game_patch/graphics/d3d11/gr_d3d11_mesh.cpp @@ -389,7 +389,30 @@ namespace df::gr::d3d11 render_context_.set_index_buffer(v3d_ib_.buffer()); auto render_cache = reinterpret_cast(lod_mesh->render_cache); - draw_cached_mesh(lod_mesh, *render_cache, params, lod_index); + + rf::MeshRenderParams v3d_params = params; + rf::Vector3 ambient{0.f, 0.f, 0.f}; + light_get_ambient(&ambient.x, &ambient.y, &ambient.z); + v3d_params.ambient_color.set( + static_cast(ambient.x * 255.f), + static_cast(ambient.y * 255.f), + static_cast(ambient.z * 255.f), + 255 + ); + v3d_params.ambient_color.red = static_cast(std::min( + static_cast(v3d_params.ambient_color.red) + 40, + 255 + )); + v3d_params.ambient_color.green = static_cast(std::min( + static_cast(v3d_params.ambient_color.green) + 40, + 255 + )); + v3d_params.ambient_color.blue = static_cast(std::min( + static_cast(v3d_params.ambient_color.blue) + 40, + 255 + )); + + draw_cached_mesh(lod_mesh, *render_cache, v3d_params, lod_index); } void MeshRenderer::render_character_vif(rf::VifLodMesh *lod_mesh, int lod_index, const rf::Vector3& pos, const rf::Matrix3& orient, const rf::CharacterInstance *ci, const rf::MeshRenderParams& params) @@ -416,7 +439,15 @@ namespace df::gr::d3d11 } render_cache->update_bone_transforms_buffer(ci, render_context_); render_cache->bind_buffers(render_context_, morphed); - draw_cached_mesh(lod_mesh, *render_cache, params, lod_index); + const bool allow_full_bright = !(get_af_server_info() + && !get_af_server_info().value().allow_fb_mesh); + rf::MeshRenderParams full_bright_params = params; + if (g_game_config.full_bright_entities + && allow_full_bright + && !(params.flags & MRF_FIRST_PERSON)) { + full_bright_params.ambient_color.set(255, 255, 255, 255); + } + draw_cached_mesh(lod_mesh, *render_cache, full_bright_params, lod_index); } void MeshRenderer::clear_vif_cache(rf::VifLodMesh *lod_mesh) @@ -458,20 +489,7 @@ namespace df::gr::d3d11 } rf::Color color{255, 255, 255}; if (!ir_scanner) { - // Ignore ambient_color from params, it changes sharply and RF uses it only indirectly for - // its hard-coded lights - float ambient_r, ambient_g, ambient_b; - light_get_ambient(&ambient_r, &ambient_g, &ambient_b); - color.set( - static_cast(ambient_r * 255.0f), - static_cast(ambient_g * 255.0f), - static_cast(ambient_b * 255.0f), - 255 - ); - // RF uses some hard-coded lights here but for now let's keep it simple - color.red += 40; - color.green += 40; - color.blue += 40; + color = params.ambient_color; } else { color = params.self_illum; } diff --git a/game_patch/object/obj_light.cpp b/game_patch/object/obj_light.cpp index 44dcf450c..eac370b58 100644 --- a/game_patch/object/obj_light.cpp +++ b/game_patch/object/obj_light.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include #include "../rf/object.h" #include "../rf/item.h" @@ -11,6 +12,7 @@ #include "../rf/crt.h" #include "../os/console.h" #include "../main/main.h" +#include "../multi/multi.h" float obj_light_scale = 1.0; @@ -141,6 +143,34 @@ ConsoleCommand2 mesh_static_lighting_cmd{ "Toggle mesh static lighting calculation", }; +CodeInjection init_mesh_dynamic_light_data_patch{ + 0x0052DBD0, + [] (const auto& regs) { + const bool allow_full_bright = !(get_af_server_info() + && !get_af_server_info().value().allow_fb_mesh); + if (g_game_config.full_bright_entities && allow_full_bright) { + // For Direct3d 11, we set in `gr_d3d11_mesh.cpp`. + auto& ambient_light = addr_as_ref(0x1C3D548); + ambient_light = rf::Vector3{255.f, 255.f, 255.f}; + } + }, +}; + +ConsoleCommand2 full_bright_entities_cmd{ + "full_bright_entities", + [] { + g_game_config.full_bright_entities = !g_game_config.full_bright_entities; + g_game_config.save(); + rf::console::print( + "Full bright entities is {}", + g_game_config.full_bright_entities + ? "enabled [multiplayer servers may ignore]" + : "disabled" + ); + }, + "Toggle full-bright entities [multiplayer servers may ignore]", +}; + void obj_light_apply_patch() { // Fix/improve items and clutters static lighting calculation: fix matrices being zero and use static lights @@ -156,4 +186,8 @@ void obj_light_apply_patch() // Commands mesh_static_lighting_cmd.register_cmd(); + full_bright_entities_cmd.register_cmd(); + + // Support full-bright entities. + init_mesh_dynamic_light_data_patch.install(); } From d85eede78d94ec0716249ac7027b06eeb23cca3c Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Mon, 13 Oct 2025 19:17:55 +1300 Subject: [PATCH 092/134] Update --- game_patch/graphics/d3d11/gr_d3d11_mesh.cpp | 4 ++-- game_patch/object/obj_light.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/game_patch/graphics/d3d11/gr_d3d11_mesh.cpp b/game_patch/graphics/d3d11/gr_d3d11_mesh.cpp index 43777f739..c312e5541 100644 --- a/game_patch/graphics/d3d11/gr_d3d11_mesh.cpp +++ b/game_patch/graphics/d3d11/gr_d3d11_mesh.cpp @@ -439,8 +439,8 @@ namespace df::gr::d3d11 } render_cache->update_bone_transforms_buffer(ci, render_context_); render_cache->bind_buffers(render_context_, morphed); - const bool allow_full_bright = !(get_af_server_info() - && !get_af_server_info().value().allow_fb_mesh); + const bool allow_full_bright = !(get_remote_server_info() + && !get_remote_server_info().value().allow_fb_mesh); rf::MeshRenderParams full_bright_params = params; if (g_game_config.full_bright_entities && allow_full_bright diff --git a/game_patch/object/obj_light.cpp b/game_patch/object/obj_light.cpp index eac370b58..9f9ecfd36 100644 --- a/game_patch/object/obj_light.cpp +++ b/game_patch/object/obj_light.cpp @@ -146,8 +146,8 @@ ConsoleCommand2 mesh_static_lighting_cmd{ CodeInjection init_mesh_dynamic_light_data_patch{ 0x0052DBD0, [] (const auto& regs) { - const bool allow_full_bright = !(get_af_server_info() - && !get_af_server_info().value().allow_fb_mesh); + const bool allow_full_bright = !(get_remote_server_info() + && !get_remote_server_info().value().allow_fb_mesh); if (g_game_config.full_bright_entities && allow_full_bright) { // For Direct3d 11, we set in `gr_d3d11_mesh.cpp`. auto& ambient_light = addr_as_ref(0x1C3D548); From 2d654ec9afec29206039a540cb59f466580ad036 Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Mon, 13 Oct 2025 19:22:45 +1300 Subject: [PATCH 093/134] Update --- game_patch/graphics/d3d11/gr_d3d11_mesh.cpp | 3 ++- game_patch/object/obj_light.cpp | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/game_patch/graphics/d3d11/gr_d3d11_mesh.cpp b/game_patch/graphics/d3d11/gr_d3d11_mesh.cpp index c312e5541..2a2dbdde8 100644 --- a/game_patch/graphics/d3d11/gr_d3d11_mesh.cpp +++ b/game_patch/graphics/d3d11/gr_d3d11_mesh.cpp @@ -9,6 +9,7 @@ #include "../../rf/math/quaternion.h" #include "../../rf/v3d.h" #include "../../rf/character.h" +#include "../../multi/multi.h" #include "gr_d3d11.h" #include "gr_d3d11_mesh.h" #include "gr_d3d11_context.h" @@ -440,7 +441,7 @@ namespace df::gr::d3d11 render_cache->update_bone_transforms_buffer(ci, render_context_); render_cache->bind_buffers(render_context_, morphed); const bool allow_full_bright = !(get_remote_server_info() - && !get_remote_server_info().value().allow_fb_mesh); + && !get_remote_server_info().value().allow_full_bright_entities); rf::MeshRenderParams full_bright_params = params; if (g_game_config.full_bright_entities && allow_full_bright diff --git a/game_patch/object/obj_light.cpp b/game_patch/object/obj_light.cpp index 9f9ecfd36..512dfb5a8 100644 --- a/game_patch/object/obj_light.cpp +++ b/game_patch/object/obj_light.cpp @@ -147,7 +147,7 @@ CodeInjection init_mesh_dynamic_light_data_patch{ 0x0052DBD0, [] (const auto& regs) { const bool allow_full_bright = !(get_remote_server_info() - && !get_remote_server_info().value().allow_fb_mesh); + && !get_remote_server_info().value().allow_full_bright_entities); if (g_game_config.full_bright_entities && allow_full_bright) { // For Direct3d 11, we set in `gr_d3d11_mesh.cpp`. auto& ambient_light = addr_as_ref(0x1C3D548); From aa603195a2c24c6d2a695238de4df1cbf691d427 Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Mon, 13 Oct 2025 19:28:08 +1300 Subject: [PATCH 094/134] Update --- game_patch/rf/v3d.h | 1 + 1 file changed, 1 insertion(+) diff --git a/game_patch/rf/v3d.h b/game_patch/rf/v3d.h index 3d363d7ca..dfc8dba95 100644 --- a/game_patch/rf/v3d.h +++ b/game_patch/rf/v3d.h @@ -145,5 +145,6 @@ namespace rf MRF_SCANNER_1 = 1, MRF_SCANNER_2 = 8, MRF_AMBIENT_COLOR = 0x80, + MRF_FIRST_PERSON = 0x400, }; } From 5a68ad31344e7ab35f86418daf8fc596eda4d5ce Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Mon, 13 Oct 2025 19:36:51 +1300 Subject: [PATCH 095/134] Update --- game_patch/graphics/d3d11/gr_d3d11_mesh.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/game_patch/graphics/d3d11/gr_d3d11_mesh.cpp b/game_patch/graphics/d3d11/gr_d3d11_mesh.cpp index 2a2dbdde8..6a550c455 100644 --- a/game_patch/graphics/d3d11/gr_d3d11_mesh.cpp +++ b/game_patch/graphics/d3d11/gr_d3d11_mesh.cpp @@ -9,6 +9,7 @@ #include "../../rf/math/quaternion.h" #include "../../rf/v3d.h" #include "../../rf/character.h" +#include "../../main/main.h" #include "../../multi/multi.h" #include "gr_d3d11.h" #include "gr_d3d11_mesh.h" From 92d4ba5bbc9351be4d56c621a97d618009a2d13a Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Mon, 13 Oct 2025 20:07:31 +1300 Subject: [PATCH 096/134] Update --- game_patch/graphics/d3d11/gr_d3d11_mesh.cpp | 37 ++++++++------------- game_patch/graphics/d3d11/gr_d3d11_mesh.h | 4 +-- 2 files changed, 16 insertions(+), 25 deletions(-) diff --git a/game_patch/graphics/d3d11/gr_d3d11_mesh.cpp b/game_patch/graphics/d3d11/gr_d3d11_mesh.cpp index 6a550c455..eadd9dd99 100644 --- a/game_patch/graphics/d3d11/gr_d3d11_mesh.cpp +++ b/game_patch/graphics/d3d11/gr_d3d11_mesh.cpp @@ -380,7 +380,7 @@ namespace df::gr::d3d11 render_caches_.clear(); } - void MeshRenderer::render_v3d_vif(rf::VifLodMesh *lod_mesh, int lod_index, const rf::Vector3& pos, const rf::Matrix3& orient, const rf::MeshRenderParams& params) + void MeshRenderer::render_v3d_vif(rf::VifLodMesh *lod_mesh, int lod_index, const rf::Vector3& pos, const rf::Matrix3& orient, rf::MeshRenderParams params) { page_in_v3d_mesh(lod_mesh); @@ -392,32 +392,21 @@ namespace df::gr::d3d11 auto render_cache = reinterpret_cast(lod_mesh->render_cache); - rf::MeshRenderParams v3d_params = params; - rf::Vector3 ambient{0.f, 0.f, 0.f}; - light_get_ambient(&ambient.x, &ambient.y, &ambient.z); - v3d_params.ambient_color.set( - static_cast(ambient.x * 255.f), - static_cast(ambient.y * 255.f), - static_cast(ambient.z * 255.f), + rf::Vector3 ambient_light{0.f, 0.f, 0.f}; + light_get_ambient(&ambient_light.x, &ambient_light.y, &ambient_light.z); + ambient_light *= 255.f; + ambient_light += 40.f; + params.ambient_color.set( + static_cast(std::min(ambient_light.x, 255.f)), + static_cast(std::min(ambient_light.y, 255.f)), + static_cast(std::min(ambient_light.z, 255.f)), 255 ); - v3d_params.ambient_color.red = static_cast(std::min( - static_cast(v3d_params.ambient_color.red) + 40, - 255 - )); - v3d_params.ambient_color.green = static_cast(std::min( - static_cast(v3d_params.ambient_color.green) + 40, - 255 - )); - v3d_params.ambient_color.blue = static_cast(std::min( - static_cast(v3d_params.ambient_color.blue) + 40, - 255 - )); draw_cached_mesh(lod_mesh, *render_cache, v3d_params, lod_index); } - void MeshRenderer::render_character_vif(rf::VifLodMesh *lod_mesh, int lod_index, const rf::Vector3& pos, const rf::Matrix3& orient, const rf::CharacterInstance *ci, const rf::MeshRenderParams& params) + void MeshRenderer::render_character_vif(rf::VifLodMesh *lod_mesh, int lod_index, const rf::Vector3& pos, const rf::Matrix3& orient, const rf::CharacterInstance *ci, f::MeshRenderParams params) { page_in_character_mesh(lod_mesh); auto render_cache = reinterpret_cast(lod_mesh->render_cache); @@ -441,15 +430,17 @@ namespace df::gr::d3d11 } render_cache->update_bone_transforms_buffer(ci, render_context_); render_cache->bind_buffers(render_context_, morphed); + const bool allow_full_bright = !(get_remote_server_info() && !get_remote_server_info().value().allow_full_bright_entities); rf::MeshRenderParams full_bright_params = params; if (g_game_config.full_bright_entities && allow_full_bright && !(params.flags & MRF_FIRST_PERSON)) { - full_bright_params.ambient_color.set(255, 255, 255, 255); + params.ambient_color.set(255, 255, 255, 255); } - draw_cached_mesh(lod_mesh, *render_cache, full_bright_params, lod_index); + + draw_cached_mesh(lod_mesh, *render_cache, params, lod_index); } void MeshRenderer::clear_vif_cache(rf::VifLodMesh *lod_mesh) diff --git a/game_patch/graphics/d3d11/gr_d3d11_mesh.h b/game_patch/graphics/d3d11/gr_d3d11_mesh.h index 84e913b67..f3e02266b 100644 --- a/game_patch/graphics/d3d11/gr_d3d11_mesh.h +++ b/game_patch/graphics/d3d11/gr_d3d11_mesh.h @@ -94,8 +94,8 @@ namespace df::gr::d3d11 public: MeshRenderer(ComPtr device, ShaderManager& shader_manager, StateManager& state_manager, RenderContext& render_context); ~MeshRenderer(); - void render_v3d_vif(rf::VifLodMesh *lod_mesh, int lod_index, const rf::Vector3& pos, const rf::Matrix3& orient, const rf::MeshRenderParams& params); - void render_character_vif(rf::VifLodMesh *lod_mesh, int lod_index, const rf::Vector3& pos, const rf::Matrix3& orient, const rf::CharacterInstance *ci, const rf::MeshRenderParams& params); + void render_v3d_vif(rf::VifLodMesh *lod_mesh, int lod_index, const rf::Vector3& pos, const rf::Matrix3& orient, rf::MeshRenderParams params); + void render_character_vif(rf::VifLodMesh *lod_mesh, int lod_index, const rf::Vector3& pos, const rf::Matrix3& orient, const rf::CharacterInstance *ci, rf::MeshRenderParams params); void clear_vif_cache(rf::VifLodMesh *lod_mesh); void page_in_v3d_mesh(rf::VifLodMesh* lod_mesh); void page_in_character_mesh(rf::VifLodMesh* lod_mesh); From 7e0473c58185b63ec98338d30af86d1d84eba924 Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Mon, 13 Oct 2025 20:10:54 +1300 Subject: [PATCH 097/134] Update --- game_patch/graphics/d3d11/gr_d3d11_mesh.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/game_patch/graphics/d3d11/gr_d3d11_mesh.cpp b/game_patch/graphics/d3d11/gr_d3d11_mesh.cpp index eadd9dd99..56ab5b5cd 100644 --- a/game_patch/graphics/d3d11/gr_d3d11_mesh.cpp +++ b/game_patch/graphics/d3d11/gr_d3d11_mesh.cpp @@ -433,7 +433,6 @@ namespace df::gr::d3d11 const bool allow_full_bright = !(get_remote_server_info() && !get_remote_server_info().value().allow_full_bright_entities); - rf::MeshRenderParams full_bright_params = params; if (g_game_config.full_bright_entities && allow_full_bright && !(params.flags & MRF_FIRST_PERSON)) { From b46eaf6b04dbaa288d2892fe7da43ebbbe4fe06a Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Mon, 13 Oct 2025 20:13:56 +1300 Subject: [PATCH 098/134] Update --- game_patch/graphics/d3d11/gr_d3d11_mesh.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/game_patch/graphics/d3d11/gr_d3d11_mesh.cpp b/game_patch/graphics/d3d11/gr_d3d11_mesh.cpp index 56ab5b5cd..297d12b5d 100644 --- a/game_patch/graphics/d3d11/gr_d3d11_mesh.cpp +++ b/game_patch/graphics/d3d11/gr_d3d11_mesh.cpp @@ -395,7 +395,10 @@ namespace df::gr::d3d11 rf::Vector3 ambient_light{0.f, 0.f, 0.f}; light_get_ambient(&ambient_light.x, &ambient_light.y, &ambient_light.z); ambient_light *= 255.f; + // RF uses some hard-coded lights here but for now let's keep it simple ambient_light += 40.f; + // Ignore ambient_color from params, it changes sharply and RF uses it only indirectly for + // its hard-coded lights params.ambient_color.set( static_cast(std::min(ambient_light.x, 255.f)), static_cast(std::min(ambient_light.y, 255.f)), From c5a58cdb34f99c2fe990d29a086edeebd53ed4e7 Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Mon, 13 Oct 2025 20:31:31 +1300 Subject: [PATCH 099/134] Update --- game_patch/graphics/d3d11/gr_d3d11_mesh.cpp | 44 ++++++++++++--------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/game_patch/graphics/d3d11/gr_d3d11_mesh.cpp b/game_patch/graphics/d3d11/gr_d3d11_mesh.cpp index 297d12b5d..7188f6f41 100644 --- a/game_patch/graphics/d3d11/gr_d3d11_mesh.cpp +++ b/game_patch/graphics/d3d11/gr_d3d11_mesh.cpp @@ -392,19 +392,22 @@ namespace df::gr::d3d11 auto render_cache = reinterpret_cast(lod_mesh->render_cache); - rf::Vector3 ambient_light{0.f, 0.f, 0.f}; - light_get_ambient(&ambient_light.x, &ambient_light.y, &ambient_light.z); - ambient_light *= 255.f; - // RF uses some hard-coded lights here but for now let's keep it simple - ambient_light += 40.f; - // Ignore ambient_color from params, it changes sharply and RF uses it only indirectly for - // its hard-coded lights - params.ambient_color.set( - static_cast(std::min(ambient_light.x, 255.f)), - static_cast(std::min(ambient_light.y, 255.f)), - static_cast(std::min(ambient_light.z, 255.f)), - 255 - ); + const bool ir_scanner = (params.flags & MRF_SCANNER_1) != 0; + if (!ir_scanner) { + rf::Vector3 ambient_light{0.f, 0.f, 0.f}; + light_get_ambient(&ambient_light.x, &ambient_light.y, &ambient_light.z); + ambient_light *= 255.f; + // RF uses some hard-coded lights here but for now let's keep it simple + ambient_light += 40.f; + // Ignore ambient_color from params, it changes sharply and RF uses it only indirectly for + // its hard-coded lights + params.ambient_color.set( + static_cast(std::min(ambient_light.x, 255.f)), + static_cast(std::min(ambient_light.y, 255.f)), + static_cast(std::min(ambient_light.z, 255.f)), + 255 + ); + } draw_cached_mesh(lod_mesh, *render_cache, v3d_params, lod_index); } @@ -434,12 +437,15 @@ namespace df::gr::d3d11 render_cache->update_bone_transforms_buffer(ci, render_context_); render_cache->bind_buffers(render_context_, morphed); - const bool allow_full_bright = !(get_remote_server_info() - && !get_remote_server_info().value().allow_full_bright_entities); - if (g_game_config.full_bright_entities - && allow_full_bright - && !(params.flags & MRF_FIRST_PERSON)) { - params.ambient_color.set(255, 255, 255, 255); + const bool ir_scanner = (params.flags & MRF_SCANNER_1) != 0; + if (!ir_scanner) { + const bool allow_full_bright = !(get_remote_server_info() + && !get_remote_server_info().value().allow_full_bright_entities); + if (g_game_config.full_bright_entities + && allow_full_bright + && !(params.flags & MRF_FIRST_PERSON)) { + params.ambient_color.set(255, 255, 255, 255); + } } draw_cached_mesh(lod_mesh, *render_cache, params, lod_index); From b0380efc98194f3b19e3771dc3fcb481ecd26a7b Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Mon, 13 Oct 2025 20:44:11 +1300 Subject: [PATCH 100/134] Update --- README.md | 3 ++- game_patch/object/entity.cpp | 4 ++-- game_patch/object/obj_light.cpp | 8 ++++---- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index df0cb752a..077081bc6 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,8 @@ Name | Description `gibs` | toggle gibs (characters exploding into parts) `muzzle_flash` | toggle muzzle flash `hit_sounds` | toggle whether to play a sound, when you hit players in multiplayer, if enabled by a server -`weapon_screen_shake` | toggle camera shake from weapon fire [multiplayer servers may force weapon shake] +`weapon_screen_shake` | toggle camera shake from weapon fire [may be force enabled by multiplayer servers] +`full_bright_entities` | toggle full-bright entities [may be force disabled by multiplayer servers] ### Server commands diff --git a/game_patch/object/entity.cpp b/game_patch/object/entity.cpp index e8c64f080..9a73d4fef 100644 --- a/game_patch/object/entity.cpp +++ b/game_patch/object/entity.cpp @@ -297,10 +297,10 @@ ConsoleCommand2 weapon_screen_shake_cmd{ "Weapon screen shake is {}", g_game_config.weapon_screen_shake ? "enabled" - : "disabled [multiplayer servers may force weapon screen shake]" + : "disabled [may be force enabled by multiplayer servers]" ); }, - "Toggle camera shake from weapon fire [multiplayer servers may force weapon screen shake]", + "Toggle camera shake from weapon fire [may be force enabled by multiplayer servers]", }; CallHook entity_update_muzzle_flash_light_hook{ diff --git a/game_patch/object/obj_light.cpp b/game_patch/object/obj_light.cpp index 512dfb5a8..d2bfb3785 100644 --- a/game_patch/object/obj_light.cpp +++ b/game_patch/object/obj_light.cpp @@ -150,7 +150,7 @@ CodeInjection init_mesh_dynamic_light_data_patch{ && !get_remote_server_info().value().allow_full_bright_entities); if (g_game_config.full_bright_entities && allow_full_bright) { // For Direct3d 11, we set in `gr_d3d11_mesh.cpp`. - auto& ambient_light = addr_as_ref(0x1C3D548); + auto& ambient_light = addr_as_ref(0x01C3D548); ambient_light = rf::Vector3{255.f, 255.f, 255.f}; } }, @@ -162,13 +162,13 @@ ConsoleCommand2 full_bright_entities_cmd{ g_game_config.full_bright_entities = !g_game_config.full_bright_entities; g_game_config.save(); rf::console::print( - "Full bright entities is {}", + "Full-bright entities is {}", g_game_config.full_bright_entities - ? "enabled [multiplayer servers may ignore]" + ? "enabled [may be force disabled by multiplayer servers]" : "disabled" ); }, - "Toggle full-bright entities [multiplayer servers may ignore]", + "Toggle full-bright entities [may be force disabled by multiplayer servers]", }; void obj_light_apply_patch() From 78264da892f2b344d7c019ec7dedd8745210e782 Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Mon, 13 Oct 2025 20:48:39 +1300 Subject: [PATCH 101/134] Update --- game_patch/graphics/d3d11/gr_d3d11_mesh.cpp | 2 +- game_patch/object/obj_light.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/game_patch/graphics/d3d11/gr_d3d11_mesh.cpp b/game_patch/graphics/d3d11/gr_d3d11_mesh.cpp index 7188f6f41..a7fa36053 100644 --- a/game_patch/graphics/d3d11/gr_d3d11_mesh.cpp +++ b/game_patch/graphics/d3d11/gr_d3d11_mesh.cpp @@ -409,7 +409,7 @@ namespace df::gr::d3d11 ); } - draw_cached_mesh(lod_mesh, *render_cache, v3d_params, lod_index); + draw_cached_mesh(lod_mesh, *render_cache, params, lod_index); } void MeshRenderer::render_character_vif(rf::VifLodMesh *lod_mesh, int lod_index, const rf::Vector3& pos, const rf::Matrix3& orient, const rf::CharacterInstance *ci, f::MeshRenderParams params) diff --git a/game_patch/object/obj_light.cpp b/game_patch/object/obj_light.cpp index d2bfb3785..01e683cdf 100644 --- a/game_patch/object/obj_light.cpp +++ b/game_patch/object/obj_light.cpp @@ -168,7 +168,7 @@ ConsoleCommand2 full_bright_entities_cmd{ : "disabled" ); }, - "Toggle full-bright entities [may be force disabled by multiplayer servers]", + "Toggle full-bright entities [may be force disabled by servers]", }; void obj_light_apply_patch() From 956a2b878fe502ee13fb5681fec59117db5d3128 Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Mon, 13 Oct 2025 20:49:55 +1300 Subject: [PATCH 102/134] Update --- game_patch/object/obj_light.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/game_patch/object/obj_light.cpp b/game_patch/object/obj_light.cpp index 01e683cdf..d2bfb3785 100644 --- a/game_patch/object/obj_light.cpp +++ b/game_patch/object/obj_light.cpp @@ -168,7 +168,7 @@ ConsoleCommand2 full_bright_entities_cmd{ : "disabled" ); }, - "Toggle full-bright entities [may be force disabled by servers]", + "Toggle full-bright entities [may be force disabled by multiplayer servers]", }; void obj_light_apply_patch() From b968ba5d5378821e4e0da935971c36dc5425af81 Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Mon, 13 Oct 2025 20:51:48 +1300 Subject: [PATCH 103/134] Update --- game_patch/graphics/d3d11/gr_d3d11_mesh.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/game_patch/graphics/d3d11/gr_d3d11_mesh.cpp b/game_patch/graphics/d3d11/gr_d3d11_mesh.cpp index a7fa36053..ba825fb06 100644 --- a/game_patch/graphics/d3d11/gr_d3d11_mesh.cpp +++ b/game_patch/graphics/d3d11/gr_d3d11_mesh.cpp @@ -412,7 +412,7 @@ namespace df::gr::d3d11 draw_cached_mesh(lod_mesh, *render_cache, params, lod_index); } - void MeshRenderer::render_character_vif(rf::VifLodMesh *lod_mesh, int lod_index, const rf::Vector3& pos, const rf::Matrix3& orient, const rf::CharacterInstance *ci, f::MeshRenderParams params) + void MeshRenderer::render_character_vif(rf::VifLodMesh *lod_mesh, int lod_index, const rf::Vector3& pos, const rf::Matrix3& orient, const rf::CharacterInstance *ci, rf::MeshRenderParams params) { page_in_character_mesh(lod_mesh); auto render_cache = reinterpret_cast(lod_mesh->render_cache); From b3c37fe25afc0b251faa6f71abbc47ffdc0a818f Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Tue, 14 Oct 2025 18:05:45 +1300 Subject: [PATCH 104/134] Update --- game_patch/object/obj_light.cpp | 2 +- game_patch/object/weapon.cpp | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/game_patch/object/obj_light.cpp b/game_patch/object/obj_light.cpp index d2bfb3785..de8798bfd 100644 --- a/game_patch/object/obj_light.cpp +++ b/game_patch/object/obj_light.cpp @@ -150,7 +150,7 @@ CodeInjection init_mesh_dynamic_light_data_patch{ && !get_remote_server_info().value().allow_full_bright_entities); if (g_game_config.full_bright_entities && allow_full_bright) { // For Direct3d 11, we set in `gr_d3d11_mesh.cpp`. - auto& ambient_light = addr_as_ref(0x01C3D548); + rf::Vector3& ambient_light = addr_as_ref(0x01C3D548); ambient_light = rf::Vector3{255.f, 255.f, 255.f}; } }, diff --git a/game_patch/object/weapon.cpp b/game_patch/object/weapon.cpp index 112ff8381..849a5ad1b 100644 --- a/game_patch/object/weapon.cpp +++ b/game_patch/object/weapon.cpp @@ -126,12 +126,6 @@ CallHook weapon_hit_wall_obj_apply_r }, }; -std::optional remote_fire_wait_override() { - return get_remote_server_info().and_then([] (const RemoteServerInfo& remote_server_info) { - return remote_server_info.semiauto_cool_down; - }); -} - CodeInjection player_fire_primary_weapon_semi_auto_patch{ 0x004A50BB, [] (auto& regs) { @@ -144,18 +138,24 @@ CodeInjection player_fire_primary_weapon_semi_auto_patch{ }, }; +std::optional fire_wait_override() { + return get_remote_server_info().and_then([] (const RemoteServerInfo& remote_server_info) { + return remote_server_info.semiauto_cool_down; + }); +} + CodeInjection entity_fire_primary_weapon_semi_auto_patch{ 0x004259B8, [] (auto& regs) { - // HACKFIX: Override fire wait for stock semi auto weapons. + // HACKFIX: Override fire wait for stock semiauto weapons. auto& fire_wait = regs.eax; const int weapon_type = regs.ebx; const rf::Entity* const entity = regs.esi; - if (remote_fire_wait_override() + if (fire_wait_override() && rf::obj_is_player(entity) && rf::weapon_is_semi_automatic(weapon_type) && fire_wait == 500) { - fire_wait = remote_fire_wait_override().value(); + fire_wait = fire_wait_override().value(); } }, }; From 9b64e3689c0637a0dd59333e076186b40d18832b Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Wed, 15 Oct 2025 03:29:30 +1300 Subject: [PATCH 105/134] Update --- docs/CHANGELOG.md | 5 +- game_patch/misc/level.cpp | 9 ++ game_patch/misc/level.h | 20 ++++ game_patch/object/event.cpp | 177 ++++++++++++++++++++++++++++++++++++ game_patch/rf/event.h | 134 +++++++++++++++++++++++++++ 5 files changed, 344 insertions(+), 1 deletion(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 74dce034b..d5ab735e4 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -1,11 +1,14 @@ Dash Faction Changelog ====================== - +Add support for Dash Level Properties chunk and full lightmap depth setting (commit f90f81a) Version 1.9.2 (not released yet) -------------------------------- [@is-this-c](https://github.com/is-this-c) - Fix a potential crash after a client quits a game if Directd3D 11 is enabled - Improve compatibility with Alpine Faction servers +- Add support for `AlpineLevelProps` and `legacy_cyclic_timers` in Alpine Faction levels +- Allow triggers to activate events in multiplayer +- Fix events that broke due to their delay parameter - Fix `PgUp`, `PgDown`, `End`, and `Home` on numeric keypads - Add `af_killsound1.wav` from Alpine Faction - Add `hit_sounds` command diff --git a/game_patch/misc/level.cpp b/game_patch/misc/level.cpp index 4d81e9504..6e75778ae 100644 --- a/game_patch/misc/level.cpp +++ b/game_patch/misc/level.cpp @@ -92,6 +92,7 @@ CodeInjection level_read_header_inj{ 0x004616FB, [] { DashLevelProps::instance().reset(rf::level.version); + AlpineLevelProps::instance().reset(); g_level_has_unsupported_event_classes = false; }, }; @@ -111,6 +112,14 @@ CodeInjection level_load_chunk_inj{ file.seek(chunk_len - 4, rf::File::seek_cur); } regs.eip = 0x004608EF; + } else if (chunk_id == alpine_props_chunk_id) { + const std::uint32_t version = file.read(); + if (version >= 1) { + AlpineLevelProps::instance().deserialize(file); + } else { + file.seek(chunk_len - 4, rf::File::seek_cur); + } + regs.eip = 0x004608EF; } }, }; diff --git a/game_patch/misc/level.h b/game_patch/misc/level.h index cbca4a04d..889922f31 100644 --- a/game_patch/misc/level.h +++ b/game_patch/misc/level.h @@ -3,8 +3,28 @@ #include #include "../rf/file/file.h" +constexpr int alpine_props_chunk_id = 0x0AFBA5ED; constexpr int dash_level_props_chunk_id = 0xDA58FA00; +struct AlpineLevelProps { + // backward compatible defaults + bool legacy_cyclic_timers = true; + + static AlpineLevelProps& instance() { + static AlpineLevelProps instance{}; + return instance; + } + + void reset() { + legacy_cyclic_timers = true; + } + + void deserialize(rf::File& file) { + legacy_cyclic_timers = file.read(); + xlog::debug("legacy_cyclic_timers {}", legacy_cyclic_timers); + } +}; + struct DashLevelProps { // backward compatible defaults diff --git a/game_patch/object/event.cpp b/game_patch/object/event.cpp index 4d1f6a106..ea3b7395b 100644 --- a/game_patch/object/event.cpp +++ b/game_patch/object/event.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include "../rf/object.h" #include "../rf/event.h" #include "../rf/entity.h" @@ -330,8 +331,184 @@ ConsoleCommand2 debug_event_msg_cmd{ } }; +CodeInjection trigger_activate_linked_objects_activate_event_in_multi_patch{ + 0x004C038C, + [] (auto& regs) { + if (rf::level.version >= 300) { + const rf::Object* const obj = regs.esi; + const rf::Event* const event = static_cast(obj); + static const std::unordered_set blocked_event_types{{ + rf::EventType::Load_Level, + rf::EventType::Drop_Point_Marker, + rf::EventType::Go_Undercover, + rf::EventType::Win_PS2_Demo, + rf::EventType::Endgame, + rf::EventType::Defuse_Nuke, + rf::EventType::Play_Video, + }}; + // RF does not allow triggers to activate events in multiplayer. + // In AF levels, we allow it, unless it is an event that would be + // problematic in multiplayer. + const rf::EventType event_type = static_cast(event->event_type); + if (!blocked_event_types.contains(event_type)) { + // Allow activation. + regs.eip = 0x004C03C2; + } else { + // Do not allow activation. + regs.eip = 0x004C03D3; + } + } + } +}; + +void event_process_handle_delay(rf::Event* const event) { + if (event->delay_timestamp.elapsed()) { + if (event_debug_enabled) { + rf::console::print( + "Processing {} message in event {} ({}) (delayed)", + event->delayed_msg ? "ON" : "OFF", + event->name, + event->uid + ); + } + + const rf::EventType event_type = static_cast(event->event_type); + if (event->delayed_msg) { + using Event_TurnOn = void(__thiscall)(rf::Event*); + if (event_type == rf::EventType::UnHide) { + const auto& event_turn_on = addr_as_ref(0x004BCDD0); + event_turn_on(event); + } else if (event_type == rf::EventType::Alarm_Siren) { + const auto& event_turn_on = addr_as_ref(0x004BA830); + event_turn_on(event); + } else if (event_type == rf::EventType::Make_Invulnerable) { + const auto& event_turn_on = addr_as_ref(0x004BC7C0); + event_turn_on(event); + } else if (event_type == rf::EventType::Cyclic_Timer) { + const auto& event_turn_on = addr_as_ref(0x004BB7A0); + event_turn_on(event); + } else if (event_type == rf::EventType::Play_Sound) { + const auto& event_turn_on = addr_as_ref(0x004BA440); + event_turn_on(event); + } else { + // xlog::warn("{} turned on", event->uid); + } + } else { + using Event_TurnOff = void(__thiscall)(rf::Event*); + if (event_type == rf::EventType::UnHide) { + const auto& event_turn_off = addr_as_ref(0x004BCDE0); + event_turn_off(event); + } else if (event_type == rf::EventType::Alarm_Siren) { + const auto& event_turn_off = addr_as_ref(0x004BA8C0); + event_turn_off(event); + } else if (event_type == rf::EventType::Make_Invulnerable) { + const auto& event_turn_off = addr_as_ref(0x004BC880); + event_turn_off(event); + } else if (event_type == rf::EventType::Cyclic_Timer) { + const auto& event_turn_off = addr_as_ref(0x004BB8A0); + event_turn_off(event); + } else if (event_type == rf::EventType::Play_Sound) { + const auto& event_turn_off = addr_as_ref(0x004BA690); + event_turn_off(event); + } else { + // xlog::warn("{} turned off", event->uid); + } + } + + if (rf::event_type_forwards_messages(event_type)) { + using Event_ActivateLinks = void(__thiscall)(rf::Event*, int, int, bool); + const auto& event_activate_links = addr_as_ref(0x004B8B00); + event_activate_links( + event, + event->trigger_handle, + event->triggered_by_handle, + event->delayed_msg + ); + } + + event->delay_timestamp.invalidate(); + } +} + +CodeInjection EventUnhide__process_patch { + 0x004BCDF0, + [] (const auto& regs) { + if (rf::level.version >= 300) { + rf::Event* const event = regs.ecx; + event_process_handle_delay(event); + } + } +}; + +CodeInjection EventAlarmSiren__process_patch { + 0x004BA880, + [] (const auto& regs) { + if (rf::level.version >= 300) { + rf::Event* const event = regs.ecx; + event_process_handle_delay(event); + } + } +}; + +CodeInjection EventMakeInvulnerable__process_patch { + 0x004BC8F0, + [] (const auto& regs) { + if (rf::level.version >= 300) { + rf::Event* const event = regs.ecx; + event_process_handle_delay(event); + } + } +}; + +CodeInjection EventCyclicTimer__process_patch { + 0x004BB7B0, + [] (const auto& regs) { + if (rf::level.version >= 300) { + rf::Event* const event = regs.ecx; + event_process_handle_delay(event); + } + } +}; + +CodeInjection EventPlaySound__process_patch { + 0x004BA570, + [] (const auto& regs) { + if (rf::level.version >= 300) { + rf::Event* const event = regs.ecx; + event_process_handle_delay(event); + } + } +}; + +CodeInjection event_type_forwards_messages_patch{ + 0x004B8C44, + [] (auto& regs) { + if (rf::level.version >= 300) { + const rf::EventType event_type = static_cast(regs.eax); + if (event_type == rf::EventType::Cyclic_Timer + && !AlpineLevelProps::instance().legacy_cyclic_timers) { + // Do not forward messages. + regs.al = false; + regs.eip = 0x004B8C5D; + } + } + } +}; + void apply_event_patches() { + event_type_forwards_messages_patch.install(); + + // In AF levels, fix events that are broken, if `delay_timestamp` is set. + EventUnhide__process_patch.install(); + EventAlarmSiren__process_patch.install(); + EventMakeInvulnerable__process_patch.install(); + EventCyclicTimer__process_patch.install(); + EventPlaySound__process_patch.install(); + + // In AF levels, allow triggers to activate events in multiplayer. + trigger_activate_linked_objects_activate_event_in_multi_patch.install(); + // Allow custom mesh (not used in clutter.tbl or items.tbl) in Switch_Model event switch_model_event_custom_mesh_patch.install(); switch_model_event_obj_lighting_and_physics_fix.install(); diff --git a/game_patch/rf/event.h b/game_patch/rf/event.h index 154bd675c..0d9894449 100644 --- a/game_patch/rf/event.h +++ b/game_patch/rf/event.h @@ -24,6 +24,140 @@ namespace rf }; static_assert(sizeof(Event) == 0x2B8); + static auto& event_type_forwards_messages = addr_as_ref(0x004B8C40); + static auto& event_lookup_from_uid = addr_as_ref(0x004B6820); static auto& event_names = addr_as_ref(0x005A1A3C); + + // There are 90 built-in events. + enum EventType : int { + Play_Sound, + Slay_Object, + Remove_Object, + Invert, + Teleport, + Goto, + Goto_Player, + Look_At, + Shoot_At, + Shoot_Once, + Explode, + Play_Animation, + Play_Custom_Animation, + Heal, + Armor, + Message, + When_Dead, + Continuous_Damage, + Shake_Player, + Give_Item_To_Player, + Cyclic_Timer, + Switch_Model, + Load_Level, + Spawn_Object, + Make_Invulnerable, + Make_Walk, + Make_Fly, + Drop_Point_Marker, + Follow_Waypoints, + Follow_Player, + Set_Friendliness, + Set_Light_State, + Switch, + Swap_Textures, + Set_AI_Mode, + Goal_Create, + Goal_Check, + Goal_Set, + Attack, + Particle_State, + Set_Liquid_Depth, + Music_Start, + Music_Stop, + Bolt_State, + Set_Gravity, + Alarm_Siren, + Alarm, + Go_Undercover, + Delay, + Monitor_State, + UnHide, + Push_Region_State, + When_Hit, + Headlamp_State, + Item_Pickup_State, + Cutscene, + Strip_Player_Weapons, + Fog_State, + Detach, + Skybox_State, + Force_Monitor_Update, + Black_Out_Player, + Turn_Off_Physics, + Teleport_Player, + Holster_Weapon, + Holster_Player_Weapon, + Modify_Rotating_Mover, + Clear_Endgame_If_Killed, + Win_PS2_Demo, + Enable_Navpoint, + Play_Vclip, + Endgame, + Mover_Pause, + Countdown_Begin, + Countdown_End, + When_Countdown_Over, + Activate_Capek_Shield, + When_Enter_Vehicle, + When_Try_Exit_Vehicle, + Fire_Weapon_No_Anim, + Never_Leave_Vehicle, + Drop_Weapon, + Ignite_Entity, + When_Cutscene_Over, + When_Countdown_Reaches, + Display_Fullscreen_Image, + Defuse_Nuke, + When_Life_Reaches, + When_Armor_Reaches, + Reverse_Mover, + // Alpine Faction events begin at 100. + Set_Variable = 100, + Clone_Entity, + Set_Player_World_Collide, + Switch_Random, + Difficulty_Gate, + HUD_Message, + Play_Video, + Set_Level_Hardness, + Sequence, + Clear_Queued, + Remove_Link, + Route_Node, + Add_Link, + Valid_Gate, + Goal_Math, + Goal_Gate, + Scope_Gate, + Inside_Gate, + Anchor_Marker, + Force_Unhide, + Set_Difficulty, + Set_Fog_Far_Clip, + AF_When_Dead, + Gametype_Gate, + When_Picked_Up, + Set_Skybox, + Set_Life, + Set_Debris, + Set_Fog_Color, + Set_Entity_Flag, + AF_Teleport_Player, + Set_Item_Drop, + AF_Heal, + Anchor_Marker_Orient, + Light_State, + World_HUD_Sprite, + Set_Light_Color, + }; } From 07442bbf385e5019d9aefd2a52b8fbce8f90a141 Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Wed, 15 Oct 2025 03:30:42 +1300 Subject: [PATCH 106/134] Update --- docs/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index d5ab735e4..cff4eec32 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -1,6 +1,6 @@ Dash Faction Changelog ====================== -Add support for Dash Level Properties chunk and full lightmap depth setting (commit f90f81a) + Version 1.9.2 (not released yet) -------------------------------- [@is-this-c](https://github.com/is-this-c) From bd8a149b9928106bdd0a646b87b5d0b6da9176f8 Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Wed, 15 Oct 2025 16:17:08 +1300 Subject: [PATCH 107/134] Update CHANGELOG.md --- docs/CHANGELOG.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index cff4eec32..092ede50b 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -5,10 +5,10 @@ Version 1.9.2 (not released yet) -------------------------------- [@is-this-c](https://github.com/is-this-c) - Fix a potential crash after a client quits a game if Directd3D 11 is enabled -- Improve compatibility with Alpine Faction servers -- Add support for `AlpineLevelProps` and `legacy_cyclic_timers` in Alpine Faction levels -- Allow triggers to activate events in multiplayer -- Fix events that broke due to their delay parameter +- Improve compatibility with Alpine Faction network protocol +- Handle `AlpineLevelProps::legacy_cyclic_timers` in Alpine Faction levels +- Allow triggers to activate events in multiplayer in Alpine Faction levels +- Fix events that broke if their delay parameter was set in Alpine Faction levels - Fix `PgUp`, `PgDown`, `End`, and `Home` on numeric keypads - Add `af_killsound1.wav` from Alpine Faction - Add `hit_sounds` command From 3fba6ab4ef5132f5e4acb0e31fd59585d3c19436 Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Wed, 15 Oct 2025 18:22:08 +1300 Subject: [PATCH 108/134] Update --- game_patch/misc/level.cpp | 27 +++++------- game_patch/misc/level.h | 33 +++++++------- game_patch/object/event.cpp | 86 ++++++++++++++----------------------- 3 files changed, 62 insertions(+), 84 deletions(-) diff --git a/game_patch/misc/level.cpp b/game_patch/misc/level.cpp index 6e75778ae..9f33dc8df 100644 --- a/game_patch/misc/level.cpp +++ b/game_patch/misc/level.cpp @@ -99,25 +99,20 @@ CodeInjection level_read_header_inj{ CodeInjection level_load_chunk_inj{ 0x00460912, - [](auto& regs) { - int chunk_id = regs.eax; + [] (auto& regs) { + const int chunk_id = regs.eax; rf::File& file = addr_as_ref(regs.esp + 0x2B0 - 0x278); - auto chunk_len = addr_as_ref(regs.esp + 0x2B0 - 0x2A0); - - if (chunk_id == dash_level_props_chunk_id) { - auto version = file.read(); - if (version == 1) { + const std::size_t chunk_len = addr_as_ref(regs.esp + 0x2B0 - 0x2A0); + if (chunk_id == dash_level_props_chunk_id + || chunk_id == alpine_props_chunk_id) { + const int chunk_end = file.tell() + chunk_len; + if (chunk_id == dash_level_props_chunk_id) { DashLevelProps::instance().deserialize(file); - } else { - file.seek(chunk_len - 4, rf::File::seek_cur); - } - regs.eip = 0x004608EF; - } else if (chunk_id == alpine_props_chunk_id) { - const std::uint32_t version = file.read(); - if (version >= 1) { + } else if (chunk_id == alpine_props_chunk_id) { AlpineLevelProps::instance().deserialize(file); - } else { - file.seek(chunk_len - 4, rf::File::seek_cur); + } + if (file.tell() != chunk_end) { + file.seek(chunk_end, rf::File::seek_set); } regs.eip = 0x004608EF; } diff --git a/game_patch/misc/level.h b/game_patch/misc/level.h index 889922f31..ed3d40352 100644 --- a/game_patch/misc/level.h +++ b/game_patch/misc/level.h @@ -7,7 +7,7 @@ constexpr int alpine_props_chunk_id = 0x0AFBA5ED; constexpr int dash_level_props_chunk_id = 0xDA58FA00; struct AlpineLevelProps { - // backward compatible defaults + // Backward-compatible defaults. bool legacy_cyclic_timers = true; static AlpineLevelProps& instance() { @@ -20,31 +20,34 @@ struct AlpineLevelProps { } void deserialize(rf::File& file) { - legacy_cyclic_timers = file.read(); - xlog::debug("legacy_cyclic_timers {}", legacy_cyclic_timers); + const std::int32_t version = file.read(); + // Forward-compatible deserialization. + if (version >= 1) { + legacy_cyclic_timers = file.read(); + xlog::debug("legacy_cyclic_timers {}", legacy_cyclic_timers); + } } }; -struct DashLevelProps -{ - // backward compatible defaults +struct DashLevelProps { + // Backward-compatible defaults. bool lightmaps_full_depth = false; - static DashLevelProps& instance() - { - static DashLevelProps instance; + static DashLevelProps& instance() { + static DashLevelProps instance{}; return instance; } - void reset(int version) - { + void reset(int version) { lightmaps_full_depth = version >= 300; } - void deserialize(rf::File& file) - { - lightmaps_full_depth = file.read(); - xlog::debug("lightmaps_full_depth {}", lightmaps_full_depth); + void deserialize(rf::File& file) { + const std::int32_t version = file.read(); + if (version == 1) { + lightmaps_full_depth = file.read(); + xlog::debug("lightmaps_full_depth {}", lightmaps_full_depth); + } } }; diff --git a/game_patch/object/event.cpp b/game_patch/object/event.cpp index ea3b7395b..2ae756951 100644 --- a/game_patch/object/event.cpp +++ b/game_patch/object/event.cpp @@ -362,7 +362,7 @@ CodeInjection trigger_activate_linked_objects_activate_event_in_multi_patch{ }; void event_process_handle_delay(rf::Event* const event) { - if (event->delay_timestamp.elapsed()) { + if (rf::level.version >= 300 && event->delay_timestamp.elapsed()) { if (event_debug_enabled) { rf::console::print( "Processing {} message in event {} ({}) (delayed)", @@ -374,44 +374,34 @@ void event_process_handle_delay(rf::Event* const event) { const rf::EventType event_type = static_cast(event->event_type); if (event->delayed_msg) { - using Event_TurnOn = void(__thiscall)(rf::Event*); - if (event_type == rf::EventType::UnHide) { - const auto& event_turn_on = addr_as_ref(0x004BCDD0); - event_turn_on(event); - } else if (event_type == rf::EventType::Alarm_Siren) { - const auto& event_turn_on = addr_as_ref(0x004BA830); - event_turn_on(event); - } else if (event_type == rf::EventType::Make_Invulnerable) { - const auto& event_turn_on = addr_as_ref(0x004BC7C0); - event_turn_on(event); - } else if (event_type == rf::EventType::Cyclic_Timer) { - const auto& event_turn_on = addr_as_ref(0x004BB7A0); - event_turn_on(event); - } else if (event_type == rf::EventType::Play_Sound) { - const auto& event_turn_on = addr_as_ref(0x004BA440); + static const std::unordered_map handlers{{ + { rf::EventType::UnHide, 0x004BCDD0 }, + { rf::EventType::Alarm_Siren, 0x004BA830 }, + { rf::EventType::Make_Invulnerable, 0x004BC7C0 }, + { rf::EventType::Cyclic_Timer, 0x004BB7A0 }, + { rf::EventType::Play_Sound, 0x004BA440 }, + }}; + if (const auto pair = handlers.find(event_type); pair != handlers.end()) { + using Event_TurnOn = void(__thiscall)(rf::Event*); + const auto& event_turn_on = addr_as_ref(pair->second); event_turn_on(event); } else { - // xlog::warn("{} turned on", event->uid); + // xlog::error("{} turned on", event->uid); } } else { - using Event_TurnOff = void(__thiscall)(rf::Event*); - if (event_type == rf::EventType::UnHide) { - const auto& event_turn_off = addr_as_ref(0x004BCDE0); - event_turn_off(event); - } else if (event_type == rf::EventType::Alarm_Siren) { - const auto& event_turn_off = addr_as_ref(0x004BA8C0); - event_turn_off(event); - } else if (event_type == rf::EventType::Make_Invulnerable) { - const auto& event_turn_off = addr_as_ref(0x004BC880); - event_turn_off(event); - } else if (event_type == rf::EventType::Cyclic_Timer) { - const auto& event_turn_off = addr_as_ref(0x004BB8A0); - event_turn_off(event); - } else if (event_type == rf::EventType::Play_Sound) { - const auto& event_turn_off = addr_as_ref(0x004BA690); + static const std::unordered_map handlers{{ + { rf::EventType::UnHide, 0x004BCDE0 }, + { rf::EventType::Alarm_Siren, 0x004BA8C0 }, + { rf::EventType::Make_Invulnerable, 0x004BC880 }, + { rf::EventType::Cyclic_Timer, 0x004BB8A0 }, + { rf::EventType::Play_Sound, 0x004BA690 }, + }}; + if (const auto pair = handlers.find(event_type); pair != handlers.end()) { + using Event_TurnOff = void(__thiscall)(rf::Event*); + const auto& event_turn_off = addr_as_ref(pair->second); event_turn_off(event); } else { - // xlog::warn("{} turned off", event->uid); + // xlog::error("{} turned off", event->uid); } } @@ -433,50 +423,40 @@ void event_process_handle_delay(rf::Event* const event) { CodeInjection EventUnhide__process_patch { 0x004BCDF0, [] (const auto& regs) { - if (rf::level.version >= 300) { - rf::Event* const event = regs.ecx; - event_process_handle_delay(event); - } + rf::Event* const event = regs.ecx; + event_process_handle_delay(event); } }; CodeInjection EventAlarmSiren__process_patch { 0x004BA880, [] (const auto& regs) { - if (rf::level.version >= 300) { - rf::Event* const event = regs.ecx; - event_process_handle_delay(event); - } + rf::Event* const event = regs.ecx; + event_process_handle_delay(event); } }; CodeInjection EventMakeInvulnerable__process_patch { 0x004BC8F0, [] (const auto& regs) { - if (rf::level.version >= 300) { - rf::Event* const event = regs.ecx; - event_process_handle_delay(event); - } + rf::Event* const event = regs.ecx; + event_process_handle_delay(event); } }; CodeInjection EventCyclicTimer__process_patch { 0x004BB7B0, [] (const auto& regs) { - if (rf::level.version >= 300) { - rf::Event* const event = regs.ecx; - event_process_handle_delay(event); - } + rf::Event* const event = regs.ecx; + event_process_handle_delay(event); } }; CodeInjection EventPlaySound__process_patch { 0x004BA570, [] (const auto& regs) { - if (rf::level.version >= 300) { - rf::Event* const event = regs.ecx; - event_process_handle_delay(event); - } + rf::Event* const event = regs.ecx; + event_process_handle_delay(event); } }; From dab14505a4869d12edc5b5b805b1c1305d10ab49 Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Wed, 15 Oct 2025 18:34:38 +1300 Subject: [PATCH 109/134] Update --- game_patch/object/event.cpp | 4 ++-- game_patch/object/weapon.cpp | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/game_patch/object/event.cpp b/game_patch/object/event.cpp index 2ae756951..589c93dfa 100644 --- a/game_patch/object/event.cpp +++ b/game_patch/object/event.cpp @@ -386,7 +386,7 @@ void event_process_handle_delay(rf::Event* const event) { const auto& event_turn_on = addr_as_ref(pair->second); event_turn_on(event); } else { - // xlog::error("{} turned on", event->uid); + xlog::debug("{} turned on", event->uid); } } else { static const std::unordered_map handlers{{ @@ -401,7 +401,7 @@ void event_process_handle_delay(rf::Event* const event) { const auto& event_turn_off = addr_as_ref(pair->second); event_turn_off(event); } else { - // xlog::error("{} turned off", event->uid); + xlog::debug("{} turned off", event->uid); } } diff --git a/game_patch/object/weapon.cpp b/game_patch/object/weapon.cpp index 849a5ad1b..ef7bbea5b 100644 --- a/game_patch/object/weapon.cpp +++ b/game_patch/object/weapon.cpp @@ -138,7 +138,7 @@ CodeInjection player_fire_primary_weapon_semi_auto_patch{ }, }; -std::optional fire_wait_override() { +std::optional get_fire_wait_override() { return get_remote_server_info().and_then([] (const RemoteServerInfo& remote_server_info) { return remote_server_info.semiauto_cool_down; }); @@ -151,11 +151,11 @@ CodeInjection entity_fire_primary_weapon_semi_auto_patch{ auto& fire_wait = regs.eax; const int weapon_type = regs.ebx; const rf::Entity* const entity = regs.esi; - if (fire_wait_override() + if (get_fire_wait_override() && rf::obj_is_player(entity) && rf::weapon_is_semi_automatic(weapon_type) && fire_wait == 500) { - fire_wait = fire_wait_override().value(); + fire_wait = get_fire_wait_override().value(); } }, }; From cd44590b70c36875f5473371743f9feb5fdd0113 Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Wed, 15 Oct 2025 18:44:26 +1300 Subject: [PATCH 110/134] Update --- game_patch/object/event.cpp | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/game_patch/object/event.cpp b/game_patch/object/event.cpp index 589c93dfa..27ad2c4aa 100644 --- a/game_patch/object/event.cpp +++ b/game_patch/object/event.cpp @@ -381,12 +381,10 @@ void event_process_handle_delay(rf::Event* const event) { { rf::EventType::Cyclic_Timer, 0x004BB7A0 }, { rf::EventType::Play_Sound, 0x004BA440 }, }}; - if (const auto pair = handlers.find(event_type); pair != handlers.end()) { + if (const auto iter = handlers.find(event_type); iter != handlers.end()) { using Event_TurnOn = void(__thiscall)(rf::Event*); - const auto& event_turn_on = addr_as_ref(pair->second); + const auto& event_turn_on = addr_as_ref(iter->second); event_turn_on(event); - } else { - xlog::debug("{} turned on", event->uid); } } else { static const std::unordered_map handlers{{ @@ -396,12 +394,10 @@ void event_process_handle_delay(rf::Event* const event) { { rf::EventType::Cyclic_Timer, 0x004BB8A0 }, { rf::EventType::Play_Sound, 0x004BA690 }, }}; - if (const auto pair = handlers.find(event_type); pair != handlers.end()) { + if (const auto iter = handlers.find(event_type); iter != handlers.end()) { using Event_TurnOff = void(__thiscall)(rf::Event*); - const auto& event_turn_off = addr_as_ref(pair->second); + const auto& event_turn_off = addr_as_ref(iter->second); event_turn_off(event); - } else { - xlog::debug("{} turned off", event->uid); } } From 2b123a0f4b86a3f8a7a437fc8ced7a3e66f24192 Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Thu, 16 Oct 2025 23:19:12 +1300 Subject: [PATCH 111/134] Update --- game_patch/object/event.cpp | 62 +++++++++++++++++++++---------------- 1 file changed, 36 insertions(+), 26 deletions(-) diff --git a/game_patch/object/event.cpp b/game_patch/object/event.cpp index 27ad2c4aa..383940e49 100644 --- a/game_patch/object/event.cpp +++ b/game_patch/object/event.cpp @@ -361,7 +361,7 @@ CodeInjection trigger_activate_linked_objects_activate_event_in_multi_patch{ } }; -void event_process_handle_delay(rf::Event* const event) { +void event_process_fixed_delay(rf::Event* const event) { if (rf::level.version >= 300 && event->delay_timestamp.elapsed()) { if (event_debug_enabled) { rf::console::print( @@ -374,29 +374,39 @@ void event_process_handle_delay(rf::Event* const event) { const rf::EventType event_type = static_cast(event->event_type); if (event->delayed_msg) { - static const std::unordered_map handlers{{ - { rf::EventType::UnHide, 0x004BCDD0 }, - { rf::EventType::Alarm_Siren, 0x004BA830 }, - { rf::EventType::Make_Invulnerable, 0x004BC7C0 }, - { rf::EventType::Cyclic_Timer, 0x004BB7A0 }, - { rf::EventType::Play_Sound, 0x004BA440 }, - }}; - if (const auto iter = handlers.find(event_type); iter != handlers.end()) { - using Event_TurnOn = void(__thiscall)(rf::Event*); - const auto& event_turn_on = addr_as_ref(iter->second); + using Event_TurnOn = void(__thiscall*)(rf::Event*); + if (event_type == rf::EventType::UnHide) { + const auto& event_turn_on = addr_as_ref(0x004BCDD0); + event_turn_on(event); + } else if (event_type == rf::EventType::Alarm_Siren) { + const auto& event_turn_on = addr_as_ref(0x004BA830); + event_turn_on(event); + } else if (event_type == rf::EventType::Make_Invulnerable) { + const auto& event_turn_on = addr_as_ref(0x004BC7C0); + event_turn_on(event); + } else if (event_type == rf::EventType::Cyclic_Timer) { + const auto& event_turn_on = addr_as_ref(0x004BB7A0); + event_turn_on(event); + } else if (event_type == rf::EventType::Play_Sound) { + const auto& event_turn_on = addr_as_ref(0x004BA440); event_turn_on(event); } } else { - static const std::unordered_map handlers{{ - { rf::EventType::UnHide, 0x004BCDE0 }, - { rf::EventType::Alarm_Siren, 0x004BA8C0 }, - { rf::EventType::Make_Invulnerable, 0x004BC880 }, - { rf::EventType::Cyclic_Timer, 0x004BB8A0 }, - { rf::EventType::Play_Sound, 0x004BA690 }, - }}; - if (const auto iter = handlers.find(event_type); iter != handlers.end()) { - using Event_TurnOff = void(__thiscall)(rf::Event*); - const auto& event_turn_off = addr_as_ref(iter->second); + using Event_TurnOff = void(__thiscall*)(rf::Event*); + if (event_type == rf::EventType::UnHide) { + const auto& event_turn_off = addr_as_ref(0x004BCDE0); + event_turn_off(event); + } else if (event_type == rf::EventType::Alarm_Siren) { + const auto& event_turn_off = addr_as_ref(0x004BA8C0); + event_turn_off(event); + } else if (event_type == rf::EventType::Make_Invulnerable) { + const auto& event_turn_off = addr_as_ref(0x004BC880); + event_turn_off(event); + } else if (event_type == rf::EventType::Cyclic_Timer) { + const auto& event_turn_off = addr_as_ref(0x004BB8A0); + event_turn_off(event); + } else if (event_type == rf::EventType::Play_Sound) { + const auto& event_turn_off = addr_as_ref(0x004BA690); event_turn_off(event); } } @@ -420,7 +430,7 @@ CodeInjection EventUnhide__process_patch { 0x004BCDF0, [] (const auto& regs) { rf::Event* const event = regs.ecx; - event_process_handle_delay(event); + event_process_fixed_delay(event); } }; @@ -428,7 +438,7 @@ CodeInjection EventAlarmSiren__process_patch { 0x004BA880, [] (const auto& regs) { rf::Event* const event = regs.ecx; - event_process_handle_delay(event); + event_process_fixed_delay(event); } }; @@ -436,7 +446,7 @@ CodeInjection EventMakeInvulnerable__process_patch { 0x004BC8F0, [] (const auto& regs) { rf::Event* const event = regs.ecx; - event_process_handle_delay(event); + event_process_fixed_delay(event); } }; @@ -444,7 +454,7 @@ CodeInjection EventCyclicTimer__process_patch { 0x004BB7B0, [] (const auto& regs) { rf::Event* const event = regs.ecx; - event_process_handle_delay(event); + event_process_fixed_delay(event); } }; @@ -452,7 +462,7 @@ CodeInjection EventPlaySound__process_patch { 0x004BA570, [] (const auto& regs) { rf::Event* const event = regs.ecx; - event_process_handle_delay(event); + event_process_fixed_delay(event); } }; From a1dbd0b41afd3731192e6f98b4ad332b72b09db3 Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Sat, 18 Oct 2025 01:35:16 +1300 Subject: [PATCH 112/134] Update --- game_patch/CMakeLists.txt | 1 + game_patch/main/main.cpp | 6 + game_patch/misc/af_options.cpp | 228 +++++++++++++++++++++++++++++++ game_patch/misc/af_options.h | 65 +++++++++ game_patch/misc/g_solid.cpp | 70 +++++++++- game_patch/misc/level.cpp | 21 ++- game_patch/misc/level.h | 2 + game_patch/object/event.cpp | 128 +++++------------ game_patch/object/obj_light.cpp | 12 ++ game_patch/object/object.cpp | 17 ++- game_patch/object/particle.cpp | 49 +++++++ game_patch/rf/event.h | 5 +- game_patch/rf/particle_emitter.h | 2 +- 13 files changed, 499 insertions(+), 107 deletions(-) create mode 100644 game_patch/misc/af_options.cpp create mode 100644 game_patch/misc/af_options.h diff --git a/game_patch/CMakeLists.txt b/game_patch/CMakeLists.txt index 2f87e760c..e369642b1 100644 --- a/game_patch/CMakeLists.txt +++ b/game_patch/CMakeLists.txt @@ -158,6 +158,7 @@ set(SRCS misc/ui.cpp misc/game.cpp misc/level.cpp + misc/af_options.cpp sound/sound.cpp sound/sound.h sound/sound_ds.cpp diff --git a/game_patch/main/main.cpp b/game_patch/main/main.cpp index a17cb32d4..4063adeb3 100644 --- a/game_patch/main/main.cpp +++ b/game_patch/main/main.cpp @@ -29,6 +29,8 @@ #include "../misc/misc.h" #include "../misc/vpackfile.h" #include "../misc/high_fps.h" +#include "../misc/level.h" +#include "../misc/af_options.h" #include "../input/input.h" #include "../rf/gr/gr.h" #include "../rf/multi.h" @@ -145,6 +147,7 @@ FunHook level_load_hook{ xlog::info("Loading level: {}", level_filename); if (!save_filename.empty()) xlog::info("Restoring game from save file: {}", save_filename); + load_af_level_info_config(level_filename); int ret = level_load_hook.call_target(level_filename, save_filename, error); if (ret != 0) xlog::warn("Loading failed: {}", error); @@ -160,6 +163,9 @@ FunHook level_init_post_hook{ [](bool transition) { level_init_post_hook.call_target(transition); xlog::info("Level loaded: {}{}", rf::level.filename, transition ? " (transition)" : ""); + if (!rf::is_dedicated_server) { + set_levelmod_autotexture_ppm(); + } }, }; diff --git a/game_patch/misc/af_options.cpp b/game_patch/misc/af_options.cpp new file mode 100644 index 000000000..4193ef18b --- /dev/null +++ b/game_patch/misc/af_options.cpp @@ -0,0 +1,228 @@ +#include "af_options.h" +#include +#include +#include +#include +#include "../rf/file/file.h" +#include + +AlpineLevelInfoConfig g_af_level_info_config; + +// trim leading and trailing whitespace +std::string trim(const std::string& str, bool remove_quotes = false) +{ + auto start = str.find_first_not_of(" \t\n\r"); + auto end = str.find_last_not_of(" \t\n\r"); + if (start == std::string::npos) { + return ""; + } + + std::string trimmed = str.substr(start, end - start + 1); + + // extract quoted value + if (remove_quotes && trimmed.size() >= 2 && trimmed.front() == '"' && trimmed.back() == '"') { + trimmed = trimmed.substr(1, trimmed.size() - 2); + } + + return trimmed; +} + +// Parsing functions for level settings +std::optional parse_float_level(const std::string& value) +{ + try { return std::stof(value); } catch (...) { return std::nullopt; } +} +std::optional parse_int_level(const std::string& value) +{ + try { return std::stoi(value); } catch (...) { return std::nullopt; } +} +std::optional parse_bool_level(const std::string& value) +{ + return value == "1" || value == "true" || value == "True"; +} +std::optional parse_string_level(const std::string& value) +{ + return trim(value, true); +} + +std::optional parse_color_level(const std::string& value) +{ + std::string trimmed_value = trim(value, true); + + try { + // Check if it's a valid hex string (6 or 8 characters) + if (!trimmed_value.empty() && trimmed_value.length() <= 8 && + std::all_of(trimmed_value.begin(), trimmed_value.end(), ::isxdigit)) { + uint32_t color = std::stoul(trimmed_value, nullptr, 16); // Parse hex + + // If it's a 24-bit color (6 characters), add full alpha (0xFF) + if (trimmed_value.length() == 6) + color = (color << 8) | 0xFF; + + return color; + } + + // Check for RGB formats using regex + std::regex rgb_pattern(R"([\{\<]?\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*[\>\}]?)"); + std::smatch matches; + + if (std::regex_match(trimmed_value, matches, rgb_pattern)) { + if (matches.size() == 4) { // First match is the full string, next 3 are R, G, B + int r = std::stoi(matches[1].str()); + int g = std::stoi(matches[2].str()); + int b = std::stoi(matches[3].str()); + + // Ensure values are within the valid range + if (r >= 0 && r <= 255 && g >= 0 && g <= 255 && b >= 0 && b <= 255) { + uint32_t color = (r << 24) | (g << 16) | (b << 8) | 0xFF; // Add full alpha + return color; + } + } + } + } + catch (...) { + return std::nullopt; // Return null if parsing fails + } + + return std::nullopt; +} + +std::optional> parse_mesh_replacement(const std::string& value) +{ + std::string trimmed_value = trim(value, true); + std::regex mesh_replacement_pattern("\\{\\s*\"([^\"]+)\"\\s*,\\s*\"([^\"]+)\"\\s*\\}"); + std::smatch matches; + + if (std::regex_match(trimmed_value, matches, mesh_replacement_pattern)) { + if (matches.size() == 3) { // Full match and 2 filename captures + return std::make_pair(trim(matches[1].str(), true), trim(matches[2].str(), true)); + } + } + return std::nullopt; +} + +// Metadata map for level options +const std::unordered_map level_info_metadata = { + {"$Lightmap Clamp Floor", {AlpineLevelInfoId::LightmapClampFloor, parse_color_level}}, + {"$Lightmap Clamp Ceiling", {AlpineLevelInfoId::LightmapClampCeiling, parse_color_level}}, + {"$Ideal Player Count", {AlpineLevelInfoId::IdealPlayerCount, parse_int_level}}, + {"$Author Contact", {AlpineLevelInfoId::AuthorContact, parse_string_level}}, + {"$Author Website", {AlpineLevelInfoId::AuthorWebsite, parse_string_level}}, + {"$Description", {AlpineLevelInfoId::Description, parse_string_level}}, + {"$Player Headlamp Color", {AlpineLevelInfoId::PlayerHeadlampColor, parse_color_level}}, + {"$Player Headlamp Intensity", {AlpineLevelInfoId::PlayerHeadlampIntensity, parse_float_level}}, + {"$Player Headlamp Range", {AlpineLevelInfoId::PlayerHeadlampRange, parse_float_level}}, + {"$Player Headlamp Radius", {AlpineLevelInfoId::PlayerHeadlampRadius, parse_float_level}}, + {"$Chat Menu 1", {AlpineLevelInfoId::ChatMap1, parse_string_level}}, + {"$Chat Menu 2", {AlpineLevelInfoId::ChatMap2, parse_string_level}}, + {"$Chat Menu 3", {AlpineLevelInfoId::ChatMap3, parse_string_level}}, + {"$Chat Menu 4", {AlpineLevelInfoId::ChatMap4, parse_string_level}}, + {"$Chat Menu 5", {AlpineLevelInfoId::ChatMap5, parse_string_level}}, + {"$Chat Menu 6", {AlpineLevelInfoId::ChatMap6, parse_string_level}}, + {"$Chat Menu 7", {AlpineLevelInfoId::ChatMap7, parse_string_level}}, + {"$Chat Menu 8", {AlpineLevelInfoId::ChatMap8, parse_string_level}}, + {"$Chat Menu 9", {AlpineLevelInfoId::ChatMap9, parse_string_level}}, + {"$Crater Texture PPM", {AlpineLevelInfoId::CraterTexturePpm, parse_float_level}} +}; + +// Load level info from filename_info.tbl +void load_level_info_config(const std::string& level_filename) +{ + std::string base_filename = level_filename.substr(0, level_filename.size() - 4); + std::string info_filename = base_filename + "_info.tbl"; + auto level_info_file = std::make_unique(); + + if (level_info_file->open(info_filename.c_str()) != 0) { + xlog::debug("Could not open {}", info_filename); + return; + } + + xlog::debug("Successfully opened {}", info_filename); + + // Read entire file content into a single string buffer + std::string file_content; + std::string buffer(2048, '\0'); + int bytes_read; + + while ((bytes_read = level_info_file->read(&buffer[0], buffer.size() - 1)) > 0) { + buffer.resize(bytes_read); + file_content += buffer; + buffer.resize(2048, '\0'); + } + + level_info_file->close(); + + // Process file content line-by-line + std::istringstream file_stream(file_content); + std::string line; + bool found_start = false; + + // Search for #Start marker + while (std::getline(file_stream, line)) { + line = trim(line, false); + if (line == "#Start") { + found_start = true; + break; + } + } + + if (!found_start) { + xlog::warn("No #Start marker found in {}", info_filename); + return; + } + + // Process options until #End marker is found + bool found_end = false; + while (std::getline(file_stream, line)) { + line = trim(line, false); + + if (line == "#End") { + found_end = true; + break; + } + if (line.empty() || line.starts_with("//")) { + continue; + } + + auto delimiter_pos = line.find(':'); + if (delimiter_pos == std::string::npos || line[0] != '$') { + continue; + } + + std::string option_name = trim(line.substr(0, delimiter_pos), false); + std::string option_value = trim(line.substr(delimiter_pos + 1), false); + + // handle mesh replacements + if (option_name == "$Mesh Replacement") { + auto parsed_value = parse_mesh_replacement(option_value); + if (parsed_value) { + g_af_level_info_config.mesh_replacements[level_filename][string_to_lower(parsed_value->first)]= parsed_value->second; + + xlog::debug("Mesh Replacement Added: {} -> {} in {}", parsed_value->first, parsed_value->second, level_filename); + } + else { + xlog::warn("Invalid mesh replacement format in {}", info_filename); + } + + continue; + } + + // handle other level info options + auto meta_it = level_info_metadata.find(option_name); + if (meta_it != level_info_metadata.end()) { + const auto& metadata = meta_it->second; + auto parsed_value = metadata.parse_function(option_value); + if (parsed_value) { + g_af_level_info_config.level_options[level_filename][metadata.id] = *parsed_value; + xlog::debug("Parsed and applied {} for {}: {}", option_name, level_filename, option_value); + } + } + else { + xlog::warn("Unknown option {} in {}", option_name, info_filename); + } + } + + if (!found_end) { + xlog::warn("No #End marker found in {}", info_filename); + } +} diff --git a/game_patch/misc/af_options.h b/game_patch/misc/af_options.h new file mode 100644 index 000000000..159eb8dd3 --- /dev/null +++ b/game_patch/misc/af_options.h @@ -0,0 +1,65 @@ +#pragma once + +#include +#include +#include +#include +#include + +// ======= Globals and utility ======= +void load_level_info_config(const std::string& level_filename); + +// ======= Alpine level info ======= +enum class AlpineLevelInfoId +{ + LightmapClampFloor, + LightmapClampCeiling, + IdealPlayerCount, + AuthorContact, + AuthorWebsite, + Description, + PlayerHeadlampColor, + PlayerHeadlampIntensity, + PlayerHeadlampRange, + PlayerHeadlampRadius, + ChatMap1, + ChatMap2, + ChatMap3, + ChatMap4, + ChatMap5, + ChatMap6, + ChatMap7, + ChatMap8, + ChatMap9, + CraterTexturePpm, + _optioncount // dummy for total count +}; + +// Variant type to represent different configuration values +using LevelInfoValue = std::variant; + +struct LevelInfoMetadata +{ + AlpineLevelInfoId id; + std::function(const std::string&)> parse_function; +}; + +// Main configuration structure for level info +struct AlpineLevelInfoConfig +{ + // maps of level options and mesh replacements + std::unordered_map> level_options; + std::unordered_map> mesh_replacements; // stored lowercase + + // Check if an option exists for a given level + bool is_option_loaded(const std::string& level, AlpineLevelInfoId option_id) const + { + auto level_it = level_options.find(level); + if (level_it != level_options.end()) { + return level_it->second.find(option_id) != level_it->second.end(); + } + return false; // no options loaded for this level + } +}; + +extern AlpineLevelInfoConfig g_af_level_info_config; diff --git a/game_patch/misc/g_solid.cpp b/game_patch/misc/g_solid.cpp index 908c7bd0a..4e9da0843 100644 --- a/game_patch/misc/g_solid.cpp +++ b/game_patch/misc/g_solid.cpp @@ -9,9 +9,11 @@ #include "../rf/os/frametime.h" #include "../rf/gameseq.h" #include "../os/console.h" +#include "../rf/level.h" #include "../bmpman/bmpman.h" #include "../bmpman/fmt_conv_templates.h" #include "level.h" +#include "af_options.h" constexpr auto reference_fps = 30.0f; constexpr auto reference_frametime = 1.0f / reference_fps; @@ -220,6 +222,15 @@ CodeInjection g_face_does_point_lie_in_face_crashfix{ }, }; +rf::gr::Color color_from_u32(const uint32_t color) { + return rf::gr::Color{ + (color >> 24) & 0xFF, + (color >> 16) & 0xFF, + (color >> 8) & 0xFF, + color & 0xFF + }; +} + CodeInjection level_load_lightmaps_color_conv_patch{ 0x004ED3E9, [](auto& regs) { @@ -232,10 +243,61 @@ CodeInjection level_load_lightmaps_color_conv_patch{ if (!rf::gr::lock(lightmap->bm_handle, 0, &lock, rf::gr::LOCK_WRITE_ONLY)) return; - // cap minimal color channel value as RF does - if (!DashLevelProps::instance().lightmaps_full_depth) { - for (int i = 0; i < lightmap->w * lightmap->h * 3; ++i) { - lightmap->buf[i] = std::max(lightmap->buf[i], static_cast(4 << 3)); // 32 + // No floor. + uint32_t floor_clamp = 0; + // No ceiling. + uint32_t ceiling_clamp = 0xFFFFFFFF; + bool should_clamp = false; + bool is_floor_clamp_defined = false; + + if (g_af_level_info_config + .is_option_loaded(rf::level.filename, AlpineLevelInfoId::LightmapClampFloor)) { + floor_clamp = std::get( + g_af_level_info_config + .level_options + .at(rf::level.filename) + .at(AlpineLevelInfoId::LightmapClampFloor) + ); + is_floor_clamp_defined = true; + should_clamp = true; + } + + if (g_af_level_info_config + .is_option_loaded(rf::level.filename, AlpineLevelInfoId::LightmapClampCeiling)) { + ceiling_clamp = std::get( + g_af_level_info_config + .level_options + .at(rf::level.filename) + .at(AlpineLevelInfoId::LightmapClampCeiling) + ); + should_clamp = true; + } + + if (!is_floor_clamp_defined + && rf::level.version < 300 + && !DashLevelProps::instance().lightmaps_full_depth) { + should_clamp = true; + constexpr int default_floor_clamp = 0x202020FF; + floor_clamp = default_floor_clamp; + } + + if (should_clamp) { + xlog::debug("Applying lightmap clamping"); + + // Extract RGB components from clamping values. + const rf::gr::Color floor = color_from_u32(floor_clamp); + const rf::gr::Color ceiling = color_from_u32(ceiling_clamp); + + for (int i = 0; i < lightmap->w * lightmap->h * 3; i += 3) { + // Apply floor clamp. + lightmap->buf[i] = std::max(lightmap->buf[i], floor.red); + lightmap->buf[i + 1] = std::max(lightmap->buf[i + 1], floor.green); + lightmap->buf[i + 2] = std::max(lightmap->buf[i + 2], floor.blue); + + // Apply ceiling clamp. + lightmap->buf[i] = std::min(lightmap->buf[i], ceiling.red); + lightmap->buf[i + 1] = std::min(lightmap->buf[i + 1], ceiling.green); + lightmap->buf[i + 2] = std::min(lightmap->buf[i + 2], ceiling.blue); } } diff --git a/game_patch/misc/level.cpp b/game_patch/misc/level.cpp index 9f33dc8df..798a16f28 100644 --- a/game_patch/misc/level.cpp +++ b/game_patch/misc/level.cpp @@ -11,8 +11,11 @@ #include "../rf/file/file.h" #include "../rf/player/player.h" #include "../rf/player/camera.h" -#include "level.h" #include "../main/main.h" +#include "level.h" +#include "af_options.h" + +static float g_crater_autotexture_ppm = 32.f; CodeInjection level_load_header_version_check_patch{ 0x004615BF, @@ -126,11 +129,25 @@ CodeInjection level_blast_ppm_injection{ int bmh = regs.edi; int w = 256, h = 256; rf::bm::get_dimensions(bmh, &w, &h); - float ppm = std::min(w, h) / 256.0 * 32.0; + float ppm = std::min(w, h) / 256.f * g_crater_autotexture_ppm; solid->set_autotexture_pixels_per_meter(ppm); }, }; +void set_levelmod_autotexture_ppm() { + if (g_af_level_info_config + .is_option_loaded(rf::level.filename, AlpineLevelInfoId::CraterTexturePpm)) { + g_crater_autotexture_ppm = std::get( + g_af_level_info_config + .level_options + .at(rf::level.filename) + .at(AlpineLevelInfoId::CraterTexturePpm) + ); + } else { + g_crater_autotexture_ppm = 32.f; + } +} + void level_apply_patch() { // Enable loading RFLs with version 300 (produced by AF editor) diff --git a/game_patch/misc/level.h b/game_patch/misc/level.h index ed3d40352..0d04406d1 100644 --- a/game_patch/misc/level.h +++ b/game_patch/misc/level.h @@ -3,6 +3,8 @@ #include #include "../rf/file/file.h" +void set_levelmod_autotexture_ppm(); + constexpr int alpine_props_chunk_id = 0x0AFBA5ED; constexpr int dash_level_props_chunk_id = 0xDA58FA00; diff --git a/game_patch/object/event.cpp b/game_patch/object/event.cpp index 383940e49..60d4eb3e3 100644 --- a/game_patch/object/event.cpp +++ b/game_patch/object/event.cpp @@ -361,118 +361,55 @@ CodeInjection trigger_activate_linked_objects_activate_event_in_multi_patch{ } }; -void event_process_fixed_delay(rf::Event* const event) { - if (rf::level.version >= 300 && event->delay_timestamp.elapsed()) { - if (event_debug_enabled) { - rf::console::print( - "Processing {} message in event {} ({}) (delayed)", - event->delayed_msg ? "ON" : "OFF", - event->name, - event->uid - ); - } - - const rf::EventType event_type = static_cast(event->event_type); - if (event->delayed_msg) { - using Event_TurnOn = void(__thiscall*)(rf::Event*); - if (event_type == rf::EventType::UnHide) { - const auto& event_turn_on = addr_as_ref(0x004BCDD0); - event_turn_on(event); - } else if (event_type == rf::EventType::Alarm_Siren) { - const auto& event_turn_on = addr_as_ref(0x004BA830); - event_turn_on(event); - } else if (event_type == rf::EventType::Make_Invulnerable) { - const auto& event_turn_on = addr_as_ref(0x004BC7C0); - event_turn_on(event); - } else if (event_type == rf::EventType::Cyclic_Timer) { - const auto& event_turn_on = addr_as_ref(0x004BB7A0); - event_turn_on(event); - } else if (event_type == rf::EventType::Play_Sound) { - const auto& event_turn_on = addr_as_ref(0x004BA440); - event_turn_on(event); - } - } else { - using Event_TurnOff = void(__thiscall*)(rf::Event*); - if (event_type == rf::EventType::UnHide) { - const auto& event_turn_off = addr_as_ref(0x004BCDE0); - event_turn_off(event); - } else if (event_type == rf::EventType::Alarm_Siren) { - const auto& event_turn_off = addr_as_ref(0x004BA8C0); - event_turn_off(event); - } else if (event_type == rf::EventType::Make_Invulnerable) { - const auto& event_turn_off = addr_as_ref(0x004BC880); - event_turn_off(event); - } else if (event_type == rf::EventType::Cyclic_Timer) { - const auto& event_turn_off = addr_as_ref(0x004BB8A0); - event_turn_off(event); - } else if (event_type == rf::EventType::Play_Sound) { - const auto& event_turn_off = addr_as_ref(0x004BA690); - event_turn_off(event); - } - } - - if (rf::event_type_forwards_messages(event_type)) { - using Event_ActivateLinks = void(__thiscall)(rf::Event*, int, int, bool); - const auto& event_activate_links = addr_as_ref(0x004B8B00); - event_activate_links( - event, - event->trigger_handle, - event->triggered_by_handle, - event->delayed_msg - ); - } - - event->delay_timestamp.invalidate(); - } -} - -CodeInjection EventUnhide__process_patch { +CodeInjection EventUnhide__process_patch{ 0x004BCDF0, [] (const auto& regs) { rf::Event* const event = regs.ecx; - event_process_fixed_delay(event); + rf::event_process_fixed_delay(event); } }; -CodeInjection EventAlarmSiren__process_patch { - 0x004BA880, - [] (const auto& regs) { - rf::Event* const event = regs.ecx; - event_process_fixed_delay(event); - } -}; - -CodeInjection EventMakeInvulnerable__process_patch { +CodeInjection EventMakeInvulnerable__process_patch{ 0x004BC8F0, [] (const auto& regs) { rf::Event* const event = regs.ecx; - event_process_fixed_delay(event); + rf::Event__process(event); } }; -CodeInjection EventCyclicTimer__process_patch { - 0x004BB7B0, - [] (const auto& regs) { - rf::Event* const event = regs.ecx; - event_process_fixed_delay(event); - } -}; - -CodeInjection EventPlaySound__process_patch { - 0x004BA570, - [] (const auto& regs) { - rf::Event* const event = regs.ecx; - event_process_fixed_delay(event); - } -}; +// These events are not implemented. +bool af_is_forward_exempt(const rf::EventType event_type) { + static const std::unordered_set forward_exempt_events{{ + rf::EventType::Set_Variable, + rf::EventType::Switch_Random, + rf::EventType::Difficulty_Gate, + rf::EventType::Sequence, + rf::EventType::Clear_Queued, + rf::EventType::Remove_Link, + rf::EventType::Add_Link, + rf::EventType::Valid_Gate, + rf::EventType::Goal_Gate, + rf::EventType::Scope_Gate, + rf::EventType::Inside_Gate, + rf::EventType::AF_When_Dead, + rf::EventType::Gametype_Gate, + rf::EventType::When_Picked_Up, + rf::EventType::Set_Entity_Flag, + rf::EventType::Light_State, + rf::EventType::World_HUD_Sprite, + rf::EventType::Set_Light_Color, + }}; + return forward_exempt_events.find(event_type) != forward_exempt_events.end(); +} CodeInjection event_type_forwards_messages_patch{ 0x004B8C44, [] (auto& regs) { if (rf::level.version >= 300) { const rf::EventType event_type = static_cast(regs.eax); - if (event_type == rf::EventType::Cyclic_Timer - && !AlpineLevelProps::instance().legacy_cyclic_timers) { + if (af_is_forward_exempt(event_type) + || (event_type == rf::EventType::Cyclic_Timer + && !AlpineLevelProps::instance().legacy_cyclic_timers)) { // Do not forward messages. regs.al = false; regs.eip = 0x004B8C5D; @@ -487,10 +424,7 @@ void apply_event_patches() // In AF levels, fix events that are broken, if `delay_timestamp` is set. EventUnhide__process_patch.install(); - EventAlarmSiren__process_patch.install(); EventMakeInvulnerable__process_patch.install(); - EventCyclicTimer__process_patch.install(); - EventPlaySound__process_patch.install(); // In AF levels, allow triggers to activate events in multiplayer. trigger_activate_linked_objects_activate_event_in_multi_patch.install(); diff --git a/game_patch/object/obj_light.cpp b/game_patch/object/obj_light.cpp index de8798bfd..be3f2f4e9 100644 --- a/game_patch/object/obj_light.cpp +++ b/game_patch/object/obj_light.cpp @@ -171,6 +171,16 @@ ConsoleCommand2 full_bright_entities_cmd{ "Toggle full-bright entities [may be force disabled by multiplayer servers]", }; +CodeInjection LevelLight__load_support_dynamic_lights_patch{ + 0x0045F500, + [] (auto& regs) { + // Will crash dedicated servers. + if (rf::level.version >= 300 && !rf::is_dedicated_server) { + regs.eip = 0x0045F507; + } + } +}; + void obj_light_apply_patch() { // Fix/improve items and clutters static lighting calculation: fix matrices being zero and use static lights @@ -190,4 +200,6 @@ void obj_light_apply_patch() // Support full-bright entities. init_mesh_dynamic_light_data_patch.install(); + + LevelLight__load_support_dynamic_lights_patch.install(); } diff --git a/game_patch/object/object.cpp b/game_patch/object/object.cpp index b941b7e71..507f087e0 100644 --- a/game_patch/object/object.cpp +++ b/game_patch/object/object.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include "../rf/object.h" #include "../rf/clutter.h" @@ -10,6 +11,7 @@ #include "../rf/level.h" #include "../rf/geometry.h" #include "../rf/math/ix.h" +#include "../misc/af_options.h" #include "object.h" #include "object_private.h" @@ -187,8 +189,19 @@ CodeInjection sort_clutter_patch{ FunHook obj_create_mesh_hook{ 0x00489FE0, - [](rf::Object* objp, const char* name, rf::VMeshType type) { - rf::VMesh* mesh = obj_create_mesh_hook.call_target(objp, name, type); + [] (rf::Object* const objp, const char* name, const rf::VMeshType type) { + const auto level = g_af_level_info_config + .mesh_replacements + .find(rf::level.filename); + if (level != g_af_level_info_config.mesh_replacements.end()) { + const auto& meshes = level->second; + const auto mesh = meshes.find(string_to_lower(name)); + if (mesh != meshes.end()) { + name = mesh->second.c_str(); + xlog::debug("Replacing mesh {} with {}", name, mesh->second); + } + } + rf::VMesh* const mesh = obj_create_mesh_hook.call_target(objp, name, type); if (mesh && (rf::level.flags & rf::LEVEL_LOADED) != 0) { obj_light_init_object(objp); } diff --git a/game_patch/object/particle.cpp b/game_patch/object/particle.cpp index 58a7c33bc..ca7044dd5 100644 --- a/game_patch/object/particle.cpp +++ b/game_patch/object/particle.cpp @@ -7,6 +7,8 @@ #include "../rf/particle_emitter.h" #include "../rf/geometry.h" #include "../rf/multi.h" +#include "../rf/level.h" +#include "../rf/player/camera.h" FunHook particle_emitter_create_hook{ 0x00497CA0, @@ -34,6 +36,50 @@ CodeInjection particle_update_accel_patch{ }, }; +CallHook particle_create_level_emitter_hook{ + 0x00496DBC, + [] ( + const int pool_id, + rf::ParticleCreateInfo& pci, + rf::GRoom* const room, + rf::Vector3* const a4, + const int parent_obj, + rf::Particle** const result, + rf::ParticleEmitter* const emitter + ) { + // Make particle emitters placed into levels respect their active distance parameter. + if (rf::level.version >= 300 + && !rf::is_server + && !rf::is_dedicated_server + && !parent_obj + && emitter->uid > 0 + ) { + const rf::Vector3 camera_pos = rf::camera_get_pos(rf::local_player->cam); + const float dist = (emitter->pos - camera_pos).len(); + if (emitter->active_distance != 0.f && emitter->active_distance <= dist) { + return; + } + } + particle_create_level_emitter_hook.call_target( + pool_id, + pci, + room, + a4, + parent_obj, + result, + emitter + ); + }, +}; + FunHook particle_create_hook{ 0x00496840, [](int pool_id, rf::ParticleCreateInfo& pci, rf::GRoom* room, rf::Vector3 *a4, int parent_obj, rf::Particle** result, rf::ParticleEmitter* emitter) { @@ -108,6 +154,9 @@ void particle_do_patch() // the searched list write_mem(0x00495158, asm_opcodes::jmp_rel_short); + // Support respecting a particle emitter's active distance parameter. + particle_create_level_emitter_hook.install(); + // Recude particle emitters maximal spawn rate particle_emitter_create_hook.install(); diff --git a/game_patch/rf/event.h b/game_patch/rf/event.h index 0d9894449..1d7ed5884 100644 --- a/game_patch/rf/event.h +++ b/game_patch/rf/event.h @@ -24,6 +24,8 @@ namespace rf }; static_assert(sizeof(Event) == 0x2B8); + static auto& Event__process = addr_as_ref(0x004B8CE0); + static auto& event_type_forwards_messages = addr_as_ref(0x004B8C40); static auto& event_lookup_from_uid = addr_as_ref(0x004B6820); @@ -121,7 +123,7 @@ namespace rf When_Life_Reaches, When_Armor_Reaches, Reverse_Mover, - // Alpine Faction events begin at 100. + // Alpine Faction 1.0 events. Set_Variable = 100, Clone_Entity, Set_Player_World_Collide, @@ -154,6 +156,7 @@ namespace rf Set_Entity_Flag, AF_Teleport_Player, Set_Item_Drop, + // Alpine Faction 1.1 events. AF_Heal, Anchor_Marker_Orient, Light_State, diff --git a/game_patch/rf/particle_emitter.h b/game_patch/rf/particle_emitter.h index ae539ccc2..4b3d5b33e 100644 --- a/game_patch/rf/particle_emitter.h +++ b/game_patch/rf/particle_emitter.h @@ -136,7 +136,7 @@ struct ParticleEmitter float time_to_change; float current_state_time; bool active; - int field_144; + int active_distance;; ParticleEmitter *next; ParticleEmitter *prev; ParticleEmitter *next_entity_emitter; From 478697f69efcf6096296f89e6d5fe3c183671b2d Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Sat, 18 Oct 2025 01:45:00 +1300 Subject: [PATCH 113/134] Update --- game_patch/misc/af_options.cpp | 4 ++-- game_patch/misc/af_options.h | 40 ++++++++++++++-------------------- 2 files changed, 18 insertions(+), 26 deletions(-) diff --git a/game_patch/misc/af_options.cpp b/game_patch/misc/af_options.cpp index 4193ef18b..1fbecbf7e 100644 --- a/game_patch/misc/af_options.cpp +++ b/game_patch/misc/af_options.cpp @@ -126,7 +126,7 @@ const std::unordered_map level_info_metadata = { }; // Load level info from filename_info.tbl -void load_level_info_config(const std::string& level_filename) +void load_af_level_info_config(const std::string& level_filename) { std::string base_filename = level_filename.substr(0, level_filename.size() - 4); std::string info_filename = base_filename + "_info.tbl"; @@ -211,7 +211,7 @@ void load_level_info_config(const std::string& level_filename) auto meta_it = level_info_metadata.find(option_name); if (meta_it != level_info_metadata.end()) { const auto& metadata = meta_it->second; - auto parsed_value = metadata.parse_function(option_value); + auto parsed_value = metadata.parse(option_value); if (parsed_value) { g_af_level_info_config.level_options[level_filename][metadata.id] = *parsed_value; xlog::debug("Parsed and applied {} for {}: {}", option_name, level_filename, option_value); diff --git a/game_patch/misc/af_options.h b/game_patch/misc/af_options.h index 159eb8dd3..a20e11395 100644 --- a/game_patch/misc/af_options.h +++ b/game_patch/misc/af_options.h @@ -6,12 +6,9 @@ #include #include -// ======= Globals and utility ======= -void load_level_info_config(const std::string& level_filename); +void load_af_level_info_config(const std::string& level_filename); -// ======= Alpine level info ======= -enum class AlpineLevelInfoId -{ +enum class AlpineLevelInfoId { LightmapClampFloor, LightmapClampCeiling, IdealPlayerCount, @@ -32,33 +29,28 @@ enum class AlpineLevelInfoId ChatMap8, ChatMap9, CraterTexturePpm, - _optioncount // dummy for total count + _last_variant }; -// Variant type to represent different configuration values +// Represents different configuration values. using LevelInfoValue = std::variant; -struct LevelInfoMetadata -{ - AlpineLevelInfoId id; - std::function(const std::string&)> parse_function; +struct LevelInfoMetadata { + AlpineLevelInfoId id{}; + std::function(const std::string&)> parse{}; }; -// Main configuration structure for level info -struct AlpineLevelInfoConfig -{ - // maps of level options and mesh replacements - std::unordered_map> level_options; - std::unordered_map> mesh_replacements; // stored lowercase +struct AlpineLevelInfoConfig { + std::unordered_map> level_options{}; + // `mesh_replacements` is lowercase. + std::unordered_map> mesh_replacements{}; - // Check if an option exists for a given level - bool is_option_loaded(const std::string& level, AlpineLevelInfoId option_id) const - { - auto level_it = level_options.find(level); - if (level_it != level_options.end()) { - return level_it->second.find(option_id) != level_it->second.end(); + bool is_option_loaded(const std::string& level, const AlpineLevelInfoId option_id) const { + const auto level = level_options.find(level); + if (level != level_options.end()) { + return level->second.find(option_id) != level->second.end(); } - return false; // no options loaded for this level + return false; } }; From ed5d48caf81cb33ef1ccb8cf322383836553c7ba Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Sat, 18 Oct 2025 01:55:13 +1300 Subject: [PATCH 114/134] Update --- docs/CHANGELOG.md | 3 +++ game_patch/misc/af_options.h | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 092ede50b..c7680f82c 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -9,6 +9,9 @@ Version 1.9.2 (not released yet) - Handle `AlpineLevelProps::legacy_cyclic_timers` in Alpine Faction levels - Allow triggers to activate events in multiplayer in Alpine Faction levels - Fix events that broke if their delay parameter was set in Alpine Faction levels +- Support loading Alpine Faction's `$Lightmap Clamp Floor`. and `$Lightmap Clamp Ceiling`, `$Crater Texture PPM`, and `$Mesh Replacement`from {map_name}_info.tbl +- Support dynamic lights +- Make particle emitters placed in levels respect their active distance parameter - Fix `PgUp`, `PgDown`, `End`, and `Home` on numeric keypads - Add `af_killsound1.wav` from Alpine Faction - Add `hit_sounds` command diff --git a/game_patch/misc/af_options.h b/game_patch/misc/af_options.h index a20e11395..784e8c3ca 100644 --- a/game_patch/misc/af_options.h +++ b/game_patch/misc/af_options.h @@ -45,8 +45,8 @@ struct AlpineLevelInfoConfig { // `mesh_replacements` is lowercase. std::unordered_map> mesh_replacements{}; - bool is_option_loaded(const std::string& level, const AlpineLevelInfoId option_id) const { - const auto level = level_options.find(level); + bool is_option_loaded(const std::string level_filename, const AlpineLevelInfoId option_id) const { + const auto level = level_options.find(level_filename); if (level != level_options.end()) { return level->second.find(option_id) != level->second.end(); } From 43cc27c3e99ce6ffacb30a966489cbd4f0040085 Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Sat, 18 Oct 2025 02:02:43 +1300 Subject: [PATCH 115/134] Update --- game_patch/object/event.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/game_patch/object/event.cpp b/game_patch/object/event.cpp index 60d4eb3e3..8b333e139 100644 --- a/game_patch/object/event.cpp +++ b/game_patch/object/event.cpp @@ -365,7 +365,7 @@ CodeInjection EventUnhide__process_patch{ 0x004BCDF0, [] (const auto& regs) { rf::Event* const event = regs.ecx; - rf::event_process_fixed_delay(event); + rf::Event__process(event); } }; From e98b3487c762f6a476494ade9ed654dfe22ec835 Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Sat, 18 Oct 2025 02:04:45 +1300 Subject: [PATCH 116/134] Update CHANGELOG for version 1.9.2 Consolidate Alpine Faction changes and features in changelog. --- docs/CHANGELOG.md | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index c7680f82c..3fc87ad95 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -5,13 +5,15 @@ Version 1.9.2 (not released yet) -------------------------------- [@is-this-c](https://github.com/is-this-c) - Fix a potential crash after a client quits a game if Directd3D 11 is enabled -- Improve compatibility with Alpine Faction network protocol -- Handle `AlpineLevelProps::legacy_cyclic_timers` in Alpine Faction levels -- Allow triggers to activate events in multiplayer in Alpine Faction levels -- Fix events that broke if their delay parameter was set in Alpine Faction levels -- Support loading Alpine Faction's `$Lightmap Clamp Floor`. and `$Lightmap Clamp Ceiling`, `$Crater Texture PPM`, and `$Mesh Replacement`from {map_name}_info.tbl -- Support dynamic lights -- Make particle emitters placed in levels respect their active distance parameter +- Features and changes derived from Alpine Faction's source code +- - Improve compatibility with Alpine Faction network protocol +- - In Alpine Faction levels +- - - Handle `AlpineLevelProps::legacy_cyclic_timers` +- - - Allow triggers to activate events in multiplayer +- - - Fix events that broke if their delay parameter was set +- - - Support loading Alpine Faction's `$Lightmap Clamp Floor`, and `$Lightmap Clamp Ceiling`, `$Crater Texture PPM`, and `$Mesh Replacement`from {map_name}_info.tbl +- - - Support dynamic lights +- - - Make particle emitters placed in levels respect their active distance parameter - Fix `PgUp`, `PgDown`, `End`, and `Home` on numeric keypads - Add `af_killsound1.wav` from Alpine Faction - Add `hit_sounds` command From 36aacbb25c13178984b1caf372a02b5983418096 Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Sat, 18 Oct 2025 02:06:51 +1300 Subject: [PATCH 117/134] Update --- game_patch/object/obj_light.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/game_patch/object/obj_light.cpp b/game_patch/object/obj_light.cpp index be3f2f4e9..b1c2dd58b 100644 --- a/game_patch/object/obj_light.cpp +++ b/game_patch/object/obj_light.cpp @@ -9,6 +9,7 @@ #include "../rf/clutter.h" #include "../rf/gr/gr.h" #include "../rf/multi.h" +#include "../rf/level.h" #include "../rf/crt.h" #include "../os/console.h" #include "../main/main.h" From c27159de238892bb03a8a154b6c9d6d6c49d1fa6 Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Sat, 18 Oct 2025 02:12:48 +1300 Subject: [PATCH 118/134] Update --- game_patch/object/particle.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/game_patch/object/particle.cpp b/game_patch/object/particle.cpp index ca7044dd5..56df6722c 100644 --- a/game_patch/object/particle.cpp +++ b/game_patch/object/particle.cpp @@ -9,6 +9,7 @@ #include "../rf/multi.h" #include "../rf/level.h" #include "../rf/player/camera.h" +#include "../rf/player/player.h" FunHook particle_emitter_create_hook{ 0x00497CA0, From 73bbe8b85d623876c1ac4cf8d1d2fad4663b83b8 Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Sat, 18 Oct 2025 02:31:04 +1300 Subject: [PATCH 119/134] Update --- game_patch/misc/af_options.h | 1 + game_patch/misc/g_solid.cpp | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/game_patch/misc/af_options.h b/game_patch/misc/af_options.h index 784e8c3ca..81ae7a066 100644 --- a/game_patch/misc/af_options.h +++ b/game_patch/misc/af_options.h @@ -5,6 +5,7 @@ #include #include #include +#include void load_af_level_info_config(const std::string& level_filename); diff --git a/game_patch/misc/g_solid.cpp b/game_patch/misc/g_solid.cpp index 4e9da0843..9bd0f8fa6 100644 --- a/game_patch/misc/g_solid.cpp +++ b/game_patch/misc/g_solid.cpp @@ -224,10 +224,10 @@ CodeInjection g_face_does_point_lie_in_face_crashfix{ rf::gr::Color color_from_u32(const uint32_t color) { return rf::gr::Color{ - (color >> 24) & 0xFF, - (color >> 16) & 0xFF, - (color >> 8) & 0xFF, - color & 0xFF + static_cast((color >> 24) & 0xFF), + static_cast((color >> 16) & 0xFF), + static_cast((color >> 8) & 0xFF), + static_cast(color & 0xFF) }; } From 980d6f241374f15b2055def9139ef240d82573be Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Sat, 18 Oct 2025 02:45:19 +1300 Subject: [PATCH 120/134] Update --- docs/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 3fc87ad95..87dff1e18 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -4,7 +4,6 @@ Dash Faction Changelog Version 1.9.2 (not released yet) -------------------------------- [@is-this-c](https://github.com/is-this-c) -- Fix a potential crash after a client quits a game if Directd3D 11 is enabled - Features and changes derived from Alpine Faction's source code - - Improve compatibility with Alpine Faction network protocol - - In Alpine Faction levels @@ -14,6 +13,7 @@ Version 1.9.2 (not released yet) - - - Support loading Alpine Faction's `$Lightmap Clamp Floor`, and `$Lightmap Clamp Ceiling`, `$Crater Texture PPM`, and `$Mesh Replacement`from {map_name}_info.tbl - - - Support dynamic lights - - - Make particle emitters placed in levels respect their active distance parameter +- Fix a potential crash after a client quits a game if Directd3D 11 is enabled - Fix `PgUp`, `PgDown`, `End`, and `Home` on numeric keypads - Add `af_killsound1.wav` from Alpine Faction - Add `hit_sounds` command From aa2c471b2e7122de8723418bf1e58916a086f4e4 Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Sat, 18 Oct 2025 03:09:57 +1300 Subject: [PATCH 121/134] Update --- game_patch/object/entity.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/game_patch/object/entity.cpp b/game_patch/object/entity.cpp index 9a73d4fef..c18925344 100644 --- a/game_patch/object/entity.cpp +++ b/game_patch/object/entity.cpp @@ -280,9 +280,9 @@ static FunHook entity_damage_hook{ CallHook entity_fire_primary_weapon_camera_shake_hook{ 0x00426C79, [] (rf::Camera* const cp, const float amplitude, const float time_seconds) { - const bool remote_force_ss = get_remote_server_info() + const bool force_screen_shake = get_remote_server_info() && !get_remote_server_info().value().allow_no_screen_shake; - if (g_game_config.weapon_screen_shake || remote_force_ss) { + if (g_game_config.weapon_screen_shake || force_screen_shake) { entity_fire_primary_weapon_camera_shake_hook.call_target(cp, amplitude, time_seconds); } }, @@ -306,9 +306,9 @@ ConsoleCommand2 weapon_screen_shake_cmd{ CallHook entity_update_muzzle_flash_light_hook{ 0x0041E814, [] (rf::Entity& ep) { - const bool remote_force_muzzle_flash = get_remote_server_info() + const bool force_muzzle_flash = get_remote_server_info() && !get_remote_server_info().value().allow_no_muzzle_flash; - if (g_game_config.muzzle_flash || remote_force_muzzle_flash) { + if (g_game_config.muzzle_flash || force_muzzle_flash) { entity_update_muzzle_flash_light_hook.call_target(ep); } }, From 5e1f1f60e0bedf76bbbc1abbd58d404c506b9f57 Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Sat, 18 Oct 2025 03:17:08 +1300 Subject: [PATCH 122/134] Update --- docs/CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 87dff1e18..22de68e8e 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -4,13 +4,13 @@ Dash Faction Changelog Version 1.9.2 (not released yet) -------------------------------- [@is-this-c](https://github.com/is-this-c) -- Features and changes derived from Alpine Faction's source code +- Changes derived from Alpine Faction's source code - - Improve compatibility with Alpine Faction network protocol - - In Alpine Faction levels - - - Handle `AlpineLevelProps::legacy_cyclic_timers` - - - Allow triggers to activate events in multiplayer - - - Fix events that broke if their delay parameter was set -- - - Support loading Alpine Faction's `$Lightmap Clamp Floor`, and `$Lightmap Clamp Ceiling`, `$Crater Texture PPM`, and `$Mesh Replacement`from {map_name}_info.tbl +- - - Support loading Alpine Faction's `$Lightmap Clamp Floor`, and `$Lightmap Clamp Ceiling`, `$Crater Texture PPM`, and `$Mesh Replacement`from `{map_name}_info.tbl` - - - Support dynamic lights - - - Make particle emitters placed in levels respect their active distance parameter - Fix a potential crash after a client quits a game if Directd3D 11 is enabled From 2562fe5df0ea74b9dbdf76866df538cc4c731e88 Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Sat, 18 Oct 2025 03:38:11 +1300 Subject: [PATCH 123/134] Update --- docs/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 22de68e8e..cbfb17596 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -4,7 +4,7 @@ Dash Faction Changelog Version 1.9.2 (not released yet) -------------------------------- [@is-this-c](https://github.com/is-this-c) -- Changes derived from Alpine Faction's source code +- Changes derived from [Alpine Faction's](https://github.com/GooberRF/alpinefaction) source code - - Improve compatibility with Alpine Faction network protocol - - In Alpine Faction levels - - - Handle `AlpineLevelProps::legacy_cyclic_timers` From 55764f316bf46aa1ff4868b8bc869303501d00d0 Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Sat, 18 Oct 2025 17:25:36 +1300 Subject: [PATCH 124/134] Update --- game_patch/object/object.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/game_patch/object/object.cpp b/game_patch/object/object.cpp index 507f087e0..d741405be 100644 --- a/game_patch/object/object.cpp +++ b/game_patch/object/object.cpp @@ -190,12 +190,12 @@ CodeInjection sort_clutter_patch{ FunHook obj_create_mesh_hook{ 0x00489FE0, [] (rf::Object* const objp, const char* name, const rf::VMeshType type) { - const auto level = g_af_level_info_config + const std::input_iterator auto level = g_af_level_info_config .mesh_replacements .find(rf::level.filename); if (level != g_af_level_info_config.mesh_replacements.end()) { const auto& meshes = level->second; - const auto mesh = meshes.find(string_to_lower(name)); + const std::input_iterator auto mesh = meshes.find(string_to_lower(name)); if (mesh != meshes.end()) { name = mesh->second.c_str(); xlog::debug("Replacing mesh {} with {}", name, mesh->second); From e6388dbf6fcf98a644820e459a1ab049e8961d65 Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Mon, 27 Oct 2025 18:26:43 +1300 Subject: [PATCH 125/134] Rename variables and add enum `ServerSoftware` --- game_patch/multi/multi.h | 7 ++++++- game_patch/multi/network.cpp | 5 +++-- game_patch/object/object.cpp | 16 +++++++++------- game_patch/rf/particle_emitter.h | 2 +- 4 files changed, 19 insertions(+), 11 deletions(-) diff --git a/game_patch/multi/multi.h b/game_patch/multi/multi.h index 413982757..b667de628 100644 --- a/game_patch/multi/multi.h +++ b/game_patch/multi/multi.h @@ -73,6 +73,11 @@ struct PlayerStatsNew : rf::PlayerLevelStats } }; +enum class ServerSoftware { + DashFaction, + AlpineFaction, +}; + struct RemoteServerInfo { struct { uint8_t major = 0; @@ -80,7 +85,7 @@ struct RemoteServerInfo { } version{}; bool saving = false; std::optional max_fov{}; - bool alpine_faction = false; + ServerSoftware software = ServerSoftware::DashFaction; bool allow_full_bright_entities = true; bool allow_light_maps_only = false; bool allow_no_screen_shake = true; diff --git a/game_patch/multi/network.cpp b/game_patch/multi/network.cpp index 09f033c8a..851305b74 100644 --- a/game_patch/multi/network.cpp +++ b/game_patch/multi/network.cpp @@ -780,6 +780,7 @@ bool parse_df_join_accept_tail(const std::byte* const tail) { if (!!(ext.flags & Flags::max_fov) && ext.max_fov >= default_fov) { remote_server_info.max_fov.emplace(ext.max_fov); } + remote_server_info.software = ServerSoftware::DashFaction; g_remote_server_info.emplace(remote_server_info); return true; } @@ -806,7 +807,7 @@ bool parse_af_join_accept_tail(const std::byte* const tail) { if (!!(ext.flags & Flags::max_fov) && ext.max_fov >= default_fov) { remote_server_info.max_fov.emplace(ext.max_fov); } - remote_server_info.alpine_faction = true; + remote_server_info.software = ServerSoftware::AlpineFaction; remote_server_info.allow_full_bright_entities = !!(ext.flags & Flags::allow_fb_mesh); remote_server_info.allow_light_maps_only = !!(ext.flags & Flags::allow_lmap); remote_server_info.allow_no_screen_shake = !!(ext.flags & Flags::allow_no_ss); @@ -843,7 +844,7 @@ ConsoleCommand2 remote_server_flags_cmd{ rf::console::print("===================="); rf::console::print( "{} Faction {}.{}", - remote_server_info.alpine_faction ? "Alpine" : "Dash", + remote_server_info.software == ServerSoftware::AlpineFaction ? "Alpine" : "Dash", remote_server_info.version.major, remote_server_info.version.minor ); diff --git a/game_patch/object/object.cpp b/game_patch/object/object.cpp index d741405be..4189eda04 100644 --- a/game_patch/object/object.cpp +++ b/game_patch/object/object.cpp @@ -190,15 +190,17 @@ CodeInjection sort_clutter_patch{ FunHook obj_create_mesh_hook{ 0x00489FE0, [] (rf::Object* const objp, const char* name, const rf::VMeshType type) { - const std::input_iterator auto level = g_af_level_info_config + const auto mesh_replacements_iter = g_af_level_info_config .mesh_replacements .find(rf::level.filename); - if (level != g_af_level_info_config.mesh_replacements.end()) { - const auto& meshes = level->second; - const std::input_iterator auto mesh = meshes.find(string_to_lower(name)); - if (mesh != meshes.end()) { - name = mesh->second.c_str(); - xlog::debug("Replacing mesh {} with {}", name, mesh->second); + if (mesh_replacements_iter != g_af_level_info_config.mesh_replacements.end()) { + const std::unordered_map& mesh_replacements = + mesh_replacements_iter->second; + const auto mesh_replacement_iter = mesh_replacements + .find(string_to_lower(name)); + if (mesh_replacement_iter != mesh_replacements.end()) { + name = mesh_replacement_iter->second.c_str(); + xlog::debug("Replacing mesh {} with {}", name, mesh_replacement_iter->second); } } rf::VMesh* const mesh = obj_create_mesh_hook.call_target(objp, name, type); diff --git a/game_patch/rf/particle_emitter.h b/game_patch/rf/particle_emitter.h index 4b3d5b33e..ed0f12a96 100644 --- a/game_patch/rf/particle_emitter.h +++ b/game_patch/rf/particle_emitter.h @@ -136,7 +136,7 @@ struct ParticleEmitter float time_to_change; float current_state_time; bool active; - int active_distance;; + int active_distance; ParticleEmitter *next; ParticleEmitter *prev; ParticleEmitter *next_entity_emitter; From b1a9c9e6ac0d376d3ea84498da5f36ff5d2cd475 Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Mon, 27 Oct 2025 19:15:25 +1300 Subject: [PATCH 126/134] Update comment --- game_patch/object/event.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/game_patch/object/event.cpp b/game_patch/object/event.cpp index 8b333e139..ed166ee06 100644 --- a/game_patch/object/event.cpp +++ b/game_patch/object/event.cpp @@ -377,8 +377,9 @@ CodeInjection EventMakeInvulnerable__process_patch{ } }; -// These events are not implemented. bool af_is_forward_exempt(const rf::EventType event_type) { + // These Alpine Faction events are forward exempt. + // They are not however implemented. static const std::unordered_set forward_exempt_events{{ rf::EventType::Set_Variable, rf::EventType::Switch_Random, From 6a49e64821b31f76ad4beaa1768fc92df98ddb97 Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Mon, 27 Oct 2025 23:32:45 +1300 Subject: [PATCH 127/134] SImplify `AlpineLevelInfoConfig` --- game_patch/misc/af_options.cpp | 12 +++++++++--- game_patch/misc/af_options.h | 23 +++++++++++++---------- game_patch/misc/g_solid.cpp | 2 -- game_patch/misc/level.cpp | 3 +-- game_patch/object/object.cpp | 17 ++++++----------- 5 files changed, 29 insertions(+), 28 deletions(-) diff --git a/game_patch/misc/af_options.cpp b/game_patch/misc/af_options.cpp index 1fbecbf7e..d0c5c3dd1 100644 --- a/game_patch/misc/af_options.cpp +++ b/game_patch/misc/af_options.cpp @@ -128,6 +128,13 @@ const std::unordered_map level_info_metadata = { // Load level info from filename_info.tbl void load_af_level_info_config(const std::string& level_filename) { + if (g_alpine_level_info_config.current_level == level_filename) { + xlog::debug("Reusing previously loaded mapname_info.tbl settings for {}", level_filename); + return; + } + + g_alpine_level_info_config.reset_for_level(level_filename); + std::string base_filename = level_filename.substr(0, level_filename.size() - 4); std::string info_filename = base_filename + "_info.tbl"; auto level_info_file = std::make_unique(); @@ -196,8 +203,7 @@ void load_af_level_info_config(const std::string& level_filename) if (option_name == "$Mesh Replacement") { auto parsed_value = parse_mesh_replacement(option_value); if (parsed_value) { - g_af_level_info_config.mesh_replacements[level_filename][string_to_lower(parsed_value->first)]= parsed_value->second; - + g_af_level_info_config.mesh_replacements[string_to_lower(parsed_value->first)] = parsed_value->second; xlog::debug("Mesh Replacement Added: {} -> {} in {}", parsed_value->first, parsed_value->second, level_filename); } else { @@ -213,7 +219,7 @@ void load_af_level_info_config(const std::string& level_filename) const auto& metadata = meta_it->second; auto parsed_value = metadata.parse(option_value); if (parsed_value) { - g_af_level_info_config.level_options[level_filename][metadata.id] = *parsed_value; + g_af_level_info_config.level_options[metadata.id] = *parsed_value; xlog::debug("Parsed and applied {} for {}: {}", option_name, level_filename, option_value); } } diff --git a/game_patch/misc/af_options.h b/game_patch/misc/af_options.h index 81ae7a066..eac48f942 100644 --- a/game_patch/misc/af_options.h +++ b/game_patch/misc/af_options.h @@ -42,16 +42,19 @@ struct LevelInfoMetadata { }; struct AlpineLevelInfoConfig { - std::unordered_map> level_options{}; - // `mesh_replacements` is lowercase. - std::unordered_map> mesh_replacements{}; - - bool is_option_loaded(const std::string level_filename, const AlpineLevelInfoId option_id) const { - const auto level = level_options.find(level_filename); - if (level != level_options.end()) { - return level->second.find(option_id) != level->second.end(); - } - return false; + std::string current_level{}; + std::unordered_map level_options{}; + // `mesh_replacements` is lower case. + std::unordered_map mesh_replacements{}; + + void reset_for_level(const std::string& level) { + current_level = level; + level_options.clear(); + mesh_replacements.clear(); + } + + bool is_option_loaded(const AlpineLevelInfoId option_id) const { + return level_options.find(option_id) != level_options.end(); } }; diff --git a/game_patch/misc/g_solid.cpp b/game_patch/misc/g_solid.cpp index 9bd0f8fa6..7a9a21591 100644 --- a/game_patch/misc/g_solid.cpp +++ b/game_patch/misc/g_solid.cpp @@ -255,7 +255,6 @@ CodeInjection level_load_lightmaps_color_conv_patch{ floor_clamp = std::get( g_af_level_info_config .level_options - .at(rf::level.filename) .at(AlpineLevelInfoId::LightmapClampFloor) ); is_floor_clamp_defined = true; @@ -267,7 +266,6 @@ CodeInjection level_load_lightmaps_color_conv_patch{ ceiling_clamp = std::get( g_af_level_info_config .level_options - .at(rf::level.filename) .at(AlpineLevelInfoId::LightmapClampCeiling) ); should_clamp = true; diff --git a/game_patch/misc/level.cpp b/game_patch/misc/level.cpp index 798a16f28..0e03caa19 100644 --- a/game_patch/misc/level.cpp +++ b/game_patch/misc/level.cpp @@ -136,11 +136,10 @@ CodeInjection level_blast_ppm_injection{ void set_levelmod_autotexture_ppm() { if (g_af_level_info_config - .is_option_loaded(rf::level.filename, AlpineLevelInfoId::CraterTexturePpm)) { + .is_option_loaded(rAlpineLevelInfoId::CraterTexturePpm)) { g_crater_autotexture_ppm = std::get( g_af_level_info_config .level_options - .at(rf::level.filename) .at(AlpineLevelInfoId::CraterTexturePpm) ); } else { diff --git a/game_patch/object/object.cpp b/game_patch/object/object.cpp index 4189eda04..0a17fd724 100644 --- a/game_patch/object/object.cpp +++ b/game_patch/object/object.cpp @@ -190,18 +190,13 @@ CodeInjection sort_clutter_patch{ FunHook obj_create_mesh_hook{ 0x00489FE0, [] (rf::Object* const objp, const char* name, const rf::VMeshType type) { - const auto mesh_replacements_iter = g_af_level_info_config + const auto mesh_replacement_iter = g_af_level_info_config .mesh_replacements - .find(rf::level.filename); - if (mesh_replacements_iter != g_af_level_info_config.mesh_replacements.end()) { - const std::unordered_map& mesh_replacements = - mesh_replacements_iter->second; - const auto mesh_replacement_iter = mesh_replacements - .find(string_to_lower(name)); - if (mesh_replacement_iter != mesh_replacements.end()) { - name = mesh_replacement_iter->second.c_str(); - xlog::debug("Replacing mesh {} with {}", name, mesh_replacement_iter->second); - } + .find(string_to_lower(name)); + if (mesh_replacement_iter != mesh_replacements.end()) { + const std::string& mesh_replacement = mesh_replacement_iter->second; + xlog::debug("Replacing mesh {} with {}", name, mesh_replacement); + name = mesh_replacement.c_str(); } rf::VMesh* const mesh = obj_create_mesh_hook.call_target(objp, name, type); if (mesh && (rf::level.flags & rf::LEVEL_LOADED) != 0) { From eb7ff991a5c9b0cddd1b5d9b2c4de2f0d4635d5d Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Mon, 27 Oct 2025 23:41:45 +1300 Subject: [PATCH 128/134] Fix build error --- game_patch/misc/g_solid.cpp | 4 ++-- game_patch/misc/level.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/game_patch/misc/g_solid.cpp b/game_patch/misc/g_solid.cpp index 7a9a21591..ce8dc9974 100644 --- a/game_patch/misc/g_solid.cpp +++ b/game_patch/misc/g_solid.cpp @@ -251,7 +251,7 @@ CodeInjection level_load_lightmaps_color_conv_patch{ bool is_floor_clamp_defined = false; if (g_af_level_info_config - .is_option_loaded(rf::level.filename, AlpineLevelInfoId::LightmapClampFloor)) { + .is_option_loaded(AlpineLevelInfoId::LightmapClampFloor)) { floor_clamp = std::get( g_af_level_info_config .level_options @@ -262,7 +262,7 @@ CodeInjection level_load_lightmaps_color_conv_patch{ } if (g_af_level_info_config - .is_option_loaded(rf::level.filename, AlpineLevelInfoId::LightmapClampCeiling)) { + .is_option_loaded(AlpineLevelInfoId::LightmapClampCeiling)) { ceiling_clamp = std::get( g_af_level_info_config .level_options diff --git a/game_patch/misc/level.cpp b/game_patch/misc/level.cpp index 0e03caa19..cb0a7ee57 100644 --- a/game_patch/misc/level.cpp +++ b/game_patch/misc/level.cpp @@ -136,7 +136,7 @@ CodeInjection level_blast_ppm_injection{ void set_levelmod_autotexture_ppm() { if (g_af_level_info_config - .is_option_loaded(rAlpineLevelInfoId::CraterTexturePpm)) { + .is_option_loaded(AlpineLevelInfoId::CraterTexturePpm)) { g_crater_autotexture_ppm = std::get( g_af_level_info_config .level_options From 0fed1c8d69fc4161dae823e5e699fde42e766844 Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Mon, 27 Oct 2025 23:46:33 +1300 Subject: [PATCH 129/134] Fix build error --- game_patch/object/object.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/game_patch/object/object.cpp b/game_patch/object/object.cpp index 0a17fd724..8c8685b84 100644 --- a/game_patch/object/object.cpp +++ b/game_patch/object/object.cpp @@ -193,7 +193,7 @@ FunHook obj_create_mesh_hoo const auto mesh_replacement_iter = g_af_level_info_config .mesh_replacements .find(string_to_lower(name)); - if (mesh_replacement_iter != mesh_replacements.end()) { + if (mesh_replacement_iter != mesh_replacement_iter.end()) { const std::string& mesh_replacement = mesh_replacement_iter->second; xlog::debug("Replacing mesh {} with {}", name, mesh_replacement); name = mesh_replacement.c_str(); From f8fec289ddfd35d7f708bff914fd02cdd8f3c2df Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Mon, 27 Oct 2025 23:50:33 +1300 Subject: [PATCH 130/134] Fix build error --- game_patch/object/object.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/game_patch/object/object.cpp b/game_patch/object/object.cpp index 8c8685b84..64670613b 100644 --- a/game_patch/object/object.cpp +++ b/game_patch/object/object.cpp @@ -193,10 +193,9 @@ FunHook obj_create_mesh_hoo const auto mesh_replacement_iter = g_af_level_info_config .mesh_replacements .find(string_to_lower(name)); - if (mesh_replacement_iter != mesh_replacement_iter.end()) { - const std::string& mesh_replacement = mesh_replacement_iter->second; - xlog::debug("Replacing mesh {} with {}", name, mesh_replacement); - name = mesh_replacement.c_str(); + if (mesh_replacement_iter != g_af_level_info_config.mesh_replacements.end()) { + xlog::debug("Replacing mesh {} with {}", name, mesh_replacement_iter->second); + name = mesh_replacement_iter->second; } rf::VMesh* const mesh = obj_create_mesh_hook.call_target(objp, name, type); if (mesh && (rf::level.flags & rf::LEVEL_LOADED) != 0) { From 742863506f87ee00a5184d8deeb86528e23992f1 Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Mon, 27 Oct 2025 23:50:52 +1300 Subject: [PATCH 131/134] Fix build error --- game_patch/object/object.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/game_patch/object/object.cpp b/game_patch/object/object.cpp index 64670613b..0dcce7afa 100644 --- a/game_patch/object/object.cpp +++ b/game_patch/object/object.cpp @@ -195,7 +195,7 @@ FunHook obj_create_mesh_hoo .find(string_to_lower(name)); if (mesh_replacement_iter != g_af_level_info_config.mesh_replacements.end()) { xlog::debug("Replacing mesh {} with {}", name, mesh_replacement_iter->second); - name = mesh_replacement_iter->second; + name = mesh_replacement_iter->second.c_str(); } rf::VMesh* const mesh = obj_create_mesh_hook.call_target(objp, name, type); if (mesh && (rf::level.flags & rf::LEVEL_LOADED) != 0) { From 61a39ffe20ef6af8ce86eba0c2966498e83f090b Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Mon, 27 Oct 2025 23:54:26 +1300 Subject: [PATCH 132/134] Fix build error --- game_patch/misc/af_options.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/game_patch/misc/af_options.cpp b/game_patch/misc/af_options.cpp index d0c5c3dd1..5f71c7adc 100644 --- a/game_patch/misc/af_options.cpp +++ b/game_patch/misc/af_options.cpp @@ -128,12 +128,12 @@ const std::unordered_map level_info_metadata = { // Load level info from filename_info.tbl void load_af_level_info_config(const std::string& level_filename) { - if (g_alpine_level_info_config.current_level == level_filename) { + if (g_af_level_info_config.current_level == level_filename) { xlog::debug("Reusing previously loaded mapname_info.tbl settings for {}", level_filename); return; } - g_alpine_level_info_config.reset_for_level(level_filename); + g_af_level_info_config.reset_for_level(level_filename); std::string base_filename = level_filename.substr(0, level_filename.size() - 4); std::string info_filename = base_filename + "_info.tbl"; From ffc489a3d04b55aa17aa8957fd7741c1082a13e2 Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Thu, 27 Nov 2025 19:26:27 +1300 Subject: [PATCH 133/134] use `rf::gr::Color::from_hex` --- game_patch/misc/g_solid.cpp | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/game_patch/misc/g_solid.cpp b/game_patch/misc/g_solid.cpp index ce8dc9974..d2df3b0a9 100644 --- a/game_patch/misc/g_solid.cpp +++ b/game_patch/misc/g_solid.cpp @@ -222,15 +222,6 @@ CodeInjection g_face_does_point_lie_in_face_crashfix{ }, }; -rf::gr::Color color_from_u32(const uint32_t color) { - return rf::gr::Color{ - static_cast((color >> 24) & 0xFF), - static_cast((color >> 16) & 0xFF), - static_cast((color >> 8) & 0xFF), - static_cast(color & 0xFF) - }; -} - CodeInjection level_load_lightmaps_color_conv_patch{ 0x004ED3E9, [](auto& regs) { @@ -283,8 +274,8 @@ CodeInjection level_load_lightmaps_color_conv_patch{ xlog::debug("Applying lightmap clamping"); // Extract RGB components from clamping values. - const rf::gr::Color floor = color_from_u32(floor_clamp); - const rf::gr::Color ceiling = color_from_u32(ceiling_clamp); + const rf::gr::Color floor = rf::gr::Color::from_hex(floor_clamp); + const rf::gr::Color ceiling = rf::gr::Color::from_hex(ceiling_clamp); for (int i = 0; i < lightmap->w * lightmap->h * 3; i += 3) { // Apply floor clamp. From c614aa516893d4e2c1af1c6870fbbd0020e85a8a Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Thu, 27 Nov 2025 19:57:31 +1300 Subject: [PATCH 134/134] Fix build error --- game_patch/misc/g_solid.cpp | 28 ++++++++++++---------------- game_patch/rf/gr/gr.h | 9 +++++++++ 2 files changed, 21 insertions(+), 16 deletions(-) diff --git a/game_patch/misc/g_solid.cpp b/game_patch/misc/g_solid.cpp index d2df3b0a9..32d26002f 100644 --- a/game_patch/misc/g_solid.cpp +++ b/game_patch/misc/g_solid.cpp @@ -239,7 +239,7 @@ CodeInjection level_load_lightmaps_color_conv_patch{ // No ceiling. uint32_t ceiling_clamp = 0xFFFFFFFF; bool should_clamp = false; - bool is_floor_clamp_defined = false; + bool floor_clamp_defined = false; if (g_af_level_info_config .is_option_loaded(AlpineLevelInfoId::LightmapClampFloor)) { @@ -248,7 +248,7 @@ CodeInjection level_load_lightmaps_color_conv_patch{ .level_options .at(AlpineLevelInfoId::LightmapClampFloor) ); - is_floor_clamp_defined = true; + floor_clamp_defined = true; should_clamp = true; } @@ -262,7 +262,7 @@ CodeInjection level_load_lightmaps_color_conv_patch{ should_clamp = true; } - if (!is_floor_clamp_defined + if (!floor_clamp_defined && rf::level.version < 300 && !DashLevelProps::instance().lightmaps_full_depth) { should_clamp = true; @@ -272,21 +272,17 @@ CodeInjection level_load_lightmaps_color_conv_patch{ if (should_clamp) { xlog::debug("Applying lightmap clamping"); - - // Extract RGB components from clamping values. - const rf::gr::Color floor = rf::gr::Color::from_hex(floor_clamp); - const rf::gr::Color ceiling = rf::gr::Color::from_hex(ceiling_clamp); + + const rf::gr::Color floor = rf::gr::Color::from_u32(floor_clamp); + const rf::gr::Color ceiling = rf::gr::Color::from_u32(ceiling_clamp); for (int i = 0; i < lightmap->w * lightmap->h * 3; i += 3) { - // Apply floor clamp. - lightmap->buf[i] = std::max(lightmap->buf[i], floor.red); - lightmap->buf[i + 1] = std::max(lightmap->buf[i + 1], floor.green); - lightmap->buf[i + 2] = std::max(lightmap->buf[i + 2], floor.blue); - - // Apply ceiling clamp. - lightmap->buf[i] = std::min(lightmap->buf[i], ceiling.red); - lightmap->buf[i + 1] = std::min(lightmap->buf[i + 1], ceiling.green); - lightmap->buf[i + 2] = std::min(lightmap->buf[i + 2], ceiling.blue); + lightmap->buf[i] = + std::clamp(lightmap->buf[i], floor.red, ceiling.red); + lightmap->buf[i + 1] = + std::clamp(lightmap->buf[i + 1], floor.green, ceiling.green); + lightmap->buf[i + 2] = + std::clamp(lightmap->buf[i + 2], floor.blue, ceiling.blue); } } diff --git a/game_patch/rf/gr/gr.h b/game_patch/rf/gr/gr.h index 76dbd3d93..12b6aee1f 100644 --- a/game_patch/rf/gr/gr.h +++ b/game_patch/rf/gr/gr.h @@ -26,6 +26,15 @@ namespace rf::gr this->alpha = a; } + static Color from_u32(const uint32_t color) { + return Color{ + static_cast((color >> 24) & 0xFF), + static_cast((color >> 16) & 0xFF), + static_cast((color >> 8) & 0xFF), + static_cast(color & 0xFF) + }; + } + bool operator==(const Color& other) const = default; };