diff --git a/code/__DEFINES/mood.dm b/code/__DEFINES/mood.dm index 5ed6b32e834..0dcdc9340d6 100644 --- a/code/__DEFINES/mood.dm +++ b/code/__DEFINES/mood.dm @@ -51,7 +51,7 @@ #define TERROR_HANDLER_EFFECT "effect" // Default cooldown for terror messages, to not get spammy -#define TERROR_MESSAGE_CD 30 SECONDS +#define TERROR_MESSAGE_CD 15 SECONDS // Values for terror buildup effects /// Initial value for effects that apply the component from spooking you @@ -76,6 +76,20 @@ /// How much terror being hugged reduces, or increases if its done by a nightmare or someone you're afraid of #define HUG_TERROR_AMOUNT 90 +/// Cooldown for phobia checks, to avoid constantly refreshing views +#define PHOBIA_CHECK_DELAY 5 SECONDS +/// Delay between phobia freakouts, also the time it takes for the buildup effect to fade away +#define PHOBIA_FREAKOUT_DELAY 12 SECONDS +/// Amount of terror granted whenever we hear a word we're afraid of +#define PHOBIA_WORD_TERROR_BUILDUP 40 +/// Amount of terror granted by each phobia trigger +#define PHOBIA_FREAKOUT_TERROR_BUILDUP 120 + +/// Cooldown for fear screams +#define TERROR_STARTLE_COOLDOWN 12 SECONDS +/// Minimum difference in fear per tick for screams to actually trigger, so we don't proc from minor fears +#define TERROR_STARTLE_MINIMUM_DIFFERENCE 40 + /// Relates to fear or resisting fear #define MOOD_EVENT_FEAR (1<<0) /// Relates to art diff --git a/code/datums/brain_damage/phobia.dm b/code/datums/brain_damage/phobia.dm index 4703750cd01..3662fc74073 100644 --- a/code/datums/brain_damage/phobia.dm +++ b/code/datums/brain_damage/phobia.dm @@ -4,21 +4,12 @@ scan_desc = "phobia" gain_text = span_warning("You start finding default values very unnerving...") lose_text = span_notice("You no longer feel afraid of default values.") + /// What do we fear exactly? var/phobia_type - /// Cooldown for proximity checks so we don't spam a range 7 view every two seconds. - COOLDOWN_DECLARE(check_cooldown) - /// Cooldown for freakouts to prevent permastunning. - COOLDOWN_DECLARE(scare_cooldown) - - ///What mood event to apply when we see the thing & freak out. - var/datum/mood_event/mood_event_type = /datum/mood_event/phobia - - var/regex/trigger_regex - //instead of cycling every atom, only cycle the relevant types - var/list/trigger_mobs - var/list/trigger_objs //also checked in mob equipment - var/list/trigger_turfs - var/list/trigger_species + /// Specific terror handler to apply, in case we want + var/terror_handler = /datum/terror_handler/phobia_source + /// What mood event to apply when we see the thing & freak out. + var/mood_event_type = /datum/mood_event/phobia /datum/brain_trauma/mild/phobia/New(new_phobia_type) if(new_phobia_type) @@ -30,132 +21,22 @@ gain_text = span_warning("You start finding [phobia_type] very unnerving...") lose_text = span_notice("You no longer feel afraid of [phobia_type].") scan_desc += " of [phobia_type]" - trigger_regex = GLOB.phobia_regexes[phobia_type] - trigger_mobs = GLOB.phobia_mobs[phobia_type] - trigger_objs = GLOB.phobia_objs[phobia_type] - trigger_turfs = GLOB.phobia_turfs[phobia_type] - trigger_species = GLOB.phobia_species[phobia_type] - ..() - -/datum/brain_trauma/mild/phobia/on_lose(silent) - owner.clear_mood_event("phobia_[phobia_type]") return ..() -/datum/brain_trauma/mild/phobia/on_life(seconds_per_tick, times_fired) - ..() - if(HAS_TRAIT(owner, TRAIT_FEARLESS)) - return - if(owner.is_blind()) - return +/datum/brain_trauma/mild/phobia/on_gain() + . = ..() + var/datum/component/fearful/fear = owner.AddComponentFrom(REF(src), /datum/component/fearful, list(/datum/terror_handler/startle)) + var/datum/terror_handler/phobia_source/phobia = fear.add_handler(terror_handler, REF(src)) + phobia.trigger_regex = GLOB.phobia_regexes[phobia_type] + phobia.trigger_mobs = GLOB.phobia_mobs[phobia_type] + phobia.trigger_objs = GLOB.phobia_objs[phobia_type] + phobia.trigger_turfs = GLOB.phobia_turfs[phobia_type] + phobia.trigger_species = GLOB.phobia_species[phobia_type] + phobia.mood_event_type = mood_event_type - if(!COOLDOWN_FINISHED(src, check_cooldown) || !COOLDOWN_FINISHED(src, scare_cooldown)) - return - - COOLDOWN_START(src, check_cooldown, 5 SECONDS) - var/list/seen_atoms = view(7, owner) - if(LAZYLEN(trigger_objs)) - for(var/obj/seen_item in seen_atoms) - if(is_scary_item(seen_item)) - freak_out(seen_item) - return - - if(LAZYLEN(trigger_turfs)) - for(var/turf/checked in seen_atoms) - if(is_type_in_typecache(checked, trigger_turfs)) - freak_out(checked) - return - - seen_atoms -= owner //make sure they aren't afraid of themselves. - if(LAZYLEN(trigger_mobs) || LAZYLEN(trigger_species) || LAZYLEN(trigger_objs)) - for(var/mob/living/checked in seen_atoms) - if (is_scary_mob(checked)) - freak_out(checked) - return - -/// Returns true if this item should be scary to us -/datum/brain_trauma/mild/phobia/proc/is_scary_item(obj/checked) - if (QDELETED(checked) || !is_type_in_typecache(checked, trigger_objs) || checked.invisibility > owner.see_invisible) - return FALSE - if (!isitem(checked)) - return TRUE - var/obj/item/checked_item = checked - return !HAS_TRAIT(checked_item, TRAIT_EXAMINE_SKIP) - -/datum/brain_trauma/mild/phobia/proc/is_scary_mob(mob/living/checked) - if(is_type_in_typecache(checked, trigger_mobs)) - return TRUE - - if (!ishuman(checked)) - return FALSE - - var/mob/living/carbon/human/as_human = checked - if (LAZYLEN(trigger_species)) - if (is_type_in_typecache(as_human.dna?.species, trigger_species)) - return TRUE - - if (!LAZYLEN(trigger_objs)) - return FALSE - - for(var/obj/item/equipped as anything in as_human.get_visible_items()) - if(is_scary_item(equipped)) - return TRUE - - return FALSE - -/datum/brain_trauma/mild/phobia/handle_hearing(datum/source, list/hearing_args) - if(!owner.can_hear() || owner == hearing_args[HEARING_SPEAKER] || !owner.has_language(hearing_args[HEARING_LANGUAGE])) //words can't trigger you if you can't hear them *taps head* - return - - if(HAS_TRAIT(owner, TRAIT_FEARLESS) || !COOLDOWN_FINISHED(src, scare_cooldown)) - return - - if(trigger_regex.Find(hearing_args[HEARING_RAW_MESSAGE]) != 0) - addtimer(CALLBACK(src, PROC_REF(freak_out), null, trigger_regex.group[2]), 1 SECONDS) //to react AFTER the chat message - hearing_args[HEARING_RAW_MESSAGE] = trigger_regex.Replace(hearing_args[HEARING_RAW_MESSAGE], "[span_phobia("$2")]$3") - -/datum/brain_trauma/mild/phobia/handle_speech(datum/source, list/speech_args) - if (HAS_TRAIT(owner, TRAIT_FEARLESS)) - return - if (trigger_regex.Find(speech_args[SPEECH_MESSAGE]) == 0) - return - - var/stutter = prob(50) - var/whisper = prob(30) - - if (!stutter && !whisper) - return - - if (whisper) - speech_args[SPEECH_SPANS] |= SPAN_SMALL_VOICE - if (stutter) - owner.set_stutter_if_lower(4 SECONDS) - to_chat(owner, span_warning("You struggle to say the word \"[span_phobia("[trigger_regex.group[2]]")]\"!")) - -/datum/brain_trauma/mild/phobia/proc/freak_out(atom/reason, trigger_word) - if(owner.stat == DEAD) - return - - var/message = pick("spooks you to the bone", "shakes you up", "terrifies you", "sends you into a panic", "sends chills down your spine") - if(trigger_word) - if (owner.has_status_effect(/datum/status_effect/minor_phobia_reaction)) - return - to_chat(owner, span_userdanger("Hearing [span_phobia(trigger_word)] [message]!")) - owner.apply_status_effect(/datum/status_effect/minor_phobia_reaction) - return - - COOLDOWN_START(src, scare_cooldown, 12 SECONDS) - if(mood_event_type) - owner.add_mood_event("phobia_[phobia_type]", mood_event_type) - - if(reason) - to_chat(owner, span_userdanger("Seeing [span_phobia(reason.name)] [message]!")) - else - to_chat(owner, span_userdanger("Something [message]!")) - - if(reason) - owner.face_atom(reason) - owner._pointed(reason) - owner.apply_status_effect(/datum/status_effect/stacking/phobia_reaction, 1, mood_event_type) +/datum/brain_trauma/mild/phobia/on_lose(silent) + . = ..() + owner.RemoveComponentSource(REF(src), /datum/component/fearful) // Defined phobia types for badminry, not included in the RNG trauma pool to avoid diluting. @@ -178,11 +59,7 @@ /datum/brain_trauma/mild/phobia/blood phobia_type = "blood" random_gain = FALSE - -/datum/brain_trauma/mild/phobia/blood/is_scary_item(obj/checked) - if (GET_ATOM_BLOOD_DNA_LENGTH(checked)) - return TRUE - return ..() + terror_handler = /datum/terror_handler/phobia_source/blood /datum/brain_trauma/mild/phobia/clowns phobia_type = "clowns" diff --git a/code/datums/brain_damage/severe.dm b/code/datums/brain_damage/severe.dm index b2b50cbe707..16289a58fe5 100644 --- a/code/datums/brain_damage/severe.dm +++ b/code/datums/brain_damage/severe.dm @@ -204,7 +204,6 @@ 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() . = ..() diff --git a/code/datums/components/fearful/effects.dm b/code/datums/components/fearful/effects.dm index 1f2de205df1..17a62201257 100644 --- a/code/datums/components/fearful/effects.dm +++ b/code/datums/components/fearful/effects.dm @@ -52,7 +52,7 @@ /datum/terror_handler/heart_problems handler_type = TERROR_HANDLER_EFFECT default = TRUE - COOLDOWN_DECLARE(effect_cd) + COOLDOWN_DECLARE(message_cd) /datum/terror_handler/heart_problems/tick(seconds_per_tick, terror_buildup) . = ..() @@ -67,6 +67,9 @@ if (terror_buildup < TERROR_BUILDUP_HEART_ATTACK || !prob(15)) owner.adjust_oxy_loss(8) + if (!COOLDOWN_FINISHED(src, message_cd)) + return + COOLDOWN_START(src, message_cd, TERROR_MESSAGE_CD) if (terror_buildup < TERROR_BUILDUP_FEAR) to_chat(owner, span_warning("Your heart skips a beat.")) else @@ -167,4 +170,48 @@ deltimer(panic_end_timer) panic_end_timer = null +/datum/terror_handler/startle + handler_type = TERROR_HANDLER_EFFECT + COOLDOWN_DECLARE(startle_cd) + +/datum/terror_handler/startle/tick(seconds_per_tick, terror_buildup) + . = ..() + if (owner.stat >= UNCONSCIOUS || !COOLDOWN_FINISHED(src, startle_cd)) + return + + if (terror_buildup < TERROR_BUILDUP_FEAR || terror_buildup - component.last_tick_buildup < TERROR_STARTLE_MINIMUM_DIFFERENCE) + return + + // The more scared we are, and the more fear we acquired at once last tick, the higher the probability of us being startled is + // Not SPT_PROB or FEAR_SCALING because this is only triggered during ticks when we actually acquire fear + if (!prob(15 * (terror_buildup - component.last_tick_buildup) / TERROR_STARTLE_MINIMUM_DIFFERENCE * terror_buildup / TERROR_BUILDUP_FEAR)) + return + + COOLDOWN_START(src, startle_cd, TERROR_STARTLE_COOLDOWN) + switch (rand(1, 3)) + if (1) + to_chat(owner, span_warning("You are startled!")) + owner.emote("jump") + owner.Immobilize(0.1 SECONDS * (terror_buildup / TERROR_BUILDUP_FEAR)) + + if (2) + owner.emote("scream") + owner.say("AAAAH!!", forced = "phobia") + if (!prob(15 * (terror_buildup / TERROR_BUILDUP_FEAR))) + return + var/held_item = owner.get_active_held_item() + if (owner.dropItemToGround(held_item)) + owner.visible_message( + span_danger("[owner.name] drops \the [held_item]!"), + span_warning("You drop \the [held_item]!"), null, COMBAT_MESSAGE_RANGE) + + if (3) + to_chat(owner, span_warning("You lose your balance!")) + owner.adjust_staggered_up_to(2 SECONDS * (terror_buildup / TERROR_BUILDUP_FEAR), 20 SECONDS) + owner.add_movespeed_modifier(/datum/movespeed_modifier/status_effect/spooked) + addtimer(CALLBACK(src, PROC_REF(speed_up)), 3 SECONDS, TIMER_STOPPABLE | TIMER_DELETE_ME) + +/datum/terror_handler/startle/proc/speed_up() + owner.remove_movespeed_modifier(/datum/movespeed_modifier/status_effect/spooked) + #undef FEAR_SCALING diff --git a/code/datums/components/fearful/fearful.dm b/code/datums/components/fearful/fearful.dm index 7b7e9953e54..a2480acb482 100644 --- a/code/datums/components/fearful/fearful.dm +++ b/code/datums/components/fearful/fearful.dm @@ -73,7 +73,7 @@ /datum/component/fearful/proc/add_handler(handler_type, source) for (var/datum/terror_handler/existing as anything in terror_handlers) - if (existing.type == handler_type) + if (existing.type == handler_type && !existing.bespoke) terror_handlers[existing] += source return @@ -83,6 +83,7 @@ if (!overriden_handlers[override_type]) overriden_handlers[override_type] = list() overriden_handlers[override_type][handler_type] = TRUE + return handler /datum/component/fearful/proc/get_fear_multiplier() var/multiplier = 1 @@ -168,11 +169,12 @@ ) return COMPONENT_BLOCK_MISC_HELP - for (var/datum/brain_trauma/mild/phobia/phobia in source.get_traumas()) - if (!phobia.is_scary_mob(hugger)) - continue + var/hug_buildup = 0 + for (var/datum/terror_handler/handler as anything in terror_handlers) + hug_buildup += handler.on_hug(hugger) - terror_buildup += HUG_TERROR_AMOUNT + if (hug_buildup > 0) + terror_buildup += hug_buildup source.visible_message( span_warning("[source] recoils in fear as [hugger] attempts to hug [source.p_them()]!"), span_boldwarning("You recoil in terror as [hugger] attempts to hug you!"), diff --git a/code/datums/components/fearful/sources.dm b/code/datums/components/fearful/sources/_sources.dm similarity index 89% rename from code/datums/components/fearful/sources.dm rename to code/datums/components/fearful/sources/_sources.dm index 62245e27f52..6c563167199 100644 --- a/code/datums/components/fearful/sources.dm +++ b/code/datums/components/fearful/sources/_sources.dm @@ -71,7 +71,7 @@ . = ..() owner.clear_mood_event("nyctophobia") -// Nightmare spell version with quicker buildup that shadows the quirk one, also handles removing the status upon reaching 0 terror +/// Nightmare spell version with quicker buildup that shadows the quirk one, also handles removing the status upon reaching 0 terror /datum/terror_handler/simple_source/nyctophobia/terrified // Takes about 30 seconds to reach maximum if you include 100 from casting the spell buildup_per_second = 15 @@ -87,6 +87,7 @@ /// Makes the owner afraid of being stuck in closets, crates, mechs, etc /datum/terror_handler/simple_source/claustrophobia buildup_per_second = 15 + COOLDOWN_DECLARE(message_cd) /datum/terror_handler/simple_source/claustrophobia/Destroy(force) owner.clear_mood_event("claustrophobia") @@ -100,8 +101,9 @@ if (isturf(owner.loc)) return FALSE - if (SPT_PROB(15, seconds_per_tick)) + if (COOLDOWN_FINISHED(src, message_cd) && SPT_PROB(15, seconds_per_tick)) to_chat(owner, span_warning("You feel trapped! Must escape... can't breathe...")) + COOLDOWN_START(src, message_cd, TERROR_MESSAGE_CD) return TRUE @@ -116,6 +118,7 @@ /// Makes the owner afraid of certain jolly figures /datum/terror_handler/simple_source/clausophobia buildup_per_second = 20 + COOLDOWN_DECLARE(message_cd) /datum/terror_handler/simple_source/clausophobia/check_condition(seconds_per_tick, terror_buildup) . = ..() @@ -142,14 +145,16 @@ if (!certified_jolly) return FALSE - if (SPT_PROB(15, seconds_per_tick)) + if (COOLDOWN_FINISHED(src, message_cd) && SPT_PROB(15, seconds_per_tick)) to_chat(owner, span_warning("Santa Claus is here! I gotta get out of here!")) + COOLDOWN_START(src, message_cd, TERROR_MESSAGE_CD) return TRUE /// Makes the owner afraid of being alone /datum/terror_handler/simple_source/monophobia buildup_per_second = 2.5 // Pretty low, ~4 minutes to reach passive cap + COOLDOWN_DECLARE(message_cd) /datum/terror_handler/simple_source/monophobia/check_condition(seconds_per_tick, terror_buildup) . = ..() @@ -167,4 +172,8 @@ if (istype(friend, /mob/living/basic/pet) || friend.ckey) return FALSE + if (COOLDOWN_FINISHED(src, message_cd) && SPT_PROB(10, seconds_per_tick)) + to_chat(owner, span_warning("You feel terribly lonely...")) + COOLDOWN_START(src, message_cd, TERROR_MESSAGE_CD) + return TRUE diff --git a/code/datums/components/fearful/sources/phobia.dm b/code/datums/components/fearful/sources/phobia.dm new file mode 100644 index 00000000000..e9303867769 --- /dev/null +++ b/code/datums/components/fearful/sources/phobia.dm @@ -0,0 +1,170 @@ +/// Complex terror source that increases buildup whenever the owner hears, sees, or tries to say something they're afraid of +/datum/terror_handler/phobia_source + handler_type = TERROR_HANDLER_SOURCE + bespoke = TRUE + /// Last time we got scared shitless for passive increase in fear + /// Regex for words that set the phobia off + var/regex/trigger_regex + // Instead of cycling every atom, only cycle the relevant types + var/list/trigger_mobs + /// Includes mob equipment + var/list/trigger_objs + var/list/trigger_turfs + var/list/trigger_species + /// What mood event to apply when we see the thing & freak out. + var/mood_event_type = /datum/mood_event/phobia + /// Cooldown for proximity checks so we don't spam a range 7 view every two seconds. + COOLDOWN_DECLARE(check_cooldown) + /// Cooldown for the actual scare effect so chat spam won't instantly send us spiraling + COOLDOWN_DECLARE(scare_cooldown) + +/datum/terror_handler/phobia_source/New(mob/living/new_owner, datum/component/fearful/new_component) + . = ..() + RegisterSignal(new_owner, COMSIG_MOB_SAY, PROC_REF(handle_speech)) + RegisterSignal(new_owner, COMSIG_MOVABLE_HEAR, PROC_REF(handle_hearing)) + +/datum/terror_handler/phobia_source/Destroy(force) + UnregisterSignal(owner, list(COMSIG_MOB_SAY, COMSIG_MOVABLE_HEAR)) + return ..() + +/datum/terror_handler/phobia_source/proc/can_trigger() + return !HAS_TRAIT(owner, TRAIT_FEARLESS) && !HAS_TRAIT(owner, TRAIT_MIND_TEMPORARILY_GONE) && owner.stat < UNCONSCIOUS + +/datum/terror_handler/phobia_source/tick(seconds_per_tick, terror_buildup) + . = ..() + if (!can_trigger()) + return + + // If we're still scared from the last trigger, keep adding just a tiiiiny buildup to prevent it from fading away + if (!COOLDOWN_FINISHED(src, scare_cooldown)) + return 0.01 + + if(!COOLDOWN_FINISHED(src, check_cooldown) || owner.is_blind()) + return + + COOLDOWN_START(src, check_cooldown, PHOBIA_CHECK_DELAY) + if(LAZYLEN(trigger_objs)) + for (var/obj/seen_thing in view(owner.client?.view || world.view, owner)) + if(is_scary_item(seen_thing)) + return freak_out(seen_thing) + + if(LAZYLEN(trigger_turfs)) + for(var/turf/checked in view(owner.client?.view || world.view, owner)) + if(is_type_in_typecache(checked, trigger_turfs)) + return freak_out(checked) + + if(LAZYLEN(trigger_mobs) || LAZYLEN(trigger_species) || LAZYLEN(trigger_objs)) + for(var/mob/living/checked in view(owner.client?.view || world.view, owner)) + if (checked != owner && is_scary_mob(checked)) + return freak_out(checked) + +/// Returns true if this item should be scary to us +/datum/terror_handler/phobia_source/proc/is_scary_item(obj/checked) + if (QDELETED(checked) || !is_type_in_typecache(checked, trigger_objs) || checked.invisibility > owner.see_invisible) + return FALSE + + if (!isitem(checked) || !ismob(checked.loc) || HAS_TRAIT(checked.loc, TRAIT_UNKNOWN_APPEARANCE)) + return TRUE + + var/obj/item/checked_item = checked + return !HAS_TRAIT(checked_item, TRAIT_EXAMINE_SKIP) + +/datum/terror_handler/phobia_source/proc/is_scary_mob(mob/living/checked) + if (checked.invisibility > owner.see_invisible || checked.alpha == 0) + return FALSE + + if (is_type_in_typecache(checked, trigger_mobs)) + return TRUE + + if (!ishuman(checked)) + return FALSE + + var/mob/living/carbon/human/as_human = checked + if (LAZYLEN(trigger_species)) + // Can't be racist(?) if you can't see their face + if (is_type_in_typecache(as_human.dna?.species, trigger_species) && !as_human.is_face_obscured()) + return TRUE + + if (!LAZYLEN(trigger_objs)) + return FALSE + + for (var/obj/item/equipped as anything in as_human.get_visible_items()) + if (is_scary_item(equipped)) + return TRUE + + return FALSE + +/datum/terror_handler/phobia_source/proc/handle_hearing(datum/source, list/hearing_args) + SIGNAL_HANDLER + + if (!can_trigger() || !COOLDOWN_FINISHED(src, scare_cooldown)) + return + + // Words can't trigger you if you can't hear them *taps head* + if(!owner.can_hear() || owner == hearing_args[HEARING_SPEAKER] || !owner.has_language(hearing_args[HEARING_LANGUAGE])) + return + + if(trigger_regex.Find(hearing_args[HEARING_RAW_MESSAGE])) + // To react AFTER the chat message + addtimer(CALLBACK(src, PROC_REF(freak_out), null, trigger_regex.group[2]), 1 SECONDS) + hearing_args[HEARING_RAW_MESSAGE] = trigger_regex.Replace(hearing_args[HEARING_RAW_MESSAGE], "[span_phobia("$2")]$3") + +/datum/terror_handler/phobia_source/proc/handle_speech(datum/source, list/speech_args) + SIGNAL_HANDLER + + if (!can_trigger()) + return + + if (trigger_regex.Find(speech_args[SPEECH_MESSAGE]) == 0) + return + + var/stutter = prob(50) + var/whisper = prob(30) + + if (!stutter && !whisper) + return + + if (whisper) + speech_args[SPEECH_SPANS] |= SPAN_SMALL_VOICE + if (stutter) + owner.set_stutter_if_lower(4 SECONDS) + to_chat(owner, span_warning("You struggle to say the word \"[span_phobia("[trigger_regex.group[2]]")]\"!")) + +/datum/terror_handler/phobia_source/proc/freak_out(reason) + COOLDOWN_START(src, scare_cooldown, 12 SECONDS) + var/message = pick("spooks you to the bone", "shakes you up", "terrifies you", "sends you into a panic", "sends chills down your spine") + if(istext(reason)) + to_chat(owner, span_bolddanger("Hearing [span_phobia(reason)] [message]!")) + owner.add_mood_event("phobia_minor", /datum/mood_event/startled) + // Because this is called from a signal and not the main process, we need to add the buildup by hand + if (component.terror_buildup < TERROR_BUILDUP_PASSIVE_MAXIMUM) + component.terror_buildup = min(component.terror_buildup + PHOBIA_WORD_TERROR_BUILDUP, TERROR_BUILDUP_PASSIVE_MAXIMUM) + return + + if(mood_event_type) + owner.add_mood_event("phobia", mood_event_type) + + if(isatom(reason)) + var/atom/as_atom = reason + to_chat(owner, span_bolddanger("Seeing [span_phobia("[as_atom.name]")] [message]!")) + else + to_chat(owner, span_bolddanger("Something [message]!")) + return PHOBIA_FREAKOUT_TERROR_BUILDUP + +/datum/terror_handler/phobia_source/on_hug(mob/living/hugger) + if (is_scary_mob(hugger)) + return HUG_TERROR_AMOUNT + return 0 + +/// Snowflake handler for hemophobia which triggers on bloodied items and mobs +/datum/terror_handler/phobia_source/blood + +/datum/terror_handler/phobia_source/blood/is_scary_item(obj/checked) + if (GET_ATOM_BLOOD_DNA_LENGTH(checked)) + return TRUE + return ..() + +/datum/terror_handler/phobia_source/blood/is_scary_mob(mob/living/checked) + if (GET_ATOM_BLOOD_DNA_LENGTH(checked)) + return TRUE + return ..() diff --git a/code/datums/components/fearful/terror_handler.dm b/code/datums/components/fearful/terror_handler.dm index 93aa6a6c5b1..1b2add3bf35 100644 --- a/code/datums/components/fearful/terror_handler.dm +++ b/code/datums/components/fearful/terror_handler.dm @@ -11,6 +11,8 @@ var/default = FALSE /// Other effects which should be disabled while this one is running var/list/overrides + /// Is this handler bespoke, and always requires a new instance upon adding even when one is already present? + var/bespoke = FALSE /datum/terror_handler/New(mob/living/new_owner, datum/component/fearful/new_component) . = ..() @@ -25,3 +27,7 @@ /// Single tick of terror handler, returns adjustment to terror buildup /datum/terror_handler/proc/tick(seconds_per_tick, terror_buildup) return 0 + +/// Additional effects when we're hugged by a mob, returns fear adjustment per hug. Should only be positive. +/datum/terror_handler/proc/on_hug(mob/living/hugger) + return 0 diff --git a/code/datums/quirks/negative_quirks/monophobia.dm b/code/datums/quirks/negative_quirks/monophobia.dm new file mode 100644 index 00000000000..5c0d732826c --- /dev/null +++ b/code/datums/quirks/negative_quirks/monophobia.dm @@ -0,0 +1,16 @@ +/datum/quirk/monophobia + name = "Monophobia" + desc = "You have an extreme fear of loneliness, and have always tried to stick to large groups." + icon = FA_ICON_PEOPLE_GROUP + value = -3 + medical_record_text = "Patient has a severe fear of being left alone." + hardcore_value = 5 + mail_goodies = list(/obj/effect/spawner/random/entertainment/plushie) + +/datum/quirk/monophobia/add(client/client_source) + var/mob/living/carbon/human/human_holder = quirk_holder + human_holder.gain_trauma(new /datum/brain_trauma/severe/monophobia(), TRAUMA_RESILIENCE_ABSOLUTE) + +/datum/quirk/monophobia/remove() + var/mob/living/carbon/human/human_holder = quirk_holder + human_holder.cure_trauma_type(/datum/brain_trauma/severe/monophobia, TRAUMA_RESILIENCE_ABSOLUTE) diff --git a/code/datums/status_effects/debuffs/phobia.dm b/code/datums/status_effects/debuffs/phobia.dm deleted file mode 100644 index 76520e4dc58..00000000000 --- a/code/datums/status_effects/debuffs/phobia.dm +++ /dev/null @@ -1,77 +0,0 @@ -/// Basically a cooldown which makes you emote when it starts, but allows it to be easily shared between multiple phobias -/datum/status_effect/minor_phobia_reaction - id = "phobia_minor" - duration = 12 SECONDS - alert_type = null - -/datum/status_effect/minor_phobia_reaction/on_apply() - . = ..() - owner.emote("scream") - owner.set_jitter_if_lower(6 SECONDS) - owner.add_mood_event("phobia_minor", /datum/mood_event/startled) - -/// Stacking severity of phobic reaction -/// The more stacks you are the more scared you are -/datum/status_effect/stacking/phobia_reaction - id = "phobia" - status_type = STATUS_EFFECT_REFRESH - stacks = 1 - max_stacks = 6 - tick_interval = 40 SECONDS - consumed_on_threshold = FALSE - -/datum/status_effect/stacking/phobia_reaction/on_creation(mob/living/new_owner, stacks_to_apply = 1, mood_event_type) - . = ..() - if (!.) - return FALSE - - if (mood_event_type) - owner.add_mood_event("phobia", mood_event_type) - return TRUE - -/datum/status_effect/stacking/phobia_reaction/refresh(effect, stacks_to_add) - . = ..() - add_stacks(stacks_to_add) - -/datum/status_effect/stacking/phobia_reaction/add_stacks(stacks_added) - . = ..() - if (stacks_added <= 0 || stacks <= 0) - return - - var/reaction = rand(1,4) - switch(reaction) - if(1) - to_chat(owner, span_warning("You are startled!")) - owner.emote("jump") - owner.Immobilize(0.1 SECONDS * stacks) - - if(2) - owner.emote("scream") - owner.say("AAAAH!!", forced = "phobia") - - if(stacks >= 5) - var/held_item = owner.get_active_held_item() - if (owner.dropItemToGround(held_item)) - owner.visible_message( - span_danger("[owner.name] drops \the [held_item]!"), - span_warning("You drop \the [held_item]!"), null, COMBAT_MESSAGE_RANGE) - - if(3) - to_chat(owner, span_warning("You lose your balance!")) - owner.adjust_staggered_up_to(2 SECONDS * stacks, 20 SECONDS) - owner.add_movespeed_modifier(/datum/movespeed_modifier/status_effect/spooked) - // We're relying on the fact that there's a 12 second application cooldown to not have to bother cancelling and replacing this timer - // So if you adjust the duration keep that in mind - addtimer(CALLBACK(src, PROC_REF(speed_up)), 1 SECONDS * stacks, TIMER_STOPPABLE | TIMER_DELETE_ME) - - if(4) - to_chat(owner, span_warning("You feel faint with fright!")) - owner.adjust_dizzy_up_to(2 SECONDS * stacks, 20 SECONDS) - owner.adjust_eye_blur_up_to(1.5 SECONDS * stacks, 6 SECONDS) - -/datum/status_effect/stacking/phobia_reaction/fadeout_effect() - to_chat(owner, span_notice("You calm down.")) - -/// Remove our active movespeed modifier -/datum/status_effect/stacking/phobia_reaction/proc/speed_up() - owner.remove_movespeed_modifier(/datum/movespeed_modifier/status_effect/spooked) diff --git a/tgstation.dme b/tgstation.dme index b4d6632d800..89193e5f27d 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -1385,8 +1385,9 @@ #include "code\datums\components\fantasy\suffixes.dm" #include "code\datums\components\fearful\effects.dm" #include "code\datums\components\fearful\fearful.dm" -#include "code\datums\components\fearful\sources.dm" #include "code\datums\components\fearful\terror_handler.dm" +#include "code\datums\components\fearful\sources\_sources.dm" +#include "code\datums\components\fearful\sources\phobia.dm" #include "code\datums\components\food\decomposition.dm" #include "code\datums\components\food\edible.dm" #include "code\datums\components\food\germ_sensitive.dm" @@ -1887,6 +1888,7 @@ #include "code\datums\quirks\negative_quirks\indebted.dm" #include "code\datums\quirks\negative_quirks\insanity.dm" #include "code\datums\quirks\negative_quirks\light_drinker.dm" +#include "code\datums\quirks\negative_quirks\monophobia.dm" #include "code\datums\quirks\negative_quirks\mute.dm" #include "code\datums\quirks\negative_quirks\narcolepsy.dm" #include "code\datums\quirks\negative_quirks\nearsighted.dm" @@ -2031,7 +2033,6 @@ #include "code\datums\status_effects\debuffs\hooked.dm" #include "code\datums\status_effects\debuffs\jitteriness.dm" #include "code\datums\status_effects\debuffs\pacifism.dm" -#include "code\datums\status_effects\debuffs\phobia.dm" #include "code\datums\status_effects\debuffs\rust_corruption.dm" #include "code\datums\status_effects\debuffs\screen_blur.dm" #include "code\datums\status_effects\debuffs\screwy_hud.dm"