From dbbd66582a461bf013028e932d903917d78ded74 Mon Sep 17 00:00:00 2001 From: MrMelbert <51863163+MrMelbert@users.noreply.github.com> Date: Sun, 28 Apr 2024 17:47:52 -0500 Subject: [PATCH] Numbness stops pain effects (#477) --- code/__DEFINES/living.dm | 6 +- maplestation_modules/code/datums/pain/pain.dm | 214 ++++++++++-------- .../code/datums/pain/pain_bodyparts.dm | 26 +-- .../datums/pain/pain_causes/surgery_pain.dm | 16 +- .../code/datums/pain/pain_helpers.dm | 14 +- .../code/datums/pain/pain_modifiers.dm | 13 ++ .../pain/pain_status_effects/pain_limp.dm | 10 +- .../code/datums/quirks/negative.dm | 13 +- .../withdrawal/luciferium_addiction.dm | 12 +- 9 files changed, 196 insertions(+), 128 deletions(-) diff --git a/code/__DEFINES/living.dm b/code/__DEFINES/living.dm index 7fd839bacfb5..ef46ae475aa0 100644 --- a/code/__DEFINES/living.dm +++ b/code/__DEFINES/living.dm @@ -58,8 +58,12 @@ /// If the mob enters shock, they will have +1 cure condition (helps cure it faster) #define TRAIT_ABATES_SHOCK "shock_abated" +/// Pain effects, such as stuttering or feedback messages ("Everything hurts") are disabled. +#define TRAIT_NO_PAIN_EFFECTS "no_pain_effects" +/// Shock buildup does not increase, only decrease. No effect if already in shock (unlike abates_shock) +#define TRAIT_NO_SHOCK_BUILDUP "no_shock_buildup" /// The trait that determines if someone has the robotic limb reattachment quirk. -#define TRAIT_ROBOTIC_LIMBATTACHMENT "trait_robotic_limbattachment" // Sticking this here because Melbert told me to +#define TRAIT_ROBOTIC_LIMBATTACHMENT "trait_robotic_limbattachment" #define COLOR_BLOOD "#c90000" diff --git a/maplestation_modules/code/datums/pain/pain.dm b/maplestation_modules/code/datums/pain/pain.dm index 5b87aba1b188..30689dcdcafd 100644 --- a/maplestation_modules/code/datums/pain/pain.dm +++ b/maplestation_modules/code/datums/pain/pain.dm @@ -85,6 +85,7 @@ RegisterSignal(parent, COMSIG_MOB_APPLY_DAMAGE, PROC_REF(add_damage_pain)) RegisterSignal(parent, COMSIG_MOB_STATCHANGE, PROC_REF(on_parent_statchance)) RegisterSignals(parent, list(COMSIG_LIVING_SET_BODY_POSITION, COMSIG_LIVING_SET_BUCKLED), PROC_REF(check_lying_pain_modifier)) + RegisterSignals(parent, list(SIGNAL_ADDTRAIT(TRAIT_NO_PAIN_EFFECTS), SIGNAL_REMOVETRAIT(TRAIT_NO_PAIN_EFFECTS)), PROC_REF(refresh_pain_attributes)) if(ishuman(parent)) RegisterSignal(parent, COMSIG_HUMAN_BURNING, PROC_REF(on_burn_tick)) @@ -105,6 +106,8 @@ COMSIG_LIVING_SET_BUCKLED, COMSIG_MOB_APPLY_DAMAGE, COMSIG_MOB_STATCHANGE, + SIGNAL_ADDTRAIT(TRAIT_NO_PAIN_EFFECTS), + SIGNAL_REMOVETRAIT(TRAIT_NO_PAIN_EFFECTS), )) /** @@ -186,7 +189,7 @@ return FALSE LAZYSET(pain_mods, key, amount) - apply_pain_attributes() + refresh_pain_attributes() return update_pain_modifier() /** @@ -314,7 +317,7 @@ */ /datum/pain/proc/on_pain_gain(obj/item/bodypart/affected_part, amount, type) affected_part.on_gain_pain_effects(amount) - apply_pain_attributes() + refresh_pain_attributes() SEND_SIGNAL(parent, COMSIG_CARBON_PAIN_GAINED, affected_part, amount, type) COOLDOWN_START(src, time_since_last_pain_loss, 60 SECONDS) @@ -333,7 +336,7 @@ */ /datum/pain/proc/on_pain_loss(obj/item/bodypart/affected_part, amount, type) affected_part.on_lose_pain_effects(amount) - apply_pain_attributes() + refresh_pain_attributes() SEND_SIGNAL(parent, COMSIG_CARBON_PAIN_LOST, affected_part, amount, type) /** @@ -528,19 +531,14 @@ /datum/pain/process(seconds_per_tick) var/has_pain = FALSE - var/determined = FALSE + var/just_cant_feel_anything = !parent.can_feel_pain() var/no_recent_pain = COOLDOWN_FINISHED(src, time_since_last_pain_loss) for(var/part in shuffle(body_zones)) var/obj/item/bodypart/checked_bodypart = body_zones[part] if(checked_bodypart.pain <= 0) continue - checked_bodypart.processed_pain_effects(seconds_per_tick) - if(!has_pain) - has_pain = TRUE - // Only doing this once, when we register if we have pain, to save processing. - determined = !!parent.has_status_effect(/datum/status_effect/determined) - - if(determined || pain_modifier < 0.5 || !COOLDOWN_FINISHED(src, time_since_last_pain_message)) + has_pain = TRUE + if(just_cant_feel_anything || !COOLDOWN_FINISHED(src, time_since_last_pain_message)) continue // 1% chance per 8 pain being experienced to get a feedback message every second if(!SPT_PROB(checked_bodypart.get_modified_pain() / 8, seconds_per_tick)) @@ -549,51 +547,54 @@ COOLDOWN_START(src, time_since_last_pain_message, 4 SECONDS) if(!has_pain) - // no-op if none of our bodyparts is in pain + // no-op if none of our bodyparts are in pain return - if(!determined) - var/curr_pain = get_average_pain() - switch(curr_pain) - if(-INFINITY to 10) - shock_buildup = max(shock_buildup - 3, -30) // staying out of pain for a while gives you a small resiliency to shock (~1 minute) + var/curr_pain = get_average_pain() + switch(curr_pain) + if(-INFINITY to 10) + shock_buildup = max(shock_buildup - 3, -30) // staying out of pain for a while gives you a small resiliency to shock (~1 minute) - if(10 to 25) - shock_buildup = max(shock_buildup - 1, -30) + if(10 to 25) + shock_buildup = max(shock_buildup - 1, -30) - if(25 to 40) - if(SPT_PROB(2, seconds_per_tick)) - to_chat(parent, span_danger(pick("Everything aches.", "Everything feels sore."))) + if(25 to 40) + if(SPT_PROB(2, seconds_per_tick)) + do_pain_message(span_danger(pick("Everything aches.", "Everything feels sore."))) - if(40 to 70) + if(40 to 70) + if(!HAS_TRAIT(parent, TRAIT_NO_SHOCK_BUILDUP)) shock_buildup += 1 - if(SPT_PROB(2, seconds_per_tick)) - to_chat(parent, span_bolddanger(pick("Everything hurts.", "Everything feels very sore.", "It hurts."))) + if(SPT_PROB(2, seconds_per_tick)) + do_pain_message(span_bolddanger(pick("Everything hurts.", "Everything feels very sore.", "It hurts."))) - if(70 to INFINITY) + if(70 to INFINITY) + if(!HAS_TRAIT(parent, TRAIT_NO_SHOCK_BUILDUP)) shock_buildup += 3 - if(SPT_PROB(2, seconds_per_tick)) - to_chat(parent, span_userdanger(pick("Stop the pain!", "Everything hurts!"))) - - // If shock buildup exceeds our health + 30 ticks then well, we enter shock - // This means at 100 health you can be in moderate pain for 130 ticks / 260 seconds / ~4 minutes before falling into shock - if(shock_buildup >= (parent.health + 30) \ - && curr_pain >= 50 \ - && !is_undergoing_shock() \ - && !parent.undergoing_cardiac_arrest() \ - ) - parent.ForceContractDisease(new /datum/disease/shock(), FALSE, TRUE) - to_chat(parent, span_userdanger("You feel your body start to shut down!")) - if(parent.stat == CONSCIOUS && !parent.incapacitated(IGNORE_RESTRAINTS|IGNORE_GRAB)) - parent.visible_message(span_danger("[parent] grabs at their chest and stares into the distance as they go into shock!"), ignored_mobs = parent) - shock_buildup = -200 // requires another 200 ticks / 400 seconds / ~6 minutes of pain to go into shock again - return + if(SPT_PROB(2, seconds_per_tick)) + do_pain_message(span_userdanger(pick("Stop the pain!", "Everything hurts!"))) + + // If shock buildup exceeds our health + 30 ticks then well, we enter shock + // This means at 100 health you can be in moderate pain for 130 ticks / 260 seconds / ~4 minutes before falling into shock + if(shock_buildup >= (parent.health + 30) \ + && curr_pain >= 50 \ + && !HAS_TRAIT(parent, TRAIT_NO_SHOCK_BUILDUP) \ + && !is_undergoing_shock() \ + && !parent.undergoing_cardiac_arrest() \ + ) + parent.ForceContractDisease(new /datum/disease/shock(), FALSE, TRUE) + to_chat(parent, span_userdanger("You feel your body start to shut down!")) + if(parent.stat == CONSCIOUS && !parent.incapacitated(IGNORE_RESTRAINTS|IGNORE_GRAB) && !HAS_TRAIT(parent, TRAIT_NO_PAIN_EFFECTS)) + parent.visible_message(span_danger("[parent] grabs at their chest and stares into the distance as they go into shock!"), ignored_mobs = parent) + shock_buildup = -200 // requires another 200 ticks / 400 seconds / ~6 minutes of pain to go into shock again + return - var/standard_effect_prob = (curr_pain * 0.05) - 0.75 // starts at 15, caps at 4.5 - var/rare_effect_prob = (curr_pain * 0.04) - 1.5 // starts at 40 - var/very_rare_effect_prob = (curr_pain * 0.03) - 2.25 // starts at 70 + var/standard_effect_prob = (curr_pain * 0.05) - 0.75 // starts at 15, caps at 4.5 + var/rare_effect_prob = (curr_pain * 0.04) - 1.5 // starts at 40 + var/very_rare_effect_prob = (curr_pain * 0.03) - 2.25 // starts at 70 - if(standard_effect_prob > 0) + if(standard_effect_prob > 0) + if(!just_cant_feel_anything) if(SPT_PROB(standard_effect_prob, seconds_per_tick)) parent.adjust_stutter_up_to(10 SECONDS * pain_modifier, 30 SECONDS) if(SPT_PROB(standard_effect_prob, seconds_per_tick)) @@ -602,44 +603,45 @@ parent.adjust_dizzy_up_to(10 SECONDS * pain_modifier, 30 SECONDS) if(curr_pain >= 70) parent.adjust_confusion_up_to(8 SECONDS * pain_modifier, 24 SECONDS) - if(SPT_PROB(standard_effect_prob * 1.2, seconds_per_tick) && parent.getStaminaLoss() <= 80) - var/stam_taken = round((0.2 * curr_pain + 8) * pain_modifier) // 10 = 10, 100 = 28, good enough - // First we apply damage, if that succeeds -> - // Check how much damage, if above a threshold -> - // Run a pain emote, if the pain emote succeeds as well -> - if(parent.apply_damage(stam_taken, STAMINA) && stam_taken >= 15 && do_pain_emote(pick("wince", "gasp"))) - parent.visible_message(span_warning("[parent] doubles over in pain!")) - - if(rare_effect_prob > 0) - if(SPT_PROB(rare_effect_prob * 2, seconds_per_tick)) - var/list/options = list("wince", "whimper") - if(curr_pain >= 70) - options.Add("cry", "scream") - do_pain_emote(pick(options), 5 SECONDS) - - if(SPT_PROB(rare_effect_prob, seconds_per_tick) && parent.body_position != LYING_DOWN) - parent.Knockdown(2 SECONDS * pain_modifier) - parent.visible_message(span_warning("[parent] collapses from pain!")) - - if(SPT_PROB(rare_effect_prob, seconds_per_tick)) - var/obj/item/held_item = parent.get_active_held_item() - var/obj/item/bodypart/active_hand = parent.get_active_hand() - if(held_item && active_hand && parent.dropItemToGround(held_item)) - if(active_hand.bodytype & BODYTYPE_ROBOTIC) - to_chat(parent, span_danger("Your hand malfunctions, causing you to drop [held_item]!")) - parent.visible_message(span_warning("[parent]'s hand malfunctions, causing them to drop [held_item]!"), ignored_mobs = parent) - do_sparks(number = 1, source = parent) - - else - to_chat(parent, span_danger("Your fumble though the pain and drop [held_item]!")) - parent.visible_message(span_warning("[parent] fumbles around and drops [held_item]!"), ignored_mobs = parent) - do_pain_emote("gasp") - - if(very_rare_effect_prob > 0) - if(SPT_PROB(very_rare_effect_prob, seconds_per_tick)) - parent.vomit(50) - if(SPT_PROB(very_rare_effect_prob, seconds_per_tick)) - parent.adjust_confusion_up_to(8 SECONDS, 24 SECONDS) + if(SPT_PROB(standard_effect_prob * 1.2, seconds_per_tick) && parent.getStaminaLoss() <= 80) + var/stam_taken = round((0.2 * curr_pain + 8) * pain_modifier) // 10 = 10, 100 = 28, good enough + if(just_cant_feel_anything) + parent.apply_damage(stam_taken * 1.2, STAMINA) + // First we apply damage, if that succeeds -> + // Check how much damage, if above a threshold -> + // Run a pain emote, if the pain emote succeeds as well -> + else if(parent.apply_damage(stam_taken, STAMINA) && stam_taken >= 15 && do_pain_emote(pick("wince", "gasp"))) + parent.visible_message(span_warning("[parent] doubles over in pain!")) + + if(rare_effect_prob > 0) + if(SPT_PROB(rare_effect_prob * 2, seconds_per_tick)) + var/list/options = list("wince", "whimper") + if(curr_pain >= 70) + options.Add("cry", "scream") + do_pain_emote(pick(options), 5 SECONDS) + if(SPT_PROB(rare_effect_prob, seconds_per_tick) && parent.body_position != LYING_DOWN && !just_cant_feel_anything) + parent.Knockdown(2 SECONDS * pain_modifier) + parent.visible_message(span_warning("[parent] collapses from pain!")) + if(SPT_PROB(rare_effect_prob, seconds_per_tick)) + var/obj/item/held_item = parent.get_active_held_item() + var/obj/item/bodypart/active_hand = parent.get_active_hand() + if(held_item && active_hand && parent.dropItemToGround(held_item)) + if(active_hand.bodytype & BODYTYPE_ROBOTIC) + to_chat(parent, span_danger("Your hand malfunctions, causing you to drop [held_item]!")) + parent.visible_message(span_warning("[parent]'s hand malfunctions, causing them to drop [held_item]!"), ignored_mobs = parent) + do_sparks(number = 1, source = parent) + else if(just_cant_feel_anything) + to_chat(parent, span_danger("Your hand spams and you drop [held_item]!")) + else + to_chat(parent, span_danger("Your fumble though the pain and drop [held_item]!")) + parent.visible_message(span_warning("[parent] fumbles around and drops [held_item]!"), ignored_mobs = parent) + do_pain_emote("gasp") + + if(very_rare_effect_prob > 0) + if(SPT_PROB(very_rare_effect_prob, seconds_per_tick)) + parent.vomit(50) + if(SPT_PROB(very_rare_effect_prob, seconds_per_tick) && !just_cant_feel_anything) + parent.adjust_confusion_up_to(8 SECONDS, 24 SECONDS) // Finally, handle pain decay over time if(HAS_TRAIT(parent, TRAIT_STASIS) || parent.on_fire || parent.stat == DEAD) @@ -698,8 +700,10 @@ /** * Apply or remove pain various modifiers from pain (mood, action speed, movement speed) based on the [average_pain]. */ -/datum/pain/proc/apply_pain_attributes() - if(pain_modifier <= 0.5) +/datum/pain/proc/refresh_pain_attributes(...) + SIGNAL_HANDLER + + if(!parent.can_feel_pain()) clear_pain_attributes() return @@ -744,13 +748,12 @@ * * returns TRUE if successful. */ -/datum/pain/proc/do_pain_emote(emote, cooldown = 3 SECONDS) - if(!emote) - emote = pick(PAIN_EMOTES) - - if(!COOLDOWN_FINISHED(src, time_since_last_pain_message)) +/datum/pain/proc/do_pain_emote(emote = pick(PAIN_EMOTES), cooldown = 3 SECONDS) + ASSERT(istext(emote)) + if(!parent.can_feel_pain()) + return FALSE + if(cooldown && !COOLDOWN_FINISHED(src, time_since_last_pain_message)) return FALSE - if(parent.stat >= UNCONSCIOUS || parent.incapacitated(IGNORE_RESTRAINTS|IGNORE_GRAB)) return FALSE @@ -758,12 +761,35 @@ COOLDOWN_START(src, time_since_last_pain_message, cooldown) return TRUE +/** + * Run a pain related message, if a few checks are successful. + * + * message - string, what message we're sending + * painless_message - optional string, what message we're sending if the mob doesn't "feel" pain + * cooldown - what cooldown to set our message cooldown to + * + * returns TRUE if successful. + * Returns FALSE if we failed to send a message, even if painless_message was provided and sent. + */ +/datum/pain/proc/do_pain_message(message, painless_message, cooldown = 0 SECONDS) + ASSERT(istext(message)) + if(!parent.can_feel_pain()) + if(painless_message) + to_chat(parent, painless_message) + return FALSE + if(parent.stat >= UNCONSCIOUS) + return FALSE + if(cooldown && !COOLDOWN_FINISHED(src, time_since_last_pain_message)) + return FALSE + + to_chat(parent, message) + COOLDOWN_START(src, time_since_last_pain_message, cooldown) + return TRUE + /** * Get the average pain of all bodyparts as a percent of the total pain. */ /datum/pain/proc/get_average_pain() - . = 0 // Runtime protection (wink) - var/max_total_pain = 0 var/total_pain = 0 for(var/zone in body_zones) diff --git a/maplestation_modules/code/datums/pain/pain_bodyparts.dm b/maplestation_modules/code/datums/pain/pain_bodyparts.dm index 1f06305dc110..51f8c85735c9 100644 --- a/maplestation_modules/code/datums/pain/pain_bodyparts.dm +++ b/maplestation_modules/code/datums/pain/pain_bodyparts.dm @@ -53,7 +53,10 @@ return FALSE if(get_modified_pain() >= 65 && can_be_disabled && !HAS_TRAIT_FROM(src, TRAIT_PARALYSIS, PAIN_LIMB_PARALYSIS)) - to_chat(owner, span_userdanger("Your [plaintext_zone] goes numb from the pain!")) + owner.pain_message( + span_userdanger("Your [plaintext_zone] goes numb from the pain!"), + span_danger("You can't move your [plaintext_zone]!") + ) ADD_TRAIT(src, TRAIT_PARALYSIS, PAIN_LIMB_PARALYSIS) update_disabled() @@ -69,18 +72,15 @@ return FALSE if(get_modified_pain() < 65 && HAS_TRAIT_FROM(src, TRAIT_PARALYSIS, PAIN_LIMB_PARALYSIS)) - to_chat(owner, span_green("You can feel your [plaintext_zone] again!")) + owner.pain_message( + span_green("You can feel your [plaintext_zone] again!"), + span_green("You can move your [plaintext_zone] again!") + ) REMOVE_TRAIT(src, TRAIT_PARALYSIS, PAIN_LIMB_PARALYSIS) update_disabled() return TRUE -/** - * Effects on this bodypart when pain is processed (every 2 seconds) - */ -/obj/item/bodypart/proc/processed_pain_effects(seconds_per_tick) - return - /** * Feedback messages from this limb when it is sustaining pain. * @@ -119,7 +119,7 @@ feedback_phrases += list("is numb from the pain") if(feedback_phrases.len) - to_chat(owner, span_danger("Your [plaintext_zone] [pick(feedback_phrases)][healing_pain ? ", [pick(healing_phrases)]." : "!"]")) + owner.pain_message(span_danger("Your [plaintext_zone] [pick(feedback_phrases)][healing_pain ? ", [pick(healing_phrases)]." : "!"]")) return TRUE // --- Chest --- @@ -171,9 +171,9 @@ side_feedback += list("You feel your ribs jostle in your [plaintext_zone]") if(side_feedback.len && last_received_pain_type == BRUTE && SPT_PROB(50, seconds_per_tick)) - to_chat(owner, span_danger("[pick(side_feedback)][healing_pain ? ", [pick(healing_phrases)]." : "!"]")) + owner.pain_message(span_danger("[pick(side_feedback)][healing_pain ? ", [pick(healing_phrases)]." : "!"]")) else if(feedback_phrases.len) - to_chat(owner, span_danger("Your [plaintext_zone] [pick(feedback_phrases)][healing_pain ? ", [pick(healing_phrases)]." : "!"]")) + owner.pain_message(span_danger("Your [plaintext_zone] [pick(feedback_phrases)][healing_pain ? ", [pick(healing_phrases)]." : "!"]")) return TRUE @@ -230,9 +230,9 @@ side_feedback += list("You feel a splitting migrane", "Pressure floods your [plaintext_zone]", "Your [plaintext_zone] feels as if it's being squeezed", "Your eyes hurt to keep open") if(side_feedback.len && last_received_pain_type == BRUTE && SPT_PROB(50, seconds_per_tick)) - to_chat(owner, span_danger("[pick(side_feedback)][healing_pain ? ", [pick(healing_phrases)]." : "!"]")) + owner.pain_message(span_danger("[pick(side_feedback)][healing_pain ? ", [pick(healing_phrases)]." : "!"]")) else if(feedback_phrases.len) - to_chat(owner, span_danger("Your [plaintext_zone] [pick(feedback_phrases)][healing_pain ? ", [pick(healing_phrases)]." : "!"]")) + owner.pain_message(span_danger("Your [plaintext_zone] [pick(feedback_phrases)][healing_pain ? ", [pick(healing_phrases)]." : "!"]")) return TRUE diff --git a/maplestation_modules/code/datums/pain/pain_causes/surgery_pain.dm b/maplestation_modules/code/datums/pain/pain_causes/surgery_pain.dm index 97443cdb71f8..db84c3b3ca92 100644 --- a/maplestation_modules/code/datums/pain/pain_causes/surgery_pain.dm +++ b/maplestation_modules/code/datums/pain/pain_causes/surgery_pain.dm @@ -18,26 +18,24 @@ // No pain from mechanics but still show the message (usually) if(mechanical_surgery) if(prob(70)) - to_chat(target, span_userdanger(pain_message)) + target.pain_message(span_userdanger(pain_message)) return FALSE target.cause_pain(target_zone, pain_amount, pain_type) - if(target.IsSleeping() || target.IsUnconscious() || target.stat >= UNCONSCIOUS) + if(target.IsSleeping() || target.stat >= UNCONSCIOUS) if(target.has_status_effect(/datum/status_effect/grouped/anesthetic)) target.add_mood_event("surgery", /datum/mood_event/anesthetic) return FALSE - - if(ispath(surgery_moodlet, /datum/mood_event)) + if(ispath(surgery_moodlet)) target.add_mood_event("surgery", surgery_moodlet) - if(pain_overlay_severity == 1 || pain_overlay_severity == 2) + if(isnum(pain_overlay_severity)) target.flash_pain_overlay(pain_overlay_severity) - // No message if the pain emote fails - if(!target.pain_controller.do_pain_emote()) + if(!target.pain_emote()) + return FALSE + if(!target.pain_message(span_userdanger(pain_message))) return FALSE - - to_chat(target, span_userdanger(pain_message)) return TRUE /datum/surgery_step/brainwash/sleeper_agent diff --git a/maplestation_modules/code/datums/pain/pain_helpers.dm b/maplestation_modules/code/datums/pain/pain_helpers.dm index 7c8d3b0cc857..4444c2f5b763 100644 --- a/maplestation_modules/code/datums/pain/pain_helpers.dm +++ b/maplestation_modules/code/datums/pain/pain_helpers.dm @@ -40,6 +40,16 @@ /mob/living/proc/pain_emote(emote, cooldown) return pain_controller?.do_pain_emote(emote, cooldown) +/** + * Runs a pain message on the pain message cooldown + * + * * message - the message to send + * * painless_message - optional, the message to send if the mob does not feel pain + * * cooldown - applies cooldown on doing similar pain messages + */ +/mob/living/proc/pain_message(message, painless_message, cooldown) + return pain_controller?.do_pain_message(message, painless_message, cooldown) + /** * Adjust the minimum pain the target zone can experience for a time * @@ -76,7 +86,7 @@ * By default mobs cannot feel pain if they have a pain modifier of 0.5 or less. */ /mob/living/proc/can_feel_pain() - return pain_controller?.pain_modifier > 0.5 + return pain_controller?.pain_modifier > 0.5 && !HAS_TRAIT(src, TRAIT_NO_PAIN_EFFECTS) /** * Adjusts the progress of pain shock on the current mob. @@ -87,6 +97,8 @@ /mob/living/proc/adjust_pain_shock(amount, down_to = -30) if(isnull(pain_controller)) return + if(amount > 0 && HAS_TRAIT(src, TRAIT_NO_SHOCK_BUILDUP)) + return ASSERT(isnum(amount)) pain_controller.shock_buildup = max(pain_controller.shock_buildup + amount, down_to) diff --git a/maplestation_modules/code/datums/pain/pain_modifiers.dm b/maplestation_modules/code/datums/pain/pain_modifiers.dm index e8df518f0a92..3d30c8f8348b 100644 --- a/maplestation_modules/code/datums/pain/pain_modifiers.dm +++ b/maplestation_modules/code/datums/pain/pain_modifiers.dm @@ -69,13 +69,26 @@ if(ishuman(owner)) var/mob/living/carbon/human/human_owner = owner human_owner.set_pain_mod(id, 0.625) + ADD_TRAIT(owner, TRAIT_NO_PAIN_EFFECTS, TRAIT_STATUS_EFFECT(id)) + ADD_TRAIT(owner, TRAIT_NO_SHOCK_BUILDUP, TRAIT_STATUS_EFFECT(id)) /datum/status_effect/determined/on_remove() if(ishuman(owner)) var/mob/living/carbon/human/human_owner = owner human_owner.unset_pain_mod(id) + REMOVE_TRAIT(owner, TRAIT_NO_PAIN_EFFECTS, TRAIT_STATUS_EFFECT(id)) + REMOVE_TRAIT(owner, TRAIT_NO_SHOCK_BUILDUP, TRAIT_STATUS_EFFECT(id)) return ..() +// Fake healthy is supposed to mimic feeling no pain +/datum/status_effect/grouped/screwy_hud/fake_healthy/on_apply() + . = ..() + ADD_TRAIT(owner, TRAIT_NO_PAIN_EFFECTS, TRAIT_STATUS_EFFECT(id)) + +/datum/status_effect/grouped/screwy_hud/fake_healthy/on_remove() + . = ..() + REMOVE_TRAIT(owner, TRAIT_NO_PAIN_EFFECTS, TRAIT_STATUS_EFFECT(id)) + // Being drunk gives a slight one, note the actual reagent gives one based on its strength /datum/status_effect/inebriated/drunk/on_apply() . = ..() diff --git a/maplestation_modules/code/datums/pain/pain_status_effects/pain_limp.dm b/maplestation_modules/code/datums/pain/pain_status_effects/pain_limp.dm index 42d7023fa1ad..dafe97accfeb 100644 --- a/maplestation_modules/code/datums/pain/pain_status_effects/pain_limp.dm +++ b/maplestation_modules/code/datums/pain/pain_status_effects/pain_limp.dm @@ -20,7 +20,10 @@ return FALSE RegisterSignals(owner, list(COMSIG_CARBON_PAIN_GAINED, COMSIG_CARBON_PAIN_LOST), PROC_REF(update_limp)) - to_chat(owner, span_danger("Your [next_leg?.plaintext_zone || "leg"] hurts to walk on!")) + owner.pain_message( + span_danger("Your [next_leg?.plaintext_zone || "leg"] hurts to walk on!"), + span_danger("You struggle to walk on your [next_leg?.plaintext_zone || "leg"]!"), + ) /datum/status_effect/limp/pain/get_examine_text() return span_warning("[owner.p_Theyre()] limping with every move.") @@ -29,7 +32,10 @@ . = ..() UnregisterSignal(owner, list(COMSIG_CARBON_PAIN_GAINED, COMSIG_CARBON_PAIN_LOST)) if(!QDELING(owner)) - to_chat(owner, span_green("Your pained limp stops!")) + owner.pain_message( + span_green("Your pained limp stops!"), + span_green("It becomes easier to walk again."), + ) /datum/status_effect/limp/pain/update_limp() var/mob/living/carbon/human/limping_human = owner diff --git a/maplestation_modules/code/datums/quirks/negative.dm b/maplestation_modules/code/datums/quirks/negative.dm index 48b482be91f2..5f72fddef690 100644 --- a/maplestation_modules/code/datums/quirks/negative.dm +++ b/maplestation_modules/code/datums/quirks/negative.dm @@ -8,7 +8,10 @@ value = -2 /datum/quirk/numb - desc = "You have difficulties feeling which part of your body is hurt." + value = -2 // This is a small buff but a large nerf so it's balanced at a relatively low cost + desc = "You don't feel pain as much as others. \ + It's harder to pinpoint which parts of your body are injured, and \ + you are immune to some effects of pain - possibly to your detriment." // Modular quirks // More vulnerabile to pain (increased pain modifier) @@ -17,8 +20,8 @@ desc = "You're less resistant to pain - Your pain naturally decreases slower and you receive more overall." icon = FA_ICON_USER_INJURED value = -6 - gain_text = "You feel sharper." - lose_text = "You feel duller." + gain_text = span_danger("You feel sharper.") + lose_text = span_notice("You feel duller.") medical_record_text = "Patient has Hyperalgesia, and is more susceptible to pain stimuli than most." mail_goodies = list(/obj/item/temperature_pack/cold) @@ -38,8 +41,8 @@ desc = "Your nerves are extremely sensitive - you may receive pain from things that wouldn't normally be painful, such as hugs." icon = FA_ICON_TIRED value = -10 - gain_text = "You feel fragile." - lose_text = "You feel less delicate." + gain_text = span_danger("You feel fragile.") + lose_text = span_notice("You feel less delicate.") medical_record_text = "Patient has Allodynia, and is extremely sensitive to touch, pain, and similar stimuli." mail_goodies = list(/obj/item/temperature_pack/cold, /obj/item/temperature_pack/heat) COOLDOWN_DECLARE(time_since_last_touch) diff --git a/maplestation_modules/code/modules/reagents/chemistry/withdrawal/luciferium_addiction.dm b/maplestation_modules/code/modules/reagents/chemistry/withdrawal/luciferium_addiction.dm index 3372ab2bbce1..d709723195fe 100644 --- a/maplestation_modules/code/modules/reagents/chemistry/withdrawal/luciferium_addiction.dm +++ b/maplestation_modules/code/modules/reagents/chemistry/withdrawal/luciferium_addiction.dm @@ -12,12 +12,18 @@ withdrawal_stage_messages = list( "I feel weak... I need some Luciferium.", "I'd punch someone if I don't get some Luciferium!", - "It hurts all over! I'd kill for Luciferium!" - ) + "It hurts all over! I'd kill for Luciferium!", + ) light_withdrawal_moodlet = /datum/mood_event/luciferium_light medium_withdrawal_moodlet = /datum/mood_event/luciferium_medium severe_withdrawal_moodlet = /datum/mood_event/luciferium_heavy +/datum/addiction/luciferium/process_addiction(mob/living/carbon/affected_carbon, seconds_per_tick, times_fired) + if(HAS_TRAIT(affected_carbon, TRAIT_STASIS)) + return + + return ..() + /datum/addiction/luciferium/withdrawal_enters_stage_1(mob/living/carbon/affected_carbon) . = ..() affected_carbon.set_pain_mod(PAIN_MOD_LUCIFERIUM_ADDICT, 1.2) @@ -69,4 +75,4 @@ if(current_addiction_cycle >= WITHDRAWAL_STAGE1_START_CYCLE) to_chat(affected_carbon, span_green("Your [name] withdrawal subsides... You have bought yourself time.")) affected_carbon.unset_pain_mod(PAIN_MOD_LUCIFERIUM_ADDICT) - . = ..() + return ..()