From 207e35cddfe2c3eb3d7305f08636c2430bb28af3 Mon Sep 17 00:00:00 2001 From: Qwertytoforty <52090703+Qwertytoforty@users.noreply.github.com> Date: Tue, 21 Nov 2023 16:19:39 -0500 Subject: [PATCH] Adds 3 ipc only implants. (#22798) * FUCK fix drugs first * done just wait for prs to be merged (sorry s34n) * removes uneeded chest implant redifine, repair implant * Apply suggestions from code review Co-authored-by: Henri215 <77684085+Henri215@users.noreply.github.com> * !human, fluid * guard variable lisst * oops * Apply suggestions from code review Co-authored-by: DGamerL <108773801+DGamerL@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: DGamerL <108773801+DGamerL@users.noreply.github.com> * Update code/modules/surgery/organs/organ_external.dm Co-authored-by: DGamerL <108773801+DGamerL@users.noreply.github.com> * Update code/game/objects/items/stacks/nanopaste.dm Co-authored-by: DGamerL <108773801+DGamerL@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: Luc <89928798+lewcc@users.noreply.github.com> * lewcs good to them? * Apply suggestions from code review Co-authored-by: Luc <89928798+lewcc@users.noreply.github.com> * not needed --------- Co-authored-by: Henri215 <77684085+Henri215@users.noreply.github.com> Co-authored-by: DGamerL <108773801+DGamerL@users.noreply.github.com> Co-authored-by: Luc <89928798+lewcc@users.noreply.github.com> --- code/__HELPERS/trait_helpers.dm | 2 + code/_globalvars/traits.dm | 2 + code/game/objects/items/stacks/nanopaste.dm | 72 ++++++++-------- .../research/designs/medical_designs.dm | 33 ++++++++ .../surgery/organs/augments_internal.dm | 84 ++++++++++++++++++- code/modules/surgery/organs/organ.dm | 2 + code/modules/surgery/organs/organ_external.dm | 30 ++++++- code/modules/surgery/organs_internal.dm | 9 +- 8 files changed, 191 insertions(+), 43 deletions(-) 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]!")