diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm
index 77de678bd1f..d2892ffae65 100644
--- a/code/__DEFINES/mobs.dm
+++ b/code/__DEFINES/mobs.dm
@@ -63,11 +63,6 @@
#define BODYPART_ORGANIC 1
#define BODYPART_ROBOTIC 2
-#define BODYPART_NOT_DISABLED 0
-#define BODYPART_DISABLED_DAMAGE 1
-#define BODYPART_DISABLED_PARALYSIS 2
-#define BODYPART_DISABLED_WOUND 3
-
#define DEFAULT_BODYPART_ICON_ORGANIC 'icons/mob/human_parts_greyscale.dmi'
#define DEFAULT_BODYPART_ICON_ROBOTIC 'icons/mob/augmentation/augments.dmi'
diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm
index 2b74bd98042..bd1f2311a86 100644
--- a/code/__DEFINES/traits.dm
+++ b/code/__DEFINES/traits.dm
@@ -203,7 +203,10 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define TRAIT_KNOW_ENGI_WIRES "know_engi_wires"
//non-mob traits
-#define TRAIT_PARALYSIS "paralysis" //Used for limb-based paralysis, where replacing the limb will fix it
+/// Used for limb-based paralysis, where replacing the limb will fix it.
+#define TRAIT_PARALYSIS "paralysis"
+/// Used for limbs.
+#define TRAIT_DISABLED_BY_WOUND "disabled-by-wound"
///Used for managing KEEP_TOGETHER in [appearance_flags]
#define TRAIT_KEEP_TOGETHER "keep-together"
diff --git a/code/_onclick/other_mobs.dm b/code/_onclick/other_mobs.dm
index e777f4a1778..4fe601ee690 100644
--- a/code/_onclick/other_mobs.dm
+++ b/code/_onclick/other_mobs.dm
@@ -7,8 +7,8 @@
/mob/living/carbon/human/UnarmedAttack(atom/A, proximity)
if(!has_active_hand()) //can't attack without a hand.
var/obj/item/bodypart/check_arm = get_active_hand()
- if(check_arm && check_arm.is_disabled() == BODYPART_DISABLED_WOUND)
- to_chat(src, "The damage in your [check_arm.name] is preventing you from using it! Get it fixed, or at least splinted!")
+ if(check_arm?.bodypart_disabled)
+ to_chat(src, "Your [check_arm.name] is in no condition to be used.")
return
to_chat(src, "You look at your arm and sigh.")
diff --git a/code/datums/brain_damage/severe.dm b/code/datums/brain_damage/severe.dm
index 77747f2c853..f40d46ba254 100644
--- a/code/datums/brain_damage/severe.dm
+++ b/code/datums/brain_damage/severe.dm
@@ -104,13 +104,13 @@
..()
for(var/X in paralysis_traits)
ADD_TRAIT(owner, X, "trauma_paralysis")
- owner.update_disabled_bodyparts()
+
/datum/brain_trauma/severe/paralysis/on_lose()
..()
for(var/X in paralysis_traits)
REMOVE_TRAIT(owner, X, "trauma_paralysis")
- owner.update_disabled_bodyparts()
+
/datum/brain_trauma/severe/paralysis/paraplegic
random_gain = FALSE
diff --git a/code/datums/wounds/_wounds.dm b/code/datums/wounds/_wounds.dm
index 833631aa3cb..032ab38152e 100644
--- a/code/datums/wounds/_wounds.dm
+++ b/code/datums/wounds/_wounds.dm
@@ -95,7 +95,7 @@
QDEL_NULL(attached_surgery)
if(limb?.wounds && (src in limb.wounds)) // destroy can call remove_wound() and remove_wound() calls qdel, so we check to make sure there's anything to remove first
remove_wound()
- limb = null
+ set_limb(null)
victim = null
return ..()
@@ -129,7 +129,7 @@
return
victim = L.owner
- limb = L
+ set_limb(L)
LAZYADD(victim.all_wounds, src)
LAZYADD(limb.wounds, src)
limb.update_wounds()
@@ -192,13 +192,45 @@
already_scarred = TRUE
remove_wound(replaced=TRUE)
new_wound.apply_wound(limb, old_wound = src, smited = smited)
+ . = new_wound
qdel(src)
- return new_wound
/// The immediate negative effects faced as a result of the wound
/datum/wound/proc/wound_injury(datum/wound/old_wound = null)
return
+
+/// Proc called to change the variable `limb` and react to the event.
+/datum/wound/proc/set_limb(new_value)
+ if(limb == new_value)
+ return FALSE //Limb can either be a reference to something or `null`. Returning the number variable makes it clear no change was made.
+ . = limb
+ limb = new_value
+ if(. && disabling)
+ var/obj/item/bodypart/old_limb = .
+ REMOVE_TRAIT(old_limb, TRAIT_PARALYSIS, src)
+ REMOVE_TRAIT(old_limb, TRAIT_DISABLED_BY_WOUND, src)
+ if(limb)
+ if(disabling)
+ ADD_TRAIT(limb, TRAIT_PARALYSIS, src)
+ ADD_TRAIT(limb, TRAIT_DISABLED_BY_WOUND, src)
+
+
+/// Proc called to change the variable `disabling` and react to the event.
+/datum/wound/proc/set_disabling(new_value)
+ if(disabling == new_value)
+ return
+ . = disabling
+ disabling = new_value
+ if(disabling)
+ if(!. && limb) //Gained disabling.
+ ADD_TRAIT(limb, TRAIT_PARALYSIS, src)
+ ADD_TRAIT(limb, TRAIT_DISABLED_BY_WOUND, src)
+ else if(. && limb) //Lost disabling.
+ REMOVE_TRAIT(limb, TRAIT_PARALYSIS, src)
+ REMOVE_TRAIT(limb, TRAIT_DISABLED_BY_WOUND, src)
+
+
/// Additional beneficial effects when the wound is gained, in case you want to give a temporary boost to allow the victim to try an escape or last stand
/datum/wound/proc/second_wind()
switch(severity)
diff --git a/code/datums/wounds/bones.dm b/code/datums/wounds/bones.dm
index 98354ff1ef2..8f91ae0eb70 100644
--- a/code/datums/wounds/bones.dm
+++ b/code/datums/wounds/bones.dm
@@ -177,7 +177,7 @@
interaction_efficiency_penalty = interaction_efficiency_penalty
if(initial(disabling))
- disabling = !limb.current_gauze
+ set_disabling(!limb.current_gauze)
limb.update_wounds()
diff --git a/code/datums/wounds/burns.dm b/code/datums/wounds/burns.dm
index a998f3645f5..afea9801279 100644
--- a/code/datums/wounds/burns.dm
+++ b/code/datums/wounds/burns.dm
@@ -82,19 +82,19 @@
if(WOUND_INFECTION_SEVERE to WOUND_INFECTION_CRITICAL)
if(!disabling && prob(2))
to_chat(victim, "Your [limb.name] completely locks up, as you struggle for control against the infection!")
- disabling = TRUE
+ set_disabling(TRUE)
else if(disabling && prob(8))
to_chat(victim, "You regain sensation in your [limb.name], but it's still in terrible shape!")
- disabling = FALSE
+ set_disabling(FALSE)
else if(prob(20))
victim.adjustToxLoss(0.5)
if(WOUND_INFECTION_CRITICAL to WOUND_INFECTION_SEPTIC)
if(!disabling && prob(3))
to_chat(victim, "You suddenly lose all sensation of the festering infection in your [limb.name]!")
- disabling = TRUE
+ set_disabling(TRUE)
else if(disabling && prob(3))
to_chat(victim, "You can barely feel your [limb.name] again, and you have to strain to retain motor control!")
- disabling = FALSE
+ set_disabling(FALSE)
else if(prob(1))
to_chat(victim, "You contemplate life without your [limb.name]...")
victim.adjustToxLoss(0.75)
diff --git a/code/datums/wounds/loss.dm b/code/datums/wounds/loss.dm
index 259d5b83980..4adb5cb2577 100644
--- a/code/datums/wounds/loss.dm
+++ b/code/datums/wounds/loss.dm
@@ -46,7 +46,7 @@
victim.visible_message(msg, "Your [dismembered_part.name] [occur_text]!")
- limb = dismembered_part
+ set_limb(dismembered_part)
severity = WOUND_SEVERITY_LOSS
second_wind()
log_wound(victim, src)
diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm
index 5318df68098..b2a9cc45b91 100644
--- a/code/modules/mob/living/carbon/carbon.dm
+++ b/code/modules/mob/living/carbon/carbon.dm
@@ -967,7 +967,7 @@
var/r_arm_index_next = 0
for(var/bodypart_path in bodyparts)
var/obj/item/bodypart/bodypart_instance = new bodypart_path()
- bodypart_instance.owner = src
+ bodypart_instance.set_owner(src)
bodyparts.Remove(bodypart_path)
add_bodypart(bodypart_instance)
switch(bodypart_instance.body_part)
@@ -1016,10 +1016,6 @@
var/obj/item/organ/I = X
I.Insert(src)
-/mob/living/carbon/proc/update_disabled_bodyparts()
- for(var/B in bodyparts)
- var/obj/item/bodypart/BP = B
- BP.update_disabled()
/mob/living/carbon/vv_get_dropdown()
. = ..()
diff --git a/code/modules/mob/living/carbon/human/examine.dm b/code/modules/mob/living/carbon/human/examine.dm
index 355c1bb1c2e..19751deb6c7 100644
--- a/code/modules/mob/living/carbon/human/examine.dm
+++ b/code/modules/mob/living/carbon/human/examine.dm
@@ -157,12 +157,13 @@
for(var/X in disabled)
var/obj/item/bodypart/body_part = X
var/damage_text
- if(body_part.is_disabled() != BODYPART_DISABLED_WOUND) // skip if it's disabled by a wound (cuz we'll be able to see the bone sticking out!)
- if(!(body_part.get_damage(include_stamina = FALSE) >= body_part.max_damage)) //we don't care if it's stamcritted
- damage_text = "limp and lifeless"
- else
- damage_text = (body_part.brute_dam >= body_part.burn_dam) ? body_part.heavy_brute_msg : body_part.heavy_burn_msg
- msg += "[capitalize(t_his)] [body_part.name] is [damage_text]!\n"
+ if(HAS_TRAIT(body_part, TRAIT_DISABLED_BY_WOUND))
+ continue // skip if it's disabled by a wound (cuz we'll be able to see the bone sticking out!)
+ if(!(body_part.get_damage(include_stamina = FALSE) >= body_part.max_damage)) //we don't care if it's stamcritted
+ damage_text = "limp and lifeless"
+ else
+ damage_text = (body_part.brute_dam >= body_part.burn_dam) ? body_part.heavy_brute_msg : body_part.heavy_burn_msg
+ msg += "[capitalize(t_his)] [body_part.name] is [damage_text]!\n"
//stores missing limbs
var/l_limbs_missing = 0
diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm
index 951558b3e6d..6a85c5e63b6 100644
--- a/code/modules/mob/living/carbon/human/human_defense.dm
+++ b/code/modules/mob/living/carbon/human/human_defense.dm
@@ -717,7 +717,7 @@
if(status == "OK" || status == "no damage")
no_damage = TRUE
var/isdisabled = ""
- if(LB.is_disabled())
+ if(LB.bodypart_disabled)
isdisabled = " is disabled"
if(no_damage)
isdisabled += " but otherwise"
diff --git a/code/modules/surgery/bodyparts/_bodyparts.dm b/code/modules/surgery/bodyparts/_bodyparts.dm
index d5907ce04b6..a3976a66c32 100644
--- a/code/modules/surgery/bodyparts/_bodyparts.dm
+++ b/code/modules/surgery/bodyparts/_bodyparts.dm
@@ -22,7 +22,12 @@
var/held_index = 0 //are we a hand? if so, which one!
var/is_pseudopart = FALSE //For limbs that don't really exist, eg chainsaws
- var/bodypart_disabled = BODYPART_NOT_DISABLED //If disabled, limb is as good as missing
+ ///If disabled, limb is as good as missing.
+ var/bodypart_disabled = FALSE
+ ///Multiplied by max_damage it returns the threshold which defines a limb being disabled or not. From 0 to 1.
+ var/disable_threshold = 1
+ ///Controls whether bodypart_disabled makes sense or not for this limb.
+ var/can_be_disabled = FALSE
var/body_damage_coeff = 1 //Multiplier of the limb's damage that gets applied to the mob
var/stam_damage_coeff = 0.75
var/brutestate = 0
@@ -91,6 +96,25 @@
var/obj/item/self_grasp/grasped_by
+/obj/item/bodypart/Initialize(mapload)
+ . = ..()
+ if(can_be_disabled)
+ RegisterSignal(src, SIGNAL_ADDTRAIT(TRAIT_PARALYSIS), .proc/on_paralysis_trait_gain)
+ RegisterSignal(src, SIGNAL_REMOVETRAIT(TRAIT_PARALYSIS), .proc/on_paralysis_trait_loss)
+
+
+/obj/item/bodypart/Destroy()
+ if(owner)
+ owner.remove_bodypart(src)
+ set_owner(null)
+ for(var/wound in wounds)
+ qdel(wound) // wounds is a lazylist, and each wound removes itself from it on deletion.
+ if(length(wounds))
+ stack_trace("[type] qdeleted with [length(wounds)] uncleared wounds")
+ wounds.Cut()
+ return ..()
+
+
/obj/item/bodypart/examine(mob/user)
. = ..()
if(brute_dam > DAMAGE_PRECISION)
@@ -98,14 +122,10 @@
if(burn_dam > DAMAGE_PRECISION)
. += "This limb has [burn_dam > 30 ? "severe" : "minor"] burns."
+
/obj/item/bodypart/blob_act()
take_damage(max_damage)
-/obj/item/bodypart/Destroy()
- if(owner)
- owner.remove_bodypart(src)
- owner = null
- return ..()
/obj/item/bodypart/attack(mob/living/carbon/C, mob/user)
if(ishuman(C))
@@ -171,13 +191,6 @@
return bodypart_organs
-/obj/item/bodypart/proc/consider_processing()
- if(stamina_dam > DAMAGE_PRECISION)
- . = TRUE
- //else if.. else if.. so on.
- else
- . = FALSE
- needs_processing = .
//Return TRUE to get whatever mob this is in to update health.
/obj/item/bodypart/proc/on_life(stam_regen)
@@ -279,21 +292,24 @@
if(can_inflict <= 0)
return FALSE
-
- brute_dam += brute
- burn_dam += burn
+ if(brute)
+ set_brute_dam(brute_dam + brute)
+ if(burn)
+ set_burn_dam(burn_dam + burn)
//We've dealt the physical damages, if there's room lets apply the stamina damage.
- stamina_dam += round(clamp(stamina, 0, max_stamina_damage - stamina_dam), DAMAGE_PRECISION)
+ if(stamina)
+ set_stamina_dam(stamina_dam + round(clamp(stamina, 0, max_stamina_damage - stamina_dam), DAMAGE_PRECISION))
- if(owner && updating_health)
- owner.updatehealth()
- if(stamina > DAMAGE_PRECISION)
- owner.update_stamina()
- owner.stam_regen_start_time = world.time + STAMINA_REGEN_BLOCK_TIME
- . = TRUE
- consider_processing()
- update_disabled()
+ if(owner)
+ if(can_be_disabled)
+ update_disabled()
+ if(updating_health)
+ owner.updatehealth()
+ if(stamina > DAMAGE_PRECISION)
+ owner.update_stamina()
+ owner.stam_regen_start_time = world.time + STAMINA_REGEN_BLOCK_TIME
+ . = TRUE
return update_bodypart_damage_state() || .
/// Allows us to roll for and apply a wound without actually dealing damage. Used for aggregate wounding power with pellet clouds
@@ -468,19 +484,53 @@
//Cannot remove negative damage (i.e. apply damage)
/obj/item/bodypart/proc/heal_damage(brute, burn, stamina, required_status, updating_health = TRUE)
- if(required_status && (status != required_status)) //So we can only heal certain kinds of limbs, ie robotic vs organic.
+ if(required_status && status != required_status) //So we can only heal certain kinds of limbs, ie robotic vs organic.
return
- brute_dam = round(max(brute_dam - brute, 0), DAMAGE_PRECISION)
- burn_dam = round(max(burn_dam - burn, 0), DAMAGE_PRECISION)
- stamina_dam = round(max(stamina_dam - stamina, 0), DAMAGE_PRECISION)
- if(owner && updating_health)
- owner.updatehealth()
- consider_processing()
- update_disabled()
+ if(brute)
+ set_brute_dam(round(max(brute_dam - brute, 0), DAMAGE_PRECISION))
+ if(burn)
+ set_burn_dam(round(max(burn_dam - burn, 0), DAMAGE_PRECISION))
+ if(stamina)
+ set_stamina_dam(round(max(stamina_dam - stamina, 0), DAMAGE_PRECISION))
+
+ if(owner)
+ if(can_be_disabled)
+ update_disabled()
+ if(updating_health)
+ owner.updatehealth()
cremation_progress = min(0, cremation_progress - ((brute_dam + burn_dam)*(100/max_damage)))
return update_bodypart_damage_state()
+
+///Proc to hook behavior associated to the change of the brute_dam variable's value.
+/obj/item/bodypart/proc/set_brute_dam(new_value)
+ if(brute_dam == new_value)
+ return
+ . = brute_dam
+ brute_dam = new_value
+
+
+///Proc to hook behavior associated to the change of the burn_dam variable's value.
+/obj/item/bodypart/proc/set_burn_dam(new_value)
+ if(burn_dam == new_value)
+ return
+ . = burn_dam
+ burn_dam = new_value
+
+
+///Proc to hook behavior associated to the change of the stamina_dam variable's value.
+/obj/item/bodypart/proc/set_stamina_dam(new_value)
+ if(stamina_dam == new_value)
+ return
+ . = stamina_dam
+ stamina_dam = new_value
+ if(stamina_dam > DAMAGE_PRECISION)
+ needs_processing = TRUE
+ else
+ needs_processing = FALSE
+
+
//Returns total damage.
/obj/item/bodypart/proc/get_damage(include_stamina = FALSE)
var/total = brute_dam + burn_dam
@@ -488,51 +538,166 @@
total = max(total, stamina_dam)
return total
+
//Checks disabled status thresholds
/obj/item/bodypart/proc/update_disabled()
if(!owner)
return
- if(!isnull(set_disabled(is_disabled()))) //set_disabled will return null when there's no change
- owner.update_mobility()
+ if(!can_be_disabled)
+ set_disabled(FALSE)
+ CRASH("update_disabled called with can_be_disabled false")
-/obj/item/bodypart/proc/is_disabled()
- if(!owner)
- return
if(HAS_TRAIT(src, TRAIT_PARALYSIS))
- return BODYPART_DISABLED_PARALYSIS
- for(var/i in wounds)
- var/datum/wound/W = i
- if(W.disabling)
- return BODYPART_DISABLED_WOUND
- if(can_dismember() && !HAS_TRAIT(owner, TRAIT_NOLIMBDISABLE))
- . = bodypart_disabled //inertia, to avoid limbs healing 0.1 damage and being re-enabled
+ set_disabled(TRUE)
+ return
- if(get_damage(TRUE) >= max_damage * (HAS_TRAIT(owner, TRAIT_EASYLIMBWOUND) ? 0.6 : 1)) //Easy limb disable disables the limb at 40% health instead of 0%
- if(!last_maxed)
+ var/total_damage = max(brute_dam + burn_dam, stamina_dam)
+
+ if(total_damage >= max_damage * disable_threshold) //Easy limb disable disables the limb at 40% health instead of 0%
+ if(!last_maxed)
+ if(owner.stat < UNCONSCIOUS)
owner.emote("scream")
- last_maxed = TRUE
- if(!is_organic_limb() || stamina_dam >= max_damage)
- return BODYPART_DISABLED_DAMAGE
- else if(bodypart_disabled && (get_damage(TRUE) <= (max_damage * 0.8))) // reenabled at 80% now instead of 50% as of wounds update
- last_maxed = FALSE
- return BODYPART_NOT_DISABLED
- else
- return BODYPART_NOT_DISABLED
- return BODYPART_NOT_DISABLED
+ last_maxed = TRUE
+ set_disabled(TRUE)
+ return
+
+ if(bodypart_disabled && total_damage <= max_damage * 0.8) // reenabled at 80% now instead of 50% as of wounds update
+ last_maxed = FALSE
+ set_disabled(FALSE)
+///Proc to change the value of the `disabled` variable and react to the event of its change.
/obj/item/bodypart/proc/set_disabled(new_disabled)
- if(isnull(new_disabled) || bodypart_disabled == new_disabled || !owner)
+ if(bodypart_disabled == new_disabled)
return
. = bodypart_disabled
bodypart_disabled = new_disabled
- if(bodypart_disabled && owner.get_item_for_held_index(held_index))
- owner.dropItemToGround(owner.get_item_for_held_index(held_index))
+
+ if(!owner)
+ return
+ if(bodypart_disabled)
+ if(!.)
+ owner.update_mobility()
+ else if (.)
+ owner.update_mobility()
owner.update_health_hud() //update the healthdoll
owner.update_body()
+///Proc to change the value of the `owner` variable and react to the event of its change.
+/obj/item/bodypart/proc/set_owner(new_owner)
+ if(owner == new_owner)
+ return FALSE //`null` is a valid option, so we need to use a num var to make it clear no change was made.
+ . = owner
+ owner = new_owner
+ var/needs_update_disabled = FALSE //Only really relevant if there's an owner
+ if(.)
+ var/mob/living/carbon/old_owner = .
+ if(can_be_disabled)
+ if(HAS_TRAIT(old_owner, TRAIT_EASYLIMBWOUND))
+ disable_threshold = initial(disable_threshold)
+ needs_update_disabled = TRUE
+ UnregisterSignal(old_owner, list(
+ SIGNAL_REMOVETRAIT(TRAIT_EASYLIMBWOUND),
+ SIGNAL_ADDTRAIT(TRAIT_EASYLIMBWOUND),
+ ))
+ if(initial(can_be_disabled))
+ if(HAS_TRAIT(old_owner, TRAIT_NOLIMBDISABLE))
+ if(!owner || !HAS_TRAIT(owner, TRAIT_NOLIMBDISABLE))
+ set_can_be_disabled(initial(can_be_disabled))
+ needs_update_disabled = TRUE
+ UnregisterSignal(old_owner, list(
+ SIGNAL_REMOVETRAIT(TRAIT_NOLIMBDISABLE),
+ SIGNAL_ADDTRAIT(TRAIT_NOLIMBDISABLE),
+ ))
+ if(owner)
+ if(can_be_disabled)
+ if(HAS_TRAIT(owner, TRAIT_EASYLIMBWOUND))
+ disable_threshold = 0.6
+ needs_update_disabled = TRUE
+ RegisterSignal(owner, SIGNAL_REMOVETRAIT(TRAIT_EASYLIMBWOUND), .proc/on_owner_easylimbwound_trait_loss)
+ RegisterSignal(owner, SIGNAL_ADDTRAIT(TRAIT_EASYLIMBWOUND), .proc/on_owner_easylimbwound_trait_gain)
+ if(initial(can_be_disabled))
+ if(HAS_TRAIT(owner, TRAIT_NOLIMBDISABLE))
+ set_can_be_disabled(FALSE)
+ needs_update_disabled = FALSE
+ RegisterSignal(owner, SIGNAL_REMOVETRAIT(TRAIT_NOLIMBDISABLE), .proc/on_owner_nolimbdisable_trait_loss)
+ RegisterSignal(owner, SIGNAL_ADDTRAIT(TRAIT_NOLIMBDISABLE), .proc/on_owner_nolimbdisable_trait_gain)
+ if(needs_update_disabled)
+ update_disabled()
+
+
+///Proc to change the value of the `can_be_disabled` variable and react to the event of its change.
+/obj/item/bodypart/proc/set_can_be_disabled(new_can_be_disabled)
+ if(can_be_disabled == new_can_be_disabled)
+ return
+ . = can_be_disabled
+ can_be_disabled = new_can_be_disabled
+ if(can_be_disabled)
+ if(owner)
+ if(HAS_TRAIT(owner, TRAIT_NOLIMBDISABLE))
+ CRASH("set_can_be_disabled to TRUE with for limb whose owner has TRAIT_NOLIMBDISABLE")
+ RegisterSignal(owner, SIGNAL_ADDTRAIT(TRAIT_PARALYSIS), .proc/on_paralysis_trait_gain)
+ RegisterSignal(owner, SIGNAL_REMOVETRAIT(TRAIT_PARALYSIS), .proc/on_paralysis_trait_loss)
+ if(HAS_TRAIT(owner, TRAIT_EASYLIMBWOUND))
+ disable_threshold = 0.6
+ RegisterSignal(owner, SIGNAL_REMOVETRAIT(TRAIT_EASYLIMBWOUND), .proc/on_owner_easylimbwound_trait_loss)
+ RegisterSignal(owner, SIGNAL_ADDTRAIT(TRAIT_EASYLIMBWOUND), .proc/on_owner_easylimbwound_trait_gain)
+ update_disabled()
+ else if(.)
+ if(owner)
+ UnregisterSignal(owner, list(
+ SIGNAL_ADDTRAIT(TRAIT_PARALYSIS),
+ SIGNAL_REMOVETRAIT(TRAIT_PARALYSIS),
+ SIGNAL_REMOVETRAIT(TRAIT_EASYLIMBWOUND),
+ SIGNAL_ADDTRAIT(TRAIT_EASYLIMBWOUND),
+ ))
+ set_disabled(FALSE)
+
+
+///Called when TRAIT_PARALYSIS is added to the limb.
+/obj/item/bodypart/proc/on_paralysis_trait_gain(obj/item/bodypart/source)
+ SIGNAL_HANDLER
+ if(can_be_disabled)
+ set_disabled(TRUE)
+
+
+///Called when TRAIT_PARALYSIS is removed from the limb.
+/obj/item/bodypart/proc/on_paralysis_trait_loss(obj/item/bodypart/source)
+ SIGNAL_HANDLER
+ if(can_be_disabled)
+ update_disabled()
+
+
+///Called when TRAIT_NOLIMBDISABLE is added to the owner.
+/obj/item/bodypart/proc/on_owner_nolimbdisable_trait_gain(mob/living/carbon/source)
+ SIGNAL_HANDLER
+ set_can_be_disabled(FALSE)
+
+
+///Called when TRAIT_NOLIMBDISABLE is removed from the owner.
+/obj/item/bodypart/proc/on_owner_nolimbdisable_trait_loss(mob/living/carbon/source)
+ SIGNAL_HANDLER
+ set_can_be_disabled(initial(can_be_disabled))
+
+
+///Called when TRAIT_EASYLIMBWOUND is added to the owner.
+/obj/item/bodypart/proc/on_owner_easylimbwound_trait_gain(mob/living/carbon/source)
+ SIGNAL_HANDLER
+ disable_threshold = 0.6
+ if(can_be_disabled)
+ update_disabled()
+
+
+///Called when TRAIT_EASYLIMBWOUND is removed from the owner.
+/obj/item/bodypart/proc/on_owner_easylimbwound_trait_loss(mob/living/carbon/source)
+ SIGNAL_HANDLER
+ disable_threshold = initial(disable_threshold)
+ if(can_be_disabled)
+ update_disabled()
+
+
//Updates an organ's brute/burn states for use by update_damage_overlays()
//Returns 1 if we need to update overlays. 0 otherwise.
/obj/item/bodypart/proc/update_bodypart_damage_state()
@@ -754,7 +919,7 @@
QDEL_NULL(current_gauze)
wound_damage_multiplier = dam_mul
- update_disabled()
+
/obj/item/bodypart/proc/get_bleed_rate()
if(status != BODYPART_ORGANIC) // maybe in the future we can bleed oil from aug parts, but not now
diff --git a/code/modules/surgery/bodyparts/dismemberment.dm b/code/modules/surgery/bodyparts/dismemberment.dm
index 12a5e424f53..41c7aafdf7e 100644
--- a/code/modules/surgery/bodyparts/dismemberment.dm
+++ b/code/modules/surgery/bodyparts/dismemberment.dm
@@ -455,8 +455,8 @@
L = newBodyPart(limb_zone, 0, 0)
if(L)
if(!noheal)
- L.brute_dam = 0
- L.burn_dam = 0
+ L.set_brute_dam(0)
+ L.set_burn_dam(0)
L.brutestate = 0
L.burnstate = 0
diff --git a/code/modules/surgery/bodyparts/parts.dm b/code/modules/surgery/bodyparts/parts.dm
index 40cf4b50b16..dc3ee8258e6 100644
--- a/code/modules/surgery/bodyparts/parts.dm
+++ b/code/modules/surgery/bodyparts/parts.dm
@@ -72,35 +72,62 @@
held_index = 1
px_x = -6
px_y = 0
+ can_be_disabled = TRUE
-/obj/item/bodypart/l_arm/is_disabled()
- if(HAS_TRAIT(owner, TRAIT_PARALYSIS_L_ARM))
- return BODYPART_DISABLED_PARALYSIS
- return ..()
+
+/obj/item/bodypart/l_arm/set_owner(new_owner)
+ . = ..()
+ if(. == FALSE)
+ return
+ var/mob/living/carbon/owner = null
+ if(owner)
+ if(HAS_TRAIT(owner, TRAIT_PARALYSIS_L_ARM))
+ ADD_TRAIT(src, TRAIT_PARALYSIS, TRAIT_PARALYSIS_L_ARM)
+ RegisterSignal(owner, SIGNAL_REMOVETRAIT(TRAIT_PARALYSIS_L_ARM), .proc/on_owner_paralysis_loss)
+ else
+ REMOVE_TRAIT(src, TRAIT_PARALYSIS, TRAIT_PARALYSIS_L_ARM)
+ RegisterSignal(owner, SIGNAL_ADDTRAIT(TRAIT_PARALYSIS_L_ARM), .proc/on_owner_paralysis_gain)
+ if(.)
+ var/mob/living/carbon/old_owner = .
+ if(HAS_TRAIT(old_owner, TRAIT_PARALYSIS_L_ARM))
+ UnregisterSignal(old_owner, SIGNAL_REMOVETRAIT(TRAIT_PARALYSIS_L_ARM))
+ if(!owner || !HAS_TRAIT(owner, TRAIT_PARALYSIS_L_ARM))
+ REMOVE_TRAIT(src, TRAIT_PARALYSIS, TRAIT_PARALYSIS_L_ARM)
+ else
+ UnregisterSignal(old_owner, SIGNAL_ADDTRAIT(TRAIT_PARALYSIS_L_ARM))
+
+
+///Proc to react to the owner gaining the TRAIT_PARALYSIS_L_ARM trait.
+/obj/item/bodypart/l_arm/proc/on_owner_paralysis_gain(mob/living/carbon/source)
+ SIGNAL_HANDLER
+ ADD_TRAIT(src, TRAIT_PARALYSIS, TRAIT_PARALYSIS_L_ARM)
+ UnregisterSignal(owner, SIGNAL_ADDTRAIT(TRAIT_PARALYSIS_L_ARM))
+ RegisterSignal(owner, SIGNAL_REMOVETRAIT(TRAIT_PARALYSIS_L_ARM), .proc/on_owner_paralysis_loss)
+
+
+///Proc to react to the owner losing the TRAIT_PARALYSIS_L_ARM trait.
+/obj/item/bodypart/l_arm/proc/on_owner_paralysis_loss(mob/living/carbon/source)
+ SIGNAL_HANDLER
+ REMOVE_TRAIT(src, TRAIT_PARALYSIS, TRAIT_PARALYSIS_L_ARM)
+ UnregisterSignal(owner, SIGNAL_REMOVETRAIT(TRAIT_PARALYSIS_L_ARM))
+ RegisterSignal(owner, SIGNAL_ADDTRAIT(TRAIT_PARALYSIS_L_ARM), .proc/on_owner_paralysis_gain)
/obj/item/bodypart/l_arm/set_disabled(new_disabled)
. = ..()
- if(isnull(.))
+ if(isnull(.) || !owner)
return
- if(. == BODYPART_NOT_DISABLED)
- if(bodypart_disabled != BODYPART_NOT_DISABLED)
- owner.set_usable_hands(owner.usable_hands + 1)
- else if(bodypart_disabled == BODYPART_NOT_DISABLED)
- owner.set_usable_hands(owner.usable_hands - 1)
- switch(bodypart_disabled)
- if(BODYPART_DISABLED_DAMAGE)
+
+ if(!.)
+ if(bodypart_disabled)
+ owner.set_usable_hands(owner.usable_hands - 1)
if(owner.stat < UNCONSCIOUS)
- owner.emote("scream")
- if(owner.stat < DEAD)
- to_chat(owner, "Your [name] is too damaged to function!")
+ to_chat(owner, "Your lose control of your [name]!")
if(held_index)
owner.dropItemToGround(owner.get_item_for_held_index(held_index))
- if(BODYPART_DISABLED_PARALYSIS)
- if(owner.stat < DEAD)
- to_chat(owner, "You can't feel your [name]!")
- if(held_index)
- owner.dropItemToGround(owner.get_item_for_held_index(held_index))
+ else if(!bodypart_disabled)
+ owner.set_usable_hands(owner.usable_hands + 1)
+
if(owner.hud_used)
var/obj/screen/inventory/hand/hand_screen_object = owner.hud_used.hand_slots["[held_index]"]
hand_screen_object?.update_icon()
@@ -119,12 +146,14 @@
icon_state = "alien_l_arm"
px_x = 0
px_y = 0
- dismemberable = 0
+ dismemberable = FALSE
+ can_be_disabled = FALSE
max_damage = 100
animal_origin = ALIEN_BODYPART
/obj/item/bodypart/l_arm/devil
- dismemberable = 0
+ dismemberable = FALSE
+ can_be_disabled = FALSE
max_damage = 5000
animal_origin = DEVIL_BODYPART
@@ -145,35 +174,62 @@
px_x = 6
px_y = 0
max_stamina_damage = 50
+ can_be_disabled = TRUE
-/obj/item/bodypart/r_arm/is_disabled()
- if(HAS_TRAIT(owner, TRAIT_PARALYSIS_R_ARM))
- return BODYPART_DISABLED_PARALYSIS
- return ..()
+
+/obj/item/bodypart/r_arm/set_owner(new_owner)
+ . = ..()
+ if(. == FALSE)
+ return
+ var/mob/living/carbon/owner = null
+ if(owner)
+ if(HAS_TRAIT(owner, TRAIT_PARALYSIS_R_ARM))
+ ADD_TRAIT(src, TRAIT_PARALYSIS, TRAIT_PARALYSIS_R_ARM)
+ RegisterSignal(owner, SIGNAL_REMOVETRAIT(TRAIT_PARALYSIS_R_ARM), .proc/on_owner_paralysis_loss)
+ else
+ REMOVE_TRAIT(src, TRAIT_PARALYSIS, TRAIT_PARALYSIS_R_ARM)
+ RegisterSignal(owner, SIGNAL_ADDTRAIT(TRAIT_PARALYSIS_R_ARM), .proc/on_owner_paralysis_gain)
+ if(.)
+ var/mob/living/carbon/old_owner = .
+ if(HAS_TRAIT(old_owner, TRAIT_PARALYSIS_R_ARM))
+ UnregisterSignal(old_owner, SIGNAL_REMOVETRAIT(TRAIT_PARALYSIS_R_ARM))
+ if(!owner || !HAS_TRAIT(owner, TRAIT_PARALYSIS_R_ARM))
+ REMOVE_TRAIT(src, TRAIT_PARALYSIS, TRAIT_PARALYSIS_R_ARM)
+ else
+ UnregisterSignal(old_owner, SIGNAL_ADDTRAIT(TRAIT_PARALYSIS_R_ARM))
+
+
+///Proc to react to the owner gaining the TRAIT_PARALYSIS_R_ARM trait.
+/obj/item/bodypart/r_arm/proc/on_owner_paralysis_gain(mob/living/carbon/source)
+ SIGNAL_HANDLER
+ ADD_TRAIT(src, TRAIT_PARALYSIS, TRAIT_PARALYSIS_R_ARM)
+ UnregisterSignal(owner, SIGNAL_ADDTRAIT(TRAIT_PARALYSIS_R_ARM))
+ RegisterSignal(owner, SIGNAL_REMOVETRAIT(TRAIT_PARALYSIS_R_ARM), .proc/on_owner_paralysis_loss)
+
+
+///Proc to react to the owner losing the TRAIT_PARALYSIS_R_ARM trait.
+/obj/item/bodypart/r_arm/proc/on_owner_paralysis_loss(mob/living/carbon/source)
+ SIGNAL_HANDLER
+ REMOVE_TRAIT(src, TRAIT_PARALYSIS, TRAIT_PARALYSIS_R_ARM)
+ UnregisterSignal(owner, SIGNAL_REMOVETRAIT(TRAIT_PARALYSIS_R_ARM))
+ RegisterSignal(owner, SIGNAL_ADDTRAIT(TRAIT_PARALYSIS_R_ARM), .proc/on_owner_paralysis_gain)
/obj/item/bodypart/r_arm/set_disabled(new_disabled)
. = ..()
- if(isnull(.))
+ if(isnull(.) || !owner)
return
- if(. == BODYPART_NOT_DISABLED)
- if(bodypart_disabled != BODYPART_NOT_DISABLED)
- owner.set_usable_hands(owner.usable_hands + 1)
- else if(bodypart_disabled == BODYPART_NOT_DISABLED)
- owner.set_usable_hands(owner.usable_hands - 1)
- switch(bodypart_disabled)
- if(BODYPART_DISABLED_DAMAGE)
+
+ if(!.)
+ if(bodypart_disabled)
+ owner.set_usable_hands(owner.usable_hands - 1)
if(owner.stat < UNCONSCIOUS)
- owner.emote("scream")
- if(owner.stat < DEAD)
- to_chat(owner, "Your [name] is too damaged to function!")
+ to_chat(owner, "Your lose control of your [name]!")
if(held_index)
owner.dropItemToGround(owner.get_item_for_held_index(held_index))
- if(BODYPART_DISABLED_PARALYSIS)
- if(owner.stat < DEAD)
- to_chat(owner, "You can't feel your [name]!")
- if(held_index)
- owner.dropItemToGround(owner.get_item_for_held_index(held_index))
+ else if(!bodypart_disabled)
+ owner.set_usable_hands(owner.usable_hands + 1)
+
if(owner.hud_used)
var/obj/screen/inventory/hand/hand_screen_object = owner.hud_used.hand_slots["[held_index]"]
hand_screen_object?.update_icon()
@@ -192,12 +248,14 @@
icon_state = "alien_r_arm"
px_x = 0
px_y = 0
- dismemberable = 0
+ dismemberable = FALSE
+ can_be_disabled = FALSE
max_damage = 100
animal_origin = ALIEN_BODYPART
/obj/item/bodypart/r_arm/devil
- dismemberable = 0
+ dismemberable = FALSE
+ can_be_disabled = FALSE
max_damage = 5000
animal_origin = DEVIL_BODYPART
@@ -215,31 +273,58 @@
px_x = -2
px_y = 12
max_stamina_damage = 50
+ can_be_disabled = TRUE
-/obj/item/bodypart/l_leg/is_disabled()
- if(HAS_TRAIT(owner, TRAIT_PARALYSIS_L_LEG))
- return BODYPART_DISABLED_PARALYSIS
- return ..()
+
+/obj/item/bodypart/l_leg/set_owner(new_owner)
+ . = ..()
+ if(. == FALSE)
+ return
+ if(owner)
+ if(HAS_TRAIT(owner, TRAIT_PARALYSIS_L_LEG))
+ ADD_TRAIT(src, TRAIT_PARALYSIS, TRAIT_PARALYSIS_L_LEG)
+ RegisterSignal(owner, SIGNAL_REMOVETRAIT(TRAIT_PARALYSIS_L_LEG), .proc/on_owner_paralysis_loss)
+ else
+ REMOVE_TRAIT(src, TRAIT_PARALYSIS, TRAIT_PARALYSIS_L_LEG)
+ RegisterSignal(owner, SIGNAL_ADDTRAIT(TRAIT_PARALYSIS_L_LEG), .proc/on_owner_paralysis_gain)
+ if(.)
+ var/mob/living/carbon/old_owner = .
+ if(HAS_TRAIT(old_owner, TRAIT_PARALYSIS_L_LEG))
+ UnregisterSignal(old_owner, SIGNAL_REMOVETRAIT(TRAIT_PARALYSIS_L_LEG))
+ if(!owner || !HAS_TRAIT(owner, TRAIT_PARALYSIS_L_LEG))
+ REMOVE_TRAIT(src, TRAIT_PARALYSIS, TRAIT_PARALYSIS_L_LEG)
+ else
+ UnregisterSignal(old_owner, SIGNAL_ADDTRAIT(TRAIT_PARALYSIS_L_LEG))
+
+
+///Proc to react to the owner gaining the TRAIT_PARALYSIS_L_LEG trait.
+/obj/item/bodypart/l_leg/proc/on_owner_paralysis_gain(mob/living/carbon/source)
+ SIGNAL_HANDLER
+ ADD_TRAIT(src, TRAIT_PARALYSIS, TRAIT_PARALYSIS_L_LEG)
+ UnregisterSignal(owner, SIGNAL_ADDTRAIT(TRAIT_PARALYSIS_L_LEG))
+ RegisterSignal(owner, SIGNAL_REMOVETRAIT(TRAIT_PARALYSIS_L_LEG), .proc/on_owner_paralysis_loss)
+
+
+///Proc to react to the owner losing the TRAIT_PARALYSIS_L_LEG trait.
+/obj/item/bodypart/l_leg/proc/on_owner_paralysis_loss(mob/living/carbon/source)
+ SIGNAL_HANDLER
+ REMOVE_TRAIT(src, TRAIT_PARALYSIS, TRAIT_PARALYSIS_L_LEG)
+ UnregisterSignal(owner, SIGNAL_REMOVETRAIT(TRAIT_PARALYSIS_L_LEG))
+ RegisterSignal(owner, SIGNAL_ADDTRAIT(TRAIT_PARALYSIS_L_LEG), .proc/on_owner_paralysis_gain)
/obj/item/bodypart/l_leg/set_disabled(new_disabled)
. = ..()
- if(isnull(.))
+ if(isnull(.) || !owner)
return
- if(. == BODYPART_NOT_DISABLED)
- if(bodypart_disabled != BODYPART_NOT_DISABLED)
- owner.set_usable_legs(owner.usable_legs + 1)
- else if(bodypart_disabled == BODYPART_NOT_DISABLED)
- owner.set_usable_legs(owner.usable_legs - 1)
- switch(bodypart_disabled)
- if(BODYPART_DISABLED_DAMAGE)
+
+ if(!.)
+ if(bodypart_disabled)
+ owner.set_usable_legs(owner.usable_legs - 1)
if(owner.stat < UNCONSCIOUS)
- owner.emote("scream")
- if(owner.stat < DEAD)
- to_chat(owner, "Your [name] is too damaged to function!")
- if(BODYPART_DISABLED_PARALYSIS)
- if(owner.stat < DEAD)
- to_chat(owner, "You can't feel your [name]!")
+ to_chat(owner, "Your lose control of your [name]!")
+ else if(!bodypart_disabled)
+ owner.set_usable_legs(owner.usable_legs + 1)
/obj/item/bodypart/l_leg/digitigrade
@@ -258,12 +343,14 @@
icon_state = "alien_l_leg"
px_x = 0
px_y = 0
- dismemberable = 0
+ dismemberable = FALSE
+ can_be_disabled = FALSE
max_damage = 100
animal_origin = ALIEN_BODYPART
/obj/item/bodypart/l_leg/devil
- dismemberable = 0
+ dismemberable = FALSE
+ can_be_disabled = FALSE
max_damage = 5000
animal_origin = DEVIL_BODYPART
@@ -283,31 +370,58 @@
px_x = 2
px_y = 12
max_stamina_damage = 50
+ can_be_disabled = TRUE
-/obj/item/bodypart/r_leg/is_disabled()
- if(HAS_TRAIT(owner, TRAIT_PARALYSIS_R_LEG))
- return BODYPART_DISABLED_PARALYSIS
- return ..()
+
+/obj/item/bodypart/r_leg/set_owner(new_owner)
+ . = ..()
+ if(. == FALSE)
+ return
+ if(owner)
+ if(HAS_TRAIT(owner, TRAIT_PARALYSIS_R_LEG))
+ ADD_TRAIT(src, TRAIT_PARALYSIS, TRAIT_PARALYSIS_R_LEG)
+ RegisterSignal(owner, SIGNAL_REMOVETRAIT(TRAIT_PARALYSIS_R_LEG), .proc/on_owner_paralysis_loss)
+ else
+ REMOVE_TRAIT(src, TRAIT_PARALYSIS, TRAIT_PARALYSIS_R_LEG)
+ RegisterSignal(owner, SIGNAL_ADDTRAIT(TRAIT_PARALYSIS_R_LEG), .proc/on_owner_paralysis_gain)
+ if(.)
+ var/mob/living/carbon/old_owner = .
+ if(HAS_TRAIT(old_owner, TRAIT_PARALYSIS_R_LEG))
+ UnregisterSignal(old_owner, SIGNAL_REMOVETRAIT(TRAIT_PARALYSIS_R_LEG))
+ if(!owner || !HAS_TRAIT(owner, TRAIT_PARALYSIS_R_LEG))
+ REMOVE_TRAIT(src, TRAIT_PARALYSIS, TRAIT_PARALYSIS_R_LEG)
+ else
+ UnregisterSignal(old_owner, SIGNAL_ADDTRAIT(TRAIT_PARALYSIS_R_LEG))
+
+
+///Proc to react to the owner gaining the TRAIT_PARALYSIS_R_LEG trait.
+/obj/item/bodypart/r_leg/proc/on_owner_paralysis_gain(mob/living/carbon/source)
+ SIGNAL_HANDLER
+ ADD_TRAIT(src, TRAIT_PARALYSIS, TRAIT_PARALYSIS_R_LEG)
+ UnregisterSignal(owner, SIGNAL_ADDTRAIT(TRAIT_PARALYSIS_R_LEG))
+ RegisterSignal(owner, SIGNAL_REMOVETRAIT(TRAIT_PARALYSIS_R_LEG), .proc/on_owner_paralysis_loss)
+
+
+///Proc to react to the owner losing the TRAIT_PARALYSIS_R_LEG trait.
+/obj/item/bodypart/r_leg/proc/on_owner_paralysis_loss(mob/living/carbon/source)
+ SIGNAL_HANDLER
+ REMOVE_TRAIT(src, TRAIT_PARALYSIS, TRAIT_PARALYSIS_R_LEG)
+ UnregisterSignal(owner, SIGNAL_REMOVETRAIT(TRAIT_PARALYSIS_R_LEG))
+ RegisterSignal(owner, SIGNAL_ADDTRAIT(TRAIT_PARALYSIS_R_LEG), .proc/on_owner_paralysis_gain)
/obj/item/bodypart/r_leg/set_disabled(new_disabled)
. = ..()
- if(isnull(.))
+ if(isnull(.) || !owner)
return
- if(. == BODYPART_NOT_DISABLED)
- if(bodypart_disabled != BODYPART_NOT_DISABLED)
- owner.set_usable_legs(owner.usable_legs + 1)
- else if(bodypart_disabled == BODYPART_NOT_DISABLED)
- owner.set_usable_legs(owner.usable_legs - 1)
- switch(bodypart_disabled)
- if(BODYPART_DISABLED_DAMAGE)
+
+ if(!.)
+ if(bodypart_disabled)
+ owner.set_usable_legs(owner.usable_legs - 1)
if(owner.stat < UNCONSCIOUS)
- owner.emote("scream")
- if(owner.stat < DEAD)
- to_chat(owner, "Your [name] is too damaged to function!")
- if(BODYPART_DISABLED_PARALYSIS)
- if(owner.stat < DEAD)
- to_chat(owner, "You can't feel your [name]!")
+ to_chat(owner, "Your lose control of your [name]!")
+ else if(!bodypart_disabled)
+ owner.set_usable_legs(owner.usable_legs + 1)
/obj/item/bodypart/r_leg/digitigrade
@@ -326,11 +440,13 @@
icon_state = "alien_r_leg"
px_x = 0
px_y = 0
- dismemberable = 0
+ dismemberable = FALSE
+ can_be_disabled = FALSE
max_damage = 100
animal_origin = ALIEN_BODYPART
/obj/item/bodypart/r_leg/devil
- dismemberable = 0
+ dismemberable = FALSE
+ can_be_disabled = FALSE
max_damage = 5000
animal_origin = DEVIL_BODYPART