Files
Bubberstation/code/datums/components/mood.dm
SkyratBot 01a6ade18c [MIRROR] Change healing by sleeping to be affected by sanity, darkness (or blindfold), and earmuffs. [MDB IGNORE] (#13758)
* Change healing by sleeping to be affected by sanity, darkness (or blindfold), and earmuffs. (#65713)

About The Pull Request

Depending on the mob's sanity level, it can have a positive or negative boost to healing effects while sleeping. Sleeping in darkness, wearing a blindfold, and using earmuffs also counts as a healing bonus. Beauty sleep is very important for 2D spessmen.
Why It's Good For The Game

This is a small gameplay change that rewards players for keeping their sanity at good levels. Also depression has also been linked with impeding wound healing in real life. The placebo effect on peoples minds is strenuously documented and I think it would be cool to see it in the game.
Changelog

cl
expansion: Healing by sleeping is now affected by sanity, sleeping in darkness (or using a blindfold), and using earmuffs. The healing from sleeping in a bed was slightly decreased.
/cl

* Change healing by sleeping to be affected by sanity, darkness (or blindfold), and earmuffs.

Co-authored-by: Tim <timothymtorres@gmail.com>
2022-05-20 13:34:33 +01:00

492 lines
17 KiB
Plaintext

#define MINOR_INSANITY_PEN 5
#define MAJOR_INSANITY_PEN 10
/datum/component/mood
var/mood //Real happiness
var/sanity = SANITY_NEUTRAL //Current sanity
var/shown_mood //Shown happiness, this is what others can see when they try to examine you, prevents antag checking by noticing traitors are always very happy.
var/mood_level = 5 //To track what stage of moodies they're on
var/sanity_level = SANITY_LEVEL_NEUTRAL //To track what stage of sanity they're on
var/mood_modifier = 1 //Modifier to allow certain mobs to be less affected by moodlets
var/list/datum/mood_event/mood_events = list()
var/insanity_effect = 0 //is the owner being punished for low mood? If so, how much?
var/atom/movable/screen/mood/screen_obj
/datum/component/mood/Initialize()
if(!isliving(parent))
return COMPONENT_INCOMPATIBLE
START_PROCESSING(SSmood, src)
RegisterSignal(parent, COMSIG_ADD_MOOD_EVENT, .proc/add_event)
RegisterSignal(parent, COMSIG_CLEAR_MOOD_EVENT, .proc/clear_event)
RegisterSignal(parent, COMSIG_ENTER_AREA, .proc/check_area_mood)
RegisterSignal(parent, COMSIG_LIVING_REVIVE, .proc/on_revive)
RegisterSignal(parent, COMSIG_MOB_HUD_CREATED, .proc/modify_hud)
RegisterSignal(parent, COMSIG_JOB_RECEIVED, .proc/register_job_signals)
RegisterSignal(parent, COMSIG_HERETIC_MASK_ACT, .proc/direct_sanity_drain)
RegisterSignal(parent, COMSIG_ON_CARBON_SLIP, .proc/on_slip)
var/mob/living/owner = parent
owner.become_area_sensitive(MOOD_COMPONENT_TRAIT)
if(owner.hud_used)
modify_hud()
var/datum/hud/hud = owner.hud_used
hud.show_hud(hud.hud_version)
/datum/component/mood/Destroy()
STOP_PROCESSING(SSmood, src)
var/atom/movable/movable_parent = parent
movable_parent.lose_area_sensitivity(MOOD_COMPONENT_TRAIT)
unmodify_hud()
return ..()
/datum/component/mood/proc/register_job_signals(datum/source, job)
SIGNAL_HANDLER
if(job in list(JOB_RESEARCH_DIRECTOR, JOB_SCIENTIST, JOB_ROBOTICIST, JOB_GENETICIST))
RegisterSignal(parent, COMSIG_ADD_MOOD_EVENT_RND, .proc/add_event) //Mood events that are only for RnD members
/datum/component/mood/proc/print_mood(mob/user)
var/msg = "[span_info("*---------*\n<EM>My current mental status:</EM>")]\n"
msg += span_notice("My current sanity: ") //Long term
//ORIGINAL
/*
switch(sanity)
if(SANITY_GREAT to INFINITY)
msg += "[span_boldnicegreen("My mind feels like a temple!")]\n"
if(SANITY_NEUTRAL to SANITY_GREAT)
msg += "[span_nicegreen("I have been feeling great lately!")]\n"
if(SANITY_DISTURBED to SANITY_NEUTRAL)
msg += "[span_nicegreen("I have felt quite decent lately.")]\n"
if(SANITY_UNSTABLE to SANITY_DISTURBED)
msg += "[span_warning("I'm feeling a little bit unhinged...")]\n"
if(SANITY_CRAZY to SANITY_UNSTABLE)
msg += "[span_warning("I'm freaking out!!")]\n"
if(SANITY_INSANE to SANITY_CRAZY)
msg += "<span class='boldwarning'>AHAHAHAHAHAHAHAHAHAH!!</span>\n"
*/
//SKYRAT EDIT CHANGE BEGIN - ALEXITHYMIA
if(!HAS_TRAIT(user, TRAIT_MOOD_NOEXAMINE))
switch(sanity)
if(SANITY_GREAT to INFINITY)
msg += "[span_nicegreen("My mind feels like a temple!")]\n"
if(SANITY_NEUTRAL to SANITY_GREAT)
msg += "[span_nicegreen("I have been feeling great lately!")]\n"
if(SANITY_DISTURBED to SANITY_NEUTRAL)
msg += "[span_nicegreen("I have felt quite decent lately.")]\n"
if(SANITY_UNSTABLE to SANITY_DISTURBED)
msg += "[span_warning("I'm feeling a little bit unhinged...")]\n"
if(SANITY_CRAZY to SANITY_UNSTABLE)
msg += "[span_boldwarning("I'm freaking out!!")]\n"
if(SANITY_INSANE to SANITY_CRAZY)
msg += "<span class='boldwarning'>AHAHAHAHAHAHAHAHAHAH!!</span>\n"
else
msg += span_notice("I don't really know.")
//SKYRAT EDIT CHANGE END
msg += span_notice("My current mood: ") //Short term
//ORIGINAL
/*
switch(mood_level)
if(1)
msg += "[span_boldwarning("I wish I was dead!")]\n"
if(2)
msg += "[span_boldwarning("I feel terrible...")]\n"
if(3)
msg += "[span_boldwarning("I feel very upset.")]\n"
if(4)
msg += "[span_warning("I'm a bit sad.")]\n"
if(5)
msg += "[span_grey("I'm alright.")]\n"
if(6)
msg += "[span_nicegreen("I feel pretty okay.")]\n"
if(7)
msg += "[span_boldnicegreen("I feel pretty good.")]\n"
if(8)
msg += "[span_boldnicegreen("I feel amazing!")]\n"
if(9)
msg += "[span_boldnicegreen("I love life!")]\n"
*/
//SKYRAT EDIT CHANGE BEGIN - ALEXITHYMIA
if(!HAS_TRAIT(user, TRAIT_MOOD_NOEXAMINE))
switch(mood_level)
if(1)
msg += "[span_boldwarning("I wish I was dead!")]\n"
if(2)
msg += "[span_boldwarning("I feel terrible...")]\n"
if(3)
msg += "[span_boldwarning("I feel very upset.")]\n"
if(4)
msg += "[span_boldwarning("I'm a bit sad.")]\n"
if(5)
msg += "[span_nicegreen("I'm alright.")]\n"
if(6)
msg += "[span_nicegreen("I feel pretty okay.")]\n"
if(7)
msg += "[span_nicegreen("I feel pretty good.")]\n"
if(8)
msg += "[span_nicegreen("I feel amazing!")]\n"
if(9)
msg += "[span_nicegreen("I love life!")]\n"
else
msg += "[span_notice("No clue.")]\n"
//SKYRAT EDIT CHANGE END
msg += "[span_notice("Moodlets:")]\n"//All moodlets
//if(mood_events.len) //ORIGINAL
if(mood_events.len && !HAS_TRAIT(user, TRAIT_MOOD_NOEXAMINE)) //SKYRAT EDIT CHANGE - ALEXITHYMIA
for(var/i in mood_events)
var/datum/mood_event/event = mood_events[i]
switch(event.mood_change)
if(-INFINITY to MOOD_LEVEL_SAD2)
msg += span_boldwarning(event.description + "\n")
if(MOOD_LEVEL_SAD2 to MOOD_LEVEL_SAD1)
msg += span_warning(event.description + "\n")
if(MOOD_LEVEL_SAD1 to MOOD_LEVEL_HAPPY1)
msg += span_grey(event.description + "\n")
if(MOOD_LEVEL_HAPPY1 to MOOD_LEVEL_HAPPY2)
msg += span_nicegreen(event.description + "\n")
if(MOOD_LEVEL_HAPPY2 to INFINITY)
msg += span_boldnicegreen(event.description + "\n")
else
msg += "[span_grey("I don't have much of a reaction to anything right now.")]\n"
to_chat(user, examine_block(msg)) //SKYRAT EDIT CHANGE
///Called after moodevent/s have been added/removed.
/datum/component/mood/proc/update_mood()
mood = 0
shown_mood = 0
for(var/i in mood_events)
var/datum/mood_event/event = mood_events[i]
mood += event.mood_change
if(!event.hidden)
shown_mood += event.mood_change
mood *= mood_modifier
shown_mood *= mood_modifier
switch(mood)
if(-INFINITY to MOOD_LEVEL_SAD4)
mood_level = 1
if(MOOD_LEVEL_SAD4 to MOOD_LEVEL_SAD3)
mood_level = 2
if(MOOD_LEVEL_SAD3 to MOOD_LEVEL_SAD2)
mood_level = 3
if(MOOD_LEVEL_SAD2 to MOOD_LEVEL_SAD1)
mood_level = 4
if(MOOD_LEVEL_SAD1 to MOOD_LEVEL_HAPPY1)
mood_level = 5
if(MOOD_LEVEL_HAPPY1 to MOOD_LEVEL_HAPPY2)
mood_level = 6
if(MOOD_LEVEL_HAPPY2 to MOOD_LEVEL_HAPPY3)
mood_level = 7
if(MOOD_LEVEL_HAPPY3 to MOOD_LEVEL_HAPPY4)
mood_level = 8
if(MOOD_LEVEL_HAPPY4 to INFINITY)
mood_level = 9
update_mood_icon()
/datum/component/mood/proc/update_mood_icon()
var/mob/living/owner = parent
if(!(owner.client || owner.hud_used))
return
screen_obj.cut_overlays()
screen_obj.color = initial(screen_obj.color)
//lets see if we have any special icons to show instead of the normal mood levels
var/list/conflicting_moodies = list()
var/highest_absolute_mood = 0
for(var/i in mood_events) //adds overlays and sees which special icons need to vie for which one gets the icon_state
var/datum/mood_event/event = mood_events[i]
if(!event.special_screen_obj)
continue
if(!event.special_screen_replace)
screen_obj.add_overlay(event.special_screen_obj)
else
conflicting_moodies += event
var/absmood = abs(event.mood_change)
if(absmood > highest_absolute_mood)
highest_absolute_mood = absmood
switch(sanity_level)
if(1)
screen_obj.color = "#2eeb9a"
if(2)
screen_obj.color = "#86d656"
if(3)
screen_obj.color = "#4b96c4"
if(4)
screen_obj.color = "#dfa65b"
if(5)
screen_obj.color = "#f38943"
if(6)
screen_obj.color = "#f15d36"
if(!conflicting_moodies.len) //no special icons- go to the normal icon states
//SKYRAT EDIT ADDITION BEGIN - ALEXITHYMIA
if(HAS_TRAIT(owner, TRAIT_MOOD_NOEXAMINE))
screen_obj.icon_state = "mood5"
screen_obj.color = "#4b96c4"
return
//SKYRAT EDIT ADDITION END
screen_obj.icon_state = "mood[mood_level]"
return
for(var/i in conflicting_moodies)
var/datum/mood_event/event = i
if(abs(event.mood_change) == highest_absolute_mood)
screen_obj.icon_state = "[event.special_screen_obj]"
break
///Called on SSmood process
/datum/component/mood/process(delta_time)
var/mob/living/moody_fellow = parent
if(moody_fellow.stat == DEAD)
return //updating sanity during death leads to people getting revived and being completely insane for simply being dead for a long time
switch(mood_level)
if(1)
setSanity(sanity-0.3*delta_time, SANITY_INSANE)
if(2)
setSanity(sanity-0.15*delta_time, SANITY_INSANE)
if(3)
setSanity(sanity-0.1*delta_time, SANITY_CRAZY)
if(4)
setSanity(sanity-0.05*delta_time, SANITY_UNSTABLE)
if(5)
setSanity(sanity, SANITY_UNSTABLE) //This makes sure that mood gets increased should you be below the minimum.
if(6)
setSanity(sanity+0.2*delta_time, SANITY_UNSTABLE)
if(7)
setSanity(sanity+0.3*delta_time, SANITY_UNSTABLE)
if(8)
setSanity(sanity+0.4*delta_time, SANITY_NEUTRAL, SANITY_MAXIMUM)
if(9)
setSanity(sanity+0.6*delta_time, SANITY_NEUTRAL, SANITY_MAXIMUM)
HandleNutrition()
// 0.416% is 15 successes / 3600 seconds. Calculated with 2 minute
// mood runtime, so 50% average uptime across the hour.
if(HAS_TRAIT(parent, TRAIT_DEPRESSION) && DT_PROB(0.416, delta_time))
add_event(null, "depression_mild", /datum/mood_event/depression_mild)
if(HAS_TRAIT(parent, TRAIT_JOLLY) && DT_PROB(0.416, delta_time))
add_event(null, "jolly", /datum/mood_event/jolly)
///Sets sanity to the specified amount and applies effects.
/datum/component/mood/proc/setSanity(amount, minimum=SANITY_INSANE, maximum=SANITY_GREAT, override = FALSE)
// If we're out of the acceptable minimum-maximum range move back towards it in steps of 0.7
// If the new amount would move towards the acceptable range faster then use it instead
if(amount < minimum)
amount += clamp(minimum - amount, 0, 0.7)
if((!override && HAS_TRAIT(parent, TRAIT_UNSTABLE)) || amount > maximum)
amount = min(sanity, amount)
if(amount == sanity) //Prevents stuff from flicking around.
return
sanity = amount
var/mob/living/master = parent
SEND_SIGNAL(master, COMSIG_CARBON_SANITY_UPDATE, amount)
switch(sanity)
if(SANITY_INSANE to SANITY_CRAZY)
setInsanityEffect(MAJOR_INSANITY_PEN)
master.add_movespeed_modifier(/datum/movespeed_modifier/sanity/insane)
master.add_actionspeed_modifier(/datum/actionspeed_modifier/low_sanity)
sanity_level = SANITY_LEVEL_INSANE
if(SANITY_CRAZY to SANITY_UNSTABLE)
setInsanityEffect(MINOR_INSANITY_PEN)
master.add_movespeed_modifier(/datum/movespeed_modifier/sanity/crazy)
master.add_actionspeed_modifier(/datum/actionspeed_modifier/low_sanity)
sanity_level = SANITY_LEVEL_CRAZY
if(SANITY_UNSTABLE to SANITY_DISTURBED)
setInsanityEffect(0)
master.add_movespeed_modifier(/datum/movespeed_modifier/sanity/disturbed)
master.add_actionspeed_modifier(/datum/actionspeed_modifier/low_sanity)
sanity_level = SANITY_LEVEL_UNSTABLE
if(SANITY_DISTURBED to SANITY_NEUTRAL)
setInsanityEffect(0)
master.remove_movespeed_modifier(MOVESPEED_ID_SANITY)
master.remove_actionspeed_modifier(ACTIONSPEED_ID_SANITY)
sanity_level = SANITY_LEVEL_DISTURBED
if(SANITY_NEUTRAL+1 to SANITY_GREAT+1) //shitty hack but +1 to prevent it from responding to super small differences
setInsanityEffect(0)
master.remove_movespeed_modifier(MOVESPEED_ID_SANITY)
master.add_actionspeed_modifier(/datum/actionspeed_modifier/high_sanity)
sanity_level = SANITY_LEVEL_NEUTRAL
if(SANITY_GREAT+1 to INFINITY)
setInsanityEffect(0)
master.remove_movespeed_modifier(MOVESPEED_ID_SANITY)
master.add_actionspeed_modifier(/datum/actionspeed_modifier/high_sanity)
sanity_level = SANITY_LEVEL_GREAT
update_mood_icon()
/datum/component/mood/proc/setInsanityEffect(newval)
if(newval == insanity_effect)
return
var/mob/living/master = parent
master.crit_threshold = (master.crit_threshold - insanity_effect) + newval
insanity_effect = newval
/datum/component/mood/proc/add_event(datum/source, category, type, ...) //Category will override any events in the same category, should be unique unless the event is based on the same thing like hunger.
SIGNAL_HANDLER
var/datum/mood_event/the_event
if(!ispath(type, /datum/mood_event))
return
if(!istext(category))
category = REF(category)
if(mood_events[category])
the_event = mood_events[category]
if(the_event.type != type)
clear_event(null, category)
else
if(the_event.timeout)
addtimer(CALLBACK(src, .proc/clear_event, null, category), the_event.timeout, TIMER_UNIQUE|TIMER_OVERRIDE)
return //Don't have to update the event.
var/list/params = args.Copy(4)
params.Insert(1, parent)
the_event = new type(arglist(params))
mood_events[category] = the_event
the_event.category = category
update_mood()
if(the_event.timeout)
addtimer(CALLBACK(src, .proc/clear_event, null, category), the_event.timeout, TIMER_UNIQUE|TIMER_OVERRIDE)
/datum/component/mood/proc/clear_event(datum/source, category)
SIGNAL_HANDLER
if(!istext(category))
category = REF(category)
var/datum/mood_event/event = mood_events[category]
if(!event)
return
mood_events -= category
qdel(event)
update_mood()
/datum/component/mood/proc/remove_temp_moods() //Removes all temp moods
for(var/i in mood_events)
var/datum/mood_event/moodlet = mood_events[i]
if(!moodlet || !moodlet.timeout)
continue
mood_events -= moodlet.category
qdel(moodlet)
update_mood()
/datum/component/mood/proc/modify_hud(datum/source)
SIGNAL_HANDLER
var/mob/living/owner = parent
var/datum/hud/hud = owner.hud_used
screen_obj = new
screen_obj.color = "#4b96c4"
hud.infodisplay += screen_obj
RegisterSignal(hud, COMSIG_PARENT_QDELETING, .proc/unmodify_hud)
RegisterSignal(screen_obj, COMSIG_CLICK, .proc/hud_click)
/datum/component/mood/proc/unmodify_hud(datum/source)
SIGNAL_HANDLER
if(!screen_obj)
return
var/mob/living/owner = parent
var/datum/hud/hud = owner.hud_used
if(hud?.infodisplay)
hud.infodisplay -= screen_obj
QDEL_NULL(screen_obj)
/datum/component/mood/proc/hud_click(datum/source, location, control, params, mob/user)
SIGNAL_HANDLER
if(user != parent)
return
print_mood(user)
/datum/component/mood/proc/HandleNutrition()
var/mob/living/L = parent
if(HAS_TRAIT(L, TRAIT_NOHUNGER))
return FALSE //no mood events for nutrition
switch(L.nutrition)
if(NUTRITION_LEVEL_FULL to INFINITY)
if (!HAS_TRAIT(L, TRAIT_VORACIOUS))
add_event(null, "nutrition", /datum/mood_event/fat)
else
add_event(null, "nutrition", /datum/mood_event/wellfed) // round and full
if(NUTRITION_LEVEL_WELL_FED to NUTRITION_LEVEL_FULL)
add_event(null, "nutrition", /datum/mood_event/wellfed)
if( NUTRITION_LEVEL_FED to NUTRITION_LEVEL_WELL_FED)
add_event(null, "nutrition", /datum/mood_event/fed)
if(NUTRITION_LEVEL_HUNGRY to NUTRITION_LEVEL_FED)
clear_event(null, "nutrition")
if(NUTRITION_LEVEL_STARVING to NUTRITION_LEVEL_HUNGRY)
add_event(null, "nutrition", /datum/mood_event/hungry)
if(0 to NUTRITION_LEVEL_STARVING)
add_event(null, "nutrition", /datum/mood_event/starving)
/datum/component/mood/proc/check_area_mood(datum/source, area/A)
SIGNAL_HANDLER
update_beauty(A)
if(A.mood_bonus && (!A.mood_trait || HAS_TRAIT(source, A.mood_trait)))
add_event(null, "area", /datum/mood_event/area, A.mood_bonus, A.mood_message)
else
clear_event(null, "area")
/datum/component/mood/proc/update_beauty(area/A)
if(A.outdoors) //if we're outside, we don't care.
clear_event(null, "area_beauty")
return FALSE
if(HAS_TRAIT(parent, TRAIT_SNOB))
switch(A.beauty)
if(-INFINITY to BEAUTY_LEVEL_HORRID)
add_event(null, "area_beauty", /datum/mood_event/horridroom)
return
if(BEAUTY_LEVEL_HORRID to BEAUTY_LEVEL_BAD)
add_event(null, "area_beauty", /datum/mood_event/badroom)
return
switch(A.beauty)
if(BEAUTY_LEVEL_BAD to BEAUTY_LEVEL_DECENT)
clear_event(null, "area_beauty")
if(BEAUTY_LEVEL_DECENT to BEAUTY_LEVEL_GOOD)
add_event(null, "area_beauty", /datum/mood_event/decentroom)
if(BEAUTY_LEVEL_GOOD to BEAUTY_LEVEL_GREAT)
add_event(null, "area_beauty", /datum/mood_event/goodroom)
if(BEAUTY_LEVEL_GREAT to INFINITY)
add_event(null, "area_beauty", /datum/mood_event/greatroom)
///Called when parent is ahealed.
/datum/component/mood/proc/on_revive(datum/source, full_heal)
SIGNAL_HANDLER
if(!full_heal)
return
remove_temp_moods()
setSanity(initial(sanity), override = TRUE)
///Causes direct drain of someone's sanity, call it with a numerical value corresponding how badly you want to hurt their sanity
/datum/component/mood/proc/direct_sanity_drain(datum/source, amount)
SIGNAL_HANDLER
setSanity(sanity + amount, override = TRUE)
///Called when parent slips.
/datum/component/mood/proc/on_slip(datum/source)
SIGNAL_HANDLER
add_event(null, "slipped", /datum/mood_event/slipped)
/datum/component/mood/proc/HandleAddictions()
if(!iscarbon(parent))
return
var/mob/living/carbon/affected_carbon = parent
if(sanity < SANITY_GREAT) ///Sanity is low, stay addicted.
return
for(var/addiction_type in affected_carbon.mind.addiction_points)
var/datum/addiction/addiction_to_remove = SSaddiction.all_addictions[type]
affected_carbon.mind.remove_addiction_points(type, addiction_to_remove.high_sanity_addiction_loss) //If true was returned, we lost the addiction!
#undef MINOR_INSANITY_PEN
#undef MAJOR_INSANITY_PEN