diff --git a/code/__DEFINES/living.dm b/code/__DEFINES/living.dm index aa7f6b8fe2fe..60d35c797ccd 100644 --- a/code/__DEFINES/living.dm +++ b/code/__DEFINES/living.dm @@ -113,10 +113,18 @@ /// Doesn't let a mob shift this atom around with move_pulled #define TRAIT_NO_MOVE_PULL "no_move_pull" -/// Boosts the heart rate of the mob +/// Boosts the heart rate of the mob (raises blood pressure) +/// One application of the trait translates to +10 bpm, which may translate to +10 blood pressure #define TRAIT_HEART_RATE_BOOST "heart_rate_boost" -/// Slows the heart rate of the mob +/// Slows the heart rate of the mob (lowers blood pressure) +/// One application of the trait translates to -10 bpm, which may translate to -10 blood pressure #define TRAIT_HEART_RATE_SLOW "heart_rate_slow" +/// Constricts blood vessels (raises blood pressure) +/// One application of the trait translates to +0.2 "vasoconstriction", which is a +0.2 multiplier to blood pressure +#define TRAIT_VASOCONSTRICTED "vasoconstricted" +/// Dilates blood vessels (lowers blood pressure) +/// One application of the trait translates to -0.2 "vasodilation", which is a -0.2 multiplier to blood pressure +#define TRAIT_VASODILATED "vasodilated" /// The trait that determines if someone has the robotic limb reattachment quirk. #define TRAIT_ROBOTIC_LIMBATTACHMENT "trait_robotic_limbattachment" @@ -179,9 +187,9 @@ #define UPDATE_SELF (UPDATE_SELF_DAMAGE | UPDATE_SELF_HEALTH) /// Threshold that heart beat becomes "slow" -#define SLOW_HEARTBEAT_THRESHOLD 6 +#define SLOW_HEARTBEAT_THRESHOLD 60 /// Threshold that heart beat becomes "fast" -#define FAST_HEARTBEAT_THRESHOLD 11 +#define FAST_HEARTBEAT_THRESHOLD 110 // Used in living mob offset list for determining pixel offsets #define PIXEL_W_OFFSET "w" diff --git a/code/datums/status_effects/buffs.dm b/code/datums/status_effects/buffs.dm index d4077a3d1d0a..9e263a1ecfca 100644 --- a/code/datums/status_effects/buffs.dm +++ b/code/datums/status_effects/buffs.dm @@ -239,10 +239,12 @@ /datum/status_effect/exercised/on_apply() owner.add_mood_event("exercise", /datum/mood_event/exercise, owner.mind.get_skill_level(/datum/skill/athletics)) + ADD_TRAIT(owner, TRAIT_HEART_RATE_BOOST, type) return ..() /datum/status_effect/exercised/on_remove() owner.clear_mood_event("exercise") + REMOVE_TRAIT(owner, TRAIT_HEART_RATE_BOOST, type) /atom/movable/screen/alert/status_effect/exercised name = "Exercise" diff --git a/code/datums/status_effects/debuffs/grabbed.dm b/code/datums/status_effects/debuffs/grabbed.dm index e52ca414ffe3..35d4374dcdf6 100644 --- a/code/datums/status_effects/debuffs/grabbed.dm +++ b/code/datums/status_effects/debuffs/grabbed.dm @@ -634,12 +634,6 @@ #undef CRIT_SOURCE #undef PIN_SOURCE -#define IS_VULNERABLE(mob) (\ - mob.incapacitated(IGNORE_GRAB|IGNORE_STASIS) \ - || mob.body_position == LYING_DOWN \ - || (mob.has_status_effect(/datum/status_effect/staggered) && mob.getStaminaLoss() >= 30) \ -) - /** * Checks how strong our grabs are. * @@ -647,7 +641,9 @@ * somewhere in the range of 0-10 for a normal human. */ /atom/movable/proc/get_grab_strength() + // resist strength and grab strength overlap in plenty of ways so we will re-use it for our base . = get_grab_resist_strength() + // strength adds a bonus on top of resist strength (stacking with the strengh bonus already factored in to resistance) if(HAS_TRAIT(src, TRAIT_STRENGTH)) . += 1 @@ -686,28 +682,41 @@ return 5 /mob/living/get_grab_resist_strength() + // base resistance is base on size . += mob_size * 2 + // athletics skill can give a decent boost . += clamp(0.5 * ((mind?.get_skill_level(/datum/skill/athletics) || 1) - 2), -1, 3) + // monkey wrangling buff if(ismonkey(src)) . -= 1 + // dead people are not actively resisting, though this is not an absurdly high nubmer like -20 + // because grabbing a corpse should still be cumbersome and unwieldy if(stat == DEAD) . -= 4 - else if(IS_VULNERABLE(src)) + // otherwise being in soft crit or incapacitated means you can't fight back well + else if(HAS_TRAIT(src, TRAIT_SOFT_CRIT) || incapacitated(IGNORE_GRAB|IGNORE_STASIS)) . -= 2 - if(HAS_TRAIT(src, TRAIT_STRENGTH)) - . += 1 + // lying down or staggered represents a vulnerable position which has you disadvanteged + if(body_position == LYING_DOWN || (has_status_effect(/datum/status_effect/staggered) && getStaminaLoss() >= 30)) + . -= 2 + // generic grab weakness such as from quirks if(HAS_TRAIT(src, TRAIT_GRABWEAKNESS)) . -= 2 - // these two are not + // grabs you grabs you grabs you if(HAS_TRAIT(src, TRAIT_SMALL)) . -= 2 + + // generic buff + if(HAS_TRAIT(src, TRAIT_STRENGTH)) + . += 1 + // hulks are the pinnacle of strength if(HAS_TRAIT(src, TRAIT_HULK)) . += 3 + // being large also helps considerably + // mutually exclusive with hulk as hulk's strength can be interpreted as coming from size else if(HAS_TRAIT(src, TRAIT_GIANT) || HAS_TRAIT(src, TRAIT_HUGE)) . += 2 -#undef IS_VULNERABLE - /** * When given a base climb speed, modifies it based on how good a climber we are. * diff --git a/code/datums/status_effects/drug_effects.dm b/code/datums/status_effects/drug_effects.dm index fa2376714c93..30d79ca657a9 100644 --- a/code/datums/status_effects/drug_effects.dm +++ b/code/datums/status_effects/drug_effects.dm @@ -12,36 +12,6 @@ desc = "You feel a bit slower than usual, it seems doing things with your hands takes longer than it usually does." icon_state = "woozy" -/datum/status_effect/high_blood_pressure - id = "high_blood_pressure" - tick_interval = -1 - status_type = STATUS_EFFECT_UNIQUE - alert_type = null - -/datum/status_effect/high_blood_pressure/on_apply() - if(!ishuman(owner)) - return FALSE - - var/mob/living/carbon/human/human_owner = owner - human_owner.physiology.bleed_mod *= 1.25 - RegisterSignal(owner, COMSIG_LIVING_HEALTHSCAN, PROC_REF(on_healthscan)) - return TRUE - -/datum/status_effect/high_blood_pressure/on_remove() - if(!ishuman(owner)) - return - - var/mob/living/carbon/human/human_owner = owner - human_owner.physiology.bleed_mod /= 1.25 - UnregisterSignal(owner, COMSIG_LIVING_HEALTHSCAN) - -/datum/status_effect/high_blood_pressure/proc/on_healthscan(datum/source, list/render_list, advanced, mob/user, mode, tochat) - SIGNAL_HANDLER - - if(owner.has_status_effect(/datum/status_effect/low_blood_pressure)) - return - render_list += "Hypertension detected.
" - /datum/status_effect/seizure id = "seizure" tick_interval = -1 diff --git a/code/game/objects/items/devices/scanners/health_analyzer.dm b/code/game/objects/items/devices/scanners/health_analyzer.dm index 8096ff5d9fac..e4bc46685fb2 100644 --- a/code/game/objects/items/devices/scanners/health_analyzer.dm +++ b/code/game/objects/items/devices/scanners/health_analyzer.dm @@ -372,18 +372,25 @@ // Blood Level var/datum/blood_type/target_blood_type = target.get_blood_type() if(target_blood_type) - var/bpm = target.get_bpm() + var/list/bp = target.get_bp_range() var/needs_heart = TRUE if(ishuman(target)) var/mob/living/carbon/human/humantarget = target needs_heart = humantarget.needs_heart() - var/bpm_format = "[needs_heart ? bpm : "n/a"] bpm" - var/level_format = "[round_and_format_decimal(target.blood_volume, 0.1)] cl" // round to 0.1 but also print "100.0" and not "100" + var/bp_format = "[bp[1]]/[bp[2]]" + span_slightly_smaller("mmHg") + var/level_format = "[round_and_format_decimal(target.blood_volume, 0.1)]" + span_slightly_smaller("cl") // round to 0.1 but also print "100.0" and not "100" var/blood_type_format = "[target_blood_type.name]" - if(needs_heart && (bpm < 60 || bpm > 100)) - bpm_format = span_alert(bpm_format) + if(needs_heart) + if(bp[1] > 140 && bp[2] > 90) // high blood pressure + bp_format = conditional_tooltip(span_alert("[bp_format] (Warning: Hypertension)"), \ + "To fix, resolve underlying causes such as pain, hypoxima, or blood loss. Side effects can be abated by \ + supplying vasodilators such as [/datum/reagent/potassium::name] or [/datum/reagent/nitroglycerin::name].", tochat) + if(bp[1] < 90 && bp[2] < 60) // low blood pressure + bp_format = conditional_tooltip(span_alert("[bp_format] (Warning: Hypotension)"), \ + "To fix, resolve underlying causes such as heart damage or blood loss. Side effects can be abated by \ + supplying vasoconstrictors such as [/datum/reagent/medicine/epinephrine::name].", tochat) switch(target.blood_volume) if(BLOOD_VOLUME_EXCESS to INFINITY) @@ -410,7 +417,7 @@ recieve_from_text += " Regenerates slowly via [target_blood_type.restoration_chem::name] reagent." blood_type_format = span_tooltip(recieve_from_text, blood_type_format) - render_list += "Heart rate: [bpm_format]
" + render_list += "Blood pressure: [bp_format]
" render_list += "Blood level: [level_format]
" render_list += "Blood type: [blood_type_format]
" diff --git a/code/game/objects/items/robot/items/hypo.dm b/code/game/objects/items/robot/items/hypo.dm index a70ffd25349b..0b742464d66f 100644 --- a/code/game/objects/items/robot/items/hypo.dm +++ b/code/game/objects/items/robot/items/hypo.dm @@ -36,7 +36,7 @@ ) #define HACKED_PEACE_REAGENTS list(\ /datum/reagent/toxin/cyanide,\ - /datum/reagent/toxin/fentanyl,\ + /datum/reagent/medicine/painkiller/fentanyl,\ /datum/reagent/toxin/sodium_thiopental,\ /datum/reagent/toxin/staminatoxin,\ /datum/reagent/toxin/sulfonal\ diff --git a/code/modules/clothing/neck/_neck.dm b/code/modules/clothing/neck/_neck.dm index c93b6cc55143..a0eda225b48e 100644 --- a/code/modules/clothing/neck/_neck.dm +++ b/code/modules/clothing/neck/_neck.dm @@ -307,7 +307,7 @@ //assess heart if(body_part == BODY_ZONE_CHEST)//if we're listening to the chest - var/heart_rate = carbon_patient.get_heart_rate() + var/heart_rate = carbon_patient.get_bpm() switch(heart_rate) if(0) render_list += "You don't hear a heartbeat!\n" diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm index 47bd0c95ae10..04c0969eb7bf 100644 --- a/code/modules/mob/living/carbon/carbon.dm +++ b/code/modules/mob/living/carbon/carbon.dm @@ -548,39 +548,46 @@ SShealth_updates.queue_update(src, UPDATE_SELF|UPDATE_MEDHUD_HEALTH) /mob/living/carbon/proc/paincrit_check() - if(crit_percent() < 100 || HAS_TRAIT(src, TRAIT_NOSOFTCRIT)) // melbert todo - if(HAS_TRAIT_FROM(src, TRAIT_SOFT_CRIT, PAINCRIT)) - Paralyze(2 SECONDS) - remove_traits(list(TRAIT_SOFT_CRIT, TRAIT_INCAPACITATED, TRAIT_IMMOBILIZED, TRAIT_FLOORED, TRAIT_HANDS_BLOCKED), PAINCRIT) + if(crit_percent() < 100) + remove_status_effect(/datum/status_effect/paincrit) return - if(HAS_TRAIT_FROM(src, TRAIT_SOFT_CRIT, PAINCRIT)) - return - var/is_standing = body_position == STANDING_UP - add_traits(list(TRAIT_SOFT_CRIT, TRAIT_INCAPACITATED, TRAIT_IMMOBILIZED, TRAIT_FLOORED, TRAIT_HANDS_BLOCKED), PAINCRIT) - if(stat == DEAD) - return - if(buckled) - visible_message( - span_warning("[src] slumps against [buckled]!"), - span_userdanger("You go limp, unable to move!"), - visible_message_flags = ALWAYS_SHOW_SELF_MESSAGE, - ) + apply_status_effect(/datum/status_effect/paincrit) - else if(is_standing && body_position != STANDING_UP) - visible_message( - span_warning("[src] collapses!"), - span_userdanger("You collapse, unable to stand!"), - visible_message_flags = ALWAYS_SHOW_SELF_MESSAGE, - ) - else if(body_position == LYING_DOWN) - visible_message( - span_warning("[src] slumps against the ground!"), - span_userdanger("You go limp, unable to get up!"), - visible_message_flags = ALWAYS_SHOW_SELF_MESSAGE, - ) - else - to_chat(src, span_userdanger("You can't will yourself to move!")) +/datum/status_effect/paincrit + id = "paincrit" + alert_type = null + tick_interval = -1 + duration = -1 + +/datum/status_effect/paincrit/nextmove_modifier() + return 3 + +/datum/status_effect/paincrit/on_apply() + . = ..() + owner.add_traits(list(TRAIT_SOFT_CRIT, TRAIT_FLOORED, TRAIT_GRABWEAKNESS), id) + owner.add_movespeed_modifier(/datum/movespeed_modifier/paincrit) + owner.add_actionspeed_modifier(/datum/actionspeed_modifier/paincrit) + owner.drop_all_held_items() + RegisterSignal(owner, SIGNAL_ADDTRAIT(TRAIT_NOSOFTCRIT), PROC_REF(clean_up)) + +/datum/status_effect/paincrit/on_remove() + . = ..() + owner.remove_traits(list(TRAIT_SOFT_CRIT, TRAIT_FLOORED, TRAIT_GRABWEAKNESS), id) + owner.remove_movespeed_modifier(/datum/movespeed_modifier/paincrit) + owner.remove_actionspeed_modifier(/datum/actionspeed_modifier/paincrit) + owner.Paralyze(2 SECONDS) + UnregisterSignal(owner, SIGNAL_ADDTRAIT(TRAIT_NOSOFTCRIT)) + +/datum/status_effect/paincrit/clean_up() + SIGNAL_HANDLER + qdel(src) + +/datum/movespeed_modifier/paincrit + multiplicative_slowdown = 9 + +/datum/actionspeed_modifier/paincrit + multiplicative_slowdown = 3 /mob/living/carbon/update_sight() if(!client) diff --git a/code/modules/mob/living/carbon/human/death.dm b/code/modules/mob/living/carbon/human/death.dm index e500632e7dc2..9be64018b8f0 100644 --- a/code/modules/mob/living/carbon/human/death.dm +++ b/code/modules/mob/living/carbon/human/death.dm @@ -81,6 +81,8 @@ GLOBAL_LIST_EMPTY(dead_players_during_shift) if(!HAS_TRAIT(src, TRAIT_NOBLOOD) && blood_volume < BLOOD_VOLUME_BAD) return BLOOD_LOSS + return "hypoxia" + if(TOX_DAMAGE) var/obj/item/organ/liver/liver = get_organ_slot(ORGAN_SLOT_LIVER) if(isnull(liver) || (liver.organ_flags & ORGAN_FAILING)) diff --git a/code/modules/mob/living/carbon/life.dm b/code/modules/mob/living/carbon/life.dm index b21798fdef92..e949271296ea 100644 --- a/code/modules/mob/living/carbon/life.dm +++ b/code/modules/mob/living/carbon/life.dm @@ -66,7 +66,7 @@ if(stat == HARD_CRIT && !internal && !external) // being on internals function as a ventilator + also makes anesthetic function (revisit later) losebreath = max(losebreath, 1) else if(HAS_TRAIT(src, TRAIT_LABOURED_BREATHING)) - losebreath += (1 / next_breath) + losebreath += (1 / max(2, next_breath)) if(losebreath < 1) var/pre_sig_return = SEND_SIGNAL(src, COMSIG_CARBON_ATTEMPT_BREATHE, seconds_per_tick, times_fired) diff --git a/code/modules/mob/living/life.dm b/code/modules/mob/living/life.dm index 6481efb8c75b..e91b8b807db3 100644 --- a/code/modules/mob/living/life.dm +++ b/code/modules/mob/living/life.dm @@ -176,9 +176,11 @@ if(feels_like > hot_threshold_high) throw_alert(ALERT_TEMPERATURE, /atom/movable/screen/alert/hot, 3) add_mood_event("hot", /datum/mood_event/overhot) + ADD_TRAIT(src, TRAIT_VASODILATED, "extreme_temperature") else if(feels_like > hot_threshold_medium) throw_alert(ALERT_TEMPERATURE, /atom/movable/screen/alert/hot, 2) add_mood_event("hot", /datum/mood_event/hot) + ADD_TRAIT(src, TRAIT_VASODILATED, "high_temperature") else throw_alert(ALERT_TEMPERATURE, /atom/movable/screen/alert/hot, 1) add_mood_event("hot", /datum/mood_event/warm) @@ -194,9 +196,11 @@ if(feels_like < cold_threshold_high) throw_alert(ALERT_TEMPERATURE, /atom/movable/screen/alert/cold, 3) add_mood_event("cold", /datum/mood_event/freezing) + ADD_TRAIT(src, TRAIT_VASOCONSTRICTED, "extreme_temperature") else if(feels_like < cold_threshold_medium) throw_alert(ALERT_TEMPERATURE, /atom/movable/screen/alert/cold, 2) add_mood_event("cold", /datum/mood_event/cold) + ADD_TRAIT(src, TRAIT_VASOCONSTRICTED, "high_temperature") else throw_alert(ALERT_TEMPERATURE, /atom/movable/screen/alert/cold, 1) add_mood_event("cold", /datum/mood_event/chilly) @@ -213,6 +217,8 @@ clear_mood_event("cold") clear_mood_event("hot") temp_alerts = FALSE + REMOVE_TRAITS_IN(src, "extreme_temperature") + REMOVE_TRAITS_IN(src, "high_temperature") /mob/living/silicon/body_temperature_alerts() return // Not yet diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index bf11c7d9cf4b..0cab70c31ff2 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -1486,7 +1486,7 @@ if(!istype(target)) CRASH("Missing target arg for can_perform_action") - if(stat != CONSCIOUS) + if(stat >= UNCONSCIOUS) to_chat(src, span_warning("You are not conscious enough for this action!")) return FALSE diff --git a/code/modules/reagents/chemistry/reagents.dm b/code/modules/reagents/chemistry/reagents.dm index 69c858fc7eea..bcd8fc48c9ce 100644 --- a/code/modules/reagents/chemistry/reagents.dm +++ b/code/modules/reagents/chemistry/reagents.dm @@ -233,8 +233,6 @@ Primarily used in reagents/reaction_agents /// Called when an overdose starts. Returning UPDATE_MOB_HEALTH will cause updatehealth() to be called on the holder mob by /datum/reagents/proc/metabolize. /datum/reagent/proc/overdose_start(mob/living/affected_mob) - to_chat(affected_mob, span_userdanger("You feel like you took too much of [name]!")) - affected_mob.add_mood_event("[type]_overdose", /datum/mood_event/overdose, name) return /** diff --git a/code/modules/reagents/chemistry/reagents/drug_reagents.dm b/code/modules/reagents/chemistry/reagents/drug_reagents.dm index e531518b2c99..a702ee6f7158 100644 --- a/code/modules/reagents/chemistry/reagents/drug_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/drug_reagents.dm @@ -13,6 +13,12 @@ if(trippy) affected_mob.clear_mood_event("[type]_high") REMOVE_TRAIT(affected_mob, TRAIT_HEART_RATE_BOOST, type) + REMOVE_TRAIT(affected_mob, TRAIT_HEART_RATE_BOOST, "[type]_overdose") + +/datum/reagent/drug/overdose_start(mob/living/affected_mob) + . = ..() + affected_mob.add_mood_event("[type]_overdose", /datum/mood_event/overdose, name) + ADD_TRAIT(affected_mob, TRAIT_HEART_RATE_BOOST, "[type]_overdose") /datum/reagent/drug/space_drugs name = "Space Drugs" @@ -35,7 +41,6 @@ /datum/reagent/drug/space_drugs/overdose_start(mob/living/affected_mob) . = ..() to_chat(affected_mob, span_userdanger("You start tripping hard!")) - affected_mob.add_mood_event("[type]_overdose", /datum/mood_event/overdose, name) /datum/reagent/drug/space_drugs/overdose_process(mob/living/affected_mob, seconds_per_tick, times_fired) . = ..() diff --git a/code/modules/reagents/chemistry/reagents/medicine_reagents.dm b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm index 64ad54e1636c..d53c1a744754 100644 --- a/code/modules/reagents/chemistry/reagents/medicine_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm @@ -990,12 +990,14 @@ ADD_TRAIT(affected_mob, TRAIT_NOCRITDAMAGE, type) ADD_TRAIT(affected_mob, TRAIT_ABATES_SHOCK, type) ADD_TRAIT(affected_mob, TRAIT_NOCRITDAMAGE, type) + ADD_TRAIT(affected_mob, TRAIT_VASOCONSTRICTED, type) /datum/reagent/medicine/epinephrine/on_mob_end_metabolize(mob/living/affected_mob) . = ..() REMOVE_TRAIT(affected_mob, TRAIT_NOCRITDAMAGE, type) REMOVE_TRAIT(affected_mob, TRAIT_ABATES_SHOCK, type) REMOVE_TRAIT(affected_mob, TRAIT_NOCRITDAMAGE, type) + REMOVE_TRAIT(affected_mob, TRAIT_VASOCONSTRICTED, type) /datum/reagent/medicine/epinephrine/on_mob_life(mob/living/carbon/affected_mob, seconds_per_tick, times_fired) . = ..() diff --git a/code/modules/reagents/chemistry/reagents/other_reagents.dm b/code/modules/reagents/chemistry/reagents/other_reagents.dm index 0164426ec630..5e2d743ae33b 100644 --- a/code/modules/reagents/chemistry/reagents/other_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/other_reagents.dm @@ -1014,7 +1014,7 @@ /datum/reagent/potassium name = "Potassium" description = "A soft, low-melting solid that can easily be cut with a knife. Reacts violently with water. \ - Often used medicinally to treat low blood pressure, but can be dangerous in high doses." + Often used medicinally to treat high blood pressure, but can be dangerous in high doses." reagent_state = SOLID color = "#A0A0A0" // rgb: 160, 160, 160 taste_description = "sweetness" @@ -1023,14 +1023,14 @@ /datum/reagent/potassium/on_mob_life(mob/living/carbon/affected_mob, seconds_per_tick, times_fired) . = ..() if(volume > 10) - ADD_TRAIT(affected_mob, TRAIT_HEART_RATE_BOOST, "[type]_low") + ADD_TRAIT(affected_mob, TRAIT_VASODILATED, "[type]_low") if(volume > 20) - ADD_TRAIT(affected_mob, TRAIT_HEART_RATE_BOOST, "[type]_high") + ADD_TRAIT(affected_mob, TRAIT_VASODILATED, "[type]_high") /datum/reagent/potassium/on_mob_end_metabolize(mob/living/affected_mob) . = ..() - REMOVE_TRAIT(affected_mob, TRAIT_HEART_RATE_BOOST, "[type]_low") - REMOVE_TRAIT(affected_mob, TRAIT_HEART_RATE_BOOST, "[type]_high") + REMOVE_TRAIT(affected_mob, TRAIT_VASODILATED, "[type]_low") + REMOVE_TRAIT(affected_mob, TRAIT_VASODILATED, "[type]_high") /datum/reagent/mercury name = "Mercury" diff --git a/code/modules/reagents/chemistry/reagents/pyrotechnic_reagents.dm b/code/modules/reagents/chemistry/reagents/pyrotechnic_reagents.dm index 702d31269450..a5a3c00a4529 100644 --- a/code/modules/reagents/chemistry/reagents/pyrotechnic_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/pyrotechnic_reagents.dm @@ -29,14 +29,15 @@ . = ..() if(affected_mob.adjustOrganLoss(ORGAN_SLOT_HEART, -1 * REM * seconds_per_tick * normalise_creation_purity(), required_organ_flag = affected_organ_flags)) return UPDATE_MOB_HEALTH - -/datum/reagent/nitroglycerin/on_mob_metabolize(mob/living/carbon/user) - . = ..() - ADD_TRAIT(user, TRAIT_HEART_RATE_BOOST, type) + if(volume > 5) + ADD_TRAIT(affected_mob, TRAIT_VASODILATED, "[type]_low") + if(volume > 10) + ADD_TRAIT(affected_mob, TRAIT_VASODILATED, "[type]_high") /datum/reagent/nitroglycerin/on_mob_end_metabolize(mob/living/affected_mob) . = ..() - REMOVE_TRAIT(affected_mob, TRAIT_HEART_RATE_BOOST, type) + REMOVE_TRAIT(affected_mob, TRAIT_VASODILATED, "[type]_low") + REMOVE_TRAIT(affected_mob, TRAIT_VASODILATED, "[type]_high") /datum/reagent/stabilizing_agent name = "Stabilizing Agent" diff --git a/code/modules/reagents/chemistry/reagents/toxin_reagents.dm b/code/modules/reagents/chemistry/reagents/toxin_reagents.dm index ce7e484a04b4..068ab701e295 100644 --- a/code/modules/reagents/chemistry/reagents/toxin_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/toxin_reagents.dm @@ -707,32 +707,43 @@ affected_mob.update_transform(RESIZE_DEFAULT_SIZE/current_size) current_size = RESIZE_DEFAULT_SIZE -/datum/reagent/toxin/fentanyl +/datum/reagent/medicine/painkiller/fentanyl name = "Fentanyl" - description = "Inhibits brain function and causes toxin damage before eventually knocking out the patient." + description = "An incredibly potent painkiller used to treat extreme pain. \ + It has a high potential for brain damage and addiction, and should be used with caution." reagent_state = LIQUID color = "#64916E" metabolization_rate = 0.5 * REAGENTS_METABOLISM creation_purity = REAGENT_STANDARD_PURITY purity = REAGENT_STANDARD_PURITY - toxpwr = 0 ph = 9 chemical_flags = REAGENT_CAN_BE_SYNTHESIZED addiction_types = list(/datum/addiction/opioids = 25) pain_modifier = 0.5 + overdose_threshold = 10 -/datum/reagent/toxin/fentanyl/on_mob_life(mob/living/carbon/affected_mob, seconds_per_tick, times_fired) +/datum/reagent/medicine/painkiller/fentanyl/on_mob_life(mob/living/carbon/affected_mob, seconds_per_tick, times_fired) . = ..() - var/need_mob_update - need_mob_update = affected_mob.adjustOrganLoss(ORGAN_SLOT_BRAIN, 3 * REM * normalise_creation_purity() * seconds_per_tick, 150) - if(affected_mob.toxloss <= 60) - need_mob_update += affected_mob.adjustToxLoss(1 * REM * normalise_creation_purity() * seconds_per_tick, updating_health = FALSE, required_biotype = affected_biotype) - if(current_cycle > 4) + if(affected_mob.getToxLoss() <= 60 && (overdosed || SPT_PROB(current_cycle / 5, seconds_per_tick))) // 1% chance per second per 1u + affected_mob.adjustToxLoss(1 * REM * normalise_creation_purity() * seconds_per_tick, required_biotype = affected_biotype) + + affected_mob.heal_pain(8 * REM * normalise_creation_purity() * seconds_per_tick) + if(current_cycle > 5) // 1u affected_mob.add_mood_event("smacked out", /datum/mood_event/narcotic_heavy, name) - if(current_cycle > 18) - affected_mob.Sleeping(40 * REM * normalise_creation_purity() * seconds_per_tick) - if(need_mob_update) - return UPDATE_MOB_HEALTH + ADD_TRAIT(affected_mob, TRAIT_HEART_RATE_SLOW, type) + if(current_cycle > 20) // 4u + affected_mob.adjust_drowsiness_up_to(4 SECONDS * REM * normalise_creation_purity() * seconds_per_tick, 24 SECONDS) + if(current_cycle > 30 || overdosed) // 6u + affected_mob.adjustOrganLoss(ORGAN_SLOT_BRAIN, clamp((current_cycle - 20) / 10, 1, 4) * REM * normalise_creation_purity() * seconds_per_tick, 150) + +/datum/reagent/medicine/painkiller/fentanyl/on_mob_end_metabolize(mob/living/affected_mob) + . = ..() + REMOVE_TRAIT(affected_mob, TRAIT_HEART_RATE_SLOW, type) + +/datum/reagent/medicine/painkiller/fentanyl/overdose_process(mob/living/affected_mob, seconds_per_tick, times_fired) + . = ..() + if(current_cycle > 10) + affected_mob.Sleeping(4 SECONDS * REM * normalise_creation_purity() * seconds_per_tick) /datum/reagent/toxin/cyanide name = "Cyanide" @@ -901,14 +912,6 @@ if(current_cycle > 22) affected_mob.Sleeping(40 * REM * normalise_creation_purity() * seconds_per_tick) -/datum/reagent/nitroglycerin/on_mob_metabolize(mob/living/carbon/user) - . = ..() - ADD_TRAIT(user, TRAIT_HEART_RATE_SLOW, type) - -/datum/reagent/nitroglycerin/on_mob_end_metabolize(mob/living/affected_mob) - . = ..() - REMOVE_TRAIT(affected_mob, TRAIT_HEART_RATE_SLOW, type) - /datum/reagent/toxin/amanitin name = "Amanitin" description = "A very powerful delayed toxin. Upon full metabolization, a massive amount of toxin damage will be dealt depending on how long it has been in the victim's bloodstream." diff --git a/code/modules/reagents/chemistry/recipes/toxins.dm b/code/modules/reagents/chemistry/recipes/toxins.dm index b13b6e4a581e..e279177de674 100644 --- a/code/modules/reagents/chemistry/recipes/toxins.dm +++ b/code/modules/reagents/chemistry/recipes/toxins.dm @@ -20,7 +20,7 @@ reaction_tags = REACTION_TAG_EASY | REACTION_TAG_DAMAGING | REACTION_TAG_CHEMICAL | REACTION_TAG_BRUTE | REACTION_TAG_TOXIN | REACTION_TAG_ORGAN | REACTION_TAG_OTHER /datum/chemical_reaction/fentanyl - results = list(/datum/reagent/toxin/fentanyl = 1) + results = list(/datum/reagent/medicine/painkiller/fentanyl = 1) required_reagents = list(/datum/reagent/drug/space_drugs = 1) mix_message = "The mixture turns cloudy, then becomes clear again." is_cold_recipe = FALSE @@ -328,7 +328,7 @@ /datum/chemical_reaction/rotatium results = list(/datum/reagent/toxin/rotatium = 3) - required_reagents = list(/datum/reagent/toxin/mindbreaker = 1, /datum/reagent/teslium = 1, /datum/reagent/toxin/fentanyl = 1) + required_reagents = list(/datum/reagent/toxin/mindbreaker = 1, /datum/reagent/teslium = 1, /datum/reagent/medicine/painkiller/fentanyl = 1) mix_message = "After sparks, fire, and the smell of mindbreaker, the mix is constantly spinning with no stop in sight." is_cold_recipe = FALSE required_temp = 100 diff --git a/code/modules/reagents/reagent_containers/cups/bottle.dm b/code/modules/reagents/reagent_containers/cups/bottle.dm index 5d8ea25b7d2b..59752a5a7b65 100644 --- a/code/modules/reagents/reagent_containers/cups/bottle.dm +++ b/code/modules/reagents/reagent_containers/cups/bottle.dm @@ -140,7 +140,7 @@ /obj/item/reagent_containers/cup/bottle/traitor/Initialize(mapload) . = ..() - extra_reagent = pick(/datum/reagent/toxin/polonium, /datum/reagent/toxin/histamine, /datum/reagent/toxin/formaldehyde, /datum/reagent/toxin/venom, /datum/reagent/toxin/fentanyl, /datum/reagent/toxin/cyanide) + extra_reagent = pick(/datum/reagent/toxin/polonium, /datum/reagent/toxin/histamine, /datum/reagent/toxin/formaldehyde, /datum/reagent/toxin/venom, /datum/reagent/medicine/painkiller/fentanyl, /datum/reagent/toxin/cyanide) reagents.add_reagent(extra_reagent, 3) /obj/item/reagent_containers/cup/bottle/polonium @@ -161,7 +161,7 @@ /obj/item/reagent_containers/cup/bottle/fentanyl name = "fentanyl bottle" desc = "A small bottle. Contains Fentanyl." - list_reagents = list(/datum/reagent/toxin/fentanyl = 30) + list_reagents = list(/datum/reagent/medicine/painkiller/fentanyl = 30) /obj/item/reagent_containers/cup/bottle/formaldehyde name = "formaldehyde bottle" diff --git a/code/modules/reagents/reagent_containers/hypospray.dm b/code/modules/reagents/reagent_containers/hypospray.dm index 2264a9a5be98..e5e9c0190e9f 100644 --- a/code/modules/reagents/reagent_containers/hypospray.dm +++ b/code/modules/reagents/reagent_containers/hypospray.dm @@ -33,6 +33,8 @@ return FALSE if(!iscarbon(affected_mob)) return FALSE + if(!pre_inject(affected_mob, user)) + return FALSE //Always log attemped injects for admins var/list/injected = list() @@ -61,6 +63,17 @@ return TRUE return FALSE +/obj/item/reagent_containers/hypospray/proc/pre_inject(mob/living/affected_mob, mob/user) + if(user.stat >= SOFT_CRIT) + user.visible_message( + span_warning("[user] struggles to inject [src] into [affected_mob == user ? user.p_themselves() : affected_mob]..."), + span_warning("You struggle with the cap of [src], trying to inject [affected_mob == user ? "yourself" : affected_mob]..."), + vision_distance = COMBAT_MESSAGE_RANGE, + visible_message_flags = ALWAYS_SHOW_SELF_MESSAGE, + ) + if(!do_after(user, 2.5 SECONDS, affected_mob)) + return FALSE + return TRUE /obj/item/reagent_containers/hypospray/cmo volume = 60 @@ -263,18 +276,18 @@ amount_per_transfer_from_this = 30 list_reagents = list( /datum/reagent/medicine/epinephrine = 8, /datum/reagent/medicine/c2/aiuri = 8, /datum/reagent/medicine/c2/libital = 8, /datum/reagent/medicine/leporazine = 6) -/obj/item/reagent_containers/hypospray/medipen/survival/inject(mob/living/affected_mob, mob/user) +/obj/item/reagent_containers/hypospray/medipen/survival/pre_inject(mob/living/affected_mob, mob/user) if(lavaland_equipment_pressure_check(get_turf(user))) amount_per_transfer_from_this = initial(amount_per_transfer_from_this) return ..() if(DOING_INTERACTION(user, DOAFTER_SOURCE_SURVIVALPEN)) to_chat(user,span_notice("You are too busy to use \the [src]!")) - return + return FALSE to_chat(user,span_notice("You start manually releasing the low-pressure gauge...")) if(!do_after(user, 10 SECONDS, affected_mob, interaction_key = DOAFTER_SOURCE_SURVIVALPEN)) - return + return FALSE amount_per_transfer_from_this = initial(amount_per_transfer_from_this) * 0.5 return ..() diff --git a/code/modules/reagents/reagent_containers/syringes.dm b/code/modules/reagents/reagent_containers/syringes.dm index da3d67db744a..c3c583223adf 100644 --- a/code/modules/reagents/reagent_containers/syringes.dm +++ b/code/modules/reagents/reagent_containers/syringes.dm @@ -323,7 +323,7 @@ list_reagents = list(/datum/reagent/drug/bath_salts = 15) /obj/item/reagent_containers/syringe/contraband/fentanyl - list_reagents = list(/datum/reagent/toxin/fentanyl = 15) + list_reagents = list(/datum/reagent/medicine/painkiller/fentanyl = 15) /obj/item/reagent_containers/syringe/contraband/morphine list_reagents = list(/datum/reagent/medicine/painkiller/morphine = 15) // NON-MODULE CHANGE diff --git a/code/modules/reagents/withdrawal/generic_addictions.dm b/code/modules/reagents/withdrawal/generic_addictions.dm index 53cf515db462..65d9e75d42b4 100644 --- a/code/modules/reagents/withdrawal/generic_addictions.dm +++ b/code/modules/reagents/withdrawal/generic_addictions.dm @@ -1,26 +1,86 @@ ///Opioids /datum/addiction/opioids - name = "opioid" - withdrawal_stage_messages = list("I feel aches in my bodies..", "I need some pain relief...", "It aches all over...I need some opioids!") + name = "painkiller" + withdrawal_stage_messages = list( + "My body aches all over...", + "I need some pain relief...", + "It hurts all over...I need some painkillers!", + ) + /// Multipliers to apply to pain on each withdrawal stage, compounded multiplicatively + var/list/pain_multipliers = list( + 1.25, // Stage 1 + 1.60, // Stage 2 + 2.50, // Stage 3 + ) + /// Lazylist of refs to all modified bodyparts to prevent double-dipping + var/list/modified_bodyparts + /// Minimum pain to add to bodyparts during withdrawal + var/min_pain = 12.5 -/datum/addiction/opioids/withdrawal_stage_1_process(mob/living/carbon/affected_carbon, seconds_per_tick) +/datum/addiction/opioids/process_addiction(mob/living/carbon/affected_carbon, seconds_per_tick) . = ..() - if(SPT_PROB(10, seconds_per_tick)) - affected_carbon.emote("yawn") + // constantly resets pain loss cooldown + COOLDOWN_MINIMUM(affected_carbon.pain_controller, time_since_last_pain_loss, seconds_per_tick * 1.5 SECONDS) + +/datum/addiction/opioids/withdrawal_enters_stage_1(mob/living/carbon/affected_carbon, seconds_per_tick) + . = ..() + RegisterSignal(affected_carbon, COMSIG_CARBON_POST_ATTACH_LIMB, PROC_REF(modify_bodypart)) + RegisterSignal(affected_carbon, COMSIG_CARBON_POST_REMOVE_LIMB, PROC_REF(unmodify_bodypart)) + for(var/obj/item/bodypart/to_hurt as anything in affected_carbon.bodyparts) + modify_bodypart(affected_carbon, to_hurt) + affected_carbon.pain_controller.refresh_pain_attributes() /datum/addiction/opioids/withdrawal_enters_stage_2(mob/living/carbon/affected_carbon) . = ..() - affected_carbon.apply_status_effect(/datum/status_effect/high_blood_pressure) + for(var/obj/item/bodypart/to_hurt as anything in affected_carbon.bodyparts) + to_hurt.bodypart_pain_modifier *= pain_multipliers[2] + ADD_TRAIT(affected_carbon, TRAIT_VASOCONSTRICTED, "low_[type]") -/datum/addiction/opioids/withdrawal_stage_3_process(mob/living/carbon/affected_carbon, seconds_per_tick) +/datum/addiction/opioids/withdrawal_enters_stage_3(mob/living/carbon/affected_carbon) . = ..() - if(affected_carbon.disgust < DISGUST_LEVEL_DISGUSTED && SPT_PROB(7.5, seconds_per_tick)) - affected_carbon.adjust_disgust(12.5 * seconds_per_tick) + for(var/obj/item/bodypart/to_hurt as anything in affected_carbon.bodyparts) + to_hurt.bodypart_pain_modifier *= pain_multipliers[3] + ADD_TRAIT(affected_carbon, TRAIT_VASOCONSTRICTED, "high_[type]") /datum/addiction/opioids/end_withdrawal(mob/living/carbon/affected_carbon) . = ..() - affected_carbon.remove_status_effect(/datum/status_effect/high_blood_pressure) - affected_carbon.set_disgust(affected_carbon.disgust * 0.5) //half their disgust to help + UnregisterSignal(affected_carbon, COMSIG_CARBON_POST_ATTACH_LIMB) + UnregisterSignal(affected_carbon, COMSIG_CARBON_POST_REMOVE_LIMB) + for(var/obj/item/bodypart/to_hurt as anything in affected_carbon.bodyparts) + unmodify_bodypart(affected_carbon, to_hurt) + affected_carbon.pain_controller.refresh_pain_attributes() + REMOVE_TRAIT(affected_carbon, TRAIT_VASOCONSTRICTED, "low_[type]") + REMOVE_TRAIT(affected_carbon, TRAIT_VASOCONSTRICTED, "high_[type]") + +/datum/addiction/opioids/proc/modify_bodypart(mob/living/carbon/affected_carbon, obj/item/bodypart/new_limb) + SIGNAL_HANDLER + + if(REF(new_limb) in modified_bodyparts) + return // already modified, don't double-dip + + LAZYADD(modified_bodyparts, REF(new_limb)) + // adds a very low base pain, so they always feel something + new_limb.min_pain += min_pain + new_limb.pain = max(new_limb.pain, new_limb.min_pain) + + // then make the bodypart feel more hurt than it actually is + // so they can get more severe feedback effects without being in a dangerous threshold + for(var/i in 1 to get_withdrawal_stage(affected_carbon)) + new_limb.bodypart_pain_modifier *= pain_multipliers[i] + + new_limb.on_gain_pain_effects(min_pain, STAMINA) + +/datum/addiction/opioids/proc/unmodify_bodypart(mob/living/carbon/affected_carbon, obj/item/bodypart/removed_limb) + SIGNAL_HANDLER + + if(!(REF(removed_limb) in modified_bodyparts)) + return // wasn't modified, nothing to do + + LAZYREMOVE(modified_bodyparts, REF(removed_limb)) + removed_limb.min_pain -= min_pain + + for(var/i in 1 to get_withdrawal_stage(affected_carbon)) + removed_limb.bodypart_pain_modifier /= pain_multipliers[i] ///Stimulants diff --git a/code/modules/surgery/organs/internal/heart/_heart.dm b/code/modules/surgery/organs/internal/heart/_heart.dm index 7d21d27f7370..fc0b91343f5a 100644 --- a/code/modules/surgery/organs/internal/heart/_heart.dm +++ b/code/modules/surgery/organs/internal/heart/_heart.dm @@ -36,16 +36,26 @@ /// whether the heart's been operated on to fix some of its damages var/operated = FALSE + /// Keeps the random variation on BPM consistent so it doesn't look weird + VAR_PRIVATE/random_bpm_modifier = 0 + /obj/item/organ/heart/update_icon_state() . = ..() icon_state = "[base_icon_state]-[beating ? "on" : "off"]" -/obj/item/organ/heart/Remove(mob/living/carbon/heartless, special, movement_flags) +/obj/item/organ/heart/on_mob_insert(mob/living/carbon/organ_owner, special, movement_flags) + . = ..() + if(beating) + organ_owner.remove_status_effect(/datum/status_effect/heart_attack) + random_bpm_modifier = rand(-5, 5) + +/obj/item/organ/heart/on_mob_remove(mob/living/carbon/organ_owner, special) . = ..() if(!special) + organ_owner.apply_status_effect(/datum/status_effect/heart_attack) addtimer(CALLBACK(src, PROC_REF(stop_if_unowned)), 12 SECONDS) playing_heartbeat_sfx = BEAT_NONE - owner?.stop_sound_channel(CHANNEL_HEARTBEAT) + organ_owner.stop_sound_channel(CHANNEL_HEARTBEAT) /obj/item/organ/heart/proc/stop_if_unowned() if(QDELETED(src)) @@ -93,6 +103,12 @@ SShealth_updates.queue_update(owner, UPDATE_MEDHUD_STATUS|UPDATE_MEDHUD_HEALTH) return TRUE +/obj/item/organ/heart/proc/stop_on_beat() + if(owner?.needs_heart() && Stop()) + if(owner.stat <= SOFT_CRIT && !owner.incapacitated(IGNORE_RESTRAINTS|IGNORE_GRAB)) + owner.visible_message(span_danger("[owner] clutches at [owner.p_their()] chest as if [owner.p_their()] heart is stopping!")) + to_chat(owner, span_userdanger("You feel a terrible pain in your chest, as if your heart has stopped!")) + /obj/item/organ/heart/OnEatFrom(eater, feeder) . = ..() Stop() @@ -120,71 +136,212 @@ // Handle "sudden" heart attack if(!beating || (organ_flags & ORGAN_FAILING)) - if(owner.can_heartattack() && Stop()) - if(owner.stat <= SOFT_CRIT && !owner.incapacitated(IGNORE_RESTRAINTS|IGNORE_GRAB)) - owner.visible_message(span_danger("[owner] clutches at [owner.p_their()] chest as if [owner.p_their()] heart is stopping!")) - to_chat(owner, span_userdanger("You feel a terrible pain in your chest, as if your heart has stopped!")) + stop_on_beat() return - if(!owner.client?.prefs.read_preference(/datum/preference/toggle/heartbeat)) - return + // randomly climbs up and down to create believable variation in heart rate + random_bpm_modifier = clamp(random_bpm_modifier + rand(-1, 1), -10, 10) var/heartrate = get_heart_rate() - switch(heartrate) - if(1 to SLOW_HEARTBEAT_THRESHOLD) - if(playing_heartbeat_sfx != BEAT_SLOW) - playing_heartbeat_sfx = BEAT_SLOW - SEND_SOUND(owner, sound('sound/health/slowbeat.ogg', repeat = TRUE, channel = CHANNEL_HEARTBEAT, volume = 40)) - if(0, SLOW_HEARTBEAT_THRESHOLD to FAST_HEARTBEAT_THRESHOLD) - if(playing_heartbeat_sfx != BEAT_NONE) - playing_heartbeat_sfx = BEAT_NONE - owner.stop_sound_channel(CHANNEL_HEARTBEAT) - if(FAST_HEARTBEAT_THRESHOLD to INFINITY) - if(playing_heartbeat_sfx != BEAT_FAST) - playing_heartbeat_sfx = BEAT_FAST - SEND_SOUND(owner, sound('sound/health/fastbeat.ogg', repeat = TRUE, channel = CHANNEL_HEARTBEAT, volume = 40)) + if(heartrate <= 0 && owner.needs_heart() && Stop()) + stop_on_beat() + if(heartrate >= 160) + apply_organ_damage((heartrate >= 200 ? 1 : 0.5) * seconds_per_tick, required_organ_flag = ORGAN_ORGANIC) + + if(owner.client?.prefs.read_preference(/datum/preference/toggle/heartbeat)) + switch(heartrate) + if(1 to SLOW_HEARTBEAT_THRESHOLD) + if(playing_heartbeat_sfx != BEAT_SLOW) + playing_heartbeat_sfx = BEAT_SLOW + SEND_SOUND(owner, sound('sound/health/slowbeat.ogg', repeat = TRUE, channel = CHANNEL_HEARTBEAT, volume = 40)) + if(0, SLOW_HEARTBEAT_THRESHOLD to FAST_HEARTBEAT_THRESHOLD) + if(playing_heartbeat_sfx != BEAT_NONE) + playing_heartbeat_sfx = BEAT_NONE + owner.stop_sound_channel(CHANNEL_HEARTBEAT) + if(FAST_HEARTBEAT_THRESHOLD to INFINITY) + if(playing_heartbeat_sfx != BEAT_FAST) + playing_heartbeat_sfx = BEAT_FAST + SEND_SOUND(owner, sound('sound/health/fastbeat.ogg', repeat = TRUE, channel = CHANNEL_HEARTBEAT, volume = 40)) + + var/bloodpressure = get_blood_pressure() + if(bloodpressure > 140) + if(SPT_PROB(10, seconds_per_tick)) + owner.adjust_dizzy_up_to(5 SECONDS, 60 SECONDS) + if(prob(10)) + owner.adjust_confusion_up_to(4 SECONDS, 20 SECONDS) + else if(!HAS_TRAIT(owner, TRAIT_INCAPACITATED)) + to_chat(owner, span_warning("You feel [pick("tired", "confused", "numb", "weak", "flush")].")) + if(SPT_PROB(10, seconds_per_tick)) + owner.adjust_eye_blur_up_to(5 SECONDS, 60 SECONDS) + if(SPT_PROB(1, seconds_per_tick)) + if(prob(90) && owner.get_bodypart(BODY_ZONE_HEAD)) + var/face_covered = owner.obscured_slots & HIDEMASK + owner.bleed(rand(2, 5), leave_pool = !face_covered) + to_chat(owner, span_warning("You get a nosebleed.")) + if(!face_covered) + owner.visible_message(span_warning("[owner] gets a nosebleed."), vision_distance = COMBAT_MESSAGE_RANGE, ignored_mobs = owner) + else + to_chat(owner, span_warning("You feel a bit nauseous.")) + if(SPT_PROB(2, seconds_per_tick)) + to_chat(owner, span_warning("Your chest feels [pick("tight", "uncomfortable")].")) + ADD_TRAIT(owner, TRAIT_LABOURED_BREATHING, type) // shortness of breath + else if(bloodpressure < 60) + if(SPT_PROB(10, seconds_per_tick)) + owner.adjust_dizzy_up_to(5 SECONDS, 60 SECONDS) + if(prob(10)) + owner.adjust_confusion_up_to(4 SECONDS, 20 SECONDS) + else if(!HAS_TRAIT(owner, TRAIT_INCAPACITATED)) + to_chat(owner, span_warning("You feel [pick("lightheaded", "tired", "confused", "like you can't focus")].")) + if(SPT_PROB(10, seconds_per_tick)) + owner.adjust_eye_blur_up_to(5 SECONDS, 60 SECONDS) + if(SPT_PROB(1, seconds_per_tick)) + owner.adjust_disgust(10, DISGUST_LEVEL_VERYGROSS) + if(SPT_PROB(2, seconds_per_tick)) + if(owner.usable_hands > 0) + to_chat(owner, span_warning("Your hand[owner.usable_hands > 1 ? "s" : ""] feel[owner.usable_hands > 1 ? "" : "s"] clammy.")) + else if(!HAS_TRAIT(owner, TRAIT_KNOCKEDOUT)) + to_chat(owner, span_warning("You feel clammy.")) + if(SPT_PROB(1, seconds_per_tick)) + if(prob(10)) + owner.emote("faint") + else if(!HAS_TRAIT(owner, TRAIT_KNOCKEDOUT)) + to_chat(owner, span_warning("Your feel like you're about to faint.")) + ADD_TRAIT(owner, TRAIT_LABOURED_BREATHING, type) // shallow breathing + else + REMOVE_TRAIT(owner, TRAIT_LABOURED_BREATHING, type) /obj/item/organ/heart/get_availability(datum/species/owner_species, mob/living/owner_mob) return owner_species.mutantheart -/// Gets the mob's heart rate as bpm (0 to 200(+)) -/mob/living/proc/get_bpm() - var/heart_rate = get_heart_rate() - if(heart_rate <= 0) +/// Gets the heart rate of the heart (resting 80, varies between 0 and 200+) +/obj/item/organ/heart/proc/get_heart_rate() + if(!is_beating() || isnull(owner)) return 0 - return heart_rate * 10 + rand(-10, 15) + var/base_amount = 80 + random_bpm_modifier + // arbitrary modifiers + base_amount += (10 * COUNT_TRAIT_SOURCES(owner, TRAIT_HEART_RATE_BOOST)) + base_amount -= (10 * COUNT_TRAIT_SOURCES(owner, TRAIT_HEART_RATE_SLOW)) + // hypoxia + base_amount += owner.getOxyLoss() / 5 + // stress (primarily pain and shock modelled here) + base_amount += owner.pain_controller?.get_total_pain() / 5 + base_amount += owner.pain_controller?.traumatic_shock / 2.5 + // low blood volume increases heart rate + base_amount += (BLOOD_VOLUME_NORMAL - owner.blood_volume) / 25 + // sprinting (to represent exercise) and actual exercise + if(ishuman(owner)) + var/mob/living/carbon/human/human_owner = owner + base_amount += (10 * ((human_owner.sprint_length_max - human_owner.sprint_length) / human_owner.sprint_length_max)) -/// Gets the mob's heart rate scaled from 0 to 20(+) -/mob/living/proc/get_heart_rate() - if(stat == DEAD) + return max(0, round(base_amount, 1)) + +/// Returns the strength of the heart as a multiplier (0 to 1+) +/obj/item/organ/heart/proc/get_heart_strength() + if(!is_beating() || isnull(owner)) return 0 - return rand(7, 9) + var/heart_strength = (maxHealth - damage) / maxHealth + // stress (boost from adrenaline) + heart_strength += (owner.has_status_effect(/datum/status_effect/determined) ? 0.2 : 0) + // low blood volume decreases heart strength + heart_strength -= ((BLOOD_VOLUME_NORMAL - owner.blood_volume) / (2 * BLOOD_VOLUME_NORMAL)) -/mob/living/carbon/human/get_heart_rate() - var/obj/item/organ/heart/heart = get_organ_slot(ORGAN_SLOT_HEART) - return heart?.get_heart_rate() || 0 + return max(0, round(heart_strength, 0.1)) -/// Gets the heart rate of the heart, scaled from 0 to 20(+) -/obj/item/organ/heart/proc/get_heart_rate() +/// Returns whether vessels are vasodialated (0.5 to 1) or vasoconstricted (1 to 2) +/obj/item/organ/heart/proc/get_heart_vessel_status() if(!is_beating() || isnull(owner)) return 0 - var/base_amount = 8 - base_amount += COUNT_TRAIT_SOURCES(owner, TRAIT_HEART_RATE_BOOST) - base_amount -= COUNT_TRAIT_SOURCES(owner, TRAIT_HEART_RATE_SLOW) - base_amount += round(owner.getOxyLoss() / 50, 0.5) - base_amount += round(owner.pain_controller?.get_total_pain() / 50, 0.5) - base_amount += round(owner.pain_controller?.traumatic_shock / 25, 0.5) - base_amount += round((BLOOD_VOLUME_NORMAL - owner.blood_volume) / 250, 0.5) - base_amount -= round((CONSCIOUSNESS_MAX - owner.consciousness) / 33, 0.5) - if(ishuman(owner)) - var/mob/living/carbon/human/human_owner = owner - base_amount += round((human_owner.sprint_length_max - human_owner.sprint_length) / human_owner.sprint_length_max, 0.1) - var/damage_multiplier = clamp(2.5 * ((maxHealth - damage) / maxHealth), 0.5, 1) + var/vessel_status = 1 + // arbitrary modifiers + vessel_status += (0.2 * COUNT_TRAIT_SOURCES(owner, TRAIT_VASOCONSTRICTED)) + vessel_status -= (0.2 * COUNT_TRAIT_SOURCES(owner, TRAIT_VASODILATED)) + // stress (primarily pain and shock modelled here) causes vasoconstriction(+) + vessel_status += owner.pain_controller?.get_total_pain() / 500 + vessel_status += owner.pain_controller?.traumatic_shock / 250 + // low blood volume causes vasoconstriction(+) + vessel_status += ((BLOOD_VOLUME_NORMAL - owner.blood_volume) / BLOOD_VOLUME_NORMAL) + + return clamp(round(vessel_status, 0.1), 0.5, 2) + +/// Returns the average blood pressure of the heart, from a combination of bpm + strength + vessel status. +/obj/item/organ/heart/proc/get_blood_pressure() + var/heart_rate = get_heart_rate() + var/heart_strength = get_heart_strength() + var/heart_vessel_status = get_heart_vessel_status() + + // TL;DR + // + // - higher heart rate = higher blood pressure + // - higher oxyloss = higher heart rate = higher blood pressure + // - higher pain/shock = higher heart rate = higher blood pressure + // - lower blood volume = higher heart rate = higher blood pressure + // + // - weak heart = lower blood pressure + // - higher damage = weaker heart = lower blood pressure + // - low blood volume = weaker heart = lower blood pressure + // + // - vasoconstricted vessels = higher blood pressure + // - vasodilated vessels = lower blood pressure + // - stress = vasoconstriction(+) = higher blood pressure + // - low blood volume = vasoconstriction(+) = higher blood pressure + // + var/blood_pressure = (heart_rate * heart_strength * heart_vessel_status) + + return max(0, round(blood_pressure, 1)) + +/// Return the mob's heart BPM +/mob/living/proc/get_bpm() + if(!(mob_biotypes & MOB_ORGANIC)) + return 0 + + return rand(7, 9) * 10 + +/mob/living/carbon/human/get_bpm() + var/obj/item/organ/heart/heart = get_organ_by_type(/obj/item/organ/heart) + return heart?.get_heart_rate() || 0 + +/// Return the mob's blood pressure +/mob/living/proc/get_bp() + if(!(mob_biotypes & MOB_ORGANIC)) + return 0 + + return rand(85, 95) + +/mob/living/carbon/human/get_bp() + var/obj/item/organ/heart/heart = get_organ_by_type(/obj/item/organ/heart) + return heart?.get_blood_pressure() || 0 + +/// The IRL average pressure of a pulse +#define AVERAGE_HUMAN_PULSE_PRESSURE 40 + +/// Return the mob's pulse pressure, which is primarily only relevant in calculating blood pressure +/mob/living/proc/get_pp() + if(!(mob_biotypes & MOB_ORGANIC)) + return 0 + + return AVERAGE_HUMAN_PULSE_PRESSURE + +/mob/living/carbon/human/get_pp() + var/obj/item/organ/heart/heart = get_organ_by_type(/obj/item/organ/heart) + if(isnull(heart)) + return needs_heart() ? 0 : AVERAGE_HUMAN_PULSE_PRESSURE + + return heart.get_heart_strength() * AVERAGE_HUMAN_PULSE_PRESSURE + +/// Returns the mob's blood pressure range (calculated from average bp with some variance) as a list/tuple of (diastolic pressure, systolic pressure) +/mob/living/proc/get_bp_range() + var/avg_blood_pressure = get_bp() + var/avg_pulse_pressure = get_pp() + + var/diastolic = avg_blood_pressure - (avg_pulse_pressure * 0.33) + var/systolic = diastolic + avg_pulse_pressure + + return list(floor(diastolic), floor(systolic)) - return clamp(round(base_amount * damage_multiplier, 0.5), 1, 100) +#undef AVERAGE_HUMAN_PULSE_PRESSURE /obj/item/organ/heart/feel_for_damage(self_aware) if(owner.needs_heart() && (!beating || (organ_flags & ORGAN_FAILING))) diff --git a/code/modules/surgery/organs/internal/lungs/_lungs.dm b/code/modules/surgery/organs/internal/lungs/_lungs.dm index f96c050afe46..8def4a1c7357 100644 --- a/code/modules/surgery/organs/internal/lungs/_lungs.dm +++ b/code/modules/surgery/organs/internal/lungs/_lungs.dm @@ -749,11 +749,8 @@ /// Handles what happens when we breathe in something we need to live /obj/item/organ/lungs/proc/heal_oxyloss_on_breath(mob/living/carbon/human/breather, datum/gas_mixture/breath) - if(HAS_TRAIT(breather, TRAIT_NOBLOOD)) - breather.adjustOxyLoss(-4) - else - // Less blood so breaths give you less oxygen - breather.adjustOxyLoss(-1 * min(5, BLOOD_VOLUME_NORMAL / breather.blood_volume)) + // Less blood makes breaths give you less oxygen + breather.adjustOxyLoss(-4 * min(1, HAS_TRAIT(breather, TRAIT_NOBLOOD) ? 1 : (BLOOD_VOLUME_NORMAL / breather.blood_volume))) /// Applies suffocation side-effects to a given Human, scaling based on ratio of required pressure VS "true" pressure. /// If pressure is greater than 0, the return value will represent the amount of gas successfully breathed. diff --git a/maplestation.dme b/maplestation.dme index c021c4fd9077..47ec64b79e2c 100644 --- a/maplestation.dme +++ b/maplestation.dme @@ -6347,12 +6347,10 @@ #include "maplestation_modules\code\datums\pain\pain_helpers.dm" #include "maplestation_modules\code\datums\pain\pain_implements.dm" #include "maplestation_modules\code\datums\pain\scrubber_tweak.dm" -#include "maplestation_modules\code\datums\pain\pain_causes\opioid_addiction_pain.dm" #include "maplestation_modules\code\datums\pain\pain_reagents\painkiller_reactions.dm" #include "maplestation_modules\code\datums\pain\pain_reagents\painkiller_related_chems.dm" #include "maplestation_modules\code\datums\pain\pain_reagents\painkillers.dm" #include "maplestation_modules\code\datums\pain\pain_status_effects\anesthetic.dm" -#include "maplestation_modules\code\datums\pain\pain_status_effects\low_blood_pressure.dm" #include "maplestation_modules\code\datums\pain\pain_status_effects\min_pain.dm" #include "maplestation_modules\code\datums\pain\pain_status_effects\pain_limp.dm" #include "maplestation_modules\code\datums\pain\pain_status_effects\sharp_pain.dm" @@ -6684,7 +6682,6 @@ #include "maplestation_modules\code\modules\mob\living\carbon\human\skrell_hair.dm" #include "maplestation_modules\code\modules\mob\living\carbon\human\heart_rework\autopulse.dm" #include "maplestation_modules\code\modules\mob\living\carbon\human\heart_rework\cpr.dm" -#include "maplestation_modules\code\modules\mob\living\carbon\human\heart_rework\heart_overrides.dm" #include "maplestation_modules\code\modules\mob\living\carbon\human\ornithid_features\armwings.dm" #include "maplestation_modules\code\modules\mob\living\carbon\human\ornithid_features\avian_tails.dm" #include "maplestation_modules\code\modules\mob\living\carbon\human\ornithid_features\plumage.dm" diff --git a/maplestation_modules/code/datums/pain/pain.dm b/maplestation_modules/code/datums/pain/pain.dm index 055e35c92726..f6e54a8c0984 100644 --- a/maplestation_modules/code/datums/pain/pain.dm +++ b/maplestation_modules/code/datums/pain/pain.dm @@ -262,14 +262,12 @@ if(!HAS_TRAIT_FROM(parent, TRAIT_SOFT_CRIT, PAINSHOCK)) set_pain_modifier(PAINSHOCK, 1.2) parent.add_max_consciousness_value(PAINSHOCK, 60) - parent.apply_status_effect(/datum/status_effect/low_blood_pressure) parent.add_traits(list(TRAIT_SOFT_CRIT, TRAIT_LABOURED_BREATHING), PAINSHOCK) else if(HAS_TRAIT_FROM(parent, TRAIT_SOFT_CRIT, PAINSHOCK)) unset_pain_modifier(PAINSHOCK) parent.remove_max_consciousness_value(PAINSHOCK) - parent.remove_status_effect(/datum/status_effect/low_blood_pressure) parent.remove_traits(list(TRAIT_SOFT_CRIT, TRAIT_LABOURED_BREATHING), PAINSHOCK) #ifdef HEALTH_DEBUG @@ -486,16 +484,6 @@ if(SPT_PROB(2, seconds_per_tick)) do_pain_message(span_userdanger(pick("Stop the pain!", "It hurts!", "You need painkillers now!"))) - if((traumatic_shock >= 20 || curr_pain >= 50) && !just_cant_feel_anything) - if(SPT_PROB(min(curr_pain / 5, 24), seconds_per_tick)) - parent.adjust_jitter_up_to(5 SECONDS * pain_modifier, 30 SECONDS) - if(SPT_PROB(min(curr_pain / 5, 24), seconds_per_tick)) - parent.adjust_eye_blur_up_to(5 SECONDS * pain_modifier, 30 SECONDS) - if(SPT_PROB(min(curr_pain / 10, 12), seconds_per_tick)) - parent.adjust_dizzy_up_to(5 SECONDS * pain_modifier, 30 SECONDS) - if(SPT_PROB(min(curr_pain / 20, 6), seconds_per_tick)) // pain applies its own stutter - parent.adjust_stutter_up_to(5 SECONDS * pain_modifier, 30 SECONDS) - if((traumatic_shock >= 40 || curr_pain >= 80) && parent.stat != HARD_CRIT) if(SPT_PROB(traumatic_shock / 60, seconds_per_tick)) //parent.vomit(VOMIT_CATEGORY_KNOCKDOWN, lost_nutrition = 7.5) @@ -559,9 +547,6 @@ else heart_attack_counter = 0 - if(traumatic_shock >= SHOCK_CRIT_THRESHOLD || curr_pain >= PAIN_CRIT_THRESOLD) - parent.adjust_jitter_up_to(5 SECONDS * pain_modifier, 120 SECONDS) - parent.paincrit_check() // Finally, handle pain decay over time diff --git a/maplestation_modules/code/datums/pain/pain_causes/opioid_addiction_pain.dm b/maplestation_modules/code/datums/pain/pain_causes/opioid_addiction_pain.dm deleted file mode 100644 index 1a9f29fed280..000000000000 --- a/maplestation_modules/code/datums/pain/pain_causes/opioid_addiction_pain.dm +++ /dev/null @@ -1,78 +0,0 @@ -/datum/addiction/opioids - name = "painkiller" - withdrawal_stage_messages = list( - "My body aches all over...", - "I need some pain relief...", - "It hurts all over...I need some painkillers!", - ) - /// Multipliers to apply to pain on each withdrawal stage, compounded multiplicatively - var/list/pain_multipliers = list( - 1.25, // Stage 1 - 1.60, // Stage 2 - 2.50, // Stage 3 - ) - /// Lazylist of refs to all modified bodyparts to prevent double-dipping - var/list/modified_bodyparts - /// Minimum pain to add to bodyparts during withdrawal - var/min_pain = 12.5 - -/datum/addiction/opioids/process_addiction(mob/living/carbon/affected_carbon, seconds_per_tick) - . = ..() - // constantly resets pain loss cooldown - COOLDOWN_MINIMUM(affected_carbon.pain_controller, time_since_last_pain_loss, seconds_per_tick * 1.5 SECONDS) - -/datum/addiction/opioids/withdrawal_enters_stage_1(mob/living/carbon/affected_carbon, seconds_per_tick) - . = ..() - RegisterSignal(affected_carbon, COMSIG_CARBON_POST_ATTACH_LIMB, PROC_REF(modify_bodypart)) - RegisterSignal(affected_carbon, COMSIG_CARBON_POST_REMOVE_LIMB, PROC_REF(unmodify_bodypart)) - for(var/obj/item/bodypart/to_hurt as anything in affected_carbon.bodyparts) - modify_bodypart(affected_carbon, to_hurt) - affected_carbon.pain_controller.refresh_pain_attributes() - -/datum/addiction/opioids/withdrawal_enters_stage_2(mob/living/carbon/affected_carbon) - . = ..() - for(var/obj/item/bodypart/to_hurt as anything in affected_carbon.bodyparts) - to_hurt.bodypart_pain_modifier *= pain_multipliers[2] - -/datum/addiction/opioids/withdrawal_enters_stage_3(mob/living/carbon/affected_carbon) - . = ..() - for(var/obj/item/bodypart/to_hurt as anything in affected_carbon.bodyparts) - to_hurt.bodypart_pain_modifier *= pain_multipliers[3] - -/datum/addiction/opioids/end_withdrawal(mob/living/carbon/affected_carbon) - . = ..() - UnregisterSignal(affected_carbon, COMSIG_CARBON_POST_ATTACH_LIMB) - UnregisterSignal(affected_carbon, COMSIG_CARBON_POST_REMOVE_LIMB) - for(var/obj/item/bodypart/to_hurt as anything in affected_carbon.bodyparts) - unmodify_bodypart(affected_carbon, to_hurt) - affected_carbon.pain_controller.refresh_pain_attributes() - -/datum/addiction/opioids/proc/modify_bodypart(mob/living/carbon/affected_carbon, obj/item/bodypart/new_limb) - SIGNAL_HANDLER - - if(REF(new_limb) in modified_bodyparts) - return // already modified, don't double-dip - - LAZYADD(modified_bodyparts, REF(new_limb)) - // adds a very low base pain, so they always feel something - new_limb.min_pain += min_pain - new_limb.pain = max(new_limb.pain, new_limb.min_pain) - - // then make the bodypart feel more hurt than it actually is - // so they can get more severe feedback effects without being in a dangerous threshold - for(var/i in 1 to get_withdrawal_stage(affected_carbon)) - new_limb.bodypart_pain_modifier *= pain_multipliers[i] - - new_limb.on_gain_pain_effects(min_pain, STAMINA) - -/datum/addiction/opioids/proc/unmodify_bodypart(mob/living/carbon/affected_carbon, obj/item/bodypart/removed_limb) - SIGNAL_HANDLER - - if(!(REF(removed_limb) in modified_bodyparts)) - return // wasn't modified, nothing to do - - LAZYREMOVE(modified_bodyparts, REF(removed_limb)) - removed_limb.min_pain -= min_pain - - for(var/i in 1 to get_withdrawal_stage(affected_carbon)) - removed_limb.bodypart_pain_modifier /= pain_multipliers[i] diff --git a/maplestation_modules/code/datums/pain/pain_implements.dm b/maplestation_modules/code/datums/pain/pain_implements.dm index 7ced35481f86..fb8a562f521b 100644 --- a/maplestation_modules/code/datums/pain/pain_implements.dm +++ b/maplestation_modules/code/datums/pain/pain_implements.dm @@ -179,10 +179,15 @@ /obj/item/reagent_containers/syringe/paracetamol name = "syringe (paracetamol)" - desc = "Contains fiteen units of Paracetamol. Used to treat general pain. Metabolizes slowly." + desc = "Contains fifteen units of Paracetamol. Used to treat general pain. Metabolizes slowly." list_reagents = list(/datum/reagent/medicine/painkiller/paracetamol = 15) -/obj/item/reagent_containers/pill/morphine/diluted +/obj/item/reagent_containers/syringe/fentanyl + name = "syringe (fentanyl)" + desc = "Contains three injections of Fentanyl diluted with Water. Used to treat extreme pain. Rapid acting, extremely addictive." + list_reagents = list(/datum/reagent/medicine/painkiller/fentanyl = 9, /datum/reagent/water = 6) + +/obj/item/reagent_containers/pill/morphine/smaller desc = "Used to treat major to severe pain. Causes moderate drowsiness. Mildly addictive." icon_state = "pill11" list_reagents = list(/datum/reagent/medicine/painkiller/morphine = 5) // Lasts ~1 minute, heals ~10 pain per bodypart (~100 pain) // NON-MODULE CHANGE @@ -202,8 +207,8 @@ /obj/item/reagent_containers/syringe/oxycodone name = "syringe (oxycodone)" - desc = "Contains three injections of Oxycodone. Used to treat severe to extreme pain. Rapid acting, may cause delirium. Very addictive." - list_reagents = list(/datum/reagent/medicine/painkiller/oxycodone = 15) + desc = "Contains three injections of Oxycodone diluted with Water. Used to treat severe to extreme pain. Rapid acting, may cause delirium, very addictive." + list_reagents = list(/datum/reagent/medicine/painkiller/oxycodone = 12, /datum/reagent/water = 3) /obj/item/reagent_containers/pill/aspirin_para_coffee name = "aspirin/paracetamol/caffeine pill" @@ -257,7 +262,7 @@ for(var/i in 1 to 3) new /obj/item/reagent_containers/pill/paracetamol(src) for(var/i in 1 to 3) - new /obj/item/reagent_containers/pill/morphine/diluted(src) + new /obj/item/reagent_containers/pill/morphine/smaller(src) for(var/i in 1 to 2) new /obj/item/reagent_containers/pill/oxycodone(src) diff --git a/maplestation_modules/code/datums/pain/pain_reagents/painkillers.dm b/maplestation_modules/code/datums/pain/pain_reagents/painkillers.dm index 5eb7810e5b1b..6ae0f4040415 100644 --- a/maplestation_modules/code/datums/pain/pain_reagents/painkillers.dm +++ b/maplestation_modules/code/datums/pain/pain_reagents/painkillers.dm @@ -18,11 +18,13 @@ // However, drinking with painkillers is toxic. var/highest_boozepwr = 0 for(var/datum/reagent/consumable/ethanol/alcohol in M.reagents.reagent_list) - if(alcohol.boozepwr > highest_boozepwr) - highest_boozepwr = alcohol.boozepwr + highest_boozepwr = max(highest_boozepwr, alcohol.boozepwr) if(highest_boozepwr > 0) - M.apply_damage(round(highest_boozepwr / 33, 0.5) * REM * seconds_per_tick, TOX) + if(SPT_PROB(highest_boozepwr / 25, seconds_per_tick)) + to_chat(M, span_warning("You feel sick.")) + M.losebreath += 1 + M.apply_damage(round(highest_boozepwr / 40, 0.5) * REM * seconds_per_tick, TOX) . = TRUE // Morphine is the well known existing painkiller. @@ -45,9 +47,11 @@ /datum/reagent/medicine/painkiller/morphine/on_mob_metabolize(mob/living/L) . = ..() L.add_movespeed_mod_immunities(type, /datum/movespeed_modifier/damage_slowdown) + ADD_TRAIT(L, TRAIT_HEART_RATE_SLOW, type) /datum/reagent/medicine/painkiller/morphine/on_mob_end_metabolize(mob/living/L) L.remove_movespeed_mod_immunities(type, /datum/movespeed_modifier/damage_slowdown) + REMOVE_TRAIT(L, TRAIT_HEART_RATE_SLOW, type) return ..() /datum/reagent/medicine/painkiller/morphine/on_mob_life(mob/living/carbon/M, seconds_per_tick, times_fired) @@ -266,7 +270,7 @@ // Oxycodone. Very addictive, heals pain very fast, also a drug. /datum/reagent/medicine/painkiller/oxycodone name = "Oxycodone" - description = "A drug that rapidly treats major to extreme pain. Highly addictive. Overdose can cause heart attacks." + description = "A painkiller that rapidly treats major to extreme pain. Highly addictive. Overdose can cause heart attacks." reagent_state = LIQUID color = "#ffcb86" metabolization_rate = 0.5 * REAGENTS_METABOLISM @@ -286,9 +290,19 @@ if(SPT_PROB(33, seconds_per_tick)) M.adjust_dizzy_up_to(2 SECONDS * REM * seconds_per_tick, 10 SECONDS) + if(current_cycle > 5) + ADD_TRAIT(M, TRAIT_HEART_RATE_SLOW, "[type]_low") + if(current_cycle > 20) + ADD_TRAIT(M, TRAIT_HEART_RATE_SLOW, "[type]_med") + ..() return TRUE +/datum/reagent/medicine/painkiller/oxycodone/on_mob_end_metabolize(mob/living/affected_mob) + . = ..() + REMOVE_TRAIT(affected_mob, TRAIT_HEART_RATE_SLOW, "[type]_low") + REMOVE_TRAIT(affected_mob, TRAIT_HEART_RATE_SLOW, "[type]_med") + /datum/reagent/medicine/painkiller/oxycodone/overdose_process(mob/living/carbon/M, seconds_per_tick, times_fired) . = ..() if(!ishuman(M)) diff --git a/maplestation_modules/code/datums/pain/pain_status_effects/low_blood_pressure.dm b/maplestation_modules/code/datums/pain/pain_status_effects/low_blood_pressure.dm deleted file mode 100644 index 99bcdd0827b7..000000000000 --- a/maplestation_modules/code/datums/pain/pain_status_effects/low_blood_pressure.dm +++ /dev/null @@ -1,38 +0,0 @@ -/datum/status_effect/low_blood_pressure - id = "low_blood_pressure" - tick_interval = 2 SECONDS - status_type = STATUS_EFFECT_UNIQUE - alert_type = null - -/datum/status_effect/low_blood_pressure/on_apply() - if(ishuman(owner)) - var/mob/living/carbon/human/human_owner = owner - human_owner.physiology.bleed_mod *= 0.75 - RegisterSignal(owner, COMSIG_LIVING_HEALTHSCAN, PROC_REF(on_healthscan)) - return TRUE - -/datum/status_effect/low_blood_pressure/on_remove() - if(ishuman(owner)) - var/mob/living/carbon/human/human_owner = owner - human_owner.physiology.bleed_mod /= 0.75 - UnregisterSignal(owner, COMSIG_LIVING_HEALTHSCAN) - -/datum/status_effect/low_blood_pressure/tick(seconds_between_ticks) - if(owner.stat == DEAD) - return - if(!HAS_TRAIT(owner, TRAIT_STASIS)) - if(SPT_PROB(20, seconds_between_ticks)) - owner.adjust_jitter_up_to(5 SECONDS, 60 SECONDS) - if(SPT_PROB(10, seconds_between_ticks)) - owner.adjust_dizzy_up_to(5 SECONDS, 60 SECONDS) - if(SPT_PROB(0.1, seconds_between_ticks)) - owner.emote("faint") - if(SPT_PROB(10, seconds_between_ticks)) - owner.adjust_eye_blur_up_to(5 SECONDS, 60 SECONDS) - -/datum/status_effect/low_blood_pressure/proc/on_healthscan(datum/source, list/render_list, advanced, mob/user, mode, tochat) - SIGNAL_HANDLER - - if(owner.has_status_effect(/datum/status_effect/high_blood_pressure)) - return - render_list += "Hypotension detected.
" diff --git a/maplestation_modules/code/datums/quirks/negative.dm b/maplestation_modules/code/datums/quirks/negative.dm index 3b91f1f024df..c0569ea4b6d3 100644 --- a/maplestation_modules/code/datums/quirks/negative.dm +++ b/maplestation_modules/code/datums/quirks/negative.dm @@ -131,13 +131,13 @@ give_cane(/obj/item/cane/crutch/wood, side) mail_goodies |= /obj/item/cane/crutch/wood if(NO_CANE) - mail_goodies |= /obj/item/reagent_containers/pill/morphine/diluted + mail_goodies |= /obj/item/reagent_containers/pill/morphine/smaller switch(intensity) if(INTENSITY_MEDIUM) mail_goodies |= /obj/item/reagent_containers/pill/paracetamol if(INTENSITY_HIGH) - mail_goodies |= /obj/item/reagent_containers/pill/morphine/diluted + mail_goodies |= /obj/item/reagent_containers/pill/morphine/smaller quirk_holder.apply_status_effect(/datum/status_effect/limp/permanent, side, intensity) diff --git a/maplestation_modules/code/game/objects/structures/vital_reader.dm b/maplestation_modules/code/game/objects/structures/vital_reader.dm index df90d24d8547..bd29de46dd32 100644 --- a/maplestation_modules/code/game/objects/structures/vital_reader.dm +++ b/maplestation_modules/code/game/objects/structures/vital_reader.dm @@ -282,11 +282,11 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/computer/vitals_reader/no_hand, 32) resp_icon_state = "resp_flat" else if(ishuman(patient)) var/mob/living/carbon/human/human_patient = patient - switch(human_patient.get_heart_rate()) + switch(human_patient.get_bpm()) if(0) ekg_icon_state = "ekg_flat" resp_icon_state = "resp_flat" - if(11 to INFINITY) + if(FAST_HEARTBEAT_THRESHOLD to INFINITY) ekg_icon_state = "ekg_fast" var/hp_color = percent_to_color((patient.maxHealth - patient.health) / patient.maxHealth) diff --git a/maplestation_modules/code/modules/mob/living/carbon/human/heart_rework/cpr.dm b/maplestation_modules/code/modules/mob/living/carbon/human/heart_rework/cpr.dm index 6d744297e45e..66179c35b77b 100644 --- a/maplestation_modules/code/modules/mob/living/carbon/human/heart_rework/cpr.dm +++ b/maplestation_modules/code/modules/mob/living/carbon/human/heart_rework/cpr.dm @@ -105,7 +105,7 @@ target.share_blood_on_touch(src, ITEM_SLOT_NECK) if(!do_after(src, 6 SECONDS, target)) return - var/own_bpm_penalty = get_heart_rate() > 11 ? 1.2 : 1 + var/own_bpm_penalty = get_bpm() > FAST_HEARTBEAT_THRESHOLD ? 1.2 : 1 var/bpm_a = round(target.get_bpm() * own_bpm_penalty, 1) if(bpm_a <= 0) to_chat(src, span_warning("[target.name] has no pulse!")) diff --git a/maplestation_modules/code/modules/mob/living/carbon/human/heart_rework/heart_overrides.dm b/maplestation_modules/code/modules/mob/living/carbon/human/heart_rework/heart_overrides.dm deleted file mode 100644 index ce4a5817f9fb..000000000000 --- a/maplestation_modules/code/modules/mob/living/carbon/human/heart_rework/heart_overrides.dm +++ /dev/null @@ -1,13 +0,0 @@ -/obj/item/organ/heart/on_mob_insert(mob/living/carbon/organ_owner, special, movement_flags) - . = ..() - if(beating) - organ_owner.remove_status_effect(/datum/status_effect/heart_attack) - -/obj/item/organ/heart/on_mob_remove(mob/living/carbon/organ_owner, special) - . = ..() - if(!special) - organ_owner.apply_status_effect(/datum/status_effect/heart_attack) - -// Glands can't stop beating but they are cringe -/obj/item/organ/heart/gland/Stop() - return FALSE diff --git a/tgui/packages/tgui/interfaces/Cryo.tsx b/tgui/packages/tgui/interfaces/Cryo.tsx index 7c42f416d3fb..639ad45721bd 100644 --- a/tgui/packages/tgui/interfaces/Cryo.tsx +++ b/tgui/packages/tgui/interfaces/Cryo.tsx @@ -18,7 +18,7 @@ const damageTypes = [ type: 'bruteLoss', }, { - label: 'Respiratory', + label: 'Hypoxia', type: 'oxyLoss', }, { diff --git a/tgui/packages/tgui/interfaces/OperatingComputer/types.ts b/tgui/packages/tgui/interfaces/OperatingComputer/types.ts index a44f887a4eb4..efbec658632c 100644 --- a/tgui/packages/tgui/interfaces/OperatingComputer/types.ts +++ b/tgui/packages/tgui/interfaces/OperatingComputer/types.ts @@ -75,7 +75,7 @@ export const damageTypes: damageType[] = [ type: 'toxLoss', }, { - label: 'Respiratory', + label: 'Hypoxia', type: 'oxyLoss', }, ];