diff --git a/code/__HELPERS/trait_helpers.dm b/code/__HELPERS/trait_helpers.dm
index b6705008791..4337fa0f207 100644
--- a/code/__HELPERS/trait_helpers.dm
+++ b/code/__HELPERS/trait_helpers.dm
@@ -217,6 +217,8 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define TRAIT_DODGE_ALL_OBJECTS "dodges_all_objects" /// Allows a mob to dodge all thrown objects
#define TRAIT_BADASS "trait_badass"
#define TRAIT_FORCED_STANDING "forced_standing" // The mob cannot be floored, or lie down
+#define TRAIT_IPC_JOINTS_MAG "ipc_joints_mag" // IPC has weaker limbs but can re-attach them with ease
+#define TRAIT_IPC_JOINTS_SEALED "ipc_joints_sealed" // The IPC's limbs will not pop off bar sharp damage (aka like a human), but will take slightly more stamina damage
#define TRAIT_HAS_GPS "has_gps" // used for /Stat
#define TRAIT_CAN_VIEW_HEALTH "can_view_health" // Also used for /Stat
diff --git a/code/_globalvars/traits.dm b/code/_globalvars/traits.dm
index 05fc457383a..3a9bd855c7a 100644
--- a/code/_globalvars/traits.dm
+++ b/code/_globalvars/traits.dm
@@ -73,6 +73,8 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_UNREVIVABLE" = TRAIT_UNREVIVABLE,
"TRAIT_CULT_IMMUNITY" = TRAIT_CULT_IMMUNITY,
"TRAIT_SHOW_WIRE_INFO" = TRAIT_SHOW_WIRE_INFO,
+ "TRAIT_IPC_JOINTS_MAG" = TRAIT_IPC_JOINTS_MAG,
+ "TRAIT_IPC_JOINTS_SEALED" = TRAIT_IPC_JOINTS_SEALED,
"TRAIT_CAN_BE_EATEN_BY_LIZARDS" = TRAIT_EDIBLE_BUG,
"TRAIT_FLATTENED" = TRAIT_FLATTENED,
"TRAIT_SM_HALLUCINATION_IMMUNE" = SM_HALLUCINATION_IMMUNE,
diff --git a/code/game/objects/items/stacks/nanopaste.dm b/code/game/objects/items/stacks/nanopaste.dm
index d0fce583b73..e296d166601 100644
--- a/code/game/objects/items/stacks/nanopaste.dm
+++ b/code/game/objects/items/stacks/nanopaste.dm
@@ -25,45 +25,47 @@
if(ishuman(M)) //Repairing robotic limbs and IPCs
var/mob/living/carbon/human/H = M
- var/obj/item/organ/external/S = H.get_organ(user.zone_selected)
+ var/obj/item/organ/external/external_limb = H.get_organ(user.zone_selected)
- if(S && S.is_robotic())
- if(S.get_damage())
- use(1)
- var/remheal = 15
- var/nremheal = 0
- S.heal_damage(robo_repair = 1) //should in, theory, heal the robotic organs in just the targeted area with it being S instead of E
- var/childlist
- if(!isnull(S.children))
- childlist = S.children.Copy()
- var/parenthealed = FALSE
- while(remheal > 0)
- var/obj/item/organ/external/E
- if(S.get_damage())
- E = S
- else if(LAZYLEN(childlist))
- E = pick_n_take(childlist)
- if(!E.get_damage() || !E.is_robotic())
- continue
- else if(S.parent && !parenthealed)
- E = S.parent
- parenthealed = TRUE
- if(!E.get_damage() || !E.is_robotic())
- break
- else
- break
- nremheal = max(remheal - E.get_damage(), 0)
- E.heal_damage(remheal, 0, 0, 1) //Healing Brute
- E.heal_damage(0, remheal, 0, 1) //Healing Burn
- remheal = nremheal
- user.visible_message("\The [user] applies some nanite paste at \the [M]'s [E.name] with \the [src].")
- if(H.bleed_rate && ismachineperson(H))
- H.bleed_rate = 0
- else
- to_chat(user, "Nothing to fix here.")
+ if(external_limb && external_limb.is_robotic())
+ robotic_limb_repair(user, external_limb, H)
else
to_chat(user, "[src] won't work on that.")
+/obj/item/stack/nanopaste/proc/robotic_limb_repair(mob/user, obj/item/organ/external/external_limb, mob/living/carbon/human/H)
+ if(!external_limb.get_damage())
+ to_chat(user, "Nothing to fix here.")
+ return
+ use(1)
+ var/remaining_heal = 15
+ var/new_remaining_heal = 0
+ external_limb.heal_damage(robo_repair = TRUE) //should in, theory, heal the robotic organs in just the targeted area with it being external_limb instead of other_external_limb
+ var/list/childlist
+ if(!isnull(external_limb.children))
+ childlist = external_limb.children.Copy()
+ var/parenthealed = FALSE
+ while(remaining_heal > 0)
+ var/obj/item/organ/external/other_external_limb
+ if(external_limb.get_damage())
+ other_external_limb = external_limb
+ else if(LAZYLEN(childlist))
+ other_external_limb = pick_n_take(childlist)
+ if(!other_external_limb.get_damage() || !other_external_limb.is_robotic())
+ continue
+ else if(external_limb.parent && !parenthealed)
+ other_external_limb = external_limb.parent
+ parenthealed = TRUE
+ if(!other_external_limb.get_damage() || !other_external_limb.is_robotic())
+ break
+ else
+ break
+ new_remaining_heal = max(remaining_heal - other_external_limb.get_damage(), 0)
+ other_external_limb.heal_damage(remaining_heal, remaining_heal, FALSE, TRUE)
+ remaining_heal = new_remaining_heal
+ user.visible_message("[user] applies some nanite paste at [H]'s [other_external_limb.name] with [src].")
+ if(H.bleed_rate && ismachineperson(H))
+ H.bleed_rate = 0
+
/obj/item/stack/nanopaste/cyborg
energy_type = /datum/robot_energy_storage/medical/nanopaste
is_cyborg = TRUE
diff --git a/code/modules/research/designs/medical_designs.dm b/code/modules/research/designs/medical_designs.dm
index 8351a0948f6..a82989c1403 100644
--- a/code/modules/research/designs/medical_designs.dm
+++ b/code/modules/research/designs/medical_designs.dm
@@ -540,6 +540,39 @@
build_path = /obj/item/organ/internal/cyberimp/brain/wire_interface
category = list("Medical")
+/datum/design/raiden_implant
+ name = "Reactive Repair Implant"
+ desc = "This implant reworks the IPC frame, in order to incorporate materials that return to their original shape after being damaged. Requires power to function."
+ id = "ci-raiden_implant"
+ req_tech = list("materials" = 5, "programming" = 5, "biotech" = 5, "magnets" = 5, "engineering" = 5)
+ build_type = PROTOLATHE | MECHFAB
+ construction_time = 60
+ materials = list(MAT_METAL = 12500, MAT_SILVER = 12000, MAT_GOLD = 2500, MAT_PLASMA = 5000)
+ build_path = /obj/item/organ/internal/cyberimp/chest/ipc_repair
+ category = list("Medical")
+
+/datum/design/monsoon_implant
+ name = "Magnetic Joints Implant"
+ desc = "This implant modifies IPC joints to use magnets, allowing easy re-attachment and fluid movement."
+ id = "ci-monsoon_implant"
+ req_tech = list("materials" = 5, "programming" = 5, "biotech" = 5, "magnets" = 5, "engineering" = 5)
+ build_type = PROTOLATHE | MECHFAB
+ construction_time = 60
+ materials = list(MAT_METAL = 12500, MAT_SILVER = 12000, MAT_GOLD = 2500, MAT_PLASMA = 5000)
+ build_path = /obj/item/organ/internal/cyberimp/chest/ipc_joints/magnetic_joints
+ category = list("Medical")
+
+/datum/design/sundown_implant
+ name = "Sealed Joints Implant"
+ desc = "This implant seals and reinforces IPC joints, securing the limbs better, though prone to locking up."
+ id = "ci-sundown_implant"
+ req_tech = list("materials" = 5, "programming" = 5, "biotech" = 5, "engineering" = 5, "combat" = 5)
+ build_type = PROTOLATHE | MECHFAB
+ construction_time = 60
+ materials = list(MAT_METAL = 12500, MAT_SILVER = 12000, MAT_GOLD = 2500, MAT_PLASMA = 5000)
+ build_path = /obj/item/organ/internal/cyberimp/chest/ipc_joints/sealed
+ category = list("Medical")
+
/////////////////////////////////////////
////////////Regular Implants/////////////
/////////////////////////////////////////
diff --git a/code/modules/surgery/organs/augments_internal.dm b/code/modules/surgery/organs/augments_internal.dm
index af1503c93aa..43d40ddec06 100644
--- a/code/modules/surgery/organs/augments_internal.dm
+++ b/code/modules/surgery/organs/augments_internal.dm
@@ -317,7 +317,6 @@
/obj/item/organ/internal/cyberimp/chest/nutriment
name = "Nutriment pump implant"
desc = "This implant will synthesize a small amount of nutriment and pumps it directly into your bloodstream when you are starving."
- icon_state = "chest_implant"
implant_color = "#00AA00"
var/hunger_threshold = NUTRITION_LEVEL_STARVING
var/synthesizing = 0
@@ -367,7 +366,6 @@
/obj/item/organ/internal/cyberimp/chest/nutriment/plus
name = "Nutriment pump implant PLUS"
desc = "This implant will synthesize a small amount of nutriment and pumps it directly into your bloodstream when you are hungry."
- icon_state = "chest_implant"
implant_color = "#006607"
hunger_threshold = NUTRITION_LEVEL_HUNGRY
poison_amount = 10
@@ -384,7 +382,6 @@
/obj/item/organ/internal/cyberimp/chest/reviver
name = "Reviver implant"
desc = "This implant will attempt to heal you out of critical condition. For the faint of heart!"
- icon_state = "chest_implant"
implant_color = "#AD0000"
origin_tech = "materials=5;programming=4;biotech=4"
slot = "heartdrive"
@@ -457,6 +454,87 @@
if(H.stat == CONSCIOUS)
to_chat(H, "You feel your heart beating again!")
+/obj/item/organ/internal/cyberimp/chest/ipc_repair
+ name = "Reactive Repair Implant"
+ desc = "This implant reworks the IPC frame, in order to incorporate materials that return to their original shape after being damaged. Requires power to function."
+ implant_color = "#0ac0d8"
+ origin_tech = "materials=4;programming=4;biotech=4;magnets=4;engineering=4"
+ slot = "stomach" //Can't have a nutriment pump with it.
+ requires_machine_person = TRUE
+
+/obj/item/organ/internal/cyberimp/chest/ipc_repair/on_life()
+ if(crit_fail)
+ return
+ if(owner.maxHealth == owner.health)
+ owner.adjust_nutrition(-0.5)
+ return //Passive damage scanning
+
+ owner.adjustBruteLoss(-0.5, robotic = TRUE)
+ owner.adjustFireLoss(-0.5, robotic = TRUE)
+ owner.adjust_nutrition(-4) //Very power inefficent. Hope you got an APC nearby.
+
+/obj/item/organ/internal/cyberimp/chest/ipc_repair/emp_act(severity)
+ if(!owner || emp_proof || crit_fail)
+ return
+ crit_fail = TRUE
+ addtimer(VARSET_CALLBACK(src, crit_fail, FALSE), 30 SECONDS / severity)
+
+/obj/item/organ/internal/cyberimp/chest/ipc_joints
+ name = "IPC ER-OR Joint Implant"
+ desc = "This is a basetype. Notify a coder!"
+ implant_color = "#eeff00"
+ origin_tech = "materials=5;programming=4;biotech=4"
+ slot = "joints"
+ requires_machine_person = TRUE
+
+/obj/item/organ/internal/cyberimp/chest/ipc_joints/magnetic_joints
+ name = "Magnetic Joints Implant"
+ desc = "This implant modifies IPC joints to use magnets, allowing easy re-attachment and fluid movement."
+ implant_color = "#670db1"
+ origin_tech = "materials=4;programming=4;biotech=4;magnets=4;engineering=4"
+
+/obj/item/organ/internal/cyberimp/chest/ipc_joints/magnetic_joints/emp_act(severity)
+ if(!owner || emp_proof)
+ return
+ if(ishuman(owner))
+ var/mob/living/carbon/human/H = owner
+ to_chat(H, "Your magnetic joints lose power!")
+ for(var/obj/item/organ/external/E in H.bodyparts)
+ if(E.body_part != UPPER_TORSO && E.body_part != LOWER_TORSO)
+ E.droplimb(TRUE) //lego disasemble sound
+
+/obj/item/organ/internal/cyberimp/chest/ipc_joints/magnetic_joints/insert(mob/living/carbon/M, special = FALSE)
+ ADD_TRAIT(M, TRAIT_IPC_JOINTS_MAG, "ipc_joint[UID()]")
+ return ..()
+
+/obj/item/organ/internal/cyberimp/chest/ipc_joints/magnetic_joints/remove(mob/living/carbon/M, special = FALSE)
+ REMOVE_TRAIT(M, TRAIT_IPC_JOINTS_MAG, "ipc_joint[UID()]")
+ return ..()
+
+/obj/item/organ/internal/cyberimp/chest/ipc_joints/sealed
+ name = "Sealed Joints Implant"
+ desc = "This implant seals and reinforces IPC joints, securing the limbs better for industrial work, though prone to locking up."
+ implant_color = "#b10d0d"
+ origin_tech = "materials=4;programming=4;biotech=4;engineering=4;combat=4;"
+
+/obj/item/organ/internal/cyberimp/chest/ipc_joints/sealed/emp_act(severity)
+ if(!owner || emp_proof)
+ return
+ var/weaken_time = (10 + (severity - 1 ? 0 : 10)) SECONDS
+ owner.Weaken(weaken_time) //Pop it and lock it
+ to_chat(owner, "Your body seizes up!")
+ return weaken_time
+
+/obj/item/organ/internal/cyberimp/chest/ipc_joints/sealed/insert(mob/living/carbon/M, special = FALSE)
+ ADD_TRAIT(M, TRAIT_IPC_JOINTS_SEALED, "ipc_joint[UID()]")
+ owner.physiology.stamina_mod *= 1.15 //15% more stamina damage, representing extra friction in limbs. I guess.
+ return ..()
+
+/obj/item/organ/internal/cyberimp/chest/ipc_joints/sealed/remove(mob/living/carbon/M, special = FALSE)
+ REMOVE_TRAIT(M, TRAIT_IPC_JOINTS_SEALED, "ipc_joint[UID()]")
+ owner.physiology.stamina_mod /= 1.15
+ return ..()
+
//BOX O' IMPLANTS
/obj/item/storage/box/cyber_implants
diff --git a/code/modules/surgery/organs/organ.dm b/code/modules/surgery/organs/organ.dm
index 710177e61ce..a496b34cd11 100644
--- a/code/modules/surgery/organs/organ.dm
+++ b/code/modules/surgery/organs/organ.dm
@@ -30,6 +30,8 @@
var/emp_proof = FALSE //is the organ immune to EMPs?
var/hidden_pain = FALSE //will it skip pain messages?
var/requires_robotic_bodypart = FALSE
+ /// When this variable is true, it can only be installed on the machine person species.
+ var/requires_machine_person = FALSE
///Should this organ be destroyed on removal?
var/destroy_on_removal = FALSE
diff --git a/code/modules/surgery/organs/organ_external.dm b/code/modules/surgery/organs/organ_external.dm
index f4bc08df16e..4a913730ba9 100644
--- a/code/modules/surgery/organs/organ_external.dm
+++ b/code/modules/surgery/organs/organ_external.dm
@@ -164,6 +164,27 @@
if(!HAS_TRAIT(owner, TRAIT_IB_IMMUNE))
limb_flags &= ~CANNOT_INT_BLEED
+/obj/item/organ/external/attack(mob/M, mob/living/user)
+ if(!ishuman(M))
+ return ..()
+ var/mob/living/carbon/human/C = M
+ if(is_robotic() && HAS_TRAIT(C, TRAIT_IPC_JOINTS_MAG) && isnull(C.bodyparts_by_name[limb_name]))
+ user.unEquip(src)
+ replaced(C)
+ C.update_body()
+ C.updatehealth()
+ C.UpdateDamageIcon()
+ if(limb_name == BODY_ZONE_HEAD)
+ var/obj/item/organ/external/head/H = C.get_organ(BODY_ZONE_HEAD)
+ var/datum/robolimb/robohead = GLOB.all_robolimbs[H.model]
+ if(robohead.is_monitor) //Ensures that if an IPC gets a head that's got a human hair wig attached to their body, the hair won't wipe.
+ H.h_style = "Bald"
+ H.f_style = "Shaved"
+ C.m_styles["head"] = "None"
+ user.visible_message(
+ "[user] has attached [C]'s [src] to the [amputation_point].",
+ "You have attached [C]'s [src] to the [amputation_point].")
+ return TRUE
/obj/item/organ/external/replaced(mob/living/carbon/human/target)
owner = target
loc = null
@@ -198,6 +219,7 @@
****************************************************/
/obj/item/organ/external/receive_damage(brute, burn, sharp, used_weapon = null, list/forbidden_limbs = list(), ignore_resists = FALSE, updating_health = TRUE)
+ var/max_limb_damage = max_damage - (HAS_TRAIT(owner, TRAIT_IPC_JOINTS_MAG) ? max_damage * 0.25 : 0)
if(tough && !ignore_resists)
brute = max(0, brute - 5)
burn = max(0, burn - 4)
@@ -226,7 +248,7 @@
// Probability of taking internal damage from sufficient force, while otherwise healthy
#define LIMB_DMG_PROB 5
// High brute damage or sharp objects may damage internal organs
- if(internal_organs && (brute_dam >= max_damage || (((sharp && brute >= LIMB_SHARP_THRESH_INT_DMG) || brute >= LIMB_THRESH_INT_DMG) && prob(LIMB_DMG_PROB))))
+ if(internal_organs && (brute_dam >= max_limb_damage || (((sharp && brute >= LIMB_SHARP_THRESH_INT_DMG) || brute >= LIMB_THRESH_INT_DMG) && prob(LIMB_DMG_PROB))))
// Damage an internal organ
if(internal_organs && internal_organs.len)
var/obj/item/organ/internal/I = pick(internal_organs)
@@ -244,14 +266,14 @@
add_autopsy_data(null, brute + burn)
// Make sure we don't exceed the maximum damage a limb can take before dismembering
- if((brute_dam + burn_dam + brute + burn) < max_damage)
+ if((brute_dam + burn_dam + brute + burn) < max_limb_damage)
brute_dam += brute
burn_dam += burn
check_for_internal_bleeding(brute)
else
//If we can't inflict the full amount of damage, spread the damage in other ways
//How much damage can we actually cause?
- var/can_inflict = max_damage - (brute_dam + burn_dam)
+ var/can_inflict = max_limb_damage - (brute_dam + burn_dam)
if(can_inflict)
if(brute > 0)
//Inflict all burte damage we can
@@ -285,7 +307,7 @@
var/obj/item/organ/external/target = pick(possible_points)
target.receive_damage(brute, burn, sharp, used_weapon, forbidden_limbs + src, ignore_resists = TRUE) //If the damage was reduced before, don't reduce it again
- if(dismember_at_max_damage && body_part != UPPER_TORSO && body_part != LOWER_TORSO) // We've ensured all damage to the mob is retained, now let's drop it, if necessary.
+ if(dismember_at_max_damage && body_part != UPPER_TORSO && body_part != LOWER_TORSO && !HAS_TRAIT(owner, TRAIT_IPC_JOINTS_SEALED)) // We've ensured all damage to the mob is retained, now let's drop it, if necessary.
droplimb(1) //Clean loss, just drop the limb and be done
var/mob/living/carbon/owner_old = owner //Need to update health, but need a reference in case the below check cuts off a limb.
diff --git a/code/modules/surgery/organs_internal.dm b/code/modules/surgery/organs_internal.dm
index 3fa25913f8e..f4a89777558 100644
--- a/code/modules/surgery/organs_internal.dm
+++ b/code/modules/surgery/organs_internal.dm
@@ -396,6 +396,10 @@
to_chat(user, "[I] is an organ that requires a robotic interface! [target]'s [parse_zone(target_zone)] does not have one.")
return SURGERY_BEGINSTEP_SKIP
+ if(I.requires_machine_person && !ismachineperson(target))
+ to_chat(user, "[I] is an organ that requires an IPC interface! [target]'s [parse_zone(target_zone)] does not have one.")
+ return SURGERY_BEGINSTEP_SKIP
+
if(target_zone != I.parent_organ || target.get_organ_slot(I.slot))
to_chat(user, "There is no room for [I] in [target]'s [parse_zone(target_zone)]!")
return SURGERY_BEGINSTEP_SKIP
@@ -427,7 +431,10 @@
if(!istype(tool))
return SURGERY_STEP_INCOMPLETE
if(I.requires_robotic_bodypart)
- to_chat(user, "[I] is an organ that requires a robotic interface[target].")
+ to_chat(user, "[I] requires a robotic interface.")
+ return SURGERY_STEP_INCOMPLETE
+ if(I.requires_machine_person && !ismachineperson(target))
+ to_chat(user, "[I] requires an IPC interface!")
return SURGERY_STEP_INCOMPLETE
if(!user.drop_item())
to_chat(user, "[I] is stuck to your hand, you can't put it in [target]!")