Files
SmArtKar e49e13b795 Reworks phobias to use the new fear system instead of handling all the code via traumas (#94455)
## About The Pull Request

Phobias have been refactored to use the new fear system instead of
handling their effects via simple triggers on themselves. The overall
idea is still the same, triggering upon seeing/hearing/saying a scary
word or thing, but instead of instantly sending you into a panic it adds
some fear and has a chance of triggering a new startle fear effect,
which mirrors some of the previous phobia effects that weren't already
included in other handlers.
To complete the phobic trio, I've added monophobia as a quirk - its
quite interesting compared to nycto- and claustrophobia, as it forces
you to stick to other people, thus indirectly incentivizing social
interactions, and could make for actually interesting moments.
Additionally, I've cleaned up some of the related code and added message
CDs to all other handlers that were missing them as to avoid chat spam
which could sometimes occur when the effect triggered far too
frequently.

## Why It's Good For The Game

Using the new system makes phobias much more immersive and interesting
to play around, as they no longer absolutely cripple you to the point of
making the game unplayable in case of some of them due to random stuns
or knockouts. It also makes them natively interact with other phobias
and quirks, which is a neat thing for character building.
Currently getting a phobia basically forces you to go to medbay as to
not risk randomly falling unconscious every once in a while if you roll
a common one (like cyborgs, blood, doctors or authority), and picking
one as a quirk is basically not an option.

## Changelog
🆑
add: Monophobia is now availible as a negative (-3) quirk.
balance: Phobias now have accumulating, and less debilitating effects
due to conversion to the new fear system.
qol: Fear effects now have a cooldown between messages as to avoid chat
spam.
code: Cleaned up some of fear code.
refactor: Refactored phobias to use the fear system
/🆑
2025-12-20 22:57:58 +00:00

210 lines
8.2 KiB
Plaintext

/// Fearful component: provides optional handling of fears and phobias for mob's mood
/// Can be applied from multiple sources, and essentially serves as a central controller for fear datums described below
/datum/component/fearful
dupe_mode = COMPONENT_DUPE_SOURCES
/// How terrified is the source?
var/terror_buildup = 0
/// List of terror handlers we currently have -> sources they're added by
var/list/terror_handlers = list()
/// List of overriden handler types, for ease of access
var/list/list/overriden_handlers = list()
/// How much buildup we had last tick? Used for non-ticking terror source tracking
var/last_tick_buildup = 0
/*
* handler_types - terror_handler(s) to add to the mob
* initial_buildup - amount of fear to add to mob from getting scared shitless by whatever added the component
* add_defaults - should terror handlers marked as "default" be added to the mob?
*/
/datum/component/fearful/Initialize(list/handler_types, initial_buildup, add_defaults = TRUE)
. = ..()
if (!isliving(parent))
return COMPONENT_INCOMPATIBLE
START_PROCESSING(SSdcs, src)
terror_buildup = initial_buildup
if (!add_defaults)
return
for (var/datum/terror_handler/handler as anything in subtypesof(/datum/terror_handler))
if (!initial(handler.default))
continue
add_handler(handler, "default")
/datum/component/fearful/Destroy(force)
STOP_PROCESSING(SSdcs, src)
QDEL_LIST(terror_handlers)
return ..()
/datum/component/fearful/RegisterWithParent()
. = ..()
RegisterSignal(parent, COMSIG_ATOM_EXAMINE, PROC_REF(on_examine))
RegisterSignal(parent, COMSIG_CARBON_PRE_MISC_HELP, PROC_REF(comfort_owner))
RegisterSignal(parent, SIGNAL_ADDTRAIT(TRAIT_FEARLESS), PROC_REF(fearless_added))
RegisterSignal(parent, COMSIG_CARBON_MOOD_CHECK, PROC_REF(on_mood_check))
/datum/component/fearful/UnregisterFromParent()
. = ..()
UnregisterSignal(parent, list(COMSIG_ATOM_EXAMINE, COMSIG_CARBON_PRE_MISC_HELP, SIGNAL_ADDTRAIT(TRAIT_FEARLESS), COMSIG_CARBON_MOOD_CHECK))
/datum/component/fearful/on_source_add(source, list/handler_types, initial_buildup, add_defaults = TRUE)
. = ..()
terror_buildup = clamp(terror_buildup + initial_buildup, 0, TERROR_BUILDUP_MAXIMUM)
for (var/handler_type in handler_types)
add_handler(handler_type, source)
/datum/component/fearful/on_source_remove(source)
for (var/datum/terror_handler/handler as anything in terror_handlers)
terror_handlers[handler] -= source
if (length(terror_handlers[handler]))
continue
terror_handlers -= handler
for (var/override_type in handler.overrides)
if (!overriden_handlers[override_type])
continue
overriden_handlers[override_type] -= handler.type
if (!length(overriden_handlers[override_type]))
overriden_handlers -= override_type
qdel(handler)
return ..()
/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 && !existing.bespoke)
terror_handlers[existing] += source
return
var/datum/terror_handler/handler = new handler_type(parent, src)
terror_handlers[handler] = list(source)
for (var/override_type in handler.overrides)
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
var/mob/living/parent_mob = parent
if(HAS_PERSONALITY(parent_mob, /datum/personality/cowardly))
multiplier *= 1.25
if(HAS_PERSONALITY(parent_mob, /datum/personality/paranoid))
multiplier *= 1.10
if(HAS_PERSONALITY(parent_mob, /datum/personality/brave))
multiplier *= 0.75
return multiplier
/datum/component/fearful/process(seconds_per_tick)
var/fear_modifier = get_fear_multiplier()
var/terror_adjustment = 0
var/list/tick_later = list()
for (var/datum/terror_handler/handler as anything in terror_handlers)
if (overriden_handlers[handler.type])
continue
if (handler.handler_type == TERROR_HANDLER_EFFECT)
tick_later += handler
continue
var/adjustment = handler.tick(seconds_per_tick, terror_buildup)
terror_buildup = clamp(terror_buildup + (adjustment * fear_modifier), 0, TERROR_BUILDUP_MAXIMUM)
terror_adjustment += adjustment
for (var/datum/terror_handler/handler as anything in tick_later)
var/adjustment = handler.tick(seconds_per_tick, terror_buildup)
terror_buildup = clamp(terror_buildup + (adjustment * fear_modifier), 0, TERROR_BUILDUP_MAXIMUM)
terror_adjustment += adjustment
// If we gained terror in any way, don't tick it down
if (terror_adjustment > 0 || terror_buildup > last_tick_buildup)
last_tick_buildup = terror_buildup
return
// Tick terror down while we're not being actively spooked
if (terror_buildup > 0)
terror_buildup = max(terror_buildup - (TERROR_BUILDUP_PASSIVE_DECREASE * (1 / fear_modifier)) * seconds_per_tick, 0)
last_tick_buildup = terror_buildup
/datum/component/fearful/proc/on_examine(mob/living/source, mob/user, list/examine_list)
SIGNAL_HANDLER
if (source.stat >= UNCONSCIOUS)
return
if(terror_buildup >= TERROR_BUILDUP_HEART_ATTACK)
examine_list += span_danger("[source.p_They()] [source.p_are()] seizing up, about to collapse in fear!")
else if(terror_buildup > TERROR_BUILDUP_PANIC)
examine_list += span_boldwarning("[source.p_They()] [source.p_are()] trembling and shaking, barely standing upright!")
else if(terror_buildup >= TERROR_BUILDUP_TERROR)
examine_list += span_boldwarning("[source] is visibly trembling and twitching. [source.p_They()] [source.p_are()] clearly in distress!")
else if(terror_buildup >= TERROR_BUILDUP_FEAR)
examine_list += span_warning("[source] looks very worried about something. [capitalize(source.p_are())] [source.p_they()] alright?")
else if (terror_buildup)
examine_list += span_smallnotice("[source] looks rather anxious. [source.p_They()] could probably use a hug...")
/datum/component/fearful/proc/comfort_owner(mob/living/carbon/source, mob/living/hugger)
SIGNAL_HANDLER
if(hugger == parent)
return
if(isnightmare(hugger))
var/lit_tiles = 0
var/unlit_tiles = 0
for(var/turf/open/turf_to_check in range(1, source))
var/light_amount = turf_to_check.get_lumcount()
if(light_amount > LIGHTING_TILE_IS_DARK)
lit_tiles++
else
unlit_tiles++
if(lit_tiles < unlit_tiles)
source.Knockdown(0.5 SECONDS)
terror_buildup += HUG_TERROR_AMOUNT
source.visible_message(
span_warning("[source] recoils in fear as [hugger] waves [hugger.p_their()] arms and shrieks at [source.p_them()]!"),
span_boldwarning("The shadows lash out at you, and you drop to the ground in fear!"),
span_hear("You hear someone shriek in fear. How embarassing!"),
)
return COMPONENT_BLOCK_MISC_HELP
var/hug_buildup = 0
for (var/datum/terror_handler/handler as anything in terror_handlers)
hug_buildup += handler.on_hug(hugger)
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!"),
span_hear("You hear someone shriek in fear. How embarassing!"),
)
return COMPONENT_BLOCK_MISC_HELP
terror_buildup -= HUG_TERROR_AMOUNT
source.visible_message(
span_notice("[source] seems to relax as [hugger] gives [source.p_them()] a comforting hug."),
span_nicegreen("You feel yourself calm down as [hugger] gives you a reassuring hug."),
span_hear("You hear shuffling and a sigh of relief."),
)
/// Remove all terror buildup when we become fearless
/datum/component/fearful/proc/fearless_added(datum/source)
SIGNAL_HANDLER
terror_buildup = 0
/datum/component/fearful/proc/on_mood_check(mob/living/source, list/mood_list)
SIGNAL_HANDLER
if(terror_buildup >= TERROR_BUILDUP_HEART_ATTACK)
mood_list += span_boldwarning("You are about to collapse in fear!")
else if(terror_buildup > TERROR_BUILDUP_PANIC)
mood_list += span_boldwarning("You are shaking in fear!")
else if(terror_buildup >= TERROR_BUILDUP_TERROR)
mood_list += span_warning("You are trembling in fear.")
else if(terror_buildup >= TERROR_BUILDUP_FEAR)
mood_list += span_warning("You feel scared.")
else if (terror_buildup)
mood_list += span_notice("You feel on the edge.")