diff --git a/code/__DEFINES/~skyrat_defines/quirks.dm b/code/__DEFINES/~skyrat_defines/quirks.dm new file mode 100644 index 00000000000..7acc8b3e9b1 --- /dev/null +++ b/code/__DEFINES/~skyrat_defines/quirks.dm @@ -0,0 +1,27 @@ +#define DEATH_CONSEQUENCES_QUIRK_NAME "Death Degradation Disorder" +#define DEATH_CONSEQUENCES_QUIRK_DESC "Patient is unusually susceptable to mortality." +#define DEATH_CONSEQUENCES_BASE_DEGRADATION_ON_DEATH 50 + +/// The victim's crit threshold cannot go below this. +#define DEATH_CONSEQUENCES_MINIMUM_VICTIM_CRIT_THRESHOLD (MAX_LIVING_HEALTH) - 1 + +#define DEATH_CONSEQUENCES_REAGENT_FLAT_AMOUNT "dc_flat_reagent_amount" +#define DEATH_CONSEQUENCES_REAGENT_MULT_AMOUNT "dc_mult_reagent_amount" +#define DEATH_CONSEQUENCES_REAGENT_METABOLIZE "dc_reagent_should_be_metabolizing" +/// If true, we will check to see if this can process. Ex. things like synths wont process formaldehyde +#define DEATH_CONSEQUENCES_REAGENT_CHECK_PROCESSING_FLAGS "dc_check_reagent_processing_flags" + +/// Absolute maximum for preferences. +#define DEATH_CONSEQUENCES_MAXIMUM_THEORETICAL_DEGRADATION 10000 +#define DEATH_CONSEQUENCES_DEFAULT_MAX_DEGRADATION 500 // arbitrary +#define DEATH_CONSEQUENCES_DEFAULT_LIVING_DEGRADATION_RECOVERY 0.01 +#define DEATH_CONSEQUENCES_DEFAULT_DEGRADATION_ON_DEATH 50 + +#define DEATH_CONSEQUENCES_DEFAULT_REZADONE_DEGRADATION_REDUCTION 0.4 +#define DEATH_CONSEQUENCES_DEFAULT_STRANGE_REAGENT_DEGRADATION_REDUCTION 0.25 +#define DEATH_CONSEQUENCES_DEFAULT_EIGENSTASIUM_DEGRADATION_REDUCTION 5 // for such a rare chem, you fucking bet +#define DEATH_CONSEQUENCES_DEFAULT_SANSUFENTANYL_DEGRADATION_REDUCTION 1 + +#define DEATH_CONSEQUENCES_SHOW_HEALTH_ANALYZER_DATA "dc_show_health_analyzer_data" + +#define DEATH_CONSEQUENCES_TIME_BETWEEN_REMINDERS 5 MINUTES diff --git a/code/game/objects/items/devices/scanners/health_analyzer.dm b/code/game/objects/items/devices/scanners/health_analyzer.dm index f7e28a2de3b..f5b41498f3f 100644 --- a/code/game/objects/items/devices/scanners/health_analyzer.dm +++ b/code/game/objects/items/devices/scanners/health_analyzer.dm @@ -178,6 +178,7 @@ if (!target.get_organ_slot(ORGAN_SLOT_BRAIN)) // kept exclusively for soul purposes render_list += "Subject lacks a brain.\n" + var/death_consequences_status_text // SKYRAT EDIT ADDITION: Death consequences quirk if(iscarbon(target)) var/mob/living/carbon/carbontarget = target if(LAZYLEN(carbontarget.get_traumas())) @@ -199,6 +200,11 @@ trauma_desc += "permanent " trauma_desc += trauma.scan_desc trauma_text += trauma_desc + // SKYRAT EDIT ADDITION START: Death Consequences Quirk + if (istype(trauma, /datum/brain_trauma/severe/death_consequences)) + var/datum/brain_trauma/severe/death_consequences/consequences_trauma = trauma + death_consequences_status_text = consequences_trauma.get_health_analyzer_link_text(user) + // SKYRAT EDIT ADDITION END: Death Consequences Quirk render_list += "Cerebral traumas detected: subject appears to be suffering from [english_list(trauma_text)].\n" if(carbontarget.quirks.len) render_list += "Subject Major Disabilities: [carbontarget.get_quirk_string(FALSE, CAT_QUIRK_MAJOR_DISABILITY, from_scan = TRUE)].\n" @@ -401,6 +407,11 @@ render_list += span_userdanger("UNKNOWN PROTO-VIRAL INFECTION DETECTED. ISOLATE IMMEDIATELY.") // SKYRAT EDIT END + // SKYRAT EDIT ADDITION - DEATH CONSEQUENCES QUIRK + if(death_consequences_status_text) + render_list += death_consequences_status_text + // SKYRAT EDIT END + if(tochat) to_chat(user, examine_block(jointext(render_list, "")), trailing_newline = FALSE, type = MESSAGE_TYPE_INFO) else diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm index 158ed2fdbfa..741c7f7a36f 100644 --- a/code/modules/client/preferences.dm +++ b/code/modules/client/preferences.dm @@ -331,6 +331,7 @@ GLOBAL_LIST_EMPTY(preferences_datums) var/datum/loadout_manager/tgui = new(usr) tgui.ui_interact(usr) return TRUE + if ("set_tricolor_preference") var/requested_preference_key = params["preference"] var/index_key = params["value"] diff --git a/code/modules/reagents/chemistry/holder/mob_life.dm b/code/modules/reagents/chemistry/holder/mob_life.dm index f3327ecdb7d..0692d166d2c 100644 --- a/code/modules/reagents/chemistry/holder/mob_life.dm +++ b/code/modules/reagents/chemistry/holder/mob_life.dm @@ -58,28 +58,13 @@ owner = reagent.holder.my_atom //SKYRAT EDIT ADDITION BEGIN - CUSTOMIZATION - if(ishuman(owner)) - var/mob/living/carbon/human/H = owner - //Check if this mob's species is set and can process this type of reagent - var/can_process = FALSE - //If we somehow avoided getting a species or reagent_flags set, we'll assume we aren't meant to process ANY reagents - if(H.dna && H.dna.species.reagent_flags) - var/owner_flags = H.dna.species.reagent_flags - if((reagent.process_flags & REAGENT_SYNTHETIC) && (owner_flags & PROCESS_SYNTHETIC)) //SYNTHETIC-oriented reagents require PROCESS_SYNTHETIC - can_process = TRUE - if((reagent.process_flags & REAGENT_ORGANIC) && (owner_flags & PROCESS_ORGANIC)) //ORGANIC-oriented reagents require PROCESS_ORGANIC - can_process = TRUE - - //If the mob can't process it, remove the reagent at it's normal rate without doing any addictions, overdoses, or on_mob_life() for the reagent - if(!can_process) - reagent.holder.remove_reagent(reagent.type, reagent.metabolization_rate) - return - //We'll assume that non-human mobs lack the ability to process synthetic-oriented reagents (adjust this if we need to change that assumption) - else - if(reagent.process_flags == REAGENT_SYNTHETIC) - reagent.holder.remove_reagent(reagent.type, reagent.metabolization_rate) - return + var/can_process = reagent_process_flags_valid(owner, reagent) + //If the mob can't process it, remove the reagent at it's normal rate without doing any addictions, overdoses, or on_mob_life() for the reagent + if(!can_process) + reagent.holder.remove_reagent(reagent.type, reagent.metabolization_rate) + return //SKYRAT EDIT ADDITION END + if(owner && reagent && (!dead || (reagent.chemical_flags & REAGENT_DEAD_PROCESS))) if(owner.reagent_check(reagent, seconds_per_tick, times_fired)) return diff --git a/modular_skyrat/master_files/code/modules/client/preferences/quirks/death_consequences.dm b/modular_skyrat/master_files/code/modules/client/preferences/quirks/death_consequences.dm new file mode 100644 index 00000000000..0ba0e301914 --- /dev/null +++ b/modular_skyrat/master_files/code/modules/client/preferences/quirks/death_consequences.dm @@ -0,0 +1,165 @@ +/datum/preference/numeric/death_consequences + abstract_type = /datum/preference/numeric/death_consequences + category = PREFERENCE_CATEGORY_MANUALLY_RENDERED + savefile_identifier = PREFERENCE_CHARACTER + + step = 0.01 + +/datum/preference/numeric/death_consequences/apply_to_human(mob/living/carbon/human/target, value, datum/preferences/preferences) + return FALSE + +/datum/preference/numeric/death_consequences/starting_degradation + savefile_key = "dc_starting_degradation" + + minimum = 0 + maximum = DEATH_CONSEQUENCES_MAXIMUM_THEORETICAL_DEGRADATION + +/datum/preference/numeric/death_consequences/starting_degradation/create_default_value() + return minimum + +/datum/preference/numeric/death_consequences/max_degradation + savefile_key = "dc_max_degradation" + + minimum = 0 + maximum = DEATH_CONSEQUENCES_MAXIMUM_THEORETICAL_DEGRADATION + +/datum/preference/numeric/death_consequences/max_degradation/create_default_value() + return DEATH_CONSEQUENCES_DEFAULT_MAX_DEGRADATION + +/datum/preference/numeric/death_consequences/living_degradation_recovery_per_second + savefile_key = "dc_living_degradation_recovery_per_second" + + minimum = -100 // if you want, you can just die slowly + maximum = 1000 + +/datum/preference/numeric/death_consequences/living_degradation_recovery_per_second/create_default_value() + return DEATH_CONSEQUENCES_DEFAULT_LIVING_DEGRADATION_RECOVERY + +/datum/preference/numeric/death_consequences/dead_degradation_per_second + savefile_key = "dc_dead_degradation_per_second" + + minimum = 0 + maximum = 1000 + +/datum/preference/numeric/death_consequences/dead_degradation_per_second/create_default_value() + return 0 + +/datum/preference/numeric/death_consequences/degradation_on_death + savefile_key = "dc_degradation_on_death" + + minimum = 0 + maximum = 1000 + +/datum/preference/numeric/death_consequences/degradation_on_death/create_default_value() + return DEATH_CONSEQUENCES_DEFAULT_DEGRADATION_ON_DEATH + +/datum/preference/numeric/death_consequences/formeldahyde_dead_degradation_mult + savefile_key = "dc_formeldahyde_dead_degradation_mult" + + minimum = 0 + maximum = 1 + +/datum/preference/numeric/death_consequences/formeldahyde_dead_degradation_mult/create_default_value() + return 0 + +/datum/preference/numeric/death_consequences/stasis_dead_degradation_mult + savefile_key = "dc_stasis_dead_degradation_mult" + + minimum = 0 + maximum = 1 + +/datum/preference/numeric/death_consequences/stasis_dead_degradation_mult/create_default_value() + return 0 + +/datum/preference/numeric/death_consequences/rezadone_living_degradation_reduction + savefile_key = "dc_rezadone_living_degradation_reduction" + + minimum = 0 + maximum = 500 + +/datum/preference/numeric/death_consequences/rezadone_living_degradation_reduction/create_default_value() + return DEATH_CONSEQUENCES_DEFAULT_REZADONE_DEGRADATION_REDUCTION + +/datum/preference/numeric/death_consequences/eigenstasium_degradation_reduction + savefile_key = "dc_eigenstasium_degradation_reduction" + + minimum = 0 + maximum = 5000 + +/datum/preference/numeric/death_consequences/eigenstasium_degradation_reduction/create_default_value() + return DEATH_CONSEQUENCES_DEFAULT_EIGENSTASIUM_DEGRADATION_REDUCTION + +/datum/preference/numeric/death_consequences/crit_threshold_reduction_min_percent_of_max + savefile_key = "dc_crit_threshold_reduction_min_percent_of_max" + + minimum = 0 + maximum = 100 + +/datum/preference/numeric/death_consequences/crit_threshold_reduction_min_percent_of_max/create_default_value() + return 0 // percent + +/datum/preference/numeric/death_consequences/crit_threshold_reduction_percent_of_max + savefile_key = "dc_crit_threshold_reduction_percent_of_max" + + minimum = 0 + maximum = 100 + +/datum/preference/numeric/death_consequences/crit_threshold_reduction_percent_of_max/create_default_value() + return 100 // percent + +/datum/preference/numeric/death_consequences/max_crit_threshold_reduction + savefile_key = "dc_max_crit_threshold_reduction" + + minimum = 0 + maximum = MAX_LIVING_HEALTH + +/datum/preference/numeric/death_consequences/max_crit_threshold_reduction/create_default_value() + return 30 + +/datum/preference/numeric/death_consequences/stamina_damage_min_percent_of_max + savefile_key = "dc_stamina_damage_min_percent_of_max" + + minimum = 0 + maximum = 100 + +/datum/preference/numeric/death_consequences/stamina_damage_min_percent_of_max/create_default_value() + return 20 // percent + +/datum/preference/numeric/death_consequences/stamina_damage_percent_of_max + savefile_key = "dc_stamina_damage_percent_of_max" + + minimum = 0 + maximum = 100 + +/datum/preference/numeric/death_consequences/stamina_damage_percent_of_max/create_default_value() + return 100 // percent + +/datum/preference/numeric/death_consequences/max_stamina_damage + savefile_key = "dc_max_stamina_damage" + + minimum = 0 + maximum = 150 + +/datum/preference/numeric/death_consequences/max_stamina_damage/create_default_value() + return 80 + +// BOOLEANS + +/datum/preference/toggle/death_consequences + abstract_type = /datum/preference/toggle/death_consequences + category = PREFERENCE_CATEGORY_MANUALLY_RENDERED + savefile_identifier = PREFERENCE_CHARACTER + +/datum/preference/toggle/death_consequences/apply_to_human(mob/living/carbon/human/target, value, datum/preferences/preferences) + return FALSE + +/datum/preference/toggle/death_consequences/permakill_at_max + savefile_key = "dc_permakill_at_max" + + default_value = FALSE // lets not be too cruel here + +/datum/preference/toggle/death_consequences/force_death_if_permakilled + savefile_key = "dc_force_death_if_permakilled" + + default_value = FALSE + diff --git a/modular_skyrat/modules/death_consequences_perk/death_consequences.dm b/modular_skyrat/modules/death_consequences_perk/death_consequences.dm new file mode 100644 index 00000000000..a655d9a8895 --- /dev/null +++ b/modular_skyrat/modules/death_consequences_perk/death_consequences.dm @@ -0,0 +1,92 @@ +/datum/quirk/death_consequences + name = DEATH_CONSEQUENCES_QUIRK_NAME + desc = "Every time you die, your body suffers long-term damage that can't easily be repaired." + medical_record_text = DEATH_CONSEQUENCES_QUIRK_DESC + icon = FA_ICON_DNA + value = 0 // due to its high customization, you can make it really inconsequential + +/datum/quirk_constant_data/death_consequences + associated_typepath = /datum/quirk/death_consequences + +/datum/quirk_constant_data/death_consequences/New() + customization_options = (subtypesof(/datum/preference/numeric/death_consequences) + subtypesof(/datum/preference/toggle/death_consequences)) + + return ..() + +/datum/quirk/death_consequences/add(client/client_source) + var/mob/living/carbon/human/human_holder = quirk_holder + human_holder.gain_trauma(/datum/brain_trauma/severe/death_consequences, TRAUMA_RESILIENCE_ABSOLUTE) + var/datum/brain_trauma/severe/death_consequences/added_trauma = human_holder.get_death_consequences_trauma() + if (!isnull(added_trauma)) + added_trauma.update_variables(client_source) + + to_chat(human_holder, span_danger("You suffer from [src]. By default, you will \ + degrade every time you die, and recover very slowly while alive. This may be expedited by resting, sleeping, being buckled \ + to something cozy, or using rezadone.\n\ + As your degradation rises, so too will negative effects, such as stamina damage or a worsened crit threshold.\n\ + You can alter your degradation on the fly via the Adjust death degradation verb, and change your settings via the Refresh death consequence variables verb.")) + +/datum/quirk/death_consequences/remove() + var/mob/living/carbon/human/human_holder = quirk_holder + human_holder.cure_trauma_type(/datum/brain_trauma/severe/death_consequences, TRAUMA_RESILIENCE_ABSOLUTE) + +/// Adjusts the mob's linked death consequences trauma (see get_death_consequences_trauma())'s degradation by increment. +/mob/verb/adjust_degradation(increment as num) + set name = "Adjust death degradation" + set category = "IC" + set instant = TRUE + + if (isnull(mind)) + to_chat(usr, span_warning("You have no mind!")) + return + + var/datum/brain_trauma/severe/death_consequences/linked_trauma = get_death_consequences_trauma() + var/mob/living/carbon/trauma_holder = linked_trauma?.owner + if (isnull(linked_trauma) || isnull(trauma_holder) || trauma_holder != mind.current) // sanity + to_chat(usr, span_warning("You don't have a body with death consequences!")) + return + + if (!isnum(increment)) + to_chat(usr, span_warning("You can artificially change the current level of your death degradation with this verb. \ + You can use this to cause degradation in ways the customization cannot. You need to enter a number to use this verb.")) + return + + if (linked_trauma.permakill_if_at_max_degradation && ((linked_trauma.current_degradation + increment) >= linked_trauma.max_degradation)) + if (tgui_alert(usr, "This will put you over/at your maximum degradation threshold and PERMANENTLY KILL YOU!!! Are you SURE you want to do this?", "WARNING", list("Yes", "No"), timeout = 7 SECONDS) != "Yes") + return + + linked_trauma.adjust_degradation(increment) + to_chat(usr, span_notice("Degradation successfully adjusted!")) + +/// Calls update_variables() on this mob's linked death consequences trauma. See that proc for further info. +/mob/verb/refresh_death_consequences() + set name = "Refresh death consequence variables" + set category = "IC" + set instant = TRUE + + if (isnull(mind)) + to_chat(usr, span_warning("You have no mind!")) + return + + var/datum/brain_trauma/severe/death_consequences/linked_trauma = get_death_consequences_trauma() + var/mob/living/carbon/trauma_holder = linked_trauma?.owner + if (isnull(linked_trauma) || isnull(trauma_holder) || trauma_holder != mind.current) // sanity + to_chat(usr, span_warning("You don't have a body with death consequences!")) + return + + linked_trauma.update_variables(client) + to_chat(usr, span_notice("Variables successfully updated!")) + +/// Searches mind.current for a death_consequences trauma. Allows this proc to be used on both ghosts and living beings to find their linked trauma. +/mob/proc/get_death_consequences_trauma() + RETURN_TYPE(/datum/brain_trauma/severe/death_consequences) + + if (isnull(mind)) + return + + if (iscarbon(mind.current)) + var/mob/living/carbon/carbon_body = mind.current + for (var/datum/brain_trauma/trauma as anything in carbon_body.get_traumas()) + if (istype(trauma, /datum/brain_trauma/severe/death_consequences)) + return trauma + // else, return null diff --git a/modular_skyrat/modules/death_consequences_perk/death_consequences_trauma.dm b/modular_skyrat/modules/death_consequences_perk/death_consequences_trauma.dm new file mode 100644 index 00000000000..a1b7380d418 --- /dev/null +++ b/modular_skyrat/modules/death_consequences_perk/death_consequences_trauma.dm @@ -0,0 +1,552 @@ +#define DEGRADATION_LEVEL_NONE "dc_level_none" +#define DEGRADATION_LEVEL_LOW "dc_level_low" +#define DEGRADATION_LEVEL_MEDIUM "dc_level_medium" +#define DEGRADATION_LEVEL_HIGH "dc_level_high" +#define DEGRADATION_LEVEL_CRITICAL "dc_level_critical" + +#define DEGRADATION_LEVEL_NONE_THRESHOLD 0.2 +#define DEGRADATION_LEVEL_LOW_THRESHOLD 0.4 +#define DEGRADATION_LEVEL_MEDIUM_THRESHOLD 0.6 +#define DEGRADATION_LEVEL_HIGH_THRESHOLD 0.8 + +/// Only rezadone at or above this purity can reduce degradation. Needed because of borg synthesizers. +#define DEATH_CONSEQUENCES_REZADONE_MINIMUM_PURITY 100 + +/datum/brain_trauma/severe/death_consequences + name = DEATH_CONSEQUENCES_QUIRK_NAME + desc = DEATH_CONSEQUENCES_QUIRK_DESC + scan_desc = "death degradation" + gain_text = span_warning("For a brief moment, you completely disassociate.") + lose_text = span_notice("You feel like you have a firm grasp on your consciousness again!") + random_gain = FALSE + + /// The current degradation we are currently at. Generally speaking, things get worse the higher this is. Can never go below 0. + var/current_degradation = 0 + /// The absolute maximum degradation we can receive. Will cause permadeath if [permakill_if_at_max_degradation] is TRUE. + var/max_degradation = DEATH_CONSEQUENCES_DEFAULT_MAX_DEGRADATION // arbitrary + /// While alive, our victim will lose degradation by this amount per second. + var/base_degradation_reduction_per_second_while_alive = DEATH_CONSEQUENCES_DEFAULT_LIVING_DEGRADATION_RECOVERY + /// When our victim dies, they will degrade by this amount, but only if the last time they died was after [time_required_between_deaths_to_degrade] ago. + var/base_degradation_on_death = DEATH_CONSEQUENCES_DEFAULT_DEGRADATION_ON_DEATH + /// While dead, our victim will degrade by this amount every second. Reduced by stasis and formeldahyde. + var/base_degradation_per_second_while_dead = 0 + + /// The last time we caused immediate degradation on death. + var/last_time_degraded_on_death = -2 MINUTES // we do this to avoid a lil bug where it cant happen at roundstart + /// If the last time we degraded on death was less than this time ago, we won't immediately degrade when our victim dies. Used for preventing things like MDs constantly reviving someone and PKing them. + var/time_required_between_deaths_to_degrade = 2 MINUTES + + /// If our victim is dead, their passive degradation will be multiplied against this if they have formal in their system. + var/formaldehyde_death_degradation_mult = 0 + /// If our victim is alive and is metabolizing rezadone, we will reduce degradation by this amount every second. + var/rezadone_degradation_decrease = DEATH_CONSEQUENCES_DEFAULT_REZADONE_DEGRADATION_REDUCTION + + /// When eigenstasium is metabolized, degradation is reduced by this. + var/eigenstasium_degradation_decrease = DEATH_CONSEQUENCES_DEFAULT_EIGENSTASIUM_DEGRADATION_REDUCTION + + /// If our victim is dead, their passive degradation will be multiplied against this if they are in stasis. + var/stasis_passive_degradation_multiplier = 0 + + /// If true, when [current_degradation] reaches [max_degradation], we will DNR and ghost our victim. + var/permakill_if_at_max_degradation = FALSE + /// If true, when [current_degradation] reaches [max_degradation], we will DNR and KILL our victim. + var/force_death_if_permakilled = FALSE + + /// If we have killed our owner permanently. + var/final_death_delivered = FALSE + + // Higher = overall less intense threshold reduction but it still maxes out once it gets there + /// The degradation we will begin reducing the crit threshold at. + var/crit_threshold_min_degradation = 0 + /// The degradation we will stop reducing the crit threshold at. + var/crit_threshold_max_degradation = 200 + /// The amount our victims crit threshold will be reduced by at [crit_threshold_max_degradation] degradation. + var/max_crit_threshold_reduction = 100 + + /// The degradation we will begin applying stamina damage at. + var/stamina_damage_minimum_degradation = 100 + /// The degradation we will stop increasing the stamina damage at. + var/stamina_damage_max_degradation = 500 + /// The amount our victims crit threshold will be reduced by at [stamina_damage_max_degradation] degradation. + var/max_stamina_damage = 80 + + /// Used for updating our crit threshold reduction. We store the previous value, then subtract it from crit threshold, to get the value we had before we adjusted. + var/crit_threshold_currently_reduced_by = 0 + + /// The last world.time we sent a message to our owner reminding them of their current degradation. Used for cooldowns and such. + var/time_of_last_message_sent = -DEATH_CONSEQUENCES_TIME_BETWEEN_REMINDERS + /// The time between each reminder ([degradation_messages]). + var/time_between_reminders = DEATH_CONSEQUENCES_TIME_BETWEEN_REMINDERS + + /// The current level of degradation. Used mostly for reminder messages. + var/current_degradation_level = DEGRADATION_LEVEL_NONE + + // Will be iterated through sequentially, so the higher a path is, the quicker it'll be searched + // Make sure to put the larger bonuses and more specific types higher than the generic ones + /// A assoc list of (atom/movable typepath -> mult), where mult is used as a multiplier against passive living degradation reduction. + var/static/list/buckled_to_recovery_mult_table = list( + /obj/structure/bed/medical = 5, + /obj/structure/bed = 3, + + /obj/structure/chair/comfy = 2, + /obj/structure/chair/sofa = 2, + /obj/structure/chair = 1.5, + + /mob/living = 1.25, // being carried + ) + /// Only used if the thing we are buckled to is not in [buckled_to_recovery_mult_table]. + var/static/buckled_to_default_mult = 1.15 + + /// The random messages that will be sent to our victim if their degradation moves to a new threshold. + /// Contains nested assoc lists of (DEGRADATION_LEVEL_DEFINE -> list((message -> weight), ...)) where ... is a indefinite number of message -> weight pairs. + var/static/list/degradation_messages = list( + DEGRADATION_LEVEL_LOW = list( + span_warning("Your body aches a little.") = 10, + span_warning("You feel a little detached from yourself.") = 10, + span_warning("You feel a little tired.") = 10, + ), + DEGRADATION_LEVEL_MEDIUM = list( + span_danger("Your whole body aches...") = 10, + span_danger("You're starting to feel disassociated from yourself...") = 10, + span_danger("You're having a little difficulty thinking...") = 10, + ), + DEGRADATION_LEVEL_HIGH = list( + span_bolddanger("Your entire body throbs!") = 10, + span_bolddanger("You feel like you're losing your grip on yourself!") = 10, + span_bolddanger("Your consciousness feels as fragile as a sheet of glass!") = 10, + span_bolddanger("You feel exhausted in every single possible way!") = 10, + ), + DEGRADATION_LEVEL_CRITICAL = list( + span_revenwarning("Everything hurts... It hurts so bad...") = 10, + span_revenwarning("It's so hard to think... It's so hard... So hard...") = 10, + span_revenwarning("Your body feels alien, like you don't belong in it...") = 10, + span_revenwarning("... Who am I?") = 1, + span_revenwarning("... Where am I?") = 1, + span_revenwarning("... What am I?") = 1, + ) + ) + + /// Assoc list of (mob -> world.time + time_to_view_extra_data_after_scan). Used for determining if someone can use our health analyzer href + var/list/mob/time_til_scan_expires = list() + /// The amount of time someone has to view our extra info via health analyzer after scanning us. + var/time_to_view_extra_data_after_scan = 5 SECONDS + +/datum/brain_trauma/severe/death_consequences/Destroy() + for (var/mob/entry as anything in time_til_scan_expires) + UnregisterSignal(entry, COMSIG_QDELETING) + time_til_scan_expires -= entry + + return ..() + +/datum/brain_trauma/severe/death_consequences/on_gain() + . = ..() + + RegisterSignal(owner, COMSIG_LIVING_POST_FULLY_HEAL, PROC_REF(victim_ahealed)) + + update_variables() + START_PROCESSING(SSprocessing, src) + +/datum/brain_trauma/severe/death_consequences/on_lose(silent) + owner.crit_threshold -= crit_threshold_currently_reduced_by + STOP_PROCESSING(SSprocessing, src) + + if (final_death_delivered) + REMOVE_TRAIT(owner, TRAIT_DNR, TRAUMA_TRAIT) + + UnregisterSignal(owner, COMSIG_LIVING_POST_FULLY_HEAL) + + return ..() + +// DEGRADATION ALTERATION / PROCESS + +/datum/brain_trauma/severe/death_consequences/on_death() + . = ..() + + if (base_degradation_on_death != 0) + if (!last_time_degraded_on_death || world.time - time_required_between_deaths_to_degrade <= last_time_degraded_on_death) + return + + adjust_degradation(base_degradation_on_death) + if (!final_death_delivered) // already sends a very spooky message if they permadie + var/visible_message = span_revenwarning("[owner] writhes for a brief moment, before going limp. You get the sense that you might want to prevent them from dying again...") + var/self_message = span_revenwarning("As your mind reels from the shock of death, you feel the ethereal tether that binds you to your body strain...") + + var/mob/dead/observer/ghost = owner.get_ghost() + var/mob/self_message_target = (ghost ? ghost : owner) + owner.visible_message(visible_message, ignored_mobs = self_message_target) + to_chat(self_message_target, self_message) + + last_time_degraded_on_death = world.time + +/datum/brain_trauma/severe/death_consequences/process(seconds_per_tick) + if (owner.status_flags & GODMODE) + return + + var/is_dead = (owner.stat == DEAD) + var/degradation_increase = get_passive_degradation_increase(is_dead) * seconds_per_tick + var/degradation_reduction = get_passive_degradation_decrease(is_dead) * seconds_per_tick + + adjust_degradation(degradation_increase - degradation_reduction) + + // Ensure our victim's stamina is at or above our minimum stamina damage + if (!is_dead) + damage_stamina(seconds_per_tick) + + if ((world.time - time_between_reminders) > time_of_last_message_sent) + send_reminder() + +/// Returns the amount, every second, degradation should INCREASE by. +/datum/brain_trauma/severe/death_consequences/proc/get_passive_degradation_increase(is_dead) + var/increase = 0 + + if (is_dead) + increase += base_degradation_per_second_while_dead + + if (owner.has_reagent(/datum/reagent/toxin/formaldehyde, needs_metabolizing = FALSE)) + var/datum/reagent/reagent_instance = owner.reagents.has_reagent(/datum/reagent/toxin/formaldehyde) + if (reagent_process_flags_valid(owner, reagent_instance)) + increase *= formaldehyde_death_degradation_mult + else + if (base_degradation_reduction_per_second_while_alive < 0) // if you wanna die slowly while alive, go ahead bud + increase -= base_degradation_reduction_per_second_while_alive + + if (HAS_TRAIT(owner, TRAIT_STASIS)) + increase *= stasis_passive_degradation_multiplier + + return increase + +/// Returns the amount, every second, degradation should DECREASE by. +/datum/brain_trauma/severe/death_consequences/proc/get_passive_degradation_decrease(is_dead) + var/decrease = 0 + + if (!is_dead) + if (base_degradation_reduction_per_second_while_alive > 0) + decrease += base_degradation_reduction_per_second_while_alive + + var/datum/reagent/rezadone_instance = owner.reagents.has_reagent(/datum/reagent/medicine/rezadone, needs_metabolizing = TRUE) + if (rezadone_instance) + if ((rezadone_instance.purity >= DEATH_CONSEQUENCES_REZADONE_MINIMUM_PURITY) && reagent_process_flags_valid(owner, rezadone_instance)) + decrease += rezadone_degradation_decrease + + if (owner.has_reagent(/datum/reagent/eigenstate)) + decrease += eigenstasium_degradation_decrease + + return (decrease * get_passive_degradation_decrease_mult()) + +#define DEGRADATION_REDUCTION_SLEEPING_MULT 3 +#define DEGRADATION_REDUCTION_RESTING_MULT 1.5 + +/// Returns a multiplier that should be used whenever degradation is passively decreased. Is determined by resting, sleeping, and buckled status. +/datum/brain_trauma/severe/death_consequences/proc/get_passive_degradation_decrease_mult() + var/decrease_mult = 1 + + if (owner.IsSleeping()) + decrease_mult *= DEGRADATION_REDUCTION_SLEEPING_MULT + else if (owner.resting) + decrease_mult *= DEGRADATION_REDUCTION_RESTING_MULT + + var/buckled_to_mult + if (owner.buckled) + for (var/atom/atom_typepath as anything in buckled_to_recovery_mult_table) + if (istype(owner.buckled, atom_typepath)) + buckled_to_mult = buckled_to_recovery_mult_table[atom_typepath] + break + if (isnull(buckled_to_mult)) + buckled_to_mult = buckled_to_default_mult + else + buckled_to_mult = 1 + + decrease_mult *= buckled_to_mult + + return decrease_mult + +#undef DEGRADATION_REDUCTION_SLEEPING_MULT +#undef DEGRADATION_REDUCTION_RESTING_MULT + +/// Setter proc for [current_degradation] that clamps the incoming value and updates effects if the value changed. +/datum/brain_trauma/severe/death_consequences/proc/adjust_degradation(adjustment) + var/old_degradation = current_degradation + current_degradation = clamp((current_degradation + adjustment), 0, max_degradation) + if (current_degradation != old_degradation) + update_degradation_level() + update_effects() + +/** + * Updates [current_degradation_level] by comparing current degradation to max. + * + * Args: + * * send_reminder_if_changed = TRUE: If TRUE, will call [send_reminder()] if [current_degradation_level] is changed. + */ +/datum/brain_trauma/severe/death_consequences/proc/update_degradation_level(send_reminder_if_changed = TRUE) + var/old_level = current_degradation_level + switch (current_degradation / max_degradation) + if (0 to DEGRADATION_LEVEL_NONE_THRESHOLD) + current_degradation_level = DEGRADATION_LEVEL_NONE + if (DEGRADATION_LEVEL_NONE_THRESHOLD to DEGRADATION_LEVEL_LOW_THRESHOLD) + current_degradation_level = DEGRADATION_LEVEL_LOW + if (DEGRADATION_LEVEL_LOW_THRESHOLD to DEGRADATION_LEVEL_MEDIUM_THRESHOLD) + current_degradation_level = DEGRADATION_LEVEL_MEDIUM + if (DEGRADATION_LEVEL_MEDIUM_THRESHOLD to DEGRADATION_LEVEL_HIGH_THRESHOLD) + current_degradation_level = DEGRADATION_LEVEL_HIGH + else + current_degradation_level = DEGRADATION_LEVEL_CRITICAL + + if (send_reminder_if_changed && !final_death_delivered && (old_level != current_degradation_level)) + send_reminder(FALSE) + +// EFFECTS + +/// Refreshes all our effects and updates their values. Kills the victim if they opted in and their degradation equals their maximum. +/datum/brain_trauma/severe/death_consequences/proc/update_effects() + var/threshold_adjustment = get_crit_threshold_adjustment() + owner.crit_threshold = ((owner.crit_threshold - crit_threshold_currently_reduced_by) + threshold_adjustment) + crit_threshold_currently_reduced_by = threshold_adjustment + + if (permakill_if_at_max_degradation && (current_degradation >= max_degradation)) + and_so_your_story_ends() + +/// Calculates the amount that we should add to our victim's critical threshold. +/datum/brain_trauma/severe/death_consequences/proc/get_crit_threshold_adjustment() + SHOULD_BE_PURE(TRUE) + + var/clamped_degradation = clamp((current_degradation - crit_threshold_min_degradation), 0, crit_threshold_max_degradation) + var/percent_to_max = (clamped_degradation / crit_threshold_max_degradation) + + var/proposed_alteration = max_crit_threshold_reduction * percent_to_max + var/proposed_threshold = ((owner.crit_threshold - crit_threshold_currently_reduced_by) + proposed_alteration) + var/overflow = max((proposed_threshold - DEATH_CONSEQUENCES_MINIMUM_VICTIM_CRIT_THRESHOLD), 0) + var/final_alteration = (proposed_alteration - overflow) + + return final_alteration + +/// Ensures our victim's stamina is at or above the minimum stamina they're supposed to have. +/datum/brain_trauma/severe/death_consequences/proc/damage_stamina(seconds_per_tick) + if (victim_properly_resting()) + return + + var/clamped_degradation = clamp((current_degradation - stamina_damage_minimum_degradation), 0, stamina_damage_max_degradation) + var/percent_to_max = min((clamped_degradation / stamina_damage_max_degradation), 1) + var/minimum_stamina_damage = max_stamina_damage * percent_to_max + + // The constantly decreasing degradation will constantly lower the minimum stamina damage, and thus, if we DONT check a range of staminaloss, + // we will always consider it "above" our minimum, and thus never delay stamina regen. + var/owner_staminaloss = owner.getStaminaLoss() + if (minimum_stamina_damage <= 0) + return + if (owner_staminaloss > (minimum_stamina_damage + 1)) + return + else if ((owner_staminaloss >= (minimum_stamina_damage - 1)) && (owner_staminaloss <= (minimum_stamina_damage + 1))) + owner.stam_regen_start_time = world.time + STAMINA_REGEN_BLOCK_TIME + return + + var/final_adjustment = (minimum_stamina_damage - owner_staminaloss) + owner.adjustStaminaLoss(final_adjustment) // we adjust instead of set for things like stamina regen timer + +/** + * Sends a flavorful to_chat to the target, picking from degradation_messages[current_degradation_level]. Can fail to send one if no message is found. + * + * Args: + * * update_cooldown = TRUE: If true, updates [time_of_last_message_sent] to be world.time. + */ +/datum/brain_trauma/severe/death_consequences/proc/send_reminder(update_cooldown = TRUE) + var/list/message_list = degradation_messages[current_degradation_level] // can return null + if (!length(message_list)) // sanity + return + var/message = pick_weight(message_list) + + if (!message) + return + + to_chat(owner, message) + + if (update_cooldown) + time_of_last_message_sent = world.time + +/// The proc we call when we permanently kill our victim due to being at maximum degradation. DNRs them, ghosts/kills them, and prints a series of highly dramatic messages, +/// befitting for a death such as this. +/datum/brain_trauma/severe/death_consequences/proc/and_so_your_story_ends() + ADD_TRAIT(owner, TRAIT_DNR, TRAUMA_TRAIT) // you're gone bro + final_death_delivered = TRUE + + // this is a sufficiently dramatic event for some dramatic to_chats + var/visible_message + var/self_message + var/log_message + + if (owner.stat == DEAD) + visible_message = span_revenwarning("The air around [owner] seems to ripple for a moment.") + self_message = span_revendanger("The metaphorical \"tether\" binding you to your body finally gives way. You try holding on, but you soon find yourself \ + falling into a deep, dark abyss...") + log_message = "has been permanently ghosted by their resonance instability quirk." + else + if (force_death_if_permakilled) // kill them - a violent and painful end + visible_message = span_revenwarning("[owner] suddenly lets out a harrowing gasp and falls to one knee, clutching their head! The remainder of their \ + body goes limp soon after, failing to stand back up.") + owner.death(gibbed = FALSE) + log_message = "has been permanently killed by their resonance instability quirk." + else // ghostize them - they simply stop thinking, forever + visible_message = span_revenwarning("[owner] jerkily arches their head upwards, untensing and going slackjawed with dilated pupils. They \ + cease all action and simply stand there, swaying.") + owner.ghostize(can_reenter_corpse = FALSE) + log_message = "has been permanently ghosted by their resonance instability quirk." + + self_message = span_revendanger("Your mind suddenly clouds, and you lose control of all thought and function. You try to squeeze your eyes shut, but you forget \ + where they are only a split second later. You drift away from yourself, further and further, until it's impossible to return...") + + var/mob/dead/observer/owner_ghost = owner.get_ghost() + var/mob/self_message_target = (owner_ghost ? owner_ghost : owner) // if youre ghosted, you still get the message + + visible_message += span_revenwarning(" You sense something terrible has happened.") // append crucial info and context clues + self_message += span_danger(" You have been killed by your death degradation, which prevents you from returning to your body or even being revived. \ + You may roleplay this however you wish - this death may be temporary, permanent - you may or may not appear in soulcatchers - it's all up to you.") + + owner.investigate_log(log_message) + owner.visible_message(visible_message, ignored_mobs = self_message_target) // finally, send it + owner.balloon_alert_to_viewers("something terrible has happened...") + to_chat(self_message_target, self_message) + +/// Returns a short-ish string containing an href to [get_specific_data]. +/datum/brain_trauma/severe/death_consequences/proc/get_health_analyzer_link_text(mob/user) + var/message = span_bolddanger("\nSubject suffers from death degradation disorder.") + if (final_death_delivered) + message += span_purple("\nNeural patterns are equivalent to the consciousness zero-point. Subject has likely succumbed.") + return message + + message += span_danger("\nCurrent degradation/max: [span_blue("[current_degradation]")]/[max_degradation].") + message += span_notice("\nView degradation specifics?") + if (permakill_if_at_max_degradation) + message += span_revenwarning("\n\nSUBJECT WILL BE PERMANENTLY KILLED IF DEGRADATION REACHES MAXIMUM!") + + if (user) + if (isnull(time_til_scan_expires[user])) + RegisterSignal(user, COMSIG_QDELETING, PROC_REF(scanning_user_qdeleting)) + time_til_scan_expires[user] = (world.time + time_to_view_extra_data_after_scan) + + return message + +/datum/brain_trauma/severe/death_consequences/proc/scanning_user_qdeleting(datum/signal_source) + SIGNAL_HANDLER + + time_til_scan_expires -= signal_source + UnregisterSignal(signal_source, COMSIG_QDELETING) + +/datum/brain_trauma/severe/death_consequences/Topic(href, list/href_list) + . = ..() + + if (href_list[DEATH_CONSEQUENCES_SHOW_HEALTH_ANALYZER_DATA]) + if (world.time <= time_til_scan_expires[usr]) + to_chat(usr, examine_block(get_specific_data()), trailing_newline = FALSE, type = MESSAGE_TYPE_INFO) + else + to_chat(usr, span_warning("Your scan has expired! Try scanning again!")) + +/// Returns a large string intended to show specifics of how this degradation work. +/datum/brain_trauma/severe/death_consequences/proc/get_specific_data() + var/message = span_bolddanger("Subject suffers from death degradation disorder.") + if (final_death_delivered) + message += span_purple("\nNeural patterns are equivalent to the consciousness zero-point. Subject has likely succumbed.") + return message + + var/owner_organic = (owner.dna.species.reagent_flags & PROCESS_ORGANIC) + message += span_danger("\nCurrent degradation/max: [span_blue("[current_degradation]")]/[max_degradation].") + if (base_degradation_reduction_per_second_while_alive) + message += span_danger("\nWhile alive, subject will recover from degradation at a rate of [span_blue("[base_degradation_reduction_per_second_while_alive] per second")].") + if (base_degradation_per_second_while_dead) + message += span_danger("\nWhile dead, subject will suffer degradation at a rate of [span_bolddanger("[base_degradation_per_second_while_dead] per second")].") + if (owner_organic && formaldehyde_death_degradation_mult != 1) + message += span_danger(" In such an event, formaldehyde will alter the degradation by [span_blue("[formaldehyde_death_degradation_mult]")]x.") + if (stasis_passive_degradation_multiplier < 1) + message += span_danger(" Stasis may be effective in slowing (or even stopping) degradation.") + if (base_degradation_on_death) + message += span_danger("\nDeath will incur a [base_degradation_on_death] degradation penalty.") + if (owner_organic && rezadone_degradation_decrease) + message += span_danger("\nRezadone of purity at or above [DEATH_CONSEQUENCES_REZADONE_MINIMUM_PURITY]% will reduce degradation by [span_blue("[rezadone_degradation_decrease]")] per second when metabolized.") + if (eigenstasium_degradation_decrease) + message += span_danger("\nEigenstasium will reduce degradation by [span_blue("[eigenstasium_degradation_decrease]")] per second when present.") + + message += span_danger("\nAll degradation reduction can be [span_blue("expedited")] by [span_blue("resting, sleeping, or being buckled to something comfortable")].") + + if (permakill_if_at_max_degradation) + message += span_revenwarning("\n\nSUBJECT WILL BE PERMANENTLY KILLED IF DEGRADATION REACHES MAXIMUM!") + + return message + +/// Used in stamina damage. Determines if our victim is resting, sleeping, or is buckled to something cozy. +/datum/brain_trauma/severe/death_consequences/proc/victim_properly_resting() + if (owner.resting || owner.IsSleeping()) + return TRUE + + if (owner.buckled) + for (var/typepath in buckled_to_recovery_mult_table) + if (istype(owner.buckled, typepath)) + return TRUE + + return FALSE + +/// Signal handler proc for healing our victim on an aheal. Permadeath can only be reversed by admin aheals. +/datum/brain_trauma/severe/death_consequences/proc/victim_ahealed(datum/signal_source, heal_flags) + SIGNAL_HANDLER + + if ((heal_flags & HEAL_AFFLICTIONS) == HEAL_AFFLICTIONS) + adjust_degradation(-INFINITY) // a good ol' regenerative extract can fix you up + if ((heal_flags & (ADMIN_HEAL_ALL)) == ADMIN_HEAL_ALL) // but only god can actually revive you + final_death_delivered = FALSE + REMOVE_TRAIT(owner, TRAIT_DNR, TRAUMA_TRAIT) + +/// Resets all our variables to our victim's preferences, if they have any. Used for the initial setup, then any time our victim manually refreshes variables. +/datum/brain_trauma/severe/death_consequences/proc/update_variables(client/source = owner.client) + // source is necessary since, in testing, i found the verb didnt work if you aghosted as owner.client was null, so we have to specify + // "hey dude this is still our mind" via the arg + + // theoretically speaking theres should be no circumstances where mind.current is shared with another mind, so this should be intrinsically + // safe, since only the true owner of the mob can actually use the verb to refresh the variables + + if (isnull(source)) + return // sanity + + var/ckey = lowertext(owner.mind?.key) + if (isnull(ckey) || ckey != source.ckey) + return // sanity + + var/datum/preferences/victim_prefs = source.prefs + if (!victim_prefs) + return + + max_degradation = victim_prefs.read_preference(/datum/preference/numeric/death_consequences/max_degradation) + current_degradation = clamp(victim_prefs.read_preference(/datum/preference/numeric/death_consequences/starting_degradation), 0, max_degradation - 1) // let's not let people instantly fucking die + + base_degradation_reduction_per_second_while_alive = victim_prefs.read_preference(/datum/preference/numeric/death_consequences/living_degradation_recovery_per_second) + base_degradation_per_second_while_dead = victim_prefs.read_preference(/datum/preference/numeric/death_consequences/dead_degradation_per_second) + base_degradation_on_death = victim_prefs.read_preference(/datum/preference/numeric/death_consequences/degradation_on_death) + + var/min_crit_threshold_percent = victim_prefs.read_preference(/datum/preference/numeric/death_consequences/crit_threshold_reduction_min_percent_of_max) + crit_threshold_min_degradation = (max_degradation * (min_crit_threshold_percent / 100)) + var/max_crit_threshold_percent = victim_prefs.read_preference(/datum/preference/numeric/death_consequences/crit_threshold_reduction_percent_of_max) + crit_threshold_max_degradation = (max_degradation * (max_crit_threshold_percent / 100)) + max_crit_threshold_reduction = victim_prefs.read_preference(/datum/preference/numeric/death_consequences/max_crit_threshold_reduction) + + var/min_stamina_damage_percent = victim_prefs.read_preference(/datum/preference/numeric/death_consequences/stamina_damage_min_percent_of_max) + stamina_damage_minimum_degradation = (max_degradation * (min_stamina_damage_percent / 100)) + var/max_stamina_damage_percent = victim_prefs.read_preference(/datum/preference/numeric/death_consequences/stamina_damage_percent_of_max) + max_stamina_damage = victim_prefs.read_preference(/datum/preference/numeric/death_consequences/max_stamina_damage) + stamina_damage_max_degradation = (max_degradation * (max_stamina_damage_percent / 100)) + + permakill_if_at_max_degradation = victim_prefs.read_preference(/datum/preference/toggle/death_consequences/permakill_at_max) + force_death_if_permakilled = victim_prefs.read_preference(/datum/preference/toggle/death_consequences/force_death_if_permakilled) + + rezadone_degradation_decrease = victim_prefs.read_preference(/datum/preference/numeric/death_consequences/rezadone_living_degradation_reduction) + eigenstasium_degradation_decrease = victim_prefs.read_preference(/datum/preference/numeric/death_consequences/eigenstasium_degradation_reduction) + + update_effects() + +#undef DEGRADATION_LEVEL_NONE +#undef DEGRADATION_LEVEL_LOW +#undef DEGRADATION_LEVEL_MEDIUM +#undef DEGRADATION_LEVEL_HIGH +#undef DEGRADATION_LEVEL_CRITICAL + +#undef DEGRADATION_LEVEL_NONE_THRESHOLD +#undef DEGRADATION_LEVEL_LOW_THRESHOLD +#undef DEGRADATION_LEVEL_MEDIUM_THRESHOLD +#undef DEGRADATION_LEVEL_HIGH_THRESHOLD + +#undef DEATH_CONSEQUENCES_REZADONE_MINIMUM_PURITY diff --git a/modular_skyrat/modules/death_consequences_perk/readme.md b/modular_skyrat/modules/death_consequences_perk/readme.md new file mode 100644 index 00000000000..9bc8ddab25b --- /dev/null +++ b/modular_skyrat/modules/death_consequences_perk/readme.md @@ -0,0 +1,48 @@ + + +https://github.com/Skyrat-SS13/Skyrat-tg/pull/23733 + +## Skyrat Medical Update + +Module ID: death_consequences + +### Description: + +A highly customizable quirk designed to make you fear death, and introduce a more fair mortality the DNR quirk is unable to. + + + +### TG Proc/File Changes: + +- healthscanner.dm: /proc/healthscan(), added text for the quirk +- species_features.tsx: Necessary for the preference UI + + +### Modular Overrides: + +- N/A + + +### Defines: + +- ~skyrat_defines/quirks.dm: A lot of prefixed defines + + +### Included files that are not contained in this module: + +- modular_skyrat\master_files\code\modules\client\preferences\quirks\death_consequences.dm + + + +### Credits: + +Niko - Original author + + diff --git a/modular_skyrat/modules/liquids/code/reagents/chemistry/holder.dm b/modular_skyrat/modules/liquids/code/reagents/chemistry/holder.dm index b9eac875c8b..0e7eb92727d 100644 --- a/modular_skyrat/modules/liquids/code/reagents/chemistry/holder.dm +++ b/modular_skyrat/modules/liquids/code/reagents/chemistry/holder.dm @@ -3,3 +3,20 @@ for(var/r_id in list_reagents) var/amt = list_reagents[r_id] add_reagent(r_id, amt, data, no_react = TRUE) + +/proc/reagent_process_flags_valid(mob/processor, datum/reagent/reagent) + if(ishuman(processor)) + var/mob/living/carbon/human/human_processor = processor + //Check if this mob's species is set and can process this type of reagent + //If we somehow avoided getting a species or reagent_flags set, we'll assume we aren't meant to process ANY reagents + if(human_processor.dna && human_processor.dna.species.reagent_flags) + var/processor_flags = human_processor.dna.species.reagent_flags + if((reagent.process_flags & REAGENT_SYNTHETIC) && (processor_flags & PROCESS_SYNTHETIC)) //SYNTHETIC-oriented reagents require PROCESS_SYNTHETIC + return TRUE + if((reagent.process_flags & REAGENT_ORGANIC) && (processor_flags & PROCESS_ORGANIC)) //ORGANIC-oriented reagents require PROCESS_ORGANIC + return TRUE + return FALSE + else if(reagent.process_flags == REAGENT_SYNTHETIC) + //We'll assume that non-human mobs lack the ability to process synthetic-oriented reagents (adjust this if we need to change that assumption) + return FALSE + return TRUE diff --git a/tgstation.dme b/tgstation.dme index 1a158914a7e..b8af4e27dc7 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -443,6 +443,7 @@ #include "code\__DEFINES\~skyrat_defines\pollution.dm" #include "code\__DEFINES\~skyrat_defines\preferences.dm" #include "code\__DEFINES\~skyrat_defines\projectiles.dm" +#include "code\__DEFINES\~skyrat_defines\quirks.dm" #include "code\__DEFINES\~skyrat_defines\reagents.dm" #include "code\__DEFINES\~skyrat_defines\research.dm" #include "code\__DEFINES\~skyrat_defines\research_categories.dm" @@ -6237,6 +6238,7 @@ #include "modular_skyrat\master_files\code\modules\client\preferences\middleware\languages.dm" #include "modular_skyrat\master_files\code\modules\client\preferences\middleware\limbs_and_markings.dm" #include "modular_skyrat\master_files\code\modules\client\preferences\middleware\species_additional_changes.dm" +#include "modular_skyrat\master_files\code\modules\client\preferences\quirks\death_consequences.dm" #include "modular_skyrat\master_files\code\modules\client\preferences\species_features\digitigrade_legs.dm" #include "modular_skyrat\master_files\code\modules\client\preferences\species_features\generate_side_shots.dm" #include "modular_skyrat\master_files\code\modules\clothing\anthro_clothes.dm" @@ -7070,6 +7072,8 @@ #include "modular_skyrat\modules\customization\modules\surgery\organs\wings.dm" #include "modular_skyrat\modules\customization\modules\surgery\organs\xenodorsal.dm" #include "modular_skyrat\modules\customization\modules\surgery\organs\xenohead.dm" +#include "modular_skyrat\modules\death_consequences_perk\death_consequences.dm" +#include "modular_skyrat\modules\death_consequences_perk\death_consequences_trauma.dm" #include "modular_skyrat\modules\decay_subsystem\code\decay_turf_handling.dm" #include "modular_skyrat\modules\decay_subsystem\code\decaySS.dm" #include "modular_skyrat\modules\decay_subsystem\code\nests.dm" diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/QuirksPage.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/QuirksPage.tsx index 3df65bb8ab1..ad779914398 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/QuirksPage.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/QuirksPage.tsx @@ -155,7 +155,7 @@ const QuirkList = (props: { onClick={(e) => { e.stopPropagation(); }} - maxWidth="300px" + maxWidth="400px" // SKYRAT EDIT - maxWidth to 600px from 300px backgroundColor="black" px="5px" py="3px"> diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/skyrat/death_degradation.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/skyrat/death_degradation.tsx new file mode 100644 index 00000000000..f709f12c3a8 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/skyrat/death_degradation.tsx @@ -0,0 +1,116 @@ +import { Feature, CheckboxInput, FeatureNumberInput } from '../../base'; + +export const dc_starting_degradation: Feature = { + name: 'A_Starting Degradation', + component: FeatureNumberInput, + description: 'The degradation you will start with.', +}; + +export const dc_max_degradation: Feature = { + name: 'A_Max Degradation', + component: FeatureNumberInput, + description: 'The absolute maximum degradation you can have.', +}; + +export const dc_living_degradation_recovery_per_second: Feature = { + name: 'B_Recovery per second while alive', + component: FeatureNumberInput, + description: + 'While alive, your degradation will be reduced by this much per second. If negative, this will cause you to slowly die.', +}; + +export const dc_dead_degradation_per_second: Feature = { + name: 'B_Degradation per second while dead', + component: FeatureNumberInput, +}; + +export const dc_degradation_on_death: Feature = { + name: 'B_Immediate degradation on death', + component: FeatureNumberInput, + description: 'Has a cooldown of around 5 minutes between deaths. ', +}; + +export const dc_stasis_dead_degradation_mult: Feature = { + name: 'B_Stasis degradation mult', + component: FeatureNumberInput, + description: + 'While on stasis, any passive degradation you receive will be reduced by this much.', +}; + +export const dc_formeldahyde_dead_degradation_mult: Feature = { + name: 'C_Formeldehyde death degradation mult', + component: FeatureNumberInput, + description: + 'If you are organic and have formeldahyde in your system, any passive degradation caused by being dead will be multiplied against this.', +}; + +export const dc_rezadone_living_degradation_reduction: Feature = { + name: 'C_Pure rezadone degradation reduction', + component: FeatureNumberInput, + description: + 'If you are organic, alive, and metabolizing rezadone at 100% purity, you will passively recover from degradation at this rate per second.', +}; + +export const dc_eigenstasium_degradation_reduction: Feature = { + name: 'C_Eigenstasium degradation reduction', + component: FeatureNumberInput, + description: + 'If you have eigenstasium in your system, you will passively recover from degradation at this rate per second. This works for synths, and while dead.', +}; + +export const dc_crit_threshold_reduction_min_percent_of_max: Feature = { + name: 'Crit threshold: Start degradation percent', + component: FeatureNumberInput, + description: + 'Crit threshold will begin decreasing when degradation is this percent to max.', +}; + +export const dc_crit_threshold_reduction_percent_of_max: Feature = { + name: 'Crit threshold: End degradation percent', + component: FeatureNumberInput, + description: + 'Crit threshold will stop decreasing and reach its maximum reduction when degradation is this percent to max.', +}; + +export const dc_max_crit_threshold_reduction: Feature = { + name: 'Crit threshold: Maximum reduction', + component: FeatureNumberInput, + description: + 'When at the ending degradation percent, crit threshold will be reduced by this, \ + with lower percentages causing equally displaced reducions, such as having 50% degradation causing 50% of this to be applied.', +}; + +export const dc_stamina_damage_min_percent_of_max: Feature = { + name: 'Stamina damage: Start degradation percent', + component: FeatureNumberInput, + description: + 'Minimum stamina damage will start increasing once degradation reaches this percent of maximum degradation.', +}; + +export const dc_stamina_damage_percent_of_max: Feature = { + name: 'Stamina damage: End degradation percent', + component: FeatureNumberInput, + description: + 'Minimum stamina damage will reach its maximum possible value once degradation reaches this percent of maximum degradation.', +}; + +export const dc_max_stamina_damage: Feature = { + name: 'Stamina damage: Maximum', + component: FeatureNumberInput, + description: + 'When at the ending degradation percent, your stamina damage will always be at LEAST this, \ + with lower percentages causing equally displaced minimums, such as having 50% degradation with 80 max stamina damage causing a minimum of 40 damage.', +}; + +export const dc_permakill_at_max: Feature = { + name: 'PK: Permaghost at maximum degradation', + component: CheckboxInput, + description: + 'If true, you will be permanently ghosted if your degradation reaches its maximum possible value.', +}; + +export const dc_force_death_if_permakilled: Feature = { + name: 'PK: Force death if permaghosted', + component: CheckboxInput, + description: 'If true, you will be permanently killed on permaghost as well.', +};