#define SLIGHT_INSANITY_PEN 1 #define MINOR_INSANITY_PEN 5 #define MAJOR_INSANITY_PEN 10 #define MOOD_INSANITY_MALUS 0.0054 // per point of sanity below SANITY_DISTURBED, a 40% debuff to skills at rock bottom depression. /datum/component/mood var/mood //Real happiness var/sanity = 100 //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 = 5 //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/obj/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_MODIFY_SANITY, .proc/modify_sanity) RegisterSignal(parent, COMSIG_LIVING_REVIVE, .proc/on_revive) RegisterSignal(parent, COMSIG_MOB_HUD_CREATED, .proc/modify_hud) var/mob/living/owner = parent 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) unmodify_hud() return ..() /datum/component/mood/proc/print_mood(mob/user) var/msg = "*---------*\nYour current mood\n" msg += "My mental status: " //Long term switch(sanity) if(SANITY_GREAT to INFINITY) msg += "My mind feels like a temple!\n" if(SANITY_NEUTRAL to SANITY_GREAT) msg += "I have been feeling great lately!\n" if(SANITY_DISTURBED to SANITY_NEUTRAL) msg += "I have felt quite decent lately.\n" if(SANITY_UNSTABLE to SANITY_DISTURBED) msg += "I'm feeling a little bit unhinged...\n" if(SANITY_CRAZY to SANITY_UNSTABLE) msg += "I'm freaking out!!\n" if(SANITY_INSANE to SANITY_CRAZY) msg += "AHAHAHAHAHAHAHAHAHAH!!\n" msg += "My current mood: " //Short term switch(mood_level) if(1) msg += "I wish I was dead!\n" if(2) msg += "I feel terrible...\n" if(3) msg += "I feel very upset.\n" if(4) msg += "I'm a bit sad.\n" if(5) msg += "I'm alright.\n" if(6) msg += "I feel pretty okay.\n" if(7) msg += "I feel pretty good.\n" if(8) msg += "I feel amazing!\n" if(9) msg += "I love life!\n" msg += "Moodlets:\n"//All moodlets if(mood_events.len) for(var/i in mood_events) var/datum/mood_event/event = mood_events[i] msg += event.description else msg += "I don't have much of a reaction to anything right now.\n" to_chat(user || parent, msg) ///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) if(sanity < 25) screen_obj.icon_state = "mood_insane" else if (owner.has_status_effect(/datum/status_effect/chem/enthrall))//Fermichem enthral chem, maybe change? screen_obj.icon_state = "mood_entrance" else screen_obj.icon_state = "mood[mood_level]" /datum/component/mood/process() //Called on SSmood process if(QDELETED(parent)) // workaround to an obnoxious sneaky periodical runtime. qdel(src) return var/mob/living/owner = parent switch(mood_level) if(1) setSanity(sanity-0.2) if(2) setSanity(sanity-0.125, minimum=SANITY_CRAZY) if(3) setSanity(sanity-0.075, minimum=SANITY_UNSTABLE) if(4) setSanity(sanity-0.025, minimum=SANITY_DISTURBED) if(5) setSanity(sanity+0.1) if(6) setSanity(sanity+0.15) if(7) setSanity(sanity+0.20) if(8) setSanity(sanity+0.25, maximum=SANITY_GREAT) if(9) setSanity(sanity+0.4, maximum=SANITY_AMAZING) HandleNutrition(owner) /datum/component/mood/proc/setSanity(amount, minimum=SANITY_INSANE, maximum=SANITY_NEUTRAL)//I'm sure bunging this in here will have no negative repercussions. var/mob/living/master = parent if(amount == sanity) return // If we're out of the acceptable minimum-maximum range move back towards it in steps of 0.5 // If the new amount would move towards the acceptable range faster then use it instead if(sanity < minimum && amount < sanity + 0.5) amount = sanity + 0.5 else if(sanity > maximum && amount > sanity - 0.5) amount = sanity - 0.5 var/old_sanity = sanity // Disturbed stops you from getting any more sane if(HAS_TRAIT(master, TRAIT_UNSTABLE)) sanity = min(amount,sanity) else sanity = amount switch(sanity) if(-INFINITY to SANITY_CRAZY) setInsanityEffect(MAJOR_INSANITY_PEN) master.add_movespeed_modifier(/datum/movespeed_modifier/sanity/insane) sanity_level = 6 if(SANITY_CRAZY to SANITY_UNSTABLE) setInsanityEffect(MINOR_INSANITY_PEN) master.add_movespeed_modifier(/datum/movespeed_modifier/sanity/crazy) sanity_level = 5 if(SANITY_UNSTABLE to SANITY_DISTURBED) setInsanityEffect(SLIGHT_INSANITY_PEN) master.add_movespeed_modifier(/datum/movespeed_modifier/sanity/disturbed) sanity_level = 4 if(SANITY_DISTURBED to SANITY_NEUTRAL) setInsanityEffect(0) master.remove_movespeed_modifier(MOVESPEED_ID_SANITY) sanity_level = 3 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) sanity_level = 2 if(SANITY_GREAT+1 to INFINITY) setInsanityEffect(0) master.remove_movespeed_modifier(MOVESPEED_ID_SANITY) sanity_level = 1 if(old_sanity > 1 && sanity == 1) RegisterSignal(master, COMSIG_MOB_SKILL_GET_AFFINITY, .proc/on_get_skill_affinity) else if(old_sanity == 1 && sanity > 1) UnregisterSignal(master, COMSIG_MOB_SKILL_GET_AFFINITY) //update_mood_icon() /datum/component/mood/proc/setInsanityEffect(newval)//More code so that the previous proc works if(newval == insanity_effect) return //var/mob/living/master = parent //master.crit_threshold = (master.crit_threshold - insanity_effect) + newval if(!insanity_effect && newval) RegisterSignal(parent, COMSIG_MOB_ACTION_SKILL_MOD, .proc/on_mob_action_skill_mod) RegisterSignal(parent, COMSIG_MOB_ITEM_ACTION_SKILLS_MOD, .proc/on_item_action_skills_mod) else if(insanity_effect && !newval) UnregisterSignal(parent, list(COMSIG_MOB_ACTION_SKILL_MOD, COMSIG_MOB_ITEM_ACTION_SKILLS_MOD)) insanity_effect = newval /datum/component/mood/proc/modify_sanity(datum/source, amount, minimum = SANITY_INSANE, maximum = SANITY_AMAZING) setSanity(sanity + amount, minimum, maximum) /datum/component/mood/proc/add_event(datum/source, category, type, param) //Category will override any events in the same category, should be unique unless the event is based on the same thing like hunger. var/datum/mood_event/the_event 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 0 //Don't have to update the event. the_event = new type(src, param)//This causes a runtime for some reason, was this me? No - there's an event floating around missing a definition. mood_events[category] = the_event 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) var/datum/mood_event/event = mood_events[category] if(!event) return 0 mood_events -= category qdel(event) update_mood() /datum/component/mood/proc/remove_temp_moods() //Removes all temp moodsfor(var/i in mood_events) for(var/i in mood_events) var/datum/mood_event/moodlet = mood_events[i] if(!moodlet || !moodlet.timeout) continue mood_events -= i qdel(moodlet) update_mood() /datum/component/mood/proc/modify_hud(datum/source) var/mob/living/owner = parent var/datum/hud/hud = owner.hud_used screen_obj = new 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) if(!screen_obj || !parent) return var/mob/living/owner = parent var/datum/hud/hud = owner.hud_used if(hud && hud.infodisplay) hud.infodisplay -= screen_obj QDEL_NULL(screen_obj) /datum/component/mood/proc/hud_click(datum/source, location, control, params, mob/user) print_mood(user) /datum/component/mood/proc/HandleNutrition(mob/living/L) switch(L.nutrition) if(NUTRITION_LEVEL_FULL to INFINITY) add_event(null, "nutrition", /datum/mood_event/fat) 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) ///Called when parent is ahealed. /datum/component/mood/proc/on_revive(datum/source, full_heal) if(!full_heal) return remove_temp_moods() setSanity(initial(sanity)) /datum/component/mood/proc/on_mob_action_skill_mod(mob/source, list/skill_args, list/mod_values) var/datum/skill/S = GLOB.skill_datums[skill_args[ACTION_SKILL_MOD_SKILL]] if(!(S.skill_flags & SKILL_USE_MOOD)) return var/debuff = 1 - (SANITY_DISTURBED - sanity) * MOOD_INSANITY_MALUS mod_values[MOD_VALUES_SKILL_MOD] *= skill_args[ACTION_SKILL_MOD_IS_MULTI] ? debuff : 1/debuff /datum/component/mood/proc/on_item_action_skills_mod(mob/source, list/skill_args, list/mod_values) if(skill_args[ITEM_SKILLS_MOD_BAD_FLAGS] & SKILL_USE_MOOD) return var/divisor = mod_values[MOD_VALUES_ITEM_SKILLS_DIV] if(!divisor) return var/obj/item/I = skill_args[ITEM_SKILLS_MOD_ITEM] var/list/L = mod_values[MOD_VALUES_ITEM_SKILLS_CHECKED] var/skills_len = length(L) var/affected_skills = skills_len for(var/k in L) var/datum/skill/S = k var/our_flags = I.used_skills[S.type]|S.skill_flags if(!(our_flags & SKILL_USE_MOOD)) affected_skills-- if(!affected_skills) return var/debuff = 1 - (SANITY_DISTURBED - sanity) * MOOD_INSANITY_MALUS * (affected_skills/skills_len) mod_values[MOD_VALUES_ITEM_SKILLS_SUM] *= skill_args[ITEM_SKILLS_MOD_IS_MULTI] ? debuff : 1/debuff /datum/component/mood/proc/on_get_skill_affinity(mob/source, skill_path, list/return_value) var/datum/skill/S = GLOB.skill_datums[skill_path] if(!S || !(S.skill_flags & SKILL_TRAIN_MOOD)) return return_value[1] *= SKILL_AFFINITY_MOOD_BONUS #undef SLIGHT_INSANITY_PEN #undef MINOR_INSANITY_PEN #undef MAJOR_INSANITY_PEN #undef MOOD_INSANITY_MALUS