Files
Bubberstation/code/datums/brain_damage/severe.dm
Y0SH1M4S73R aac161bce9 Allows blocking language speech and comprehension separately (#91884)
## About The Pull Request

There was a PR I wanted to make, but one of the features I wanted the
addition to have was for an affected mob to only speak aphasic, but
understand whatever languages they'd normally be able to understand.
This, in turn, required refactoring language holders to separately block
language speech and comprehension. This change, I decided, would be good
to split into its own PR.

## Why It's Good For The Game

Allows more fine-grained control over what languages an atom is blocked
from speaking or understanding. This will be useful for future PRs or
admin events.

## Changelog

no player-facing changes
2025-07-07 15:55:16 +12:00

372 lines
14 KiB
Plaintext

//Severe traumas, when your brain gets abused way too much.
//These range from very annoying to completely debilitating.
//They cannot be cured with chemicals, and require brain surgery to solve.
/datum/brain_trauma/severe
abstract_type = /datum/brain_trauma/severe
resilience = TRAUMA_RESILIENCE_SURGERY
/datum/brain_trauma/severe/mute
name = "Mutism"
desc = "Patient is completely unable to speak."
scan_desc = "extensive damage to the brain's speech center"
gain_text = span_warning("You forget how to speak!")
lose_text = span_notice("You suddenly remember how to speak.")
/datum/brain_trauma/severe/mute/on_gain()
ADD_TRAIT(owner, TRAIT_MUTE, TRAUMA_TRAIT)
. = ..()
/datum/brain_trauma/severe/mute/on_lose()
REMOVE_TRAIT(owner, TRAIT_MUTE, TRAUMA_TRAIT)
..()
/datum/brain_trauma/severe/aphasia
name = "Aphasia"
desc = "Patient is unable to speak or understand any language."
scan_desc = "extensive damage to the brain's language center"
gain_text = span_warning("You have trouble forming words in your head...")
lose_text = span_notice("You suddenly remember how languages work.")
/datum/brain_trauma/severe/aphasia/on_gain()
owner.add_blocked_language(subtypesof(/datum/language) - /datum/language/aphasia, source = LANGUAGE_APHASIA)
owner.grant_language(/datum/language/aphasia, source = LANGUAGE_APHASIA)
. = ..()
/datum/brain_trauma/severe/aphasia/on_lose()
if(!QDELING(owner))
owner.remove_blocked_language(subtypesof(/datum/language), source = LANGUAGE_APHASIA)
owner.remove_language(/datum/language/aphasia, source = LANGUAGE_APHASIA)
..()
/datum/brain_trauma/severe/blindness
name = "Cerebral Blindness"
desc = "Patient's brain is no longer connected to its eyes."
scan_desc = "extensive damage to the brain's occipital lobe"
gain_text = span_warning("You can't see!")
lose_text = span_notice("Your vision returns.")
/datum/brain_trauma/severe/blindness/on_gain()
owner.become_blind(TRAUMA_TRAIT)
. = ..()
/datum/brain_trauma/severe/blindness/on_lose()
owner.cure_blind(TRAUMA_TRAIT)
..()
/datum/brain_trauma/severe/paralysis
name = "Paralysis"
desc = "Patient's brain can no longer control part of its motor functions."
scan_desc = "cerebral paralysis"
gain_text = ""
lose_text = ""
var/paralysis_type
var/list/paralysis_traits = list()
//for descriptions
/datum/brain_trauma/severe/paralysis/New(specific_type)
if(specific_type)
paralysis_type = specific_type
if(!paralysis_type)
paralysis_type = pick("full","left","right","arms","legs","r_arm","l_arm","r_leg","l_leg")
var/subject
switch(paralysis_type)
if("full")
subject = "your body"
paralysis_traits = list(TRAIT_PARALYSIS_L_ARM, TRAIT_PARALYSIS_R_ARM, TRAIT_PARALYSIS_L_LEG, TRAIT_PARALYSIS_R_LEG)
if("left")
subject = "the left side of your body"
paralysis_traits = list(TRAIT_PARALYSIS_L_ARM, TRAIT_PARALYSIS_L_LEG)
if("right")
subject = "the right side of your body"
paralysis_traits = list(TRAIT_PARALYSIS_R_ARM, TRAIT_PARALYSIS_R_LEG)
if("arms")
subject = "your arms"
paralysis_traits = list(TRAIT_PARALYSIS_L_ARM, TRAIT_PARALYSIS_R_ARM)
if("legs")
subject = "your legs"
paralysis_traits = list(TRAIT_PARALYSIS_L_LEG, TRAIT_PARALYSIS_R_LEG)
if("r_arm")
subject = "your right arm"
paralysis_traits = list(TRAIT_PARALYSIS_R_ARM)
if("l_arm")
subject = "your left arm"
paralysis_traits = list(TRAIT_PARALYSIS_L_ARM)
if("r_leg")
subject = "your right leg"
paralysis_traits = list(TRAIT_PARALYSIS_R_LEG)
if("l_leg")
subject = "your left leg"
paralysis_traits = list(TRAIT_PARALYSIS_L_LEG)
gain_text = span_warning("You can't feel [subject] anymore!")
lose_text = span_notice("You can feel [subject] again!")
/datum/brain_trauma/severe/paralysis/on_gain()
. = ..()
for(var/X in paralysis_traits)
ADD_TRAIT(owner, X, TRAUMA_TRAIT)
/datum/brain_trauma/severe/paralysis/on_lose()
..()
for(var/X in paralysis_traits)
REMOVE_TRAIT(owner, X, TRAUMA_TRAIT)
/datum/brain_trauma/severe/paralysis/paraplegic
random_gain = FALSE
paralysis_type = "legs"
resilience = TRAUMA_RESILIENCE_ABSOLUTE
/datum/brain_trauma/severe/paralysis/hemiplegic
random_gain = FALSE
resilience = TRAUMA_RESILIENCE_ABSOLUTE
/datum/brain_trauma/severe/paralysis/hemiplegic/left
paralysis_type = "left"
/datum/brain_trauma/severe/paralysis/hemiplegic/right
paralysis_type = "right"
/datum/brain_trauma/severe/narcolepsy
name = "Narcolepsy"
desc = "Patient may involuntarily fall asleep during normal activities."
scan_desc = "traumatic narcolepsy"
gain_text = span_warning("You have a constant feeling of drowsiness...")
lose_text = span_notice("You feel awake and aware again.")
/// Odds seconds_per_tick the user falls asleep
var/sleep_chance = 1
/// Odds seconds_per_tick the user falls asleep while running
var/sleep_chance_running = 2
/// Odds seconds_per_tick the user falls asleep while drowsy
var/sleep_chance_drowsy = 3
/// Time values for how long the user will stay drowsy
var/drowsy_time_minimum = 20 SECONDS
var/drowsy_time_maximum = 30 SECONDS
/// Time values for how long the user will stay asleep
var/sleep_time_minimum = 6 SECONDS
var/sleep_time_maximum = 6 SECONDS
/datum/brain_trauma/severe/narcolepsy/on_life(seconds_per_tick, times_fired)
if(owner.IsSleeping())
return
/// If any of these are in the user's blood, return early
var/static/list/immunity_medicine = list(
/datum/reagent/medicine/modafinil,
/datum/reagent/medicine/synaptizine,
) //don't add too many, as most stimulant reagents already have a drowsy-removing effect
for(var/medicine in immunity_medicine)
if(owner.reagents.has_reagent(medicine))
return
var/drowsy = !!owner.has_status_effect(/datum/status_effect/drowsiness)
var/caffeinated = HAS_TRAIT(owner, TRAIT_STIMULATED)
var/final_sleep_chance = sleep_chance
if(owner.move_intent == MOVE_INTENT_RUN)
final_sleep_chance += sleep_chance_running
if(drowsy)
final_sleep_chance += sleep_chance_drowsy //stack drowsy ontop of base or running odds with the += operator
if(caffeinated)
final_sleep_chance *= 0.5 //make it harder to fall asleep on caffeine
if(!SPT_PROB(final_sleep_chance, seconds_per_tick))
return
//if not drowsy, don't fall asleep but make them drowsy
if(!drowsy)
to_chat(owner, span_warning("You feel tired..."))
owner.adjust_drowsiness(rand(drowsy_time_minimum, drowsy_time_maximum))
if(prob(50))
owner.emote("yawn")
else if(prob(33)) //rarest message is a custom emote
owner.visible_message("rubs [owner.p_their()] eyes.", visible_message_flags = EMOTE_MESSAGE)
//drowsy, so fall asleep. you've had your chance to remedy it
else
to_chat(owner, span_warning("You fall asleep."))
owner.Sleeping(rand(sleep_time_minimum, sleep_time_maximum))
if(prob(50) && owner.IsSleeping())
owner.emote("snore")
/datum/brain_trauma/severe/narcolepsy/permanent
scan_desc = "chronic narcolepsy" //less odds to fall asleep than parent, but sleeps for longer
sleep_chance = 0.333
sleep_chance_running = 0.333
sleep_chance_drowsy = 1
sleep_time_minimum = 20 SECONDS
sleep_time_maximum = 30 SECONDS
/datum/brain_trauma/severe/monophobia
name = "Monophobia"
desc = "Patient feels sick and distressed when not around other people, leading to potentially lethal levels of stress."
scan_desc = "monophobia"
gain_text = span_warning("You feel really lonely...")
lose_text = span_notice("You feel like you could be safe on your own.")
var/stress = 0
/datum/brain_trauma/severe/monophobia/on_gain()
. = ..()
owner.AddComponentFrom(REF(src), /datum/component/fearful, list(/datum/terror_handler/vomiting, /datum/terror_handler/simple_source/monophobia))
/datum/brain_trauma/severe/monophobia/on_lose(silent)
. = ..()
owner.RemoveComponentSource(REF(src), /datum/component/fearful)
/datum/brain_trauma/severe/discoordination
name = "Discoordination"
desc = "Patient is unable to use complex tools or machinery."
scan_desc = "extreme discoordination"
gain_text = span_warning("You can barely control your hands!")
lose_text = span_notice("You feel in control of your hands again.")
/datum/brain_trauma/severe/discoordination/on_gain()
. = ..()
owner.apply_status_effect(/datum/status_effect/discoordinated)
/datum/brain_trauma/severe/discoordination/on_lose()
owner.remove_status_effect(/datum/status_effect/discoordinated)
return ..()
/datum/brain_trauma/severe/pacifism
name = "Traumatic Non-Violence"
desc = "Patient is extremely unwilling to harm others in violent ways."
scan_desc = "pacific syndrome"
gain_text = span_notice("You feel oddly peaceful.")
lose_text = span_notice("You no longer feel compelled to not harm.")
/datum/brain_trauma/severe/pacifism/on_gain()
ADD_TRAIT(owner, TRAIT_PACIFISM, TRAUMA_TRAIT)
. = ..()
/datum/brain_trauma/severe/pacifism/on_lose()
REMOVE_TRAIT(owner, TRAIT_PACIFISM, TRAUMA_TRAIT)
..()
/datum/brain_trauma/severe/hypnotic_stupor
name = "Hypnotic Stupor"
desc = "Patient is prone to episodes of extreme stupor that leaves them extremely suggestible."
scan_desc = "oneiric feedback loop"
gain_text = span_warning("You feel somewhat dazed.")
lose_text = span_notice("You feel like a fog was lifted from your mind.")
/datum/brain_trauma/severe/hypnotic_stupor/on_lose() //hypnosis must be cleared separately, but brain surgery should get rid of both anyway
..()
owner.remove_status_effect(/datum/status_effect/trance)
/datum/brain_trauma/severe/hypnotic_stupor/on_life(seconds_per_tick, times_fired)
..()
if(SPT_PROB(0.5, seconds_per_tick) && !owner.has_status_effect(/datum/status_effect/trance))
owner.apply_status_effect(/datum/status_effect/trance, rand(100,300), FALSE)
/datum/brain_trauma/severe/hypnotic_trigger
name = "Hypnotic Trigger"
desc = "Patient has a trigger phrase set in their subconscious that will trigger a suggestible trance-like state."
scan_desc = "oneiric feedback loop"
gain_text = span_warning("You feel odd, like you just forgot something important.")
lose_text = span_notice("You feel like a weight was lifted from your mind.")
random_gain = FALSE
var/trigger_phrase = "Nanotrasen"
/datum/brain_trauma/severe/hypnotic_trigger/New(phrase)
..()
if(phrase)
trigger_phrase = phrase
/datum/brain_trauma/severe/hypnotic_trigger/on_lose() //hypnosis must be cleared separately, but brain surgery should get rid of both anyway
..()
owner.remove_status_effect(/datum/status_effect/trance)
/datum/brain_trauma/severe/hypnotic_trigger/handle_hearing(datum/source, list/hearing_args)
if(!owner.can_hear() || owner == hearing_args[HEARING_SPEAKER])
return
var/regex/reg = new("(\\b[REGEX_QUOTE(trigger_phrase)]\\b)","ig")
if(findtext(hearing_args[HEARING_RAW_MESSAGE], reg))
addtimer(CALLBACK(src, PROC_REF(hypnotrigger)), 1 SECONDS) //to react AFTER the chat message
hearing_args[HEARING_RAW_MESSAGE] = reg.Replace(hearing_args[HEARING_RAW_MESSAGE], span_hypnophrase("*********"))
/datum/brain_trauma/severe/hypnotic_trigger/proc/hypnotrigger()
to_chat(owner, span_warning("The words trigger something deep within you, and you feel your consciousness slipping away..."))
owner.apply_status_effect(/datum/status_effect/trance, rand(100,300), FALSE)
/datum/brain_trauma/severe/dyslexia
name = "Dyslexia"
desc = "Patient is unable to read or write."
scan_desc = "dyslexia"
gain_text = span_warning("You have trouble reading or writing...")
lose_text = span_notice("You suddenly remember how to read and write.")
/datum/brain_trauma/severe/dyslexia/on_gain()
ADD_TRAIT(owner, TRAIT_ILLITERATE, TRAUMA_TRAIT)
. = ..()
/datum/brain_trauma/severe/dyslexia/on_lose()
REMOVE_TRAIT(owner, TRAIT_ILLITERATE, TRAUMA_TRAIT)
..()
/datum/brain_trauma/severe/kleptomaniac
name = "Kleptomania"
desc = "Patient is prone to stealing things."
scan_desc = "kleptomania"
gain_text = span_warning("You feel a sudden urge to take that. Surely no one will notice.")
lose_text = span_notice("You no longer feel the urge to take things.")
/// Cooldown between allowing steal attempts
COOLDOWN_DECLARE(steal_cd)
/datum/brain_trauma/severe/kleptomaniac/on_gain()
. = ..()
RegisterSignal(owner, COMSIG_MOB_APPLY_DAMAGE, PROC_REF(damage_taken))
/datum/brain_trauma/severe/kleptomaniac/on_lose()
. = ..()
UnregisterSignal(owner, COMSIG_MOB_APPLY_DAMAGE)
/datum/brain_trauma/severe/kleptomaniac/proc/damage_taken(datum/source, damage_amount, damage_type, ...)
SIGNAL_HANDLER
// While you're fighting someone (or dying horribly) your mind has more important things to focus on than pocketing stuff
if(damage_amount >= 5 && (damage_type == BRUTE || damage_type == BURN || damage_type == STAMINA))
COOLDOWN_START(src, steal_cd, 12 SECONDS)
/datum/brain_trauma/severe/kleptomaniac/on_life(seconds_per_tick, times_fired)
if(owner.usable_hands <= 0)
return
if(!SPT_PROB(5, seconds_per_tick))
return
if(!COOLDOWN_FINISHED(src, steal_cd))
return
if(!owner.has_active_hand() || !owner.get_empty_held_indexes())
return
// If our main hand is full, that means our offhand is empty, so try stealing with that
var/steal_to_offhand = !!owner.get_active_held_item()
var/curr_index = owner.active_hand_index
var/pre_dir = owner.dir
if(steal_to_offhand)
owner.swap_hand(owner.get_inactive_hand_index())
var/list/stealables = list()
for(var/obj/item/potential_stealable in oview(1, owner))
if(potential_stealable.w_class >= WEIGHT_CLASS_BULKY)
continue
if(potential_stealable.anchored || !(potential_stealable.interaction_flags_item & INTERACT_ITEM_ATTACK_HAND_PICKUP))
continue
stealables += potential_stealable
for(var/obj/item/stealable as anything in shuffle(stealables))
if(!owner.CanReach(stealable, view_only = TRUE) || stealable.IsObscured())
continue
// Try to do a raw click on the item with one of our empty hands, to pick it up (duh)
owner.log_message("attempted to pick up (kleptomania)", LOG_ATTACK, color = "orange")
owner.ClickOn(stealable)
// No feedback message. Intentional, you may not even realize you picked up something
break
if(steal_to_offhand)
owner.swap_hand(curr_index)
owner.setDir(pre_dir)
// Gives you a small buffer - not to avoid spam, but to make it more subtle / less predictable
COOLDOWN_START(src, steal_cd, 8 SECONDS)