From 7b1be281ea146ed39b00a3726ac084a5074334d2 Mon Sep 17 00:00:00 2001 From: WisoAltred Date: Wed, 28 Jan 2026 15:04:01 +0200 Subject: [PATCH 1/8] Change pause special character behavior (\p) to allow multiplications instead of having to spam it. Adds a parser to any numbers after a detected \p for pause chat message, matches it upto a given max to be multiplied to a given base const. (The regex will limit this to 1000 regardless, but the const is hard-coded either way.). ordered them according to the pattern in courtroom.h but it would be nicer if they were not separated. Also skips by the given amount of characters by calculating it via the text pos, so \p20 would skip 4, \p200 would skip 5, etc. Currently technically silently fails if the user tries doing -1 or \p50000 or so, so it spits out any numbers beyond the initial 4 digits to at least indicate that. --- src/courtroom.cpp | 37 ++++++++++++++++++++++++++++++++++++- src/courtroom.h | 7 +++++++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/src/courtroom.cpp b/src/courtroom.cpp index 7646794da..ea7b39ce4 100644 --- a/src/courtroom.cpp +++ b/src/courtroom.cpp @@ -3506,6 +3506,31 @@ void Courtroom::handle_ic_speaking() start_chat_ticking(); } +struct PauseInfo +{ + int multiplier; + int digit_count; +}; + + +// returns multiplier and number of digits to skip +static PauseInfo parse_pause_multiplier(const QString &text, int start_pos) +{ + + // matches upto 999 (and 1000) and also prevents leading zeros + static QRegularExpression pause_regex("^([1-9]\\d{0,2}|1000)"); + QRegularExpressionMatch match = pause_regex.match(text.mid(start_pos)); + + if (match.hasMatch()) + { + int value = match.captured(1).toInt(); + int length = match.capturedLength(0); + return {value, length}; + } + + return {1, 0}; // default: multiplier=1, no digits to skip +} + QString Courtroom::filter_ic_text(QString p_text, bool html, int target_pos, int default_color) { QString p_text_escaped; @@ -3723,6 +3748,12 @@ QString Courtroom::filter_ic_text(QString p_text, bool html, int target_pos, int if (f_character == "s" || f_character == "f" || f_character == "p") // screenshake/flash/pause { skip = true; + // also skip any following digits + if (f_character == "p") + { + PauseInfo info = parse_pause_multiplier(p_text, check_pos + f_char_bytes); + check_pos += info.digit_count; + } } parse_escape_seq = false; @@ -4201,6 +4232,7 @@ void Courtroom::start_chat_ticking() tick_pos = 0; blip_ticker = 0; + pause_multiplier = 1; text_crawl = Options::getInstance().textCrawlSpeed(); blip_rate = Options::getInstance().blipRate(); blank_blip = Options::getInstance().blankBlip(); @@ -4420,6 +4452,9 @@ void Courtroom::chat_tick() if (f_character == "p") { formatting_char = true; + PauseInfo info = parse_pause_multiplier(f_message, tick_pos); + pause_multiplier = info.multiplier; + tick_pos += info.digit_count; } next_character_is_not_special = false; } @@ -4443,7 +4478,7 @@ void Courtroom::chat_tick() { if (f_character == "p") { - chat_tick_timer->start(100); // wait the pause lol + chat_tick_timer->start(pause_base_ms * pause_multiplier); } else { diff --git a/src/courtroom.h b/src/courtroom.h index dc8cdf3ae..242154803 100644 --- a/src/courtroom.h +++ b/src/courtroom.h @@ -378,6 +378,8 @@ class Courtroom : public QMainWindow int real_tick_pos = 0; // used to determine how often blips sound int blip_ticker = 0; + // pause multiplier for \p{numbers} + int pause_multiplier = 1; int blip_rate = 2; int rainbow_counter = 0; bool rainbow_appended = false; @@ -450,6 +452,11 @@ class Courtroom : public QMainWindow // amount by which we multiply the delay when we parse punctuation chars const int punctuation_modifier = 3; + // maximum pause multiplier for \p{numbers} so it does not just pause forever + const int pause_multiplier_max = 1000; + // base pause duration for a \p with no digits + const int pause_base_ms = 100; + // amount of ghost blocks int ghost_blocks = 0; From 72de2833c9921d0481530124d461ad7cfbe9325c Mon Sep 17 00:00:00 2001 From: WisoAltred Date: Wed, 28 Jan 2026 15:17:24 +0200 Subject: [PATCH 2/8] Format format --- src/courtroom.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/courtroom.cpp b/src/courtroom.cpp index ea7b39ce4..ca718c556 100644 --- a/src/courtroom.cpp +++ b/src/courtroom.cpp @@ -3516,19 +3516,16 @@ struct PauseInfo // returns multiplier and number of digits to skip static PauseInfo parse_pause_multiplier(const QString &text, int start_pos) { - // matches upto 999 (and 1000) and also prevents leading zeros static QRegularExpression pause_regex("^([1-9]\\d{0,2}|1000)"); QRegularExpressionMatch match = pause_regex.match(text.mid(start_pos)); - if (match.hasMatch()) { int value = match.captured(1).toInt(); int length = match.capturedLength(0); return {value, length}; } - - return {1, 0}; // default: multiplier=1, no digits to skip + return {1, 0}; // default: multiplier=1, no digits to skip } QString Courtroom::filter_ic_text(QString p_text, bool html, int target_pos, int default_color) From 3bd664687c1cd17ba0faae058557b45baceee41e Mon Sep 17 00:00:00 2001 From: WisoAltred Date: Wed, 28 Jan 2026 15:18:33 +0200 Subject: [PATCH 3/8] format 2 format 2 --- src/courtroom.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/courtroom.cpp b/src/courtroom.cpp index ca718c556..8a6dcb0d1 100644 --- a/src/courtroom.cpp +++ b/src/courtroom.cpp @@ -3512,7 +3512,6 @@ struct PauseInfo int digit_count; }; - // returns multiplier and number of digits to skip static PauseInfo parse_pause_multiplier(const QString &text, int start_pos) { From bf7c3fe3233bac2993927ded6fca9c4bc3254788 Mon Sep 17 00:00:00 2001 From: Wiso <61384316+WisoAltred@users.noreply.github.com> Date: Thu, 29 Jan 2026 21:17:47 +0200 Subject: [PATCH 4/8] numbers denote ms instead of multiplier also removes unnecessary variables, as well as adds validation fix for commit so it doesn't ruin history --- src/courtroom.cpp | 52 ++++++++++++++++++++++++++--------------------- src/courtroom.h | 6 +----- 2 files changed, 30 insertions(+), 28 deletions(-) diff --git a/src/courtroom.cpp b/src/courtroom.cpp index 8a6dcb0d1..d49e3be54 100644 --- a/src/courtroom.cpp +++ b/src/courtroom.cpp @@ -3508,23 +3508,28 @@ void Courtroom::handle_ic_speaking() struct PauseInfo { - int multiplier; + int ms; int digit_count; + bool valid; }; -// returns multiplier and number of digits to skip -static PauseInfo parse_pause_multiplier(const QString &text, int start_pos) +static PauseInfo parse_pause_duration(const QString &text, int start_pos) { - // matches upto 999 (and 1000) and also prevents leading zeros - static QRegularExpression pause_regex("^([1-9]\\d{0,2}|1000)"); + static const int max_digits = QString::number(10000).length(); + static const QRegularExpression pause_regex(QString("^([1-9]\\d{0,%1})").arg(max_digits - 1)); QRegularExpressionMatch match = pause_regex.match(text.mid(start_pos)); - if (match.hasMatch()) + if (!match.hasMatch()) { - int value = match.captured(1).toInt(); - int length = match.capturedLength(0); - return {value, length}; + return {0, 0, false}; } - return {1, 0}; // default: multiplier=1, no digits to skip + bool ok = false; + int value = match.captured(1).toInt(&ok); + int length = match.capturedLength(0); + if (!ok || value < 1 || value > 10000) + { + return {0, 0, false}; + } + return {value, length, true}; } QString Courtroom::filter_ic_text(QString p_text, bool html, int target_pos, int default_color) @@ -3744,11 +3749,13 @@ QString Courtroom::filter_ic_text(QString p_text, bool html, int target_pos, int if (f_character == "s" || f_character == "f" || f_character == "p") // screenshake/flash/pause { skip = true; - // also skip any following digits - if (f_character == "p") + if (f_character == "p") // also skip any following digits { - PauseInfo info = parse_pause_multiplier(p_text, check_pos + f_char_bytes); - check_pos += info.digit_count; + PauseInfo info = parse_pause_duration(p_text, check_pos + f_char_bytes); + if (info.valid) + { + check_pos += info.digit_count; + } } } @@ -4228,7 +4235,6 @@ void Courtroom::start_chat_ticking() tick_pos = 0; blip_ticker = 0; - pause_multiplier = 1; text_crawl = Options::getInstance().textCrawlSpeed(); blip_rate = Options::getInstance().blipRate(); blank_blip = Options::getInstance().blankBlip(); @@ -4448,9 +4454,14 @@ void Courtroom::chat_tick() if (f_character == "p") { formatting_char = true; - PauseInfo info = parse_pause_multiplier(f_message, tick_pos); - pause_multiplier = info.multiplier; - tick_pos += info.digit_count; + PauseInfo info = parse_pause_duration(f_message, tick_pos); + if (info.valid) + { + tick_pos += info.digit_count; + real_tick_pos += f_char_length; + chat_tick_timer->start(info.ms); + return; + } } next_character_is_not_special = false; } @@ -4472,11 +4483,6 @@ void Courtroom::chat_tick() if ((msg_delay <= 0 && tick_pos < f_message.size() - 1) || formatting_char) { - if (f_character == "p") - { - chat_tick_timer->start(pause_base_ms * pause_multiplier); - } - else { chat_tick_timer->start(0); // Don't bother rendering anything out as we're // doing the SPEED. (there's latency otherwise) diff --git a/src/courtroom.h b/src/courtroom.h index 242154803..e672eaf14 100644 --- a/src/courtroom.h +++ b/src/courtroom.h @@ -378,8 +378,6 @@ class Courtroom : public QMainWindow int real_tick_pos = 0; // used to determine how often blips sound int blip_ticker = 0; - // pause multiplier for \p{numbers} - int pause_multiplier = 1; int blip_rate = 2; int rainbow_counter = 0; bool rainbow_appended = false; @@ -453,9 +451,7 @@ class Courtroom : public QMainWindow const int punctuation_modifier = 3; // maximum pause multiplier for \p{numbers} so it does not just pause forever - const int pause_multiplier_max = 1000; - // base pause duration for a \p with no digits - const int pause_base_ms = 100; + const int max_pause_duration = 10000; // amount of ghost blocks int ghost_blocks = 0; From b2e42529f5fae244bbfbdf16e0ff9b701217a216 Mon Sep 17 00:00:00 2001 From: Wiso <61384316+WisoAltred@users.noreply.github.com> Date: Thu, 29 Jan 2026 23:03:54 +0200 Subject: [PATCH 5/8] Simpler parse function now defaults to 1s on \p with no numbers Co-Authored-By: Leifa <26681464+TrickyLeifa@users.noreply.github.com> --- src/courtroom.cpp | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/src/courtroom.cpp b/src/courtroom.cpp index d49e3be54..0df6180e4 100644 --- a/src/courtroom.cpp +++ b/src/courtroom.cpp @@ -3515,21 +3515,20 @@ struct PauseInfo static PauseInfo parse_pause_duration(const QString &text, int start_pos) { - static const int max_digits = QString::number(10000).length(); - static const QRegularExpression pause_regex(QString("^([1-9]\\d{0,%1})").arg(max_digits - 1)); - QRegularExpressionMatch match = pause_regex.match(text.mid(start_pos)); - if (!match.hasMatch()) + int pos = start_pos; + while (pos < text.length() && text[pos].isDigit()) { - return {0, 0, false}; + pos++; } - bool ok = false; - int value = match.captured(1).toInt(&ok); - int length = match.capturedLength(0); - if (!ok || value < 1 || value > 10000) + + if (pos == start_pos) { - return {0, 0, false}; + return {1000, 0, true}; } - return {value, length, true}; + + bool ok; + int value = qMin(10000, text.mid(start_pos, pos - start_pos).toInt(&ok)); + return ok ? PauseInfo{value, pos - start_pos, true} : PauseInfo{0, 0, false}; } QString Courtroom::filter_ic_text(QString p_text, bool html, int target_pos, int default_color) From 99f0d738bd24de0ceaccb65436295f261ab51464 Mon Sep 17 00:00:00 2001 From: WisoAltred Date: Mon, 2 Feb 2026 09:26:06 +0200 Subject: [PATCH 6/8] Change validation from bool check to std optional the voices will tell you this is more "idiomatic" and "readable" --- src/courtroom.cpp | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/src/courtroom.cpp b/src/courtroom.cpp index 0df6180e4..16b4ef9a2 100644 --- a/src/courtroom.cpp +++ b/src/courtroom.cpp @@ -3510,10 +3510,9 @@ struct PauseInfo { int ms; int digit_count; - bool valid; }; -static PauseInfo parse_pause_duration(const QString &text, int start_pos) +static std::optional parse_pause_duration(const QString &text, int start_pos) { int pos = start_pos; while (pos < text.length() && text[pos].isDigit()) @@ -3523,12 +3522,12 @@ static PauseInfo parse_pause_duration(const QString &text, int start_pos) if (pos == start_pos) { - return {1000, 0, true}; + return PauseInfo{1000, 0}; } bool ok; int value = qMin(10000, text.mid(start_pos, pos - start_pos).toInt(&ok)); - return ok ? PauseInfo{value, pos - start_pos, true} : PauseInfo{0, 0, false}; + return ok ? std::optional{PauseInfo{value, pos - start_pos}} : std::nullopt; } QString Courtroom::filter_ic_text(QString p_text, bool html, int target_pos, int default_color) @@ -3750,10 +3749,9 @@ QString Courtroom::filter_ic_text(QString p_text, bool html, int target_pos, int skip = true; if (f_character == "p") // also skip any following digits { - PauseInfo info = parse_pause_duration(p_text, check_pos + f_char_bytes); - if (info.valid) + if (auto info = parse_pause_duration(p_text, check_pos + f_char_bytes)) { - check_pos += info.digit_count; + check_pos += info->digit_count; } } } @@ -4453,12 +4451,11 @@ void Courtroom::chat_tick() if (f_character == "p") { formatting_char = true; - PauseInfo info = parse_pause_duration(f_message, tick_pos); - if (info.valid) + if (auto info = parse_pause_duration(f_message, tick_pos)) { - tick_pos += info.digit_count; + tick_pos += info->digit_count; real_tick_pos += f_char_length; - chat_tick_timer->start(info.ms); + chat_tick_timer->start(info->ms); return; } } From 98f2e4dc4518bd527a862a9962e9ad01b08399b3 Mon Sep 17 00:00:00 2001 From: WisoAltred Date: Mon, 2 Feb 2026 09:34:53 +0200 Subject: [PATCH 7/8] doesnt need to be explicit --- src/courtroom.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/courtroom.cpp b/src/courtroom.cpp index 16b4ef9a2..1cc57cf20 100644 --- a/src/courtroom.cpp +++ b/src/courtroom.cpp @@ -3525,9 +3525,8 @@ static std::optional parse_pause_duration(const QString &text, int st return PauseInfo{1000, 0}; } - bool ok; - int value = qMin(10000, text.mid(start_pos, pos - start_pos).toInt(&ok)); - return ok ? std::optional{PauseInfo{value, pos - start_pos}} : std::nullopt; + int value = qMin(10000, text.mid(start_pos, pos - start_pos).toInt()); + return std::optional{PauseInfo{value, pos - start_pos}}; } QString Courtroom::filter_ic_text(QString p_text, bool html, int target_pos, int default_color) From 0204796e02f93407e84343b6d0e77b8b6a69080b Mon Sep 17 00:00:00 2001 From: Wiso <61384316+WisoAltred@users.noreply.github.com> Date: Thu, 5 Feb 2026 20:33:15 +0200 Subject: [PATCH 8/8] Don't need max int anymore. --- src/courtroom.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/courtroom.h b/src/courtroom.h index e672eaf14..dc8cdf3ae 100644 --- a/src/courtroom.h +++ b/src/courtroom.h @@ -450,9 +450,6 @@ class Courtroom : public QMainWindow // amount by which we multiply the delay when we parse punctuation chars const int punctuation_modifier = 3; - // maximum pause multiplier for \p{numbers} so it does not just pause forever - const int max_pause_duration = 10000; - // amount of ghost blocks int ghost_blocks = 0;