Revamps speech impeding effects (#93067)

## About The Pull Request

Redoes drunk slurring and stuttering

Drunk slurring is a bit more tame now - it will no longer make your
sentence completely illegible by nuking half the letters for
apostrophes, now instead making it illegible through normal slurring.
Instead it's focused more about creating longer, drawn out words

```
Lanny Kirkson slurs, "I'm drrrunk, what'ch yooouuurr exkoose?"
```

Stuttering no longer targets letters in the middle of the sentence - it
now only stutters the first letter, or first two letters in the case of
digraphs

```
Lanny Kirkson stammers, "H-holy sh-sh-shit y-you sh-shot me w-with a B-BSA I-I-I-I'm g-g-gonna D-D-DIE!!"
Lanny Kirkson stammers, "I-I-I-I N-N-NEED H-HELP!"
```

Cult and heretic slurring is tangentially affected by the change in
logic, but in testing it was not noticeably different - however if too
many cult callouts get fully understood we can just bump the
probabilities up.

## Why It's Good For The Game

These effects are primarily intended for flavor but end up mangling your
sentences to a frankly excessive degree. Reigning these in produces much
more readable, aesthetically pleasing, and dare I say immersive results

## Changelog

🆑 Melbert
qol: Reworked stammering. Only the first character is stammered, and
digraphs are accounted for.
qol: Reworked drunken slurring. No longer inserts a bunch of random
apostrophes, now creates more + longer drawn out words.
/🆑
This commit is contained in:
MrMelbert
2025-09-22 07:49:02 -05:00
committed by GitHub
parent 9b91bb9be4
commit 9a0ee5ec67
2 changed files with 90 additions and 65 deletions

View File

@@ -33,11 +33,11 @@
return
var/final_phrase = ""
var/original_char = ""
for(var/i = 1, i <= length(phrase), i += length(original_char))
original_char = phrase[i]
final_phrase += apply_speech(original_char)
var/list/words = splittext(phrase, " ")
var/list/new_words = list()
for(var/i in 1 to length(words))
new_words += apply_speech(words[i], i)
final_phrase = jointext(new_words, " ")
if(final_phrase == phrase)
return // No change was done, whatever
@@ -51,13 +51,13 @@
/**
* Applies the speech effects on the past character, changing
* the original_char into the modified_char.
* the original_word into some modified_word
*
* Return the modified_char to be reapplied to the message.
* Return the newly modified word
*/
/datum/status_effect/speech/proc/apply_speech(original_char)
/datum/status_effect/speech/proc/apply_speech(original_word, index)
stack_trace("[type] didn't implement apply_speech.")
return original_char
return original_word
/datum/status_effect/speech/stutter
id = "stutter"
@@ -65,33 +65,41 @@
tts_filter = "tremolo=f=10:d=0.8,rubberband=tempo=0.5"
/// The probability of adding a stutter to any character
var/stutter_prob = 80
var/stutter_prob = 75
/// The chance of a four character stutter
var/four_char_chance = 10
/// The chance of a three character stutter
var/three_char_chance = 20
/// The chance of a two character stutter
var/two_char_chance = 95
/// Regex of characters we won't apply a stutter to
var/static/regex/no_stutter
var/two_char_chance = 90
/// Regex to apply generically to stuttered words
/// * 1st capture group is any leading invalid characters (can be empty)
/// * 2nd capture group is either a digraph (th, qu, ch) or a consonant
/// * 3rd capture group is the rest of the word (can be empty)
VAR_FINAL/static/regex/stutter_regex
/datum/status_effect/speech/stutter/on_creation(mob/living/new_owner, ...)
. = ..()
if(!.)
return
if(!no_stutter)
no_stutter = regex(@@[aeiouAEIOU ""''()[\]{}.!?,:;_`~-]@)
stutter_regex ||= regex(@@^([\s"'()[\]{}.!?,:;_`~-]*\b)([^aeoiuh\d]h|qu|[^\d])(.*)@, "i")
return ..()
/datum/status_effect/speech/stutter/apply_speech(original_char)
if(prob(stutter_prob) && !no_stutter.Find(original_char))
if(prob(four_char_chance))
return "[original_char]-[original_char]-[original_char]-[original_char]"
if(prob(three_char_chance))
return "[original_char]-[original_char]-[original_char]"
if(prob(two_char_chance))
return "[original_char]-[original_char]"
/datum/status_effect/speech/stutter/apply_speech(original_word, index)
if(!prob(stutter_prob))
return original_word
return original_char
if(stutter_regex.Find(original_word))
return "[stutter_regex.group[1]][stutter_char(stutter_regex.group[2])][stutter_regex.group[3]]"
return original_word // i give up
/datum/status_effect/speech/stutter/proc/stutter_char(some_char)
if(prob(four_char_chance))
return "[some_char]-[some_char]-[some_char]-[some_char]"
if(prob(three_char_chance))
return "[some_char]-[some_char]-[some_char]"
if(prob(two_char_chance))
return "[some_char]-[some_char]"
return some_char
/datum/status_effect/speech/stutter/anxiety
id = "anxiety_stutter"
@@ -133,7 +141,7 @@
if(prob(capitalize_prob))
var/exclamation = pick("!", "!!", "!!!")
message = uppertext(message)
message += "[apply_speech(exclamation, exclamation)]"
message += "[stutter_char(exclamation)]"
message_args[TREAT_MESSAGE_ARG] = message
@@ -170,6 +178,12 @@
/// Strings that are appended to a character - populated in on_creation
var/list/string_additions
/// Regex for characters we won't apply any slurring to
var/static/regex/no_slur
/// If the last character we processed was a replacement, don't do another replacement right after it
VAR_PRIVATE/replacement_dupe_check = FALSE
/datum/status_effect/speech/slurring/on_creation(mob/living/new_owner, duration = 10 SECONDS)
. = ..()
if(!.)
@@ -184,46 +198,57 @@
string_replacements = speech_changes["string_replacements"]
string_additions = speech_changes["string_additions"]
/datum/status_effect/speech/slurring/apply_speech(original_char)
no_slur ||= regex(@@[ "'()[\]{}.!?,:;_`~-]@)
/datum/status_effect/speech/slurring/apply_speech(original_word, index)
var/original_char = ""
var/modified_word = ""
for(var/i = 1, i <= length(original_word), i += length(original_char))
original_char = original_word[i]
modified_word += slur_character(original_char, i)
return modified_word
/datum/status_effect/speech/slurring/proc/slur_character(original_char, index)
var/modified_char = original_char
var/allow_slurring = index != 1 && !no_slur.Find(modified_char)
var/lower_char = LOWER_TEXT(modified_char)
if(prob(common_prob) && (lower_char in common_replacements))
var/to_replace = common_replacements[lower_char]
if(islist(to_replace))
modified_char = pick(to_replace)
else
modified_char = to_replace
modified_char = islist(to_replace) ? pick(to_replace) : to_replace
replacement_dupe_check = FALSE
if(prob(uncommon_prob) && (modified_char in uncommon_replacements))
else if(prob(uncommon_prob) && (modified_char in uncommon_replacements))
var/to_replace = uncommon_replacements[modified_char]
if(islist(to_replace))
modified_char = pick(to_replace)
else
modified_char = to_replace
modified_char = islist(to_replace) ? pick(to_replace) : to_replace
replacement_dupe_check = FALSE
if(prob(replacement_prob))
var/replacements_len = length(string_replacements)
var/additions_len = length(string_additions)
if(replacements_len && additions_len)
// Calculate the probability of grabbing a replacement vs an addition
var/weight = (replacements_len + additions_len) / replacements_len * 100
if(prob(weight))
else if(allow_slurring) // Don't do replacements on the first character, or punctuation
if(!replacement_dupe_check && prob(replacement_prob))
var/replacements_len = length(string_replacements)
var/additions_len = length(string_additions)
if(replacements_len && additions_len)
// Calculate the probability of grabbing a replacement vs an addition
var/weight = (replacements_len + additions_len) / replacements_len * 100
if(prob(weight))
modified_char = pick(string_replacements)
else
modified_char += pick(string_additions)
else if(replacements_len)
modified_char = pick(string_replacements)
else
else if(additions_len)
modified_char += pick(string_additions)
replacement_dupe_check = TRUE
else if(replacements_len)
modified_char = pick(string_replacements)
else if(prob(doubletext_prob))
modified_char += "[modified_char][prob(50) ? "" : modified_char]"
replacement_dupe_check = FALSE
else if(additions_len)
modified_char += pick(string_additions)
if(prob(doubletext_prob))
if(prob(50))
modified_char += "[modified_char]"
else
modified_char += "[modified_char][modified_char]"
else
// If we don't allow replacements we don't want the next character to be a replacement either
// This makes some more coherent speech by disallowing structures like "Y'''e"
replacement_dupe_check = TRUE
return modified_char
@@ -231,8 +256,8 @@
id = "generic_slurring"
common_prob = 33
uncommon_prob = 0
replacement_prob = 5
doubletext_prob = 10
replacement_prob = 24
doubletext_prob = 40
text_modification_file = "slurring_drunk_text.json"
/datum/status_effect/speech/slurring/drunk
@@ -248,13 +273,13 @@
var/current_drunkness = owner.get_drunk_amount()
// These numbers are arbitarily picked
// Common replacements start at about 20, and maxes out at about 85
common_prob = clamp((current_drunkness * 0.8) - 16, 0, 50)
common_prob = clamp((current_drunkness * 0.8) - 16, 4, 50)
// Uncommon replacements (burping) start at 50 and max out at 110 (when you are dying)
uncommon_prob = clamp((current_drunkness * 0.2) - 10, 0, 12)
uncommon_prob = clamp((current_drunkness * 0.3) - 10, 0, 12)
// Replacements start at 20 and max out at about 60
replacement_prob = clamp((current_drunkness * 0.4) - 8, 0, 12)
replacement_prob = clamp((current_drunkness * 0.6) - 8, 0, 12)
// Double texting start out at about 25 and max out at about 60
doubletext_prob = clamp((current_drunkness * 0.5) - 12, 0, 20)
doubletext_prob = clamp((current_drunkness * 0.8) - 8, 2, 50)
return ..()
/datum/status_effect/speech/slurring/cult

View File

@@ -10,9 +10,9 @@
},
"uncommon": {
" ": "...huuuhhh...",
".": " *BURP*."
".": "... *BURP*.",
",": ", *hic* "
}
},
"string_additions": ["'"]
}
}
}