diff --git a/code/__DEFINES/combat.dm b/code/__DEFINES/combat.dm
index 6e317a7a08a..193daf37c67 100644
--- a/code/__DEFINES/combat.dm
+++ b/code/__DEFINES/combat.dm
@@ -54,8 +54,6 @@
#define EFFECT_UNCONSCIOUS "unconscious"
#define EFFECT_PARALYZE "paralyze"
#define EFFECT_IMMOBILIZE "immobilize"
-#define EFFECT_STUTTER "stutter"
-#define EFFECT_SLUR "slur"
#define EFFECT_EYE_BLUR "eye_blur"
#define EFFECT_DROWSY "drowsy"
#define EFFECT_JITTER "jitter"
diff --git a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_living.dm b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_living.dm
index a39377c7c50..de9f1997a61 100644
--- a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_living.dm
+++ b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_living.dm
@@ -89,5 +89,8 @@
#define COMSIG_LIVING_Z_IMPACT "living_z_impact"
#define NO_Z_IMPACT_DAMAGE (1<<0)
+/// From mob/living/treat_message(): (list/message_args)
+#define COMSIG_LIVING_TREAT_MESSAGE "living_treat_message"
+
///From obj/item/toy/crayon/spraycan
#define COMSIG_LIVING_MOB_PAINTED "living_mob_painted"
diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm
index 68e863459ba..f58f97725eb 100644
--- a/code/__DEFINES/traits.dm
+++ b/code/__DEFINES/traits.dm
@@ -647,7 +647,11 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
/// The item is magically cursed
#define CURSED_ITEM_TRAIT(item_type) "cursed_item_[item_type]"
#define ABSTRACT_ITEM_TRAIT "abstract-item"
+/// A trait given by any status effect
#define STATUS_EFFECT_TRAIT "status-effect"
+/// A trait given by a specific status effect (not sure why we need both but whatever!)
+#define TRAIT_STATUS_EFFECT(effect_id) "[effect_id]-trait"
+
#define CLOTHING_TRAIT "clothing"
#define HELMET_TRAIT "helmet"
/// inherited from the mask
diff --git a/code/__DEFINES/vv.dm b/code/__DEFINES/vv.dm
index 5f9322eb00c..3df60080109 100644
--- a/code/__DEFINES/vv.dm
+++ b/code/__DEFINES/vv.dm
@@ -123,6 +123,9 @@
#define VV_HK_OFFER_GHOSTS "offer_ghosts"
#define VV_HK_SDQL_SPELL "sdql_spell"
+// /mob/living
+#define VV_HK_GIVE_SPEECH_IMPEDIMENT "impede_speech"
+
// /mob/living/carbon
#define VV_HK_MAKE_AI "aiify"
#define VV_HK_MODIFY_BODYPART "mod_bodypart"
diff --git a/code/__HELPERS/honkerblast.dm b/code/__HELPERS/honkerblast.dm
index bd05d07417a..b1bdd1cb9dc 100644
--- a/code/__HELPERS/honkerblast.dm
+++ b/code/__HELPERS/honkerblast.dm
@@ -21,7 +21,7 @@
for(var/mob/living/carbon/victim in severely_honked)
victim.Unconscious(40)
victim.Stun(100)
- victim.stuttering += 15
+ victim.adjust_timed_status_effect(30 SECONDS, /datum/status_effect/speech/stutter)
victim.Jitter(500)
var/obj/item/organ/ears/ears = victim.getorganslot(ORGAN_SLOT_EARS)
ears?.adjustEarDamage(10, 15)
diff --git a/code/datums/brain_damage/creepy_trauma.dm b/code/datums/brain_damage/creepy_trauma.dm
index d20dabc3634..8124140451c 100644
--- a/code/datums/brain_damage/creepy_trauma.dm
+++ b/code/datums/brain_damage/creepy_trauma.dm
@@ -76,9 +76,9 @@
if(prob(25)) // 25% chances to be nervous and stutter.
if(prob(50)) // 12.5% chance (previous check taken into account) of doing something suspicious.
addtimer(CALLBACK(src, .proc/on_failed_social_interaction), rand(1, 3) SECONDS)
- else if(owner.stuttering == 0)
+ else if(!owner.has_status_effect(/datum/status_effect/speech/stutter))
to_chat(owner, span_warning("Being near [obsession] makes you nervous and you begin to stutter..."))
- owner.stuttering = max(3, owner.stuttering)
+ owner.set_timed_status_effect(6 SECONDS, /datum/status_effect/speech/stutter, only_if_higher = TRUE)
/datum/brain_trauma/special/obsessed/proc/on_hug(datum/source, mob/living/hugger, mob/living/hugged)
SIGNAL_HANDLER
diff --git a/code/datums/brain_damage/mild.dm b/code/datums/brain_damage/mild.dm
index 77b8e10e1c5..f2905f2d01f 100644
--- a/code/datums/brain_damage/mild.dm
+++ b/code/datums/brain_damage/mild.dm
@@ -27,12 +27,11 @@
lose_text = "You feel in control of your speech."
/datum/brain_trauma/mild/stuttering/on_life(delta_time, times_fired)
- owner.stuttering = min(owner.stuttering + 5, 25)
- ..()
+ owner.adjust_timed_status_effect(5 SECONDS * delta_time, /datum/status_effect/speech/stutter, max_duration = 50 SECONDS)
/datum/brain_trauma/mild/stuttering/on_lose()
- owner.stuttering = 0
- ..()
+ owner.remove_status_effect(/datum/status_effect/speech/stutter)
+ return ..()
/datum/brain_trauma/mild/dumbness
name = "Dumbness"
@@ -44,25 +43,20 @@
/datum/brain_trauma/mild/dumbness/on_gain()
ADD_TRAIT(owner, TRAIT_DUMB, TRAUMA_TRAIT)
SEND_SIGNAL(owner, COMSIG_ADD_MOOD_EVENT, "dumb", /datum/mood_event/oblivious)
- ..()
+ return ..()
/datum/brain_trauma/mild/dumbness/on_life(delta_time, times_fired)
- owner.derpspeech = min(owner.derpspeech + 5, 25)
+ owner.adjust_timed_status_effect(5 SECONDS * delta_time, /datum/status_effect/speech/stutter/derpspeech, max_duration = 50 SECONDS)
if(DT_PROB(1.5, delta_time))
owner.emote("drool")
- //SKYRAT EDIT REMOVAL BEGIN
- /*
else if(owner.stat == CONSCIOUS && DT_PROB(1.5, delta_time))
owner.say(pick_list_replacements(BRAIN_DAMAGE_FILE, "brain_damage"), forced = "brain damage")
- */
- //SKYRAT EDIT REMOVAL END
- ..()
/datum/brain_trauma/mild/dumbness/on_lose()
REMOVE_TRAIT(owner, TRAIT_DUMB, TRAUMA_TRAIT)
- owner.derpspeech = 0
+ owner.remove_status_effect(/datum/status_effect/speech/stutter/derpspeech)
SEND_SIGNAL(owner, COMSIG_CLEAR_MOOD_EVENT, "dumb")
- ..()
+ return ..()
/datum/brain_trauma/mild/speech_impediment
name = "Speech Impediment"
@@ -97,7 +91,7 @@
owner.add_confusion(10)
owner.blur_eyes(10)
if(6 to 9)
- owner.slurring += 30
+ owner.adjust_timed_status_effect(1 MINUTES, /datum/status_effect/speech/slurring/drunk)
if(10)
to_chat(owner, span_notice("You forget for a moment what you were doing."))
owner.Stun(20)
diff --git a/code/datums/brain_damage/phobia.dm b/code/datums/brain_damage/phobia.dm
index 16c10c5fd3c..49f8db7ef28 100644
--- a/code/datums/brain_damage/phobia.dm
+++ b/code/datums/brain_damage/phobia.dm
@@ -126,7 +126,7 @@
owner.dizziness += 10
owner.add_confusion(10)
owner.Jitter(10)
- owner.stuttering += 10
+ owner.adjust_timed_status_effect(20 SECONDS, /datum/status_effect/speech/stutter)
// Defined phobia types for badminry, not included in the RNG trauma pool to avoid diluting.
diff --git a/code/datums/diseases/adrenal_crisis.dm b/code/datums/diseases/adrenal_crisis.dm
index 44eeec8b36b..9fe40fb6354 100644
--- a/code/datums/diseases/adrenal_crisis.dm
+++ b/code/datums/diseases/adrenal_crisis.dm
@@ -29,7 +29,7 @@
affected_mob.Unconscious(40)
if(DT_PROB(10, delta_time))
- affected_mob.slurring += 7
+ affected_mob.adjust_timed_status_effect(14 SECONDS, /datum/status_effect/speech/slurring/drunk)
if(DT_PROB(7, delta_time))
affected_mob.Dizzy(10)
diff --git a/code/datums/diseases/advance/symptoms/sensory.dm b/code/datums/diseases/advance/symptoms/sensory.dm
index 5a9423c81a9..e123b33de73 100644
--- a/code/datums/diseases/advance/symptoms/sensory.dm
+++ b/code/datums/diseases/advance/symptoms/sensory.dm
@@ -38,7 +38,8 @@
if(A.stage >= 3)
M.dizziness = max(0, M.dizziness - 2)
M.adjust_drowsyness(-2)
- M.slurring = max(0, M.slurring - 2)
+ M.adjust_timed_status_effect(-1 SECONDS, /datum/status_effect/speech/slurring/drunk)
+
M.set_confusion(max(0, M.get_confusion() - 2))
if(purge_alcohol)
M.reagents.remove_all_type(/datum/reagent/consumable/ethanol, 3)
diff --git a/code/datums/diseases/brainrot.dm b/code/datums/diseases/brainrot.dm
index 1498ded821f..89e6b085a23 100644
--- a/code/datums/diseases/brainrot.dm
+++ b/code/datums/diseases/brainrot.dm
@@ -55,4 +55,4 @@
if(prob(1))
affected_mob.emote("snore")
if(DT_PROB(7.5, delta_time))
- affected_mob.stuttering += 3
+ affected_mob.adjust_timed_status_effect(6 SECONDS, /datum/status_effect/speech/stutter)
diff --git a/code/datums/diseases/decloning.dm b/code/datums/diseases/decloning.dm
index 886a3bcfc5b..b85d0a1bc93 100644
--- a/code/datums/diseases/decloning.dm
+++ b/code/datums/diseases/decloning.dm
@@ -47,7 +47,7 @@
affected_mob.adjustOrganLoss(ORGAN_SLOT_BRAIN, 1, 170)
affected_mob.adjustCloneLoss(2, FALSE)
if(DT_PROB(7.5, delta_time))
- affected_mob.stuttering += 3
+ affected_mob.adjust_timed_status_effect(6 SECONDS, /datum/status_effect/speech/stutter)
if(5)
if(DT_PROB(1, delta_time))
affected_mob.emote("itch")
diff --git a/code/datums/elements/spooky.dm b/code/datums/elements/spooky.dm
index 5ff4c07219e..f1b68c6760a 100644
--- a/code/datums/elements/spooky.dm
+++ b/code/datums/elements/spooky.dm
@@ -22,7 +22,7 @@
if(!istype(U.dna.species, /datum/species/skeleton))
U.adjustStaminaLoss(35) //Extra Damage
U.Jitter(35)
- U.stuttering = 20
+ U.set_timed_status_effect(40 SECONDS, /datum/status_effect/speech/stutter)
if(U.getStaminaLoss() > 95)
to_chat(U, "Your ears weren't meant for this spectral sound.")
INVOKE_ASYNC(src, .proc/spectral_change, U)
@@ -36,7 +36,7 @@
H.adjustStaminaLoss(25)
H.Paralyze(15) //zombies can't resist the doot
C.Jitter(35)
- C.stuttering = 20
+ C.set_timed_status_effect(40 SECONDS, /datum/status_effect/speech/stutter)
if((!istype(H.dna.species, /datum/species/skeleton)) && (!istype(H.dna.species, /datum/species/golem)) && (!istype(H.dna.species, /datum/species/android)) && (!istype(H.dna.species, /datum/species/jelly)))
C.adjustStaminaLoss(25) //boneless humanoids don't lose the will to live
to_chat(C, "DOOT")
@@ -44,7 +44,7 @@
else //the sound will spook monkeys.
C.Jitter(15)
- C.stuttering = 20
+ C.set_timed_status_effect(40 SECONDS, /datum/status_effect/speech/stutter)
/datum/element/spooky/proc/spectral_change(mob/living/carbon/human/H, mob/user)
if((H.getStaminaLoss() > 95) && (!istype(H.dna.species, /datum/species/skeleton)) && (!istype(H.dna.species, /datum/species/golem)) && (!istype(H.dna.species, /datum/species/android)) && (!istype(H.dna.species, /datum/species/jelly)))
diff --git a/code/datums/mutations/speech.dm b/code/datums/mutations/speech.dm
index 43653d060f8..30203107604 100644
--- a/code/datums/mutations/speech.dm
+++ b/code/datums/mutations/speech.dm
@@ -9,8 +9,7 @@
/datum/mutation/human/nervousness/on_life(delta_time, times_fired)
if(DT_PROB(5, delta_time))
- owner.stuttering = max(10, owner.stuttering)
-
+ owner.set_timed_status_effect(20 SECONDS, /datum/status_effect/speech/stutter, only_if_higher = TRUE)
/datum/mutation/human/wacky
name = "Wacky"
diff --git a/code/datums/quirks/negative.dm b/code/datums/quirks/negative.dm
index 9d00e2ac13c..22230dbdba4 100644
--- a/code/datums/quirks/negative.dm
+++ b/code/datums/quirks/negative.dm
@@ -544,26 +544,13 @@
to_chat(quirker, span_danger("You feel self-conscious and stop talking. You need a moment to recover!"))
break
if(prob(max(5,(nearby_people*12.5*moodmod)))) //Minimum 1/20 chance of stutter
- word = html_decode(word)
- var/leng = length(word)
- var/stuttered = ""
- var/newletter = ""
- var/rawchar = ""
- var/static/regex/nostutter = regex(@@[aeiouAEIOU ""''()[\]{}.!?,:;_`~-]@)
- for(var/i = 1, i <= leng, i += length(rawchar))
- rawchar = newletter = word[i]
- if(prob(80) && !nostutter.Find(rawchar))
- if(prob(10))
- newletter = "[newletter]-[newletter]-[newletter]-[newletter]"
- else if(prob(20))
- newletter = "[newletter]-[newletter]-[newletter]"
- else
- newletter = "[newletter]-[newletter]"
- stuttered += newletter
- sanitize(stuttered)
- new_message += stuttered
+ // Add a short stutter, THEN treat our word
+ quirker.adjust_timed_status_effect(0.5 SECONDS, /datum/status_effect/speech/stutter)
+ new_message += quirker.treat_message(word)
+
else
new_message += word
+
message = jointext(new_message, " ")
var/mob/living/carbon/human/quirker = quirk_holder
if(prob(min(50,(0.50*(nearby_people*12.5)*moodmod)))) //Max 50% chance of not talking
@@ -607,7 +594,7 @@
quirk_holder.Jitter(10)
msg += "causing you to start fidgeting!"
if(2)
- quirk_holder.stuttering = max(3, quirk_holder.stuttering)
+ quirk_holder.set_timed_status_effect(6 SECONDS, /datum/status_effect/speech/stutter, only_if_higher = TRUE)
msg += "causing you to start stuttering!"
if(3)
quirk_holder.Stun(2 SECONDS)
diff --git a/code/datums/status_effects/debuffs.dm b/code/datums/status_effects/debuffs/debuffs.dm
similarity index 99%
rename from code/datums/status_effects/debuffs.dm
rename to code/datums/status_effects/debuffs/debuffs.dm
index 026c6069adf..9e76ae9fb24 100644
--- a/code/datums/status_effects/debuffs.dm
+++ b/code/datums/status_effects/debuffs/debuffs.dm
@@ -1,5 +1,3 @@
-#define TRAIT_STATUS_EFFECT(effect_id) "[effect_id]-trait"
-
//Largely negative status effects go here, even if they have small benificial effects
//STUN EFFECTS
/datum/status_effect/incapacitating
diff --git a/code/datums/status_effects/debuffs/speech_debuffs.dm b/code/datums/status_effects/debuffs/speech_debuffs.dm
new file mode 100644
index 00000000000..62fb365a1ff
--- /dev/null
+++ b/code/datums/status_effects/debuffs/speech_debuffs.dm
@@ -0,0 +1,225 @@
+/datum/status_effect/speech
+ id = null
+ alert_type = null
+
+/datum/status_effect/speech/on_creation(mob/living/new_owner, duration = 10 SECONDS)
+ src.duration = duration
+ return ..()
+
+/datum/status_effect/speech/on_apply()
+ RegisterSignal(owner, COMSIG_LIVING_TREAT_MESSAGE, .proc/handle_message)
+ RegisterSignal(owner, COMSIG_LIVING_POST_FULLY_HEAL, .proc/on_heal)
+ return TRUE
+
+/datum/status_effect/speech/on_remove()
+ UnregisterSignal(owner, COMSIG_LIVING_TREAT_MESSAGE)
+ UnregisterSignal(owner, COMSIG_LIVING_POST_FULLY_HEAL)
+
+/// Signal proc for [COMSIG_LIVING_POST_FULLY_HEAL], removes all stutters and slurring on full heal
+/datum/status_effect/speech/proc/on_heal(datum/source, admin_revive)
+ SIGNAL_HANDLER
+
+ qdel(src)
+
+/**
+ * Signal proc for [COMSIG_LIVING_TREAT_MESSAGE]
+ *
+ * Iterates over all of the characters in the past message
+ * and calls apply_speech() on each.
+ *
+ * message_args[1] is the original message passed into the signal.
+ */
+/datum/status_effect/speech/proc/handle_message(datum/source, list/message_args)
+ SIGNAL_HANDLER
+
+ var/phrase = html_decode(message_args[1])
+ if(!length(phrase))
+ 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, original_char)
+
+ message_args[1] = sanitize(final_phrase)
+
+/**
+ * Applies the speech effects on the past character, changing
+ * the original_char into the modified_char.
+ *
+ * Return the modified_char to be reapplied to the message.
+ */
+/datum/status_effect/speech/proc/apply_speech(original_char, modified_char)
+ stack_trace("[type] didn't implement apply_speech.")
+ return original_char
+
+/datum/status_effect/speech/stutter
+ id = "stutter"
+ /// The probability of adding a stutter to any character
+ var/stutter_prob = 80
+ /// Regex of characters we won't apply a stutter to
+ var/static/regex/no_stutter
+
+/datum/status_effect/speech/stutter/on_creation(mob/living/new_owner, ...)
+ . = ..()
+ if(!.)
+ return
+ if(!no_stutter)
+ no_stutter = regex(@@[aeiouAEIOU ""''()[\]{}.!?,:;_`~-]@)
+
+/datum/status_effect/speech/stutter/apply_speech(original_char, modified_char)
+ if(prob(stutter_prob) && !no_stutter.Find(original_char))
+ if(prob(10))
+ modified_char = "[modified_char]-[modified_char]-[modified_char]-[modified_char]"
+ else if(prob(20))
+ modified_char = "[modified_char]-[modified_char]-[modified_char]"
+ else if(prob(95))
+ modified_char = "[modified_char]-[modified_char]"
+ else
+ modified_char = ""
+
+ return modified_char
+
+/datum/status_effect/speech/stutter/derpspeech
+ id = "derp_stutter"
+ /// The probability of making our message entirely uppercase + adding exclamations
+ var/capitalize_prob = 50
+ /// The probability of adding a stutter to the entire message, if we're not already stuttering
+ var/message_stutter_prob = 15
+
+/datum/status_effect/speech/stutter/derpspeech/handle_message(datum/source, list/message_args)
+
+ var/message = html_decode(message_args[1])
+
+ message = replacetext(message, " am ", " ")
+ message = replacetext(message, " is ", " ")
+ message = replacetext(message, " are ", " ")
+ message = replacetext(message, "you", "u")
+ message = replacetext(message, "help", "halp")
+ message = replacetext(message, "grief", "grife")
+ message = replacetext(message, "space", "spess")
+ message = replacetext(message, "carp", "crap")
+ message = replacetext(message, "reason", "raisin")
+
+ if(prob(capitalize_prob))
+ var/exclamation = pick("!", "!!", "!!!")
+ message = uppertext(message)
+ message += "[apply_speech(exclamation, exclamation)]"
+
+ message_args[1] = message
+
+ var/mob/living/living_source = source
+ if(!isliving(source) || living_source.has_status_effect(/datum/status_effect/speech/stutter))
+ return
+
+ // If we're not stuttering, we have a chance of calling parent here, adding stutter effects
+ if(prob(message_stutter_prob))
+ return ..()
+
+ // Otherwise just return and don't call parent, we already modified our speech
+ return
+
+/datum/status_effect/speech/slurring
+ /// The chance that any given character in a message will be replaced with a common character
+ var/common_prob = 25
+ /// The chance that any given character in a message will be replaced with an uncommon character
+ var/uncommon_prob = 10
+ /// The chance that any given character will be entirely replaced with a new string / will have a string appended onto it
+ var/replacement_prob = 5
+ /// The chance that any given character will be doubled, or even tripled
+ var/doubletext_prob = 0
+
+ /// The file we pull text modifications from
+ var/text_modification_file = ""
+
+ /// Common replacements for characters - populated in on_creation
+ var/list/common_replacements
+ /// Uncommon replacements for characters - populated in on_creation
+ var/list/uncommon_replacements
+ /// Strings that fully replace a character - populated in on_creation
+ var/list/string_replacements
+ /// Strings that are appended to a character - populated in on_creation
+ var/list/string_additions
+
+/datum/status_effect/speech/slurring/on_creation(mob/living/new_owner, duration = 10 SECONDS)
+ . = ..()
+ if(!.)
+ return
+
+ if(!text_modification_file)
+ CRASH("[type] was created without a text modification file.")
+
+ var/list/speech_changes = strings(text_modification_file, "replacements")
+ common_replacements = speech_changes["characters"]["common"]
+ uncommon_replacements = speech_changes["characters"]["uncommon"]
+ string_replacements = speech_changes["string_replacements"]
+ string_additions = speech_changes["string_additions"]
+
+/datum/status_effect/speech/slurring/apply_speech(original_char, modified_char)
+
+ var/lower_char = lowertext(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
+
+ 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
+
+ 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))
+ modified_char = pick(string_replacements)
+ else
+ modified_char += pick(string_additions)
+
+ else if(replacements_len)
+ modified_char = pick(string_replacements)
+
+ 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]"
+
+ return modified_char
+
+/datum/status_effect/speech/slurring/drunk
+ id = "drunk_slurring"
+ common_prob = 33
+ uncommon_prob = 5
+ replacement_prob = 5
+ doubletext_prob = 10
+ text_modification_file = "slurring_drunk_text.json"
+
+/datum/status_effect/speech/slurring/cult
+ id = "cult_slurring"
+ common_prob = 50
+ uncommon_prob = 25
+ replacement_prob = 33
+ doubletext_prob = 0
+ text_modification_file = "slurring_cult_text.json"
+
+/datum/status_effect/speech/slurring/heretic
+ id = "heretic_slurring"
+ common_prob = 50
+ uncommon_prob = 20
+ replacement_prob = 30
+ doubletext_prob = 5
+ text_modification_file = "slurring_heretic_text.json"
diff --git a/code/game/objects/items/devices/paicard.dm b/code/game/objects/items/devices/paicard.dm
index b465dec9060..e6c73bc7b6b 100644
--- a/code/game/objects/items/devices/paicard.dm
+++ b/code/game/objects/items/devices/paicard.dm
@@ -117,9 +117,9 @@
if("fix_speech")
to_chat(pai, span_notice("Your owner has corrected your speech modulation!"))
to_chat(usr, span_notice("You fix the pAI's speech modulator."))
- pai.stuttering = 0
- pai.slurring = 0
- pai.derpspeech = 0
+ for(var/effect in typesof(/datum/status_effect/speech))
+ pai.remove_status_effect(effect)
+
if("request")
if(!pai)
SSpai.findPAI(src, usr)
diff --git a/code/game/objects/items/hand_items.dm b/code/game/objects/items/hand_items.dm
index 8e234302bd2..a5a38b460a0 100644
--- a/code/game/objects/items/hand_items.dm
+++ b/code/game/objects/items/hand_items.dm
@@ -540,7 +540,7 @@
other_msg = "stammers softly for a moment before choking on something!"
self_msg = "You feel your tongue disappear down your throat as you fight to remember how to make words!"
addtimer(CALLBACK(living_target, /atom/movable.proc/say, pick("Uhhh...", "O-oh, uhm...", "I- uhhhhh??", "You too!!", "What?")), rand(0.5 SECONDS, 1.5 SECONDS))
- living_target.stuttering += rand(5, 15)
+ living_target.adjust_timed_status_effect(rand(10 SECONDS, 30 SECONDS), /datum/status_effect/speech/stutter)
if(3)
other_msg = "locks up with a stunned look on [living_target.p_their()] face, staring at [firer ? firer : "the ceiling"]!"
self_msg = "Your brain completely fails to process what just happened, leaving you rooted in place staring at [firer ? "[firer]" : "the ceiling"] for what feels like an eternity!"
diff --git a/code/game/objects/items/melee/baton.dm b/code/game/objects/items/melee/baton.dm
index 40dc9e8497c..68371563267 100644
--- a/code/game/objects/items/melee/baton.dm
+++ b/code/game/objects/items/melee/baton.dm
@@ -371,7 +371,7 @@
/obj/item/melee/baton/telescopic/contractor_baton/additional_effects_non_cyborg(mob/living/target, mob/living/user)
target.Jitter(20)
- target.stuttering += 20
+ target.adjust_timed_status_effect(40 SECONDS, /datum/status_effect/speech/stutter)
/obj/item/melee/baton/security
name = "stun baton"
@@ -563,8 +563,8 @@
*/
/obj/item/melee/baton/security/additional_effects_non_cyborg(mob/living/target, mob/living/user)
target.Jitter(20)
-// target.set_confusion(max(10, target.get_confusion())) SKYRAT EDIT - Less CC on an already strong melee weapon.
- target.stuttering = max(8, target.stuttering)
+ //target.set_confusion(max(10, target.get_confusion())) SKYRAT EDIT REMOVAL
+ target.set_timed_status_effect(16 SECONDS, /datum/status_effect/speech/stutter, only_if_higher = TRUE)
SEND_SIGNAL(target, COMSIG_LIVING_MINOR_SHOCK)
addtimer(CALLBACK(src, .proc/apply_stun_effect_end, target), 2 SECONDS)
diff --git a/code/game/objects/items/robot/robot_items.dm b/code/game/objects/items/robot/robot_items.dm
index 078aedc48cb..ccbd75340c9 100644
--- a/code/game/objects/items/robot/robot_items.dm
+++ b/code/game/objects/items/robot/robot_items.dm
@@ -23,7 +23,7 @@
user.do_attack_animation(M)
M.Paralyze(100)
- M.apply_effect(EFFECT_STUTTER, 5)
+ M.adjust_timed_status_effect(10 SECONDS, /datum/status_effect/speech/stutter)
M.visible_message(span_danger("[user] prods [M] with [src]!"), \
span_userdanger("[user] prods you with [src]!"))
@@ -327,12 +327,12 @@
switch(bang_effect)
if(1)
C.add_confusion(5)
- C.stuttering += 10
+ C.adjust_timed_status_effect(20 SECONDS, /datum/status_effect/speech/stutter)
C.Jitter(10)
if(2)
C.Paralyze(40)
C.add_confusion(10)
- C.stuttering += 15
+ C.adjust_timed_status_effect(30 SECONDS, /datum/status_effect/speech/stutter)
C.Jitter(25)
playsound(get_turf(src), 'sound/machines/warning-buzzer.ogg', 130, 3)
cooldown = world.time + 600
diff --git a/code/game/objects/structures/watercloset.dm b/code/game/objects/structures/watercloset.dm
index a07d82d9ae2..32897631252 100644
--- a/code/game/objects/structures/watercloset.dm
+++ b/code/game/objects/structures/watercloset.dm
@@ -352,7 +352,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/urinal, 32)
if(baton.cell?.charge && baton.active)
flick("baton_active", src)
user.Paralyze(baton.knockdown_time)
- user.stuttering = baton.knockdown_time*0.5
+ user.set_timed_status_effect(baton.knockdown_time, /datum/status_effect/speech/stutter)
baton.cell.use(baton.cell_hit_cost)
user.visible_message(span_warning("[user] shocks [user.p_them()]self while attempting to wash the active [baton.name]!"), \
span_userdanger("You unwisely attempt to wash [baton] while it's still on."))
@@ -557,7 +557,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/urinal, 32)
if(baton.cell?.charge && baton.active)
flick("baton_active", src)
user.Paralyze(baton.knockdown_time)
- user.stuttering = baton.knockdown_time*0.5
+ user.set_timed_status_effect(baton.knockdown_time, /datum/status_effect/speech/stutter)
baton.cell.use(baton.cell_hit_cost)
user.visible_message(span_warning("[user] shocks [user.p_them()]self while attempting to wash the active [baton.name]!"), \
span_userdanger("You unwisely attempt to wash [baton] while it's still on."))
diff --git a/code/modules/admin/smites/bsa.dm b/code/modules/admin/smites/bsa.dm
index d737c711ef9..15dc72ee033 100644
--- a/code/modules/admin/smites/bsa.dm
+++ b/code/modules/admin/smites/bsa.dm
@@ -1,7 +1,7 @@
#define BSA_CHANCE_TO_BREAK_TILE_TO_PLATING 80
#define BSA_MAX_DAMAGE 99
-#define BSA_PARALYZE_TIME (40 SECONDS)
-#define BSA_STUTTER_TIME 20
+#define BSA_PARALYZE_TIME 40 SECONDS
+#define BSA_STUTTER_TIME 40 SECONDS
/// Fires the BSA at the target
/datum/smite/bsa
@@ -27,7 +27,7 @@
else
target.adjustBruteLoss(min(BSA_MAX_DAMAGE, target.health - 1))
target.Paralyze(BSA_PARALYZE_TIME)
- target.stuttering = BSA_STUTTER_TIME
+ target.set_timed_status_effect(BSA_STUTTER_TIME, /datum/status_effect/speech/stutter)
#undef BSA_CHANCE_TO_BREAK_TILE_TO_PLATING
#undef BSA_MAX_DAMAGE
diff --git a/code/modules/antagonists/abductor/equipment/abduction_gear.dm b/code/modules/antagonists/abductor/equipment/abduction_gear.dm
index 2c78841d023..1df2d3d9c6f 100644
--- a/code/modules/antagonists/abductor/equipment/abduction_gear.dm
+++ b/code/modules/antagonists/abductor/equipment/abduction_gear.dm
@@ -514,7 +514,7 @@ Congratulations! You are now trained for invasive xenobiology research!"}
span_userdanger("[user] stuns you with [src]!"))
target.Jitter(20)
target.set_confusion(max(10, target.get_confusion()))
- target.stuttering = max(8, target.stuttering)
+ target.set_timed_status_effect(16 SECONDS, /datum/status_effect/speech/stutter, only_if_higher = TRUE)
SEND_SIGNAL(target, COMSIG_LIVING_MINOR_SHOCK)
target.Paralyze(knockdown_time * (HAS_TRAIT(target, TRAIT_STUNRESISTANCE) ? 0.1 : 1))
if(BATON_SLEEP)
diff --git a/code/modules/antagonists/cult/blood_magic.dm b/code/modules/antagonists/cult/blood_magic.dm
index fb6ade629bb..7d85850eb8f 100644
--- a/code/modules/antagonists/cult/blood_magic.dm
+++ b/code/modules/antagonists/cult/blood_magic.dm
@@ -442,8 +442,8 @@
else if(iscarbon(target))
var/mob/living/carbon/carbon_target = target
carbon_target.silent += 6
- carbon_target.stuttering += 15
- carbon_target.cultslurring += 15
+ carbon_target.adjust_timed_status_effect(30 SECONDS, /datum/status_effect/speech/stutter)
+ carbon_target.adjust_timed_status_effect(30 SECONDS, /datum/status_effect/speech/slurring/cult)
carbon_target.Jitter(1.5 SECONDS)
uses--
..()
@@ -740,7 +740,7 @@
if(H.stat == DEAD)
to_chat(user,span_warning("[H.p_their(TRUE)] blood has stopped flowing, you'll have to find another way to extract it."))
return
- if(H.cultslurring)
+ if(H.has_status_effect(/datum/status_effect/speech/slurring/cult))
to_chat(user,span_danger("[H.p_their(TRUE)] blood has been tainted by an even stronger form of blood magic, it's no use to us like this!"))
return
if(H.blood_volume > BLOOD_VOLUME_SAFE)
diff --git a/code/modules/antagonists/cult/runes.dm b/code/modules/antagonists/cult/runes.dm
index 2844cb7202a..8c58d99a857 100644
--- a/code/modules/antagonists/cult/runes.dm
+++ b/code/modules/antagonists/cult/runes.dm
@@ -279,8 +279,9 @@ structure_check() searches for nearby cultist structures required for the invoca
if(ishuman(convertee))
var/mob/living/carbon/human/H = convertee
H.uncuff()
- H.stuttering = 0
- H.cultslurring = 0
+ H.remove_status_effect(/datum/status_effect/speech/slurring/cult)
+ H.remove_status_effect(/datum/status_effect/speech/stutter)
+
if(prob(1) || SSevents.holidays && SSevents.holidays[APRIL_FOOLS])
H.say("You son of a bitch! I'm in.", forced = "That son of a bitch! They're in.")
if(isshade(convertee))
diff --git a/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_knowledge.dm b/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_knowledge.dm
index 8f2c1637fb8..81b4c86f2b5 100644
--- a/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_knowledge.dm
+++ b/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_knowledge.dm
@@ -363,9 +363,8 @@
SEND_SIGNAL(sac_target, COMSIG_CLEAR_MOOD_EVENT, "shadow_realm")
// Wherever we end up, we sure as hell won't be able to explain
- sac_target.slurring += 20
- sac_target.cultslurring += 20
- sac_target.stuttering += 20
+ sac_target.adjust_timed_status_effect(40 SECONDS, /datum/status_effect/speech/slurring/heretic)
+ sac_target.adjust_timed_status_effect(40 SECONDS, /datum/status_effect/speech/stutter)
// They're already back on the station for some reason, don't bother teleporting
if(is_station_level(sac_target.z))
diff --git a/code/modules/antagonists/heretic/magic/mansus_grasp.dm b/code/modules/antagonists/heretic/magic/mansus_grasp.dm
index 42a6b8182b6..704a774f9ea 100644
--- a/code/modules/antagonists/heretic/magic/mansus_grasp.dm
+++ b/code/modules/antagonists/heretic/magic/mansus_grasp.dm
@@ -82,7 +82,7 @@
hit.adjustBruteLoss(10)
if(iscarbon(hit))
var/mob/living/carbon/carbon_hit = hit
- carbon_hit.cultslurring += 2
+ carbon_hit.adjust_timed_status_effect(4 SECONDS, /datum/status_effect/speech/slurring/heretic)
carbon_hit.AdjustKnockdown(5 SECONDS)
carbon_hit.adjustStaminaLoss(80)
@@ -106,7 +106,7 @@
playsound(carbon_user, 'sound/effects/wounds/sizzle1.ogg', 70, vary = TRUE)
if(prob(50))
carbon_user.emote("scream")
- carbon_user.stuttering += 13
+ carbon_user.adjust_timed_status_effect(26 SECONDS, /datum/status_effect/speech/stutter)
on_mob_hit(user, user)
diff --git a/code/modules/antagonists/heretic/structures/carving_knife.dm b/code/modules/antagonists/heretic/structures/carving_knife.dm
index c092cb3b070..0d5bf3f08d7 100644
--- a/code/modules/antagonists/heretic/structures/carving_knife.dm
+++ b/code/modules/antagonists/heretic/structures/carving_knife.dm
@@ -239,7 +239,7 @@
var/mob/living/carbon/carbon_victim = victim
carbon_victim.adjustStaminaLoss(80)
carbon_victim.silent += 10
- carbon_victim.stuttering += 30
+ carbon_victim.adjust_timed_status_effect(1 MINUTES, /datum/status_effect/speech/stutter)
carbon_victim.add_confusion(5)
carbon_victim.Jitter(10)
carbon_victim.Dizzy(20)
diff --git a/code/modules/mob/living/carbon/alien/organs.dm b/code/modules/mob/living/carbon/alien/organs.dm
index e792556ea44..021cfcb6a68 100644
--- a/code/modules/mob/living/carbon/alien/organs.dm
+++ b/code/modules/mob/living/carbon/alien/organs.dm
@@ -139,7 +139,7 @@
owner.jitteriness += 30
owner.add_confusion(30)
- owner.stuttering += 30
+ owner.adjust_timed_status_effect(1 MINUTES, /datum/status_effect/speech/stutter)
recent_queen_death = TRUE
owner.throw_alert(ALERT_XENO_NOQUEEN, /atom/movable/screen/alert/alien_vulnerable)
diff --git a/code/modules/mob/living/carbon/carbon_defense.dm b/code/modules/mob/living/carbon/carbon_defense.dm
index 743c83c340c..3016c66fd1d 100644
--- a/code/modules/mob/living/carbon/carbon_defense.dm
+++ b/code/modules/mob/living/carbon/carbon_defense.dm
@@ -226,9 +226,8 @@
do_sparks(5, TRUE, src)
var/power = M.powerlevel + rand(0,3)
- Paralyze(power*20)
- if(stuttering < power)
- stuttering = power
+ Paralyze(power * 2 SECONDS)
+ set_timed_status_effect(power * 2 SECONDS, /datum/status_effect/speech/stutter, only_if_higher = TRUE)
if (prob(stunprob) && M.powerlevel >= 8)
adjustFireLoss(M.powerlevel * rand(6,10))
updatehealth()
@@ -441,7 +440,7 @@
//Jitter and other fluff.
jitteriness += 1000
do_jitter_animation(jitteriness)
- stuttering += 2
+ adjust_timed_status_effect(4 SECONDS, /datum/status_effect/speech/stutter)
addtimer(CALLBACK(src, .proc/secondary_shock, should_stun), 20)
return shock_damage
diff --git a/code/modules/mob/living/carbon/human/human_say.dm b/code/modules/mob/living/carbon/human/human_say.dm
index 84bbf696c5a..5d27c1bcfa0 100644
--- a/code/modules/mob/living/carbon/human/human_say.dm
+++ b/code/modules/mob/living/carbon/human/human_say.dm
@@ -1,12 +1,13 @@
/mob/living/carbon/human/say_mod(input, list/message_mods = list())
verb_say = dna.species.say_mod
- if(slurring)
+ // Any subtype of slurring in our status effects make us "slur"
+ if(locate(/datum/status_effect/speech/slurring) in status_effects)
if (HAS_TRAIT(src, TRAIT_SIGN_LANG))
return "loosely signs"
else
return "slurs"
- else
- . = ..()
+
+ return ..()
/mob/living/carbon/human/GetVoice()
if(istype(wear_mask, /obj/item/clothing/mask/chameleon))
diff --git a/code/modules/mob/living/carbon/life.dm b/code/modules/mob/living/carbon/life.dm
index 9c323859c71..bca99209396 100644
--- a/code/modules/mob/living/carbon/life.dm
+++ b/code/modules/mob/living/carbon/life.dm
@@ -451,7 +451,7 @@ All effects don't start immediately, but rather get worse over time; the rate is
*/
#define BALLMER_POINTS 5
-//this updates all special effects: stun, sleeping, knockdown, druggy, stuttering, etc..
+// This updates all special effects that really should be status effect datums: Druggy, Hallucinations, Drunkenness, Mute, etc..
/mob/living/carbon/handle_status_effects(delta_time, times_fired)
..()
@@ -519,7 +519,7 @@ All effects don't start immediately, but rather get worse over time; the rate is
if(drunkenness >= 6)
SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "drunk", /datum/mood_event/drunk)
if(DT_PROB(16, delta_time))
- slurring += 2
+ adjust_timed_status_effect(4 SECONDS, /datum/status_effect/speech/slurring/drunk)
jitteriness = max(jitteriness - (1.5 * delta_time), 0)
throw_alert(ALERT_DRUNK, /atom/movable/screen/alert/drunk)
sound_environment_override = SOUND_ENVIRONMENT_PSYCHOTIC
@@ -530,8 +530,10 @@ All effects don't start immediately, but rather get worse over time; the rate is
REMOVE_TRAIT(src, TRAIT_NUMBED, src) //SKYRAT EDIT START - ANESTHETIC EFFECT REMOVAL
clear_alert("numbed") //SKYRAT EDIT END
- if(drunkenness >= 11 && slurring < 5)
- slurring += 0.6 * delta_time
+ if(drunkenness >= 11)
+ var/datum/status_effect/speech/slurring/drunk/already_slurring = has_status_effect(/datum/status_effect/speech/slurring/drunk)
+ if(!already_slurring || already_slurring.duration - world.time <= 10 SECONDS)
+ adjust_timed_status_effect(1.2 SECONDS * delta_time, /datum/status_effect/speech/slurring/drunk)
if(mind && (is_scientist_job(mind.assigned_role) || is_research_director_job(mind.assigned_role)))
if(SSresearch.science_tech)
diff --git a/code/modules/mob/living/damage_procs.dm b/code/modules/mob/living/damage_procs.dm
index dced134476c..e6c4777455f 100644
--- a/code/modules/mob/living/damage_procs.dm
+++ b/code/modules/mob/living/damage_procs.dm
@@ -104,11 +104,6 @@
Immobilize(effect * hit_percent)
if(EFFECT_UNCONSCIOUS)
Unconscious(effect * hit_percent)
- if(EFFECT_SLUR)
- slurring = max(slurring,(effect * hit_percent))
- if(EFFECT_STUTTER)
- if((status_flags & CANSTUN) && !HAS_TRAIT(src, TRAIT_STUNIMMUNE)) // stun is usually associated with stutter
- stuttering = max(stuttering,(effect * hit_percent))
if(EFFECT_EYE_BLUR)
blur_eyes(effect * hit_percent)
if(EFFECT_DROWSY)
@@ -118,10 +113,30 @@
jitteriness = max(jitteriness,(effect * hit_percent))
return TRUE
-/// applies multiple effects at once via [/mob/living/proc/apply_effect]
-/mob/living/proc/apply_effects(stun = 0, knockdown = 0, unconscious = 0, slur = 0, stutter = 0, eyeblur = 0, drowsy = 0, blocked = 0, stamina = 0, jitter = 0, paralyze = 0, immobilize = 0)
+/**
+ * Applies multiple effects at once via [/mob/living/proc/apply_effect]
+ *
+ * Pretty much only used for projectiles applying effects on hit,
+ * don't use this for anything else please just cause the effects directly
+ */
+/mob/living/proc/apply_effects(
+ stun = 0,
+ knockdown = 0,
+ unconscious = 0,
+ slur = 0 SECONDS, // Speech impediment, not technically an effect
+ stutter = 0 SECONDS, // Ditto
+ eyeblur = 0,
+ drowsy = 0,
+ blocked = 0, // This one's not an effect, don't be confused - it's block chance
+ stamina = 0, // This one's a damage type, and not an effect
+ jitter = 0,
+ paralyze = 0,
+ immobilize = 0,
+ )
+
if(blocked >= 100)
return FALSE
+
if(stun)
apply_effect(stun, EFFECT_STUN, blocked)
if(knockdown)
@@ -132,18 +147,21 @@
apply_effect(paralyze, EFFECT_PARALYZE, blocked)
if(immobilize)
apply_effect(immobilize, EFFECT_IMMOBILIZE, blocked)
- if(slur)
- apply_effect(slur, EFFECT_SLUR, blocked)
- if(stutter)
- apply_effect(stutter, EFFECT_STUTTER, blocked)
if(eyeblur)
apply_effect(eyeblur, EFFECT_EYE_BLUR, blocked)
if(drowsy)
apply_effect(drowsy, EFFECT_DROWSY, blocked)
- if(stamina)
- apply_damage(stamina, STAMINA, null, blocked)
if(jitter)
apply_effect(jitter, EFFECT_JITTER, blocked)
+
+ if(stamina)
+ apply_damage(stamina, STAMINA, null, blocked)
+
+ if(slur)
+ adjust_timed_status_effect(slur, /datum/status_effect/speech/slurring/drunk)
+ if(stutter)
+ adjust_timed_status_effect(stutter, /datum/status_effect/speech/stutter)
+
return TRUE
diff --git a/code/modules/mob/living/life.dm b/code/modules/mob/living/life.dm
index 3f8447ade8c..fcfd8585513 100644
--- a/code/modules/mob/living/life.dm
+++ b/code/modules/mob/living/life.dm
@@ -157,16 +157,16 @@
return reagents.has_reagent(reagent, amount, needs_metabolizing)
/*
- * this updates some effects: mostly old stuff such as drunkness, druggy, stuttering, etc.
+ * this updates some effects: mostly old stuff such as drunkness, druggy, etc.
* that should be converted to status effect datums one day.
+ * ^ March 4th, 2021
+ * Stuttering and slurring has been removed,
+ * but carbons still have a ton of effects that need to be moved over
+ * Good luck reader
+ * ^ April 6th, 2022
*/
/mob/living/proc/handle_status_effects(delta_time, times_fired)
- if(stuttering)
- stuttering = max(stuttering - (0.5 * delta_time), 0)
- if(slurring)
- slurring = max(slurring - (0.5 * delta_time),0)
- if(cultslurring)
- cultslurring = max(cultslurring - (0.5 * delta_time), 0)
+ return
/mob/living/proc/handle_traits(delta_time, times_fired)
//Eyes
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index 72f3a7d2594..a0ea323e3af 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -890,8 +890,6 @@
set_confusion(0)
dizziness = 0
set_drowsyness(0)
- stuttering = 0
- slurring = 0
jitteriness = 0
stop_sound_channel(CHANNEL_HEARTBEAT)
SEND_SIGNAL(src, COMSIG_LIVING_POST_FULLY_HEAL, admin_revive)
@@ -1753,6 +1751,19 @@
"}
+/mob/living/vv_get_dropdown()
+ . = ..()
+ VV_DROPDOWN_OPTION("", "---------")
+ VV_DROPDOWN_OPTION(VV_HK_GIVE_SPEECH_IMPEDIMENT, "Impede Speech (Slurring, stuttering, etc)")
+
+/mob/living/vv_do_topic(list/href_list)
+ . = ..()
+
+ if(href_list[VV_HK_GIVE_SPEECH_IMPEDIMENT])
+ if(!check_rights(NONE))
+ return
+ admin_give_speech_impediment(usr)
+
/mob/living/proc/move_to_error_room()
var/obj/effect/landmark/error/error_landmark = locate(/obj/effect/landmark/error) in GLOB.landmarks_list
if(error_landmark)
@@ -2276,3 +2287,25 @@
if(SEND_SIGNAL(src, COMSIG_LIVING_WRITE_MEMORY, dead, gibbed) & COMPONENT_DONT_WRITE_MEMORY)
return FALSE
return TRUE
+
+/// Admin only proc for giving a certain speech impediment to this mob
+/mob/living/proc/admin_give_speech_impediment(mob/admin)
+ if(!admin || !check_rights(NONE))
+ return
+
+ var/list/impediments = list()
+ for(var/datum/status_effect/possible as anything in typesof(/datum/status_effect/speech))
+ if(!initial(possible.id))
+ continue
+
+ impediments[initial(possible.id)] = possible
+
+ var/chosen = tgui_input_list(admin, "What speech impediment?", "Impede Speech", impediments)
+ if(!chosen || !ispath(impediments[chosen], /datum/status_effect/speech) || QDELETED(src) || !check_rights(NONE))
+ return
+
+ var/duration = tgui_input_number(admin, "How long should it last (in seconds)? Max is infinite duration.", "Duration", 0, INFINITY, 0 SECONDS)
+ if(!isnum(duration) || duration <= 0 || QDELETED(src) || !check_rights(NONE))
+ return
+
+ adjust_timed_status_effect(duration SECONDS, impediments[chosen])
diff --git a/code/modules/mob/living/living_defines.dm b/code/modules/mob/living/living_defines.dm
index ded09a43b61..a43c4acc872 100644
--- a/code/modules/mob/living/living_defines.dm
+++ b/code/modules/mob/living/living_defines.dm
@@ -126,12 +126,6 @@
var/list/status_effects ///a list of all status effects the mob has
var/druggy = 0
- //Speech
- var/stuttering = 0
- var/slurring = 0
- var/cultslurring = 0
- var/derpspeech = 0
-
var/list/implants = null
var/last_words ///used for database logging
diff --git a/code/modules/mob/living/living_say.dm b/code/modules/mob/living/living_say.dm
index 5ec91e53064..2185bd3b3bf 100644
--- a/code/modules/mob/living/living_say.dm
+++ b/code/modules/mob/living/living_say.dm
@@ -442,21 +442,10 @@ GLOBAL_LIST_INIT(message_modes_stat_limits, list(
/mob/living/proc/treat_message(message)
-
if(HAS_TRAIT(src, TRAIT_UNINTELLIGIBLE_SPEECH))
message = unintelligize(message)
- if(derpspeech)
- message = derpspeech(message, stuttering)
-
- if(stuttering)
- message = stutter(message)
-
- if(slurring)
- message = slur(message)
-
- if(cultslurring)
- message = cultslur(message)
+ SEND_SIGNAL(src, COMSIG_LIVING_TREAT_MESSAGE, args)
message = capitalize(message)
@@ -497,12 +486,12 @@ GLOBAL_LIST_INIT(message_modes_stat_limits, list(
. = "[verb_whisper] in [p_their()] last breath"
else if(message_mods[MODE_SING])
. = verb_sing
- else if(stuttering)
+ else if(has_status_effect(/datum/status_effect/speech/stutter))
if(HAS_TRAIT(src, TRAIT_SIGN_LANG))
. = "shakily signs"
else
. = "stammers"
- else if(derpspeech)
+ else if(has_status_effect(/datum/status_effect/speech/stutter/derpspeech))
if(HAS_TRAIT(src, TRAIT_SIGN_LANG))
. = "incoherently signs"
else
diff --git a/code/modules/mob/living/silicon/pai/pai_defense.dm b/code/modules/mob/living/silicon/pai/pai_defense.dm
index 517f2608b5f..066cd3497f5 100644
--- a/code/modules/mob/living/silicon/pai/pai_defense.dm
+++ b/code/modules/mob/living/silicon/pai/pai_defense.dm
@@ -14,13 +14,13 @@
//Ask and you shall receive
switch(rand(1, 3))
if(1)
- stuttering += 30/severity //temporary, clears in a few ticks after silent is over.
+ adjust_timed_status_effect(1 MINUTES / severity, /datum/status_effect/speech/stutter)
to_chat(src, span_danger("Warning: Feedback loop detected in speech module."))
if(2)
- slurring = INFINITY // permanent until speech is fixed through the pAI card UI by someone else.
+ adjust_timed_status_effect(INFINITY, /datum/status_effect/speech/slurring/drunk)
to_chat(src, span_danger("Warning: Audio synthesizer CPU stuck."))
if(3)
- derpspeech = 1 // Ditto.
+ adjust_timed_status_effect(INFINITY, /datum/status_effect/speech/stutter/derpspeech)
to_chat(src, span_danger("Warning: Vocabulary databank corrupted."))
if(prob(40))
mind.language_holder.selected_language = get_random_spoken_language()
diff --git a/code/modules/mob/living/simple_animal/bot/honkbot.dm b/code/modules/mob/living/simple_animal/bot/honkbot.dm
index d45de538b18..b07e09c43c7 100644
--- a/code/modules/mob/living/simple_animal/bot/honkbot.dm
+++ b/code/modules/mob/living/simple_animal/bot/honkbot.dm
@@ -61,11 +61,12 @@
icon_state = "[initial(icon_state)]-c"
addtimer(CALLBACK(src, /atom.proc/update_appearance), 0.2 SECONDS)
if(!ishuman(current_target))
- current_target.stuttering = 20
current_target.Paralyze(8 SECONDS)
+ current_target.set_timed_status_effect(40 SECONDS, /datum/status_effect/speech/stutter)
addtimer(CALLBACK(src, .proc/limiting_spam_false), cooldowntime)
return
- current_target.stuttering = 20
+
+ current_target.set_timed_status_effect(40 SECONDS, /datum/status_effect/speech/stutter)
var/obj/item/organ/ears/target_ears = current_target.getorganslot(ORGAN_SLOT_EARS)
if(target_ears && !HAS_TRAIT_FROM(current_target, TRAIT_DEAF, CLOTHING_TRAIT))
target_ears.adjustEarDamage(0, 5) //far less damage than the H.O.N.K.
diff --git a/code/modules/mob/living/simple_animal/bot/secbot.dm b/code/modules/mob/living/simple_animal/bot/secbot.dm
index 2dc6ed75240..84c332eedb7 100644
--- a/code/modules/mob/living/simple_animal/bot/secbot.dm
+++ b/code/modules/mob/living/simple_animal/bot/secbot.dm
@@ -303,13 +303,13 @@
if(harm)
weapon.attack(current_target, src)
if(ishuman(current_target))
- current_target.stuttering = 5
+ current_target.set_timed_status_effect(10 SECONDS, /datum/status_effect/speech/stutter)
current_target.StaminaKnockdown(60,TRUE) // SKYRAT EDIT CHANGE
var/mob/living/carbon/human/human_target = current_target
threat = human_target.assess_threat(judgement_criteria, weaponcheck = CALLBACK(src, .proc/check_for_weapons))
else
current_target.StaminaKnockdown(60,TRUE) // SKYRAT EDIT CHANGE
- current_target.stuttering = 5
+ current_target.set_timed_status_effect(10 SECONDS, /datum/status_effect/speech/stutter)
threat = current_target.assess_threat(judgement_criteria, weaponcheck = CALLBACK(src, .proc/check_for_weapons))
log_combat(src, target, "stunned")
diff --git a/code/modules/mob/living/status_procs.dm b/code/modules/mob/living/status_procs.dm
index 2e96cf1932c..2f20fd13510 100644
--- a/code/modules/mob/living/status_procs.dm
+++ b/code/modules/mob/living/status_procs.dm
@@ -636,3 +636,94 @@
*/
/mob/living/proc/reset_shocked()
flags_1 &= ~ SHOCKED_1
+
+/**
+ * Adjusts a timed status effect on the mob,taking into account any existing timed status effects.
+ * This can be any status effect that takes into account "duration" with their initialize arguments.
+ *
+ * Positive durations will add deciseconds to the duration of existing status effects
+ * or apply a new status effect of that duration to the mob.
+ *
+ * Negative durations will remove deciseconds from the duration of an existing version of the status effect,
+ * removing the status effect entirely if the duration becomes less than zero (less than the current world time).
+ *
+ * duration - the duration, in deciseconds, to add or remove from the effect
+ * effect - the type of status effect being adjusted on the mob
+ * max_duration - optional - if set, positive durations will only be added UP TO the passed max duration
+ */
+/mob/living/proc/adjust_timed_status_effect(duration, effect, max_duration)
+ if(!isnum(duration))
+ CRASH("adjust_timed_status_effect: called with an invalid duration. (Got: [duration])")
+
+ if(!ispath(effect, /datum/status_effect))
+ CRASH("adjust_timed_status_effect: called with an invalid effect type. (Got: [effect])")
+
+ // If we have a max duration set, we need to check our duration does not exceed it
+ if(isnum(max_duration))
+ if(max_duration <= 0)
+ CRASH("adjust_timed_status_effect: Called with an invalid max_duration. (Got: [max_duration])")
+
+ if(duration >= max_duration)
+ duration = max_duration
+
+ var/datum/status_effect/existing = has_status_effect(effect)
+ if(existing)
+ if(isnum(max_duration) && duration > 0)
+ // Check the duration remaining on the existing status effect
+ // If it's greater than / equal to our passed max duration, we don't need to do anything
+ var/remaining_duration = existing.duration - world.time
+ if(remaining_duration >= max_duration)
+ return
+
+ // Otherwise, add duration up to the max (max_duration - remaining_duration),
+ // or just add duration if it doesn't exceed our max at all
+ existing.duration += min(max_duration - remaining_duration, duration)
+
+ else
+ existing.duration += duration
+
+ // If the duration was decreased and is now less 0 seconds,
+ // qdel it / clean up the status effect immediately
+ // (rather than waiting for the process tick to handle it)
+ if(existing.duration <= world.time)
+ qdel(existing)
+
+ else if(duration > 0)
+ apply_status_effect(effect, duration)
+
+/**
+ * Sets a timed status effect of some kind on a mob to a specific value.
+ * If only_if_higher is TRUE, it will only set the value up to the passed duration,
+ * so any pre-existing status effects of the same type won't be reduced down
+ *
+ * duration - the duration, in deciseconds, of the effect. 0 or lower will either remove the current effect or do nothing if none are present
+ * effect - the type of status effect given to the mob
+ * only_if_higher - if TRUE, we will only set the effect to the new duration if the new duration is longer than any existing duration
+ */
+/mob/living/proc/set_timed_status_effect(duration, effect, only_if_higher = FALSE)
+ if(!isnum(duration))
+ CRASH("set_timed_status_effect: called with an invalid duration. (Got: [duration])")
+
+ if(!ispath(effect, /datum/status_effect))
+ CRASH("set_timed_status_effect: called with an invalid effect type. (Got: [effect])")
+
+ var/datum/status_effect/existing = has_status_effect(effect)
+ if(existing)
+ // set_timed_status_effect to 0 technically acts as a way to clear effects,
+ // though remove_status_effect would achieve the same goal more explicitly.
+ if(duration <= 0)
+ qdel(existing)
+ return
+
+ if(only_if_higher)
+ // If the existing status effect has a higher remaining duration
+ // than what we aim to set it to, don't downgrade it - do nothing (return)
+ var/remaining_duration = existing.duration - world.time
+ if(remaining_duration >= duration)
+ return
+
+ // Set the duration accordingly
+ existing.duration = world.time + duration
+
+ else if(duration > 0)
+ apply_status_effect(effect, duration)
diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm
index 435288f5f94..787cf1c33c7 100644
--- a/code/modules/mob/mob_helpers.dm
+++ b/code/modules/mob/mob_helpers.dm
@@ -72,129 +72,6 @@
. += "*"
return sanitize(.)
-/**
- * Makes you speak like you're drunk
- */
-/proc/slur(phrase)
- phrase = html_decode(phrase)
- var/leng = length(phrase)
- . = ""
- var/newletter = ""
- var/rawchar = ""
- for(var/i = 1, i <= leng, i += length(rawchar))
- rawchar = newletter = phrase[i]
- if(prob(33))
- var/lowerletter = lowertext(newletter)
- if(lowerletter == "o")
- newletter = "u"
- else if(lowerletter == "s")
- newletter = "ch"
- else if(lowerletter == "a")
- newletter = "ah"
- else if(lowerletter == "u")
- newletter = "oo"
- else if(lowerletter == "c")
- newletter = "k"
- if(prob(5))
- if(newletter == " ")
- newletter = "...huuuhhh..."
- else if(newletter == ".")
- newletter = " *BURP*."
- if(prob(15))
- switch(rand(1,3))
- if(1)
- newletter += "'"
- if(2)
- newletter += "[newletter]"
- if(3)
- newletter += "[newletter][newletter]"
- . += "[newletter]"
- return sanitize(.)
-
-/// Makes you talk like you got cult stunned, which is slurring but with some dark messages
-/proc/cultslur(phrase) // Inflicted on victims of a stun talisman
- phrase = html_decode(phrase)
- var/leng = length(phrase)
- . = ""
- var/newletter = ""
- var/rawchar = ""
- for(var/i = 1, i <= leng, i += length(rawchar))
- rawchar = newletter = phrase[i]
- if(prob(50))
- var/lowerletter = lowertext(newletter)
- if(lowerletter == "o")
- newletter = "u"
- else if(lowerletter == "t")
- newletter = "ch"
- else if(lowerletter == "a")
- newletter = "ah"
- else if(lowerletter == "u")
- newletter = "oo"
- else if(lowerletter == "c")
- newletter = " NAR "
- else if(lowerletter == "s")
- newletter = " SIE "
- if(prob(25))
- if(newletter == " ")
- newletter = " no hope... "
- else if(newletter == "H")
- newletter = " IT COMES... "
-
- if(prob(33))
- switch(rand(1, 5))
- if(1)
- newletter = "'"
- if(2)
- newletter += "agn"
- if(3)
- newletter = "fth"
- if(4)
- newletter = "nglu"
- if(5)
- newletter = "glor"
- . += newletter
- return sanitize(.)
-
-///Adds stuttering to the message passed in
-/proc/stutter(phrase)
- phrase = html_decode(phrase)
- var/leng = length(phrase)
- . = ""
- var/newletter = ""
- var/rawchar = ""
- var/static/regex/nostutter = regex(@@[aeiouAEIOU ""''()[\]{}.!?,:;_`~-]@)
- for(var/i = 1, i <= leng, i += length(rawchar))
- rawchar = newletter = phrase[i]
- if(prob(80) && !nostutter.Find(rawchar))
- if(prob(10))
- newletter = "[newletter]-[newletter]-[newletter]-[newletter]"
- else if(prob(20))
- newletter = "[newletter]-[newletter]-[newletter]"
- else if (prob(5))
- newletter = ""
- else
- newletter = "[newletter]-[newletter]"
- . += newletter
- return sanitize(.)
-
-///Convert a message to derpy speak
-/proc/derpspeech(message, stuttering)
- message = replacetext(message, " am ", " ")
- message = replacetext(message, " is ", " ")
- message = replacetext(message, " are ", " ")
- message = replacetext(message, "you", "u")
- message = replacetext(message, "help", "halp")
- message = replacetext(message, "grief", "grife")
- message = replacetext(message, "space", "spess")
- message = replacetext(message, "carp", "crap")
- message = replacetext(message, "reason", "raisin")
- if(prob(50))
- message = uppertext(message)
- message += "[stutter(pick("!", "!!", "!!!"))]"
- if(!stuttering && prob(15))
- message = stutter(message)
- return message
-
/**
* Turn text into complete gibberish!
*
diff --git a/code/modules/ninja/suit/ninja_equipment_actions/ninja_adrenaline.dm b/code/modules/ninja/suit/ninja_equipment_actions/ninja_adrenaline.dm
index ff368603eba..06fad9c95fb 100644
--- a/code/modules/ninja/suit/ninja_equipment_actions/ninja_adrenaline.dm
+++ b/code/modules/ninja/suit/ninja_equipment_actions/ninja_adrenaline.dm
@@ -25,7 +25,7 @@
ninja.SetImmobilized(0)
ninja.SetParalyzed(0)
ninja.adjustStaminaLoss(-200)
- ninja.stuttering = 0
+ ninja.remove_status_effect(/datum/status_effect/speech/stutter)
ninja.reagents.add_reagent(/datum/reagent/medicine/stimulants, 5)
ninja.say(pick("A CORNERED FOX IS MORE DANGEROUS THAN A JACKAL!","HURT ME MOOORRREEE!","IMPRESSIVE!"), forced = "ninjaboost")
a_boost = FALSE
diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm
index af587e41b02..537627b38fe 100644
--- a/code/modules/projectiles/projectile.dm
+++ b/code/modules/projectiles/projectile.dm
@@ -142,18 +142,22 @@
var/decayedRange //stores original range
var/reflect_range_decrease = 5 //amount of original range that falls off when reflecting, so it doesn't go forever
var/reflectable = NONE // Can it be reflected or not?
- //Effects
+ // Status effects applied on hit
var/stun = 0
var/knockdown = 0
var/paralyze = 0
var/immobilize = 0
var/unconscious = 0
- var/stutter = 0
- var/slur = 0
var/eyeblur = 0
var/drowsy = 0
- var/stamina = 0
var/jitter = 0
+ /// Extra stamina damage applied on projectile hit (in addition to the main damage)
+ var/stamina = 0
+ /// Stuttering applied on projectile hit
+ var/stutter = 0 SECONDS
+ /// Slurring applied on projectile hit
+ var/slur = 0 SECONDS
+
var/dismemberment = 0 //The higher the number, the greater the bonus to dismembering. 0 will not dismember at all.
var/impact_effect_type //what type of impact effect to show when hitting something
var/log_override = FALSE //is this type spammed enough to not log? (KAs)
diff --git a/code/modules/projectiles/projectile/bullets/cannonball.dm b/code/modules/projectiles/projectile/bullets/cannonball.dm
index 3fe2761b391..587e1a2e99d 100644
--- a/code/modules/projectiles/projectile/bullets/cannonball.dm
+++ b/code/modules/projectiles/projectile/bullets/cannonball.dm
@@ -7,7 +7,7 @@
projectile_piercing = ALL
dismemberment = 0
paralyze = 5 SECONDS
- stutter = 10 SECONDS
+ stutter = 20 SECONDS
embedding = null
hitsound = 'sound/effects/meteorimpact.ogg'
hitsound_wall = 'sound/weapons/sonic_jackhammer.ogg'
diff --git a/code/modules/projectiles/projectile/bullets/shotgun.dm b/code/modules/projectiles/projectile/bullets/shotgun.dm
index 6d7bb9e97c3..ab4394a68b5 100644
--- a/code/modules/projectiles/projectile/bullets/shotgun.dm
+++ b/code/modules/projectiles/projectile/bullets/shotgun.dm
@@ -34,7 +34,7 @@
name = "stunslug"
damage = 5
paralyze = 100
- stutter = 5
+ stutter = 10 SECONDS
jitter = 20
range = 7
icon_state = "spark"
diff --git a/code/modules/projectiles/projectile/energy/ebow.dm b/code/modules/projectiles/projectile/energy/ebow.dm
index 6d489029057..3d76e9ae1cb 100644
--- a/code/modules/projectiles/projectile/energy/ebow.dm
+++ b/code/modules/projectiles/projectile/energy/ebow.dm
@@ -7,7 +7,7 @@
stamina = 60
eyeblur = 10
knockdown = 10
- slur = 5
+ slur = 10 SECONDS
/obj/projectile/energy/bolt/halloween
name = "candy corn"
diff --git a/code/modules/projectiles/projectile/energy/stun.dm b/code/modules/projectiles/projectile/energy/stun.dm
index 84d7adc5ed4..e556f60bcef 100644
--- a/code/modules/projectiles/projectile/energy/stun.dm
+++ b/code/modules/projectiles/projectile/energy/stun.dm
@@ -3,9 +3,8 @@
icon_state = "spark"
color = "#FFFF00"
nodamage = FALSE
- //paralyze = 100 //SKYRAT EDIT REMOVAL - COMBAT
- stamina = 80 //SKYRAT EDIT ADDITION - COMBAT
- stutter = 5
+ stamina = 80 // SKYRAT EDIT CHANGE
+ stutter = 10 SECONDS
jitter = 20
hitsound = 'sound/weapons/taserhit.ogg'
//range = 7 //ORIGINAL
diff --git a/code/modules/projectiles/projectile/special/hallucination.dm b/code/modules/projectiles/projectile/special/hallucination.dm
index 306c684b894..9cb974e3806 100644
--- a/code/modules/projectiles/projectile/special/hallucination.dm
+++ b/code/modules/projectiles/projectile/special/hallucination.dm
@@ -167,7 +167,7 @@
/obj/projectile/hallucination/taser/hal_apply_effect()
hal_target.Paralyze(100)
- hal_target.stuttering += 20
+ hal_target.adjust_timed_status_effect(40 SECONDS, /datum/status_effect/speech/stutter)
if(hal_target.dna && hal_target.dna.check_mutation(/datum/mutation/human/hulk))
hal_target.say(pick(";RAAAAAAAARGH!", ";HNNNNNNNNNGGGGGGH!", ";GWAAAAAAAARRRHHH!", "NNNNNNNNGGGGGGGGHH!", ";AAAAAAARRRGH!" ), forced = "hulk")
else if((hal_target.status_flags & CANKNOCKDOWN) && !HAS_TRAIT(hal_target, TRAIT_STUNIMMUNE))
@@ -200,7 +200,7 @@
/obj/projectile/hallucination/ebow/hal_apply_effect()
hal_target.Paralyze(100)
- hal_target.stuttering += 5
+ hal_target.adjust_timed_status_effect(10 SECONDS, /datum/status_effect/speech/stutter)
hal_target.adjustStaminaLoss(8)
/obj/projectile/hallucination/change
diff --git a/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm b/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm
index 3bdfd7620db..04c1bbbbd74 100644
--- a/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm
+++ b/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm
@@ -1503,9 +1503,7 @@ All effects don't start immediately, but rather get worse over time; the rate is
if(!HAS_TRAIT(drinker, TRAIT_ALCOHOL_TOLERANCE))
drinker.set_confusion(max(drinker.get_confusion() + (2 * REM * delta_time),0))
drinker.Dizzy(10 * REM * delta_time)
- if (!drinker.slurring)
- drinker.slurring = 1 * REM * delta_time
- drinker.slurring += 3 * REM * delta_time
+ drinker.adjust_timed_status_effect(6 SECONDS * REM * delta_time, /datum/status_effect/speech/slurring/drunk)
switch(current_cycle)
if(51 to 200)
drinker.Sleeping(100 * REM * delta_time)
@@ -1532,9 +1530,8 @@ All effects don't start immediately, but rather get worse over time; the rate is
drinker.dizziness += 1.5 * REM * delta_time
switch(current_cycle)
if(15 to 45)
- if(!drinker.slurring)
- drinker.slurring = 1 * REM * delta_time
- drinker.slurring += 3 * REM * delta_time
+ drinker.adjust_timed_status_effect(3 SECONDS * REM * delta_time, /datum/status_effect/speech/slurring/drunk)
+
if(45 to 55)
if(DT_PROB(30, delta_time))
drinker.set_confusion(max(drinker.get_confusion() + 3, 0))
@@ -1607,8 +1604,8 @@ All effects don't start immediately, but rather get worse over time; the rate is
chemical_flags = REAGENT_CAN_BE_SYNTHESIZED
/datum/reagent/consumable/ethanol/hippies_delight/on_mob_life(mob/living/carbon/drinker, delta_time, times_fired)
- if (!drinker.slurring)
- drinker.slurring = 1 * REM * delta_time
+ drinker.set_timed_status_effect(1 SECONDS * REM * delta_time, /datum/status_effect/speech/slurring/drunk, only_if_higher = TRUE)
+
switch(current_cycle)
if(1 to 5)
drinker.Dizzy(10 * REM * delta_time)
@@ -1666,9 +1663,9 @@ All effects don't start immediately, but rather get worse over time; the rate is
liquid_fire_power = 2 //SKYRAT EDIT ADDITION
/datum/reagent/consumable/ethanol/narsour/on_mob_life(mob/living/carbon/drinker, delta_time, times_fired)
- drinker.cultslurring = min(drinker.cultslurring + (3 * REM * delta_time), 3)
- drinker.stuttering = min(drinker.stuttering + (3 * REM * delta_time), 3)
- ..()
+ drinker.adjust_timed_status_effect(6 SECONDS * REM * delta_time, /datum/status_effect/speech/slurring/cult, max_duration = 6 SECONDS)
+ drinker.adjust_timed_status_effect(6 SECONDS * REM * delta_time, /datum/status_effect/speech/stutter, max_duration = 6 SECONDS)
+ return ..()
/datum/reagent/consumable/ethanol/triple_sec
name = "Triple Sec"
@@ -2447,7 +2444,7 @@ All effects don't start immediately, but rather get worse over time; the rate is
if(drinker.mind?.holy_role)
drinker.adjustFireLoss(-2.5 * REM * delta_time, 0)
drinker.jitteriness = max(drinker.jitteriness - (1 * REM * delta_time), 0)
- drinker.stuttering = max(drinker.stuttering - (1 * REM * delta_time), 0)
+ drinker.adjust_timed_status_effect(-2 SECONDS * REM * delta_time, /datum/status_effect/speech/stutter)
return ..()
/datum/reagent/consumable/ethanol/blazaam
diff --git a/code/modules/reagents/chemistry/reagents/drug_reagents.dm b/code/modules/reagents/chemistry/reagents/drug_reagents.dm
index 3e593b513ec..5b2e2c55d48 100644
--- a/code/modules/reagents/chemistry/reagents/drug_reagents.dm
+++ b/code/modules/reagents/chemistry/reagents/drug_reagents.dm
@@ -445,8 +445,8 @@
addiction_types = list(/datum/addiction/hallucinogens = 12)
/datum/reagent/drug/mushroomhallucinogen/on_mob_life(mob/living/carbon/psychonaut, delta_time, times_fired)
- if(!psychonaut.slurring)
- psychonaut.slurring = 1 * REM * delta_time
+ psychonaut.set_timed_status_effect(1 SECONDS * REM * delta_time, /datum/status_effect/speech/slurring/drunk, only_if_higher = TRUE)
+
switch(current_cycle)
if(1 to 5)
if(DT_PROB(5, delta_time))
diff --git a/code/modules/reagents/chemistry/reagents/medicine_reagents.dm b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm
index 2986415b634..0bfd6817dc7 100644
--- a/code/modules/reagents/chemistry/reagents/medicine_reagents.dm
+++ b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm
@@ -82,9 +82,10 @@
M.silent = FALSE
M.dizziness = 0
M.disgust = 0
- M.set_drowsyness(0)
- M.stuttering = 0
- M.slurring = 0
+ M.drowsyness = 0
+ // Remove all speech related status effects
+ for(var/effect in typesof(/datum/status_effect/speech))
+ M.remove_status_effect(effect)
M.jitteriness = 0
M.hallucination = 0
REMOVE_TRAITS_NOT_IN(M, list(SPECIES_TRAIT, ROUNDSTART_TRAIT, ORGAN_TRAIT))
@@ -997,13 +998,13 @@
ph = 4
purity = REAGENT_STANDARD_PURITY
chemical_flags = REAGENT_CAN_BE_SYNTHESIZED
- inverse_chem_val = 0.35
- inverse_chem = /datum/reagent/inverse/antihol
+ inverse_chem_val = 0.35
+ inverse_chem = /datum/reagent/inverse/antihol
/datum/reagent/medicine/antihol/on_mob_life(mob/living/carbon/M, delta_time, times_fired)
M.dizziness = 0
M.set_drowsyness(0)
- M.slurring = 0
+ M.remove_status_effect(/datum/status_effect/speech/slurring/drunk)
M.set_confusion(0)
M.reagents.remove_all_type(/datum/reagent/consumable/ethanol, 3 * REM * delta_time * normalise_creation_purity(), FALSE, TRUE)
M.adjustToxLoss(-0.2 * REM * delta_time, 0)
@@ -1354,7 +1355,7 @@
switch(overdose_progress)
if(1 to 40)
M.jitteriness = min(M.jitteriness + (1 * REM * delta_time), 10)
- M.stuttering = min(M.stuttering + (1 * REM * delta_time), 10)
+ M.adjust_timed_status_effect(2 SECONDS * REM * delta_time, /datum/status_effect/speech/stutter, max_duration = 20 SECONDS)
M.Dizzy(5 * REM * delta_time)
if(DT_PROB(30, delta_time))
M.losebreath++
@@ -1362,7 +1363,7 @@
M.adjustOxyLoss(0.1 * REM * delta_time, 0)
M.adjustStaminaLoss(0.1 * REM * delta_time, 0)
M.jitteriness = min(M.jitteriness + (1 * REM * delta_time), 20)
- M.stuttering = min(M.stuttering + (1 * REM * delta_time), 20)
+ M.adjust_timed_status_effect(2 SECONDS * REM * delta_time, /datum/status_effect/speech/stutter, max_duration = 40 SECONDS)
M.Dizzy(10 * REM * delta_time)
if(DT_PROB(30, delta_time))
M.losebreath++
diff --git a/code/modules/reagents/chemistry/reagents/other_reagents.dm b/code/modules/reagents/chemistry/reagents/other_reagents.dm
index 21c41d5bd78..5a3327c0892 100644
--- a/code/modules/reagents/chemistry/reagents/other_reagents.dm
+++ b/code/modules/reagents/chemistry/reagents/other_reagents.dm
@@ -292,9 +292,7 @@
for(var/datum/action/innate/cult/blood_spell/BS in BM.spells)
qdel(BS)
if(data["misc"] >= (25 SECONDS)) // 10 units
- if(!M.stuttering)
- M.stuttering = 1
- M.stuttering = min(M.stuttering + (2 * delta_time), 10)
+ M.adjust_timed_status_effect(4 SECONDS * delta_time, /datum/status_effect/speech/stutter, max_duration = 20 SECONDS)
M.Dizzy(5)
if(IS_CULTIST(M) && DT_PROB(10, delta_time))
M.say(pick("Av'te Nar'Sie","Pa'lid Mors","INO INO ORA ANA","SAT ANA!","Daim'niodeis Arc'iai Le'eones","R'ge Na'sie","Diabo us Vo'iscum","Eld' Mon Nobis"), forced = "holy water")
@@ -308,7 +306,7 @@
M.mind.remove_antag_datum(/datum/antagonist/cult)
M.Unconscious(100)
M.jitteriness = 0
- M.stuttering = 0
+ M.remove_status_effect(/datum/status_effect/speech/stutter)
holder.remove_reagent(type, volume) // maybe this is a little too perfect and a max() cap on the statuses would be better??
return
holder.remove_reagent(type, 1 * REAGENTS_METABOLISM * delta_time) //fixed consumption to prevent balancing going out of whack
diff --git a/code/modules/reagents/chemistry/reagents/toxin_reagents.dm b/code/modules/reagents/chemistry/reagents/toxin_reagents.dm
index 5cbd3f2d9d1..2802580546f 100644
--- a/code/modules/reagents/chemistry/reagents/toxin_reagents.dm
+++ b/code/modules/reagents/chemistry/reagents/toxin_reagents.dm
@@ -254,7 +254,7 @@
if(1 to 5)
M.add_confusion(1 * REM * delta_time)
M.adjust_drowsyness(1 * REM * delta_time)
- M.slurring += 3 * REM * delta_time
+ M.adjust_timed_status_effect(6 SECONDS * REM * delta_time, /datum/status_effect/speech/slurring/drunk)
if(5 to 8)
M.adjustStaminaLoss(40 * REM * delta_time, 0)
if(9 to INFINITY)
diff --git a/code/modules/surgery/organs/stomach/_stomach.dm b/code/modules/surgery/organs/stomach/_stomach.dm
index 39793ae622b..d386d48e7c3 100644
--- a/code/modules/surgery/organs/stomach/_stomach.dm
+++ b/code/modules/surgery/organs/stomach/_stomach.dm
@@ -210,7 +210,7 @@
var/pukeprob = 2.5 + (0.025 * disgusted.disgust)
if(disgusted.disgust >= DISGUST_LEVEL_GROSS)
if(DT_PROB(5, delta_time))
- disgusted.stuttering += 1
+ disgusted.adjust_timed_status_effect(2 SECONDS, /datum/status_effect/speech/stutter)
disgusted.add_confusion(2)
if(DT_PROB(5, delta_time) && !disgusted.stat)
to_chat(disgusted, span_warning("You feel kind of iffy..."))
@@ -218,7 +218,7 @@
if(disgusted.disgust >= DISGUST_LEVEL_VERYGROSS)
if(DT_PROB(pukeprob, delta_time)) //iT hAndLeS mOrE ThaN PukInG
disgusted.add_confusion(2.5)
- disgusted.stuttering += 1
+ disgusted.adjust_timed_status_effect(2 SECONDS, /datum/status_effect/speech/stutter)
disgusted.vomit(10, 0, 1, 0, 1, 0)
disgusted.Dizzy(5)
if(disgusted.disgust >= DISGUST_LEVEL_DISGUSTED)
diff --git a/code/modules/vehicles/mecha/equipment/weapons/weapons.dm b/code/modules/vehicles/mecha/equipment/weapons/weapons.dm
index aa07af6affb..1eca7e3422e 100644
--- a/code/modules/vehicles/mecha/equipment/weapons/weapons.dm
+++ b/code/modules/vehicles/mecha/equipment/weapons/weapons.dm
@@ -194,7 +194,7 @@
continue
to_chat(M, "HONK")
M.SetSleeping(0)
- M.stuttering += 20
+ M.adjust_timed_status_effect(40 SECONDS, /datum/status_effect/speech/stutter)
var/obj/item/organ/ears/ears = M.getorganslot(ORGAN_SLOT_EARS)
if(ears)
ears.adjustEarDamage(0, 30)
diff --git a/modular_skyrat/modules/contractor/code/items/baton.dm b/modular_skyrat/modules/contractor/code/items/baton.dm
index 2fdb0ffa725..4bb372d8825 100644
--- a/modular_skyrat/modules/contractor/code/items/baton.dm
+++ b/modular_skyrat/modules/contractor/code/items/baton.dm
@@ -76,7 +76,7 @@
if((upgrade_flags & BATON_FOCUS_UPGRADE))
if(target == user.mind?.opposing_force?.contractor_hub?.current_contract?.contract.target?.current) // Pain
target.apply_damage(BONUS_STAMINA_DAM, STAMINA, BODY_ZONE_CHEST)
- target.stuttering += BONUS_STUTTER
+ target.adjust_timed_status_effect(10 SECONDS, /datum/status_effect/speech/stutter)
/obj/item/melee/baton/telescopic/contractor_baton/examine(mob/user)
. = ..()
diff --git a/modular_skyrat/modules/customization/modules/mob/living/carbon/human/human.dm b/modular_skyrat/modules/customization/modules/mob/living/carbon/human/human.dm
index 304178f27f1..7828865f9ab 100644
--- a/modular_skyrat/modules/customization/modules/mob/living/carbon/human/human.dm
+++ b/modular_skyrat/modules/customization/modules/mob/living/carbon/human/human.dm
@@ -110,7 +110,7 @@
return
var/list/choices = list("Drunkenness", "Stuttering", "Jittering")
- if(slurring >= 10 || stuttering >= 10 || jitteriness >= 10) //Give the option to end the impairment if there's one ongoing.
+ if(jitteriness >= 10) //Give the option to end the impairment if there's one ongoing.
var/disable = tgui_input_list(src, "Stop performing existing impairment?", "Impairments", choices)
if(disable)
acting_expiry(disable)
@@ -122,10 +122,6 @@
var/duration = tgui_input_number(src, "How long would you like to feign [impairment] for?", "Duration in seconds", 25, 36000)
switch(impairment)
- if("Drunkenness")
- slurring = duration
- if("Stuttering")
- stuttering = duration
if("Jittering")
jitteriness = duration
@@ -136,10 +132,6 @@
/mob/living/carbon/human/proc/acting_expiry(var/impairment) //End only the impairment we're affected by.
if(impairment)
switch(impairment)
- if("Drunkenness")
- slurring = 0
- if("Stuttering")
- stuttering = 0
if("Jittering")
jitteriness = 0
to_chat(src, "You are no longer feigning [impairment].")
diff --git a/modular_skyrat/modules/customization/modules/surgery/organs/vox.dm b/modular_skyrat/modules/customization/modules/surgery/organs/vox.dm
index 5a5e620d835..97a24b6c511 100644
--- a/modular_skyrat/modules/customization/modules/surgery/organs/vox.dm
+++ b/modular_skyrat/modules/customization/modules/surgery/organs/vox.dm
@@ -35,11 +35,11 @@
to_chat(owner, span_boldwarning("You feel [pick("like your brain is being fried", "a sharp pain in your head")]!"))
owner.adjustOrganLoss(ORGAN_SLOT_BRAIN, 20, 150)
owner.jitteriness += 30
- owner.stuttering += 30
+ owner.adjust_timed_status_effect(30 SECONDS, /datum/status_effect/speech/stutter)
owner.add_confusion(10)
if(2)
to_chat(owner, span_warning("You feel [pick("disoriented", "confused", "dizzy")]."))
owner.adjustOrganLoss(ORGAN_SLOT_BRAIN, 10, 150)
owner.jitteriness += 10
- owner.stuttering += 10
+ owner.adjust_timed_status_effect(30 SECONDS, /datum/status_effect/speech/stutter)
owner.add_confusion(3)
diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_clothing/shockcollar.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_clothing/shockcollar.dm
index 0445de35d89..8e46051af67 100644
--- a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_clothing/shockcollar.dm
+++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_clothing/shockcollar.dm
@@ -50,7 +50,7 @@
L.Paralyze(30)
L.adjustPain(10)
- L.stuttering += 60
+ L.adjust_timed_status_effect(30 SECONDS, /datum/status_effect/speech/stutter)
if(master)
if(isassembly(master))
diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/kinky_shocker.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/kinky_shocker.dm
index 60f2ccd1a0e..5cbe4baf72b 100644
--- a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/kinky_shocker.dm
+++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/kinky_shocker.dm
@@ -284,4 +284,4 @@
target.do_jitter_animation()
target.adjustStaminaLoss(3)
target.adjustPain(9)
- target.stuttering += 20
+ target.adjust_timed_status_effect(30 SECONDS, /datum/status_effect/speech/stutter)
diff --git a/modular_skyrat/modules/morenarcotics/code/quaalude.dm b/modular_skyrat/modules/morenarcotics/code/quaalude.dm
index ba953a87e83..fc7b4650341 100644
--- a/modular_skyrat/modules/morenarcotics/code/quaalude.dm
+++ b/modular_skyrat/modules/morenarcotics/code/quaalude.dm
@@ -21,8 +21,7 @@
var/atom/movable/plane_master_controller/game_plane_master_controller = M.hud_used.plane_master_controllers[PLANE_MASTERS_GAME]
game_plane_master_controller.add_filter("quaalude_wave", 10, wave_filter(300, 300, 3, 0, WAVE_SIDEWAYS))
M.set_drugginess(15 * REM * delta_time)
- if(!M.slurring)
- M.slurring = 10 * REM * delta_time
+ M.adjust_timed_status_effect(1 MINUTES, /datum/status_effect/speech/slurring/drunk)
M.Dizzy(5 * REM * delta_time)
M.adjustStaminaLoss(-5 * REM * delta_time, 0)
if(DT_PROB(3.5, delta_time))
diff --git a/strings/slurring_cult_text.json b/strings/slurring_cult_text.json
new file mode 100644
index 00000000000..192893bdc85
--- /dev/null
+++ b/strings/slurring_cult_text.json
@@ -0,0 +1,27 @@
+{
+ "replacements": {
+ "characters": {
+ "common": {
+ "o": "u",
+ "t": "ch",
+ "a": "ah",
+ "u": "oo",
+ "c": " NAR ",
+ "s": " SIE "
+ },
+ "uncommon": {
+ " ": " no hope... ",
+ "H": " IT COMES... "
+ }
+ },
+ "string_replacements": [
+ "'",
+ "fth",
+ "nglu",
+ "glor"
+ ],
+ "string_additions": [
+ "agn"
+ ]
+ }
+}
diff --git a/strings/slurring_drunk_text.json b/strings/slurring_drunk_text.json
new file mode 100644
index 00000000000..a68a00b0858
--- /dev/null
+++ b/strings/slurring_drunk_text.json
@@ -0,0 +1,20 @@
+{
+ "replacements": {
+ "characters": {
+ "common": {
+ "o": "u",
+ "s": "ch",
+ "a": "ah",
+ "u": "oo",
+ "c": "k"
+ },
+ "uncommon": {
+ " ": "...huuuhhh...",
+ ".": " *BURP*."
+ }
+ },
+ "string_additions": [
+ "'"
+ ]
+ }
+}
diff --git a/strings/slurring_heretic_text.json b/strings/slurring_heretic_text.json
new file mode 100644
index 00000000000..d5b87ec911c
--- /dev/null
+++ b/strings/slurring_heretic_text.json
@@ -0,0 +1,32 @@
+{
+ "replacements": {
+ "characters": {
+ "common": {
+ "o": "u",
+ "t": "ch",
+ "a": "ah",
+ "c": "th",
+ "i": "ks",
+ "m": "nth"
+ },
+ "uncommon": {
+ " ": " endless... ",
+ "H": " THE HANDS... ",
+ "h": " BRIGHT ",
+ "s": " LEAK ",
+ "r": " CRACK "
+ }
+ },
+ "string_replacements": [
+ "'",
+ "br",
+ "th",
+ "see",
+ "etch"
+ ],
+ "string_additions": [
+ "ah",
+ "wth"
+ ]
+ }
+}
diff --git a/tgstation.dme b/tgstation.dme
index bc80b9451af..e98dd0cbbbf 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -1165,12 +1165,13 @@
#include "code\datums\station_traits\positive_traits.dm"
#include "code\datums\status_effects\agent_pinpointer.dm"
#include "code\datums\status_effects\buffs.dm"
-#include "code\datums\status_effects\debuffs.dm"
#include "code\datums\status_effects\drug_effects.dm"
#include "code\datums\status_effects\gas.dm"
#include "code\datums\status_effects\neutral.dm"
#include "code\datums\status_effects\status_effect.dm"
#include "code\datums\status_effects\wound_effects.dm"
+#include "code\datums\status_effects\debuffs\debuffs.dm"
+#include "code\datums\status_effects\debuffs\speech_debuffs.dm"
#include "code\datums\weather\weather.dm"
#include "code\datums\weather\weather_types\ash_storm.dm"
#include "code\datums\weather\weather_types\floor_is_lava.dm"