Files
Bubberstation/code/datums/brain_damage/special.dm
MrMelbert 153d09e4c4 Fix Primal Instincts (#82741)
## About The Pull Request

#82539 randomly made these two lines switch their AI controllers to idle
rather than on, when the intent was for them to wake up immediately to
do make the mob attack or fight or do something

## Changelog

🆑 Melbert
fix: Primal Instincts and Living Flesh should be a tad more reactive
/🆑
2024-04-18 18:23:00 -06:00

528 lines
19 KiB
Plaintext

//Brain traumas that are rare and/or somewhat beneficial;
//they are the easiest to cure, which means that if you want
//to keep them, you can't cure your other traumas
/datum/brain_trauma/special
abstract_type = /datum/brain_trauma/special
/datum/brain_trauma/special/godwoken
name = "Godwoken Syndrome"
desc = "Patient occasionally and uncontrollably channels an eldritch god when speaking."
scan_desc = "god delusion"
gain_text = span_notice("You feel a higher power inside your mind...")
lose_text = span_warning("The divine presence leaves your head, no longer interested.")
/datum/brain_trauma/special/godwoken/on_life(seconds_per_tick, times_fired)
..()
if(SPT_PROB(2, seconds_per_tick))
if(prob(33) && (owner.IsStun() || owner.IsParalyzed() || owner.IsUnconscious()))
speak("unstun", TRUE)
else if(prob(60) && owner.health <= owner.crit_threshold)
speak("heal", TRUE)
else if(prob(30) && owner.combat_mode)
speak("aggressive")
else
speak("neutral", prob(25))
/datum/brain_trauma/special/godwoken/on_gain()
ADD_TRAIT(owner, TRAIT_HOLY, TRAUMA_TRAIT)
..()
/datum/brain_trauma/special/godwoken/on_lose()
REMOVE_TRAIT(owner, TRAIT_HOLY, TRAUMA_TRAIT)
..()
/datum/brain_trauma/special/godwoken/proc/speak(type, include_owner = FALSE)
var/message
switch(type)
if("unstun")
message = pick_list_replacements(BRAIN_DAMAGE_FILE, "god_unstun")
if("heal")
message = pick_list_replacements(BRAIN_DAMAGE_FILE, "god_heal")
if("neutral")
message = pick_list_replacements(BRAIN_DAMAGE_FILE, "god_neutral")
if("aggressive")
message = pick_list_replacements(BRAIN_DAMAGE_FILE, "god_aggressive")
else
message = pick_list_replacements(BRAIN_DAMAGE_FILE, "god_neutral")
playsound(get_turf(owner), 'sound/magic/clockwork/invoke_general.ogg', 200, TRUE, 5)
voice_of_god(message, owner, list("colossus","yell"), 2.5, include_owner, name, TRUE)
/datum/brain_trauma/special/bluespace_prophet
name = "Bluespace Prophecy"
desc = "Patient can sense the bob and weave of bluespace around them, showing them passageways no one else can see."
scan_desc = "bluespace attunement"
gain_text = span_notice("You feel the bluespace pulsing around you...")
lose_text = span_warning("The faint pulsing of bluespace fades into silence.")
/// Cooldown so we can't teleport literally everywhere on a whim
COOLDOWN_DECLARE(portal_cooldown)
/datum/brain_trauma/special/bluespace_prophet/on_life(seconds_per_tick, times_fired)
if(!COOLDOWN_FINISHED(src, portal_cooldown))
return
COOLDOWN_START(src, portal_cooldown, 10 SECONDS)
var/list/turf/possible_turfs = list()
for(var/turf/T as anything in RANGE_TURFS(8, owner))
if(T.density)
continue
var/clear = TRUE
for(var/obj/O in T)
if(O.density)
clear = FALSE
break
if(clear)
possible_turfs += T
if(!LAZYLEN(possible_turfs))
return
var/turf/first_turf = pick(possible_turfs)
if(!first_turf)
return
possible_turfs -= (possible_turfs & range(first_turf, 3))
var/turf/second_turf = pick(possible_turfs)
if(!second_turf)
return
var/obj/effect/client_image_holder/bluespace_stream/first = new(first_turf, owner)
var/obj/effect/client_image_holder/bluespace_stream/second = new(second_turf, owner)
first.linked_to = second
second.linked_to = first
/obj/effect/client_image_holder/bluespace_stream
name = "bluespace stream"
desc = "You see a hidden pathway through bluespace..."
image_icon = 'icons/effects/effects.dmi'
image_state = "bluestream"
image_layer = ABOVE_MOB_LAYER
var/obj/effect/client_image_holder/bluespace_stream/linked_to
/obj/effect/client_image_holder/bluespace_stream/Initialize(mapload, list/mobs_which_see_us)
. = ..()
QDEL_IN(src, 30 SECONDS)
/obj/effect/client_image_holder/bluespace_stream/generate_image()
. = ..()
apply_wibbly_filters(.)
/obj/effect/client_image_holder/bluespace_stream/Destroy()
if(!QDELETED(linked_to))
qdel(linked_to)
linked_to = null
return ..()
/obj/effect/client_image_holder/bluespace_stream/attack_hand(mob/user, list/modifiers)
. = ..()
if(.)
return
if(!(user in who_sees_us) || !linked_to)
return
var/slip_in_message = pick("slides sideways in an odd way, and disappears", "jumps into an unseen dimension",\
"sticks one leg straight out, wiggles [user.p_their()] foot, and is suddenly gone", "stops, then blinks out of reality", \
"is pulled into an invisible vortex, vanishing from sight")
var/slip_out_message = pick("silently fades in", "leaps out of thin air","appears", "walks out of an invisible doorway",\
"slides out of a fold in spacetime")
to_chat(user, span_notice("You try to align with the bluespace stream..."))
if(!do_after(user, 2 SECONDS, target = src))
return
var/turf/source_turf = get_turf(src)
var/turf/destination_turf = get_turf(linked_to)
new /obj/effect/temp_visual/bluespace_fissure(source_turf)
new /obj/effect/temp_visual/bluespace_fissure(destination_turf)
user.visible_message(span_warning("[user] [slip_in_message]."), ignored_mobs = user)
if(do_teleport(user, destination_turf, no_effects = TRUE))
user.visible_message(span_warning("[user] [slip_out_message]."), span_notice("...and find your way to the other side."))
else
user.visible_message(span_warning("[user] [slip_out_message], ending up exactly where they left."), span_notice("...and find yourself where you started?"))
/obj/effect/client_image_holder/bluespace_stream/attack_tk(mob/user)
to_chat(user, span_warning("\The [src] actively rejects your mind, and the bluespace energies surrounding it disrupt your telekinesis!"))
return COMPONENT_CANCEL_ATTACK_CHAIN
/datum/brain_trauma/special/quantum_alignment
name = "Quantum Alignment"
desc = "Patient is prone to frequent spontaneous quantum entanglement, against all odds, causing spatial anomalies."
scan_desc = "quantum alignment"
gain_text = span_notice("You feel faintly connected to everything around you...")
lose_text = span_warning("You no longer feel connected to your surroundings.")
var/atom/linked_target = null
var/linked = FALSE
var/returning = FALSE
/// Cooldown for snapbacks
COOLDOWN_DECLARE(snapback_cooldown)
/datum/brain_trauma/special/quantum_alignment/on_life(seconds_per_tick, times_fired)
if(linked)
if(QDELETED(linked_target))
linked_target = null
linked = FALSE
return
if(!returning && COOLDOWN_FINISHED(src, snapback_cooldown))
start_snapback()
return
if(SPT_PROB(2, seconds_per_tick))
try_entangle()
/datum/brain_trauma/special/quantum_alignment/proc/try_entangle()
//Check for pulled mobs
if(ismob(owner.pulling))
entangle(owner.pulling)
return
//Check for adjacent mobs
for(var/mob/living/L in oview(1, owner))
if(owner.Adjacent(L))
entangle(L)
return
//Check for pulled objects
if(isobj(owner.pulling))
entangle(owner.pulling)
return
//Check main hand
var/obj/item/held_item = owner.get_active_held_item()
if(held_item && !(HAS_TRAIT(held_item, TRAIT_NODROP)))
entangle(held_item)
return
//Check off hand
held_item = owner.get_inactive_held_item()
if(held_item && !(HAS_TRAIT(held_item, TRAIT_NODROP)))
entangle(held_item)
return
//Just entangle with the turf
entangle(get_turf(owner))
/datum/brain_trauma/special/quantum_alignment/proc/entangle(atom/target)
to_chat(owner, span_notice("You start feeling a strong sense of connection to [target]."))
linked_target = target
linked = TRUE
COOLDOWN_START(src, snapback_cooldown, rand(45 SECONDS, 10 MINUTES))
/datum/brain_trauma/special/quantum_alignment/proc/start_snapback()
if(QDELETED(linked_target))
linked_target = null
linked = FALSE
return
to_chat(owner, span_warning("Your connection to [linked_target] suddenly feels extremely strong... you can feel it pulling you!"))
owner.playsound_local(owner, 'sound/magic/lightning_chargeup.ogg', 75, FALSE)
returning = TRUE
addtimer(CALLBACK(src, PROC_REF(snapback)), 10 SECONDS)
/datum/brain_trauma/special/quantum_alignment/proc/snapback()
returning = FALSE
if(QDELETED(linked_target))
to_chat(owner, span_notice("The connection fades abruptly, and the pull with it."))
linked_target = null
linked = FALSE
return
to_chat(owner, span_warning("You're pulled through spacetime!"))
do_teleport(owner, get_turf(linked_target), null, channel = TELEPORT_CHANNEL_QUANTUM)
owner.playsound_local(owner, 'sound/magic/repulse.ogg', 100, FALSE)
linked_target = null
linked = FALSE
/datum/brain_trauma/special/psychotic_brawling
name = "Violent Psychosis"
desc = "Patient fights in unpredictable ways, ranging from helping his target to hitting them with brutal strength."
scan_desc = "violent psychosis"
gain_text = span_warning("You feel unhinged...")
lose_text = span_notice("You feel more balanced.")
var/datum/martial_art/psychotic_brawling/psychotic_brawling
/datum/brain_trauma/special/psychotic_brawling/on_gain()
..()
psychotic_brawling = new()
psychotic_brawling.allow_temp_override = FALSE
if(!psychotic_brawling.teach(owner, TRUE))
to_chat(owner, span_notice("But your martial knowledge keeps you grounded."))
qdel(src)
/datum/brain_trauma/special/psychotic_brawling/on_lose()
..()
psychotic_brawling.fully_remove(owner)
QDEL_NULL(psychotic_brawling)
/datum/brain_trauma/special/psychotic_brawling/bath_salts
name = "Chemical Violent Psychosis"
/datum/brain_trauma/special/tenacity
name = "Tenacity"
desc = "Patient is psychologically unaffected by pain and injuries, and can remain standing far longer than a normal person."
scan_desc = "traumatic neuropathy"
gain_text = span_warning("You suddenly stop feeling pain.")
lose_text = span_warning("You realize you can feel pain again.")
/datum/brain_trauma/special/tenacity/on_gain()
owner.add_traits(list(TRAIT_NOSOFTCRIT, TRAIT_NOHARDCRIT, TRAIT_ANALGESIA), TRAUMA_TRAIT)
..()
/datum/brain_trauma/special/tenacity/on_lose()
owner.remove_traits(list(TRAIT_NOSOFTCRIT, TRAIT_NOHARDCRIT, TRAIT_ANALGESIA), TRAUMA_TRAIT)
..()
/datum/brain_trauma/special/death_whispers
name = "Functional Cerebral Necrosis"
desc = "Patient's brain is stuck in a functional near-death state, causing occasional moments of lucid hallucinations, which are often interpreted as the voices of the dead."
scan_desc = "chronic functional necrosis"
gain_text = span_warning("You feel dead inside.")
lose_text = span_notice("You feel alive again.")
var/active = FALSE
/datum/brain_trauma/special/death_whispers/on_life()
..()
if(!active && prob(2))
whispering()
/datum/brain_trauma/special/death_whispers/on_lose()
if(active)
cease_whispering()
..()
/datum/brain_trauma/special/death_whispers/proc/whispering()
ADD_TRAIT(owner, TRAIT_SIXTHSENSE, TRAUMA_TRAIT)
active = TRUE
addtimer(CALLBACK(src, PROC_REF(cease_whispering)), rand(5 SECONDS, 30 SECONDS))
/datum/brain_trauma/special/death_whispers/proc/cease_whispering()
REMOVE_TRAIT(owner, TRAIT_SIXTHSENSE, TRAUMA_TRAIT)
active = FALSE
/datum/brain_trauma/special/existential_crisis
name = "Existential Crisis"
desc = "Patient's hold on reality becomes faint, causing occasional bouts of non-existence."
scan_desc = "existential crisis"
gain_text = span_warning("You feel less real.")
lose_text = span_notice("You feel more substantial again.")
var/obj/effect/abstract/sync_holder/veil/veil
/// A cooldown to prevent constantly erratic dolphining through the fabric of reality
COOLDOWN_DECLARE(crisis_cooldown)
/datum/brain_trauma/special/existential_crisis/on_life(seconds_per_tick, times_fired)
..()
if(!veil && COOLDOWN_FINISHED(src, crisis_cooldown) && SPT_PROB(1.5, seconds_per_tick))
if(isturf(owner.loc))
fade_out()
/datum/brain_trauma/special/existential_crisis/on_lose()
if(veil)
fade_in()
..()
/datum/brain_trauma/special/existential_crisis/proc/fade_out()
if(veil)
return
var/duration = rand(5 SECONDS, 45 SECONDS)
veil = new(owner.drop_location())
to_chat(owner, span_warning("[pick(list(
"Do you even exist?",
"To be or not to be...",
"Why exist?",
"You simply fade away.",
"You stop keeping it real.",
"You stop thinking for a moment. Therefore you are not.",
))]"))
owner.forceMove(veil)
COOLDOWN_START(src, crisis_cooldown, 1 MINUTES)
addtimer(CALLBACK(src, PROC_REF(fade_in)), duration)
/datum/brain_trauma/special/existential_crisis/proc/fade_in()
QDEL_NULL(veil)
to_chat(owner, span_notice("You fade back into reality."))
COOLDOWN_START(src, crisis_cooldown, 1 MINUTES)
//base sync holder is in desynchronizer.dm
/obj/effect/abstract/sync_holder/veil
name = "non-existence"
desc = "Existence is just a state of mind."
/datum/brain_trauma/special/beepsky
name = "Criminal"
desc = "Patient seems to be a criminal."
scan_desc = "criminal mind"
gain_text = span_warning("Justice is coming for you.")
lose_text = span_notice("You were absolved for your crimes.")
random_gain = FALSE
/// A ref to our fake beepsky image that we chase the owner with
var/obj/effect/client_image_holder/securitron/beepsky
/datum/brain_trauma/special/beepsky/Destroy()
QDEL_NULL(beepsky)
return ..()
/datum/brain_trauma/special/beepsky/on_gain()
create_securitron()
return ..()
/datum/brain_trauma/special/beepsky/proc/create_securitron()
QDEL_NULL(beepsky)
var/turf/where = locate(owner.x + pick(-12, 12), owner.y + pick(-12, 12), owner.z)
beepsky = new(where, owner)
/datum/brain_trauma/special/beepsky/on_lose()
QDEL_NULL(beepsky)
return ..()
/datum/brain_trauma/special/beepsky/on_life()
if(QDELETED(beepsky) || !beepsky.loc || beepsky.z != owner.z)
if(prob(30))
create_securitron()
else
return
if(get_dist(owner, beepsky) >= 10 && prob(20))
create_securitron()
if(owner.stat != CONSCIOUS)
if(prob(20))
owner.playsound_local(beepsky, 'sound/voice/beepsky/iamthelaw.ogg', 50)
return
if(get_dist(owner, beepsky) <= 1)
owner.playsound_local(owner, 'sound/weapons/egloves.ogg', 50)
owner.visible_message(span_warning("[owner]'s body jerks as if it was shocked."), span_userdanger("You feel the fist of the LAW."))
owner.adjustStaminaLoss(rand(40, 70))
QDEL_NULL(beepsky)
if(prob(20) && get_dist(owner, beepsky) <= 8)
owner.playsound_local(beepsky, 'sound/voice/beepsky/criminal.ogg', 40)
/obj/effect/client_image_holder/securitron
name = "Securitron"
desc = "The LAW is coming."
image_icon = 'icons/mob/silicon/aibots.dmi'
image_state = "secbot-c"
/obj/effect/client_image_holder/securitron/Initialize(mapload)
. = ..()
name = pick("Officer Beepsky", "Officer Johnson", "Officer Pingsky")
START_PROCESSING(SSfastprocess, src)
/obj/effect/client_image_holder/securitron/Destroy()
STOP_PROCESSING(SSfastprocess,src)
return ..()
/obj/effect/client_image_holder/securitron/process()
if(prob(40))
return
var/mob/victim = pick(who_sees_us)
forceMove(get_step_towards(src, victim))
if(prob(5))
var/beepskys_cry = "Level 10 infraction alert!"
to_chat(victim, "[span_name("[name]")] exclaims, \"[span_robot("[beepskys_cry]")]")
if(victim.client?.prefs.read_preference(/datum/preference/toggle/enable_runechat))
victim.create_chat_message(src, raw_message = beepskys_cry, spans = list("robotic"))
// Used by Veteran Security Advisor job.
/datum/brain_trauma/special/ptsd
name = "Combat PTSD"
desc = "The patient is experiencing PTSD stemming from past combat exposure, resulting in a lack of emotions. Additionally, they are experiencing mild hallucinations."
scan_desc = "PTSD"
gain_text = span_warning("You're thrust back into the chaos of past! Explosions! Gunfire! Emotions, gone AWOL!")
lose_text = span_notice("You feel flashbacks of past fade, as your emotions return and mind clear.")
resilience = TRAUMA_RESILIENCE_ABSOLUTE
can_gain = TRUE
random_gain = FALSE
/// Our cooldown declare for causing hallucinations
COOLDOWN_DECLARE(ptsd_hallucinations)
var/list/ptsd_hallucinations_list = list(
/datum/hallucination/fake_sound/normal/boom,
/datum/hallucination/fake_sound/normal/distant_boom,
/datum/hallucination/stray_bullet,
/datum/hallucination/battle/gun/disabler,
/datum/hallucination/battle/gun/laser,
/datum/hallucination/battle/bomb,
/datum/hallucination/battle/e_sword,
/datum/hallucination/battle/harm_baton,
/datum/hallucination/battle/stun_prod,
)
/datum/brain_trauma/special/ptsd/on_life(seconds_per_tick, times_fired)
if(owner.stat != CONSCIOUS)
return
if(!COOLDOWN_FINISHED(src, ptsd_hallucinations))
return
owner.cause_hallucination(pick(ptsd_hallucinations_list), "Caused by The Combat PTSD brain trauma")
COOLDOWN_START(src, ptsd_hallucinations, rand(10 SECONDS, 10 MINUTES))
/datum/brain_trauma/special/ptsd/on_gain()
owner.add_mood_event("combat_ptsd", /datum/mood_event/desentized)
owner.mob_mood?.mood_modifier -= 1 //Basically nothing can change your mood
owner.mob_mood?.sanity_level = SANITY_DISTURBED //Makes sanity on a unstable level unless cured
..()
/datum/brain_trauma/special/ptsd/on_lose()
owner.clear_mood_event("combat_ptsd")
owner.mob_mood?.mood_modifier += 1
owner.mob_mood?.sanity_level = SANITY_GREAT
return ..()
/datum/brain_trauma/special/primal_instincts
name = "Feral Instincts"
desc = "Patient's mind is stuck in a primal state, causing them to act on instinct rather than reason."
scan_desc = "ferality"
gain_text = span_warning("Your pupils dilate, and it becomes harder to think straight.")
lose_text = span_notice("Your mind clears, and you feel more in control.")
resilience = TRAUMA_RESILIENCE_SURGERY
/// Tracks any existing AI controller, so we can restore it when we're cured
var/old_ai_controller_type
/datum/brain_trauma/special/primal_instincts/on_gain()
. = ..()
if(!isnull(owner.ai_controller))
old_ai_controller_type = owner.ai_controller.type
QDEL_NULL(owner.ai_controller)
owner.ai_controller = new /datum/ai_controller/monkey(owner)
owner.ai_controller.continue_processing_when_client = TRUE
owner.ai_controller.can_idle = FALSE
owner.ai_controller.set_ai_status(AI_STATUS_OFF)
/datum/brain_trauma/special/primal_instincts/on_lose(silent)
. = ..()
if(QDELING(owner))
return
QDEL_NULL(owner.ai_controller)
if(old_ai_controller_type)
owner.ai_controller = new old_ai_controller_type(owner)
owner.remove_language(/datum/language/monkey, UNDERSTOOD_LANGUAGE, TRAUMA_TRAIT)
/datum/brain_trauma/special/primal_instincts/on_life(seconds_per_tick, times_fired)
if(isnull(owner.ai_controller))
qdel(src)
return
if(!SPT_PROB(3, seconds_per_tick))
return
owner.grant_language(/datum/language/monkey, UNDERSTOOD_LANGUAGE, TRAUMA_TRAIT)
owner.ai_controller.set_blackboard_key(BB_MONKEY_AGGRESSIVE, prob(75))
if(owner.ai_controller.ai_status == AI_STATUS_OFF)
owner.ai_controller.set_ai_status(AI_STATUS_ON)
owner.log_message("became controlled by monkey instincts ([owner.ai_controller.blackboard[BB_MONKEY_AGGRESSIVE] ? "aggressive" : "docile"])", LOG_ATTACK, color = "orange")
to_chat(owner, span_warning("You feel the urge to act on your primal instincts..."))
// extend original timer if we roll the effect while it's already ongoing
addtimer(CALLBACK(src, PROC_REF(primal_instincts_off)), rand(20 SECONDS, 40 SECONDS), TIMER_UNIQUE|TIMER_NO_HASH_WAIT|TIMER_OVERRIDE|TIMER_DELETE_ME)
/datum/brain_trauma/special/primal_instincts/proc/primal_instincts_off()
owner.ai_controller.set_ai_status(AI_STATUS_OFF)
owner.remove_language(/datum/language/monkey, UNDERSTOOD_LANGUAGE, TRAUMA_TRAIT)
to_chat(owner, span_green("The urge subsides."))