From 50fb4d6f2e7466b50a08a9f29dd3b1c788c040e0 Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Sat, 29 Aug 2026 17:06:07 +1200 Subject: [PATCH 01/11] Trim in `gr_fit_string` --- game_patch/graphics/gr_font.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/game_patch/graphics/gr_font.cpp b/game_patch/graphics/gr_font.cpp index c5cde238..ea3e112f 100644 --- a/game_patch/graphics/gr_font.cpp +++ b/game_patch/graphics/gr_font.cpp @@ -623,7 +623,7 @@ int gr_fit_string( return text_w; } - while (text_w + suffix_w > max_width && !text.empty()) { + while ((text_w + suffix_w > max_width || text.back() == ' ') && !text.empty()) { const auto [last_w, last_h] = rf::gr::get_char_size(text.back(), font_id); text_w -= last_w; text.pop_back(); From 56a5a73e1a69725cf454467f2f4e6652a20140db Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Sat, 29 Aug 2026 17:07:13 +1200 Subject: [PATCH 02/11] Improve `dot_cmd` --- game_patch/os/commands.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/game_patch/os/commands.cpp b/game_patch/os/commands.cpp index 540762d7..b958dec8 100644 --- a/game_patch/os/commands.cpp +++ b/game_patch/os/commands.cpp @@ -29,9 +29,9 @@ ConsoleCommand2 dot_cmd{ if (string_icontains(cmd->name, pattern) || (cmd->help && string_icontains(cmd->help, pattern))) { if (cmd->help) { - rf::console::print("{} - {}", cmd->name, cmd->help); + rf::console::print(" {} - {}", cmd->name, cmd->help); } else { - rf::console::print("{}", cmd->name); + rf::console::print(" {}", cmd->name); } } } From 72e5c4c45486f5476df6862b36c954a35ed810ee Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Sat, 29 Aug 2026 17:07:42 +1200 Subject: [PATCH 03/11] Add `print` --- game_patch/rf/os/console.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/game_patch/rf/os/console.h b/game_patch/rf/os/console.h index 2677862e..796add61 100644 --- a/game_patch/rf/os/console.h +++ b/game_patch/rf/os/console.h @@ -65,6 +65,16 @@ namespace rf::console output(s.c_str(), nullptr); } + template + inline void print( + const rf::gr::Color& color, + const std::format_string fmt, + Args&&... args + ) { + const std::string s = std::format(fmt, std::forward(args)...); + output(s.c_str(), &color); + } + //static auto& commands = addr_as_ref(0x01775530); static auto& num_commands = addr_as_ref(0x0177567C); From e46573cc311de8a3df0448aa89ce9bc03bcbe750 Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Thu, 27 Aug 2026 20:04:06 +1200 Subject: [PATCH 04/11] Use GLOB_RECURSE for EXPERIMENTAL_SRCS --- 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 1997655b..4dbe623a 100644 --- a/game_patch/CMakeLists.txt +++ b/game_patch/CMakeLists.txt @@ -1,4 +1,4 @@ -file(GLOB EXPERIMENTAL_SRCS experimental/*.cpp experimental/*.h) +file(GLOB_RECURSE EXPERIMENTAL_SRCS experimental/*.cpp experimental/*.h) set(SRCS main/main.cpp From bd5b294244c52cc98c827d56d2140a1dc9a5ac2d Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Sat, 29 Aug 2026 16:59:37 +1200 Subject: [PATCH 05/11] Update key.cpp --- game_patch/input/key.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/game_patch/input/key.cpp b/game_patch/input/key.cpp index 108b333c..0e4a5b6f 100644 --- a/game_patch/input/key.cpp +++ b/game_patch/input/key.cpp @@ -94,6 +94,8 @@ FunHook key_to_ascii_hook{ if (key & rf::KEY_CTRLED) { key_state[VK_CONTROL] = 0x80; } + // HACKFIX. Must be set for `ToUnicode` to produce capitalized letters. + key_state[VK_CAPITAL] = GetKeyState(VK_CAPITAL) & 1; int scan_code = key & 0x7F; auto vk = MapVirtualKeyA(scan_code, MAPVK_VSC_TO_VK); WCHAR unicode_chars[3]; From 8509f4900d09276e2c7bcf98f78c827bd1d2255e Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Sun, 30 Aug 2026 05:33:58 +1200 Subject: [PATCH 06/11] Update CHANGELOG.md --- docs/CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 0f9c8e4a..36c6601f 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -15,6 +15,9 @@ Version 1.5.0 (TBD): Not yet released [@GooberRF](https://github.com/GooberRF) - Fix phantom visual flag mesh being visible after Salvage flag is picked up on rare occasions +[@is-this-c](https://github.com/is-this-c) +- Support `Caps Lock` in multi hud chat + Version 1.4.0 (Lupin): Released Aug-25-2026 -------------------------------- ### Major features From cb28665eb3e75d4474277c604970d253e58f7dec Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Sun, 30 Aug 2026 16:32:05 +1200 Subject: [PATCH 07/11] Update dedi_cfg.cpp --- game_patch/multi/dedi_cfg.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/game_patch/multi/dedi_cfg.cpp b/game_patch/multi/dedi_cfg.cpp index 41e0547e..0eb6e956 100644 --- a/game_patch/multi/dedi_cfg.cpp +++ b/game_patch/multi/dedi_cfg.cpp @@ -2138,11 +2138,13 @@ void print_alpine_dedicated_server_config_info(std::string& output, bool verbose std::format_to(iter, " Levels in rotation: {}\n", cfg.levels.size()); std::format_to(iter, " Dynamic rotation: {}\n", cfg.dynamic_rotation); std::format_to(iter, " Demo auto record: {}\n", cfg.demo_auto_record); - std::format_to(iter, " Demo chat record: {}\n", cfg.demo_chat_record); - std::format_to(iter, " FactionFiles demo upload: {}\n", cfg.fflink_demo_upload); - std::format_to(iter, " FactionFiles demo max MB: {}\n", cfg.fflink_demo_max_mb); - std::format_to(iter, " FactionFiles demo queue max: {}\n", cfg.fflink_demo_queue_max); - std::format_to(iter, " FactionFiles demo delete after send: {}\n", cfg.fflink_demo_delete_after_send); + if (cfg.demo_auto_record) { + std::format_to(iter, " Demo chat record: {}\n", cfg.demo_chat_record); + std::format_to(iter, " FactionFiles demo upload: {}\n", cfg.fflink_demo_upload); + std::format_to(iter, " FactionFiles demo max MB: {}\n", cfg.fflink_demo_max_mb); + std::format_to(iter, " FactionFiles demo queue max: {}\n", cfg.fflink_demo_queue_max); + std::format_to(iter, " FactionFiles demo delete after send: {}\n", cfg.fflink_demo_delete_after_send); + } if (rf::mod_param.found()) { std::format_to(iter, " TC mod loaded: {}\n", rf::mod_param.get_arg()); From fbe29ae8d6e374cadeab633b004cc74a8ee9c9d7 Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Sun, 30 Aug 2026 16:33:03 +1200 Subject: [PATCH 08/11] 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 36c6601f..9f46422f 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -16,7 +16,7 @@ Version 1.5.0 (TBD): Not yet released - Fix phantom visual flag mesh being visible after Salvage flag is picked up on rare occasions [@is-this-c](https://github.com/is-this-c) -- Support `Caps Lock` in multi hud chat +- Support `Caps Lock` in the console and multi hud chat Version 1.4.0 (Lupin): Released Aug-25-2026 -------------------------------- From 60c5e6fc3310a4ac58d2f84ba318935c736ca21b Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Sun, 30 Aug 2026 16:54:16 +1200 Subject: [PATCH 09/11] Update dedi_cfg.cpp --- game_patch/multi/dedi_cfg.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/game_patch/multi/dedi_cfg.cpp b/game_patch/multi/dedi_cfg.cpp index 0eb6e956..ed9cb01e 100644 --- a/game_patch/multi/dedi_cfg.cpp +++ b/game_patch/multi/dedi_cfg.cpp @@ -2137,13 +2137,15 @@ void print_alpine_dedicated_server_config_info(std::string& output, bool verbose std::format_to(iter, " Max players: {}\n", netgame.max_players); std::format_to(iter, " Levels in rotation: {}\n", cfg.levels.size()); std::format_to(iter, " Dynamic rotation: {}\n", cfg.dynamic_rotation); - std::format_to(iter, " Demo auto record: {}\n", cfg.demo_auto_record); + std::format_to(iter, " Auto-record demos: {}\n", cfg.demo_auto_record); if (cfg.demo_auto_record) { - std::format_to(iter, " Demo chat record: {}\n", cfg.demo_chat_record); - std::format_to(iter, " FactionFiles demo upload: {}\n", cfg.fflink_demo_upload); - std::format_to(iter, " FactionFiles demo max MB: {}\n", cfg.fflink_demo_max_mb); - std::format_to(iter, " FactionFiles demo queue max: {}\n", cfg.fflink_demo_queue_max); - std::format_to(iter, " FactionFiles demo delete after send: {}\n", cfg.fflink_demo_delete_after_send); + std::format_to(iter, " Include chat: {}\n", cfg.demo_chat_record); + std::format_to(iter, " Upload to FactionFiles: {}\n", cfg.fflink_demo_upload); + if (cfg.fflink_demo_upload) { + std::format_to(iter, " Max size: {} MiB\n", cfg.fflink_demo_max_mb); + std::format_to(iter, " Max queue: {}\n", cfg.fflink_demo_queue_max); + std::format_to(iter, " Delete after send: {}\n", cfg.fflink_demo_delete_after_send); + } } if (rf::mod_param.found()) { From f630a1569f7614e400b3aa1dcd21d52d4893337a Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Mon, 31 Aug 2026 01:05:04 +1200 Subject: [PATCH 10/11] Fix --- docs/CHANGELOG.md | 2 +- game_patch/graphics/gr_font.cpp | 2 +- game_patch/multi/dedi_cfg.cpp | 17 ++++++++--------- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 9f46422f..c32d0839 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -16,7 +16,7 @@ Version 1.5.0 (TBD): Not yet released - Fix phantom visual flag mesh being visible after Salvage flag is picked up on rare occasions [@is-this-c](https://github.com/is-this-c) -- Support `Caps Lock` in the console and multi hud chat +- Let `Caps Lock` capitalize Version 1.4.0 (Lupin): Released Aug-25-2026 -------------------------------- diff --git a/game_patch/graphics/gr_font.cpp b/game_patch/graphics/gr_font.cpp index ea3e112f..9c382254 100644 --- a/game_patch/graphics/gr_font.cpp +++ b/game_patch/graphics/gr_font.cpp @@ -623,7 +623,7 @@ int gr_fit_string( return text_w; } - while ((text_w + suffix_w > max_width || text.back() == ' ') && !text.empty()) { + while (!text.empty() && (text_w + suffix_w > max_width || text.back() == ' ')) { const auto [last_w, last_h] = rf::gr::get_char_size(text.back(), font_id); text_w -= last_w; text.pop_back(); diff --git a/game_patch/multi/dedi_cfg.cpp b/game_patch/multi/dedi_cfg.cpp index ed9cb01e..709306f3 100644 --- a/game_patch/multi/dedi_cfg.cpp +++ b/game_patch/multi/dedi_cfg.cpp @@ -2137,15 +2137,14 @@ void print_alpine_dedicated_server_config_info(std::string& output, bool verbose std::format_to(iter, " Max players: {}\n", netgame.max_players); std::format_to(iter, " Levels in rotation: {}\n", cfg.levels.size()); std::format_to(iter, " Dynamic rotation: {}\n", cfg.dynamic_rotation); - std::format_to(iter, " Auto-record demos: {}\n", cfg.demo_auto_record); - if (cfg.demo_auto_record) { - std::format_to(iter, " Include chat: {}\n", cfg.demo_chat_record); - std::format_to(iter, " Upload to FactionFiles: {}\n", cfg.fflink_demo_upload); - if (cfg.fflink_demo_upload) { - std::format_to(iter, " Max size: {} MiB\n", cfg.fflink_demo_max_mb); - std::format_to(iter, " Max queue: {}\n", cfg.fflink_demo_queue_max); - std::format_to(iter, " Delete after send: {}\n", cfg.fflink_demo_delete_after_send); - } + std::format_to(iter, " Demos:\n"); + std::format_to(iter, " Auto-record: {}\n", cfg.demo_auto_record); + std::format_to(iter, " Include chat: {}\n", cfg.demo_chat_record); + std::format_to(iter, " Upload to FactionFiles: {}\n", cfg.fflink_demo_upload); + if (cfg.fflink_demo_upload) { + std::format_to(iter, " Max size: {} MiB\n", cfg.fflink_demo_max_mb); + std::format_to(iter, " Max queue: {}\n", cfg.fflink_demo_queue_max); + std::format_to(iter, " Delete after send: {}\n", cfg.fflink_demo_delete_after_send); } if (rf::mod_param.found()) { From 97c7e431ec6dfedf9872cef76111df320e141add Mon Sep 17 00:00:00 2001 From: is-this-c <87069698+is-this-c@users.noreply.github.com> Date: Mon, 31 Aug 2026 02:58:59 +1200 Subject: [PATCH 11/11] Update dedi_cfg.cpp --- game_patch/multi/dedi_cfg.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/game_patch/multi/dedi_cfg.cpp b/game_patch/multi/dedi_cfg.cpp index 709306f3..203b3e1e 100644 --- a/game_patch/multi/dedi_cfg.cpp +++ b/game_patch/multi/dedi_cfg.cpp @@ -2138,13 +2138,13 @@ void print_alpine_dedicated_server_config_info(std::string& output, bool verbose std::format_to(iter, " Levels in rotation: {}\n", cfg.levels.size()); std::format_to(iter, " Dynamic rotation: {}\n", cfg.dynamic_rotation); std::format_to(iter, " Demos:\n"); - std::format_to(iter, " Auto-record: {}\n", cfg.demo_auto_record); std::format_to(iter, " Include chat: {}\n", cfg.demo_chat_record); - std::format_to(iter, " Upload to FactionFiles: {}\n", cfg.fflink_demo_upload); + std::format_to(iter, " Auto-record: {}\n", cfg.demo_auto_record); + std::format_to(iter, " Upload to FactionFiles: {}\n", cfg.fflink_demo_upload); if (cfg.fflink_demo_upload) { - std::format_to(iter, " Max size: {} MiB\n", cfg.fflink_demo_max_mb); - std::format_to(iter, " Max queue: {}\n", cfg.fflink_demo_queue_max); - std::format_to(iter, " Delete after send: {}\n", cfg.fflink_demo_delete_after_send); + std::format_to(iter, " Max size: {} MiB\n", cfg.fflink_demo_max_mb); + std::format_to(iter, " Max queue: {}\n", cfg.fflink_demo_queue_max); + std::format_to(iter, " Delete after send: {}\n", cfg.fflink_demo_delete_after_send); } if (rf::mod_param.found()) {