diff --git a/code/__DEFINES/DNA.dm b/code/__DEFINES/DNA.dm index bfffa1c9039c..d08001525289 100644 --- a/code/__DEFINES/DNA.dm +++ b/code/__DEFINES/DNA.dm @@ -143,6 +143,12 @@ #define HAS_FLESH 23 /// if we have bones (can suffer bone wounds) #define HAS_BONE 24 +/// Can't be husked. +#define NOHUSK 25 +/// limbs start out as robotic; but also use organic icons. If you want to use the default ones, you'll have to use on_species_gain +#define ROBOTIC_LIMBS 26 +/// have no mouth to ingest/eat with +#define NOMOUTH 27 //organ slots #define ORGAN_SLOT_BRAIN "brain" diff --git a/code/__DEFINES/is_helpers.dm b/code/__DEFINES/is_helpers.dm index 7759f4403d79..44d76436c73b 100644 --- a/code/__DEFINES/is_helpers.dm +++ b/code/__DEFINES/is_helpers.dm @@ -80,6 +80,7 @@ GLOBAL_LIST_INIT(turfs_without_ground, typecacheof(list( #define isvampire(A) (is_species(A,/datum/species/vampire)) #define ispreternis(A) (is_species(A,/datum/species/preternis)) #define isszlachta(A) (is_species(A, /datum/species/szlachta)) +#define isipc(A) (is_species(A, /datum/species/ipc)) //more carbon mobs #define ismonkey(A) (istype(A, /mob/living/carbon/monkey)) diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm index 550e1c49a51a..20476612d831 100644 --- a/code/__DEFINES/mobs.dm +++ b/code/__DEFINES/mobs.dm @@ -93,6 +93,14 @@ #define DEVIL_BODYPART "devil" /*see __DEFINES/inventory.dm for bodypart bitflag defines*/ +//Reagent Metabolization flags, defines the type of reagents that affect this mob +#define PROCESS_ORGANIC 1 //Only processes reagents with "ORGANIC" or "ORGANIC | SYNTHETIC" +#define PROCESS_SYNTHETIC 2 //Only processes reagents with "SYNTHETIC" or "ORGANIC | SYNTHETIC" + +// Reagent type flags, defines the types of mobs this reagent will affect +#define ORGANIC 1 +#define SYNTHETIC 2 + // Health/damage defines for carbon mobs #define HUMAN_MAX_OXYLOSS 3 #define HUMAN_CRIT_MAX_OXYLOSS (SSmobs.wait/30) @@ -180,6 +188,7 @@ //Nutrition levels for humans #define NUTRITION_LEVEL_FAT 600 #define NUTRITION_LEVEL_FULL 550 +#define NUTRITION_LEVEL_MOSTLY_FULL 500 #define NUTRITION_LEVEL_WELL_FED 450 #define NUTRITION_LEVEL_FED 350 #define NUTRITION_LEVEL_HUNGRY 250 diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm index cd66a605156f..b1b820eaf30e 100644 --- a/code/__DEFINES/traits.dm +++ b/code/__DEFINES/traits.dm @@ -219,6 +219,8 @@ #define TRAIT_COAGULATING "coagulating" //from coagulant reagents, this doesn't affect the bleeding itself but does affect the bleed warning messages #define TRAIT_NOPULSE "nopulse" // Your heart doesn't beat #define TRAIT_MASQUERADE "masquerade" // Falsifies Health analyzer blood levels +#define TRAIT_NOCLONE "noclone" // No cloning +#define TRAIT_NODEFIB "nodefib" // No defibbing #define TRAIT_COLDBLOODED "coldblooded" // Your body is literal room temperature. Does not make you immune to the temp #define TRAIT_EAT_MORE "eat_more" //You get hungry three times as fast #define TRAIT_MESONS "mesons" diff --git a/code/__HELPERS/global_lists.dm b/code/__HELPERS/global_lists.dm index 43c60420ef43..2fc29b9dddec 100644 --- a/code/__HELPERS/global_lists.dm +++ b/code/__HELPERS/global_lists.dm @@ -39,6 +39,9 @@ init_sprite_accessory_subtypes(/datum/sprite_accessory/ethereal_mark, GLOB.ethereal_mark_list) init_sprite_accessory_subtypes(/datum/sprite_accessory/pod_hair, GLOB.pod_hair_list) init_sprite_accessory_subtypes(/datum/sprite_accessory/pod_flower, GLOB.pod_flower_list) + init_sprite_accessory_subtypes(/datum/sprite_accessory/ipc_screens, GLOB.ipc_screens_list) + init_sprite_accessory_subtypes(/datum/sprite_accessory/ipc_antennas, GLOB.ipc_antennas_list) + init_sprite_accessory_subtypes(/datum/sprite_accessory/ipc_chassis, GLOB.ipc_chassis_list) diff --git a/code/__HELPERS/mobs.dm b/code/__HELPERS/mobs.dm index f1034b72a3c6..8208603898ea 100644 --- a/code/__HELPERS/mobs.dm +++ b/code/__HELPERS/mobs.dm @@ -84,6 +84,12 @@ init_sprite_accessory_subtypes(/datum/sprite_accessory/pod_hair, GLOB.pod_hair_list) if(!GLOB.pod_flower_list.len) init_sprite_accessory_subtypes(/datum/sprite_accessory/pod_flower, GLOB.pod_flower_list) + if(!GLOB.ipc_screens_list.len) + init_sprite_accessory_subtypes(/datum/sprite_accessory/ipc_screens, GLOB.ipc_screens_list) + if(!GLOB.ipc_antennas_list.len) + init_sprite_accessory_subtypes(/datum/sprite_accessory/ipc_antennas, GLOB.ipc_antennas_list) + if(!GLOB.ipc_chassis_list.len) + init_sprite_accessory_subtypes(/datum/sprite_accessory/ipc_chassis, GLOB.ipc_chassis_list) //For now we will always return none for tail_human and ears. this shit was unreadable if you do somethign like this make it at least readable return(list( @@ -108,7 +114,10 @@ "dome" = pick(GLOB.dome_list), "dorsal_tubes" = pick(GLOB.dorsal_tubes_list), "ethereal_mark" = pick(GLOB.ethereal_mark_list), - "pod_hair" = pick(GLOB.pod_hair_list) + "pod_hair" = pick(GLOB.pod_hair_list), + "ipc_screen" = pick(GLOB.ipc_screens_list), + "ipc_antenna" = pick(GLOB.ipc_antennas_list), + "ipc_chassis" = pick(GLOB.ipc_chassis_list) )) /proc/random_hair_style(gender) @@ -180,6 +189,13 @@ if(!findname(.)) break +/proc/random_unique_ipc_name(attempts_to_find_unique_name=10) + for(var/i in 1 to attempts_to_find_unique_name) + . = capitalize(ipc_name()) + + if(!findname(.)) + break + /proc/random_skin_tone() return pick(GLOB.skin_tones) diff --git a/code/__HELPERS/names.dm b/code/__HELPERS/names.dm index e7296796cada..5409e6afd071 100644 --- a/code/__HELPERS/names.dm +++ b/code/__HELPERS/names.dm @@ -19,6 +19,9 @@ /proc/moth_name() return "[pick(GLOB.moth_first)] [pick(GLOB.moth_last)]" +/proc/ipc_name() + return "[pick(GLOB.posibrain_names)]-[rand(100, 999)]" + GLOBAL_VAR(command_name) /proc/command_name() if (GLOB.command_name) diff --git a/code/_globalvars/lists/flavor_misc.dm b/code/_globalvars/lists/flavor_misc.dm index 21b43fc86081..1d9a18b54672 100644 --- a/code/_globalvars/lists/flavor_misc.dm +++ b/code/_globalvars/lists/flavor_misc.dm @@ -42,6 +42,9 @@ GLOBAL_LIST_EMPTY(r_wings_list) GLOBAL_LIST_EMPTY(moth_wings_list) GLOBAL_LIST_EMPTY(moth_wingsopen_list) GLOBAL_LIST_EMPTY(caps_list) +GLOBAL_LIST_EMPTY(ipc_screens_list) +GLOBAL_LIST_EMPTY(ipc_antennas_list) +GLOBAL_LIST_EMPTY(ipc_chassis_list) GLOBAL_LIST_EMPTY(ethereal_mark_list) //ethereal face marks GLOBAL_LIST_INIT(color_list_ethereal, list("F Class(Green)" = "97ee63", "F2 Class (Light Green)" = "00fa9a", "F3 Class (Dark Green)" = "37835b", "M Class (Red)" = "9c3030", "M1 Class (Purple)" = "ee82ee", "G Class (Yellow)" = "fbdf56", "O Class (Blue)" = "3399ff", "A Class (Cyan)" = "00ffff")) diff --git a/code/datums/emotes.dm b/code/datums/emotes.dm index fa3c4fe736f2..07f1d5ad4936 100644 --- a/code/datums/emotes.dm +++ b/code/datums/emotes.dm @@ -11,6 +11,7 @@ var/message_robot = "" //Message displayed if the user is a robot var/message_AI = "" //Message displayed if the user is an AI var/message_monkey = "" //Message displayed if the user is a monkey + var/message_ipc = "" // Message to display if the user is an IPC var/message_simple = "" //Message to display if the user is a simple_animal var/message_param = "" //Message to display if a param was given var/emote_type = EMOTE_VISIBLE //Whether the emote is visible or audible @@ -117,6 +118,8 @@ . = message_AI else if(ismonkey(user) && message_monkey) . = message_monkey + else if(isipc(user) && message_ipc) + . = message_ipc else if(isanimal(user) && message_simple) . = message_simple @@ -193,4 +196,4 @@ if(M.stat == DEAD && M.client && (M.client.prefs.chat_toggles & CHAT_GHOSTSIGHT) && !(M in viewers(T, null))) M.show_message("[FOLLOW_LINK(M, src)] [ghost_text]") - visible_message(text, visible_message_flags = EMOTE_MESSAGE) \ No newline at end of file + visible_message(text, visible_message_flags = EMOTE_MESSAGE) diff --git a/code/datums/traits/good.dm b/code/datums/traits/good.dm index f7b016629220..077bfe5bb051 100644 --- a/code/datums/traits/good.dm +++ b/code/datums/traits/good.dm @@ -10,6 +10,11 @@ lose_text = span_notice("You can taste again!") medical_record_text = "Patient suffers from ageusia and is incapable of tasting food or reagents." +/datum/quirk/no_taste/check_quirk(datum/preferences/prefs) + if(prefs.pref_species && (NOMOUTH in prefs.pref_species.species_traits)) // Cant drink + return "You don't have the ability to eat!" + return FALSE + /datum/quirk/alcohol_tolerance name = "Alcohol Tolerance" desc = "You become drunk more slowly and suffer fewer drawbacks from alcohol." @@ -19,6 +24,11 @@ lose_text = span_danger("You don't feel as resistant to alcohol anymore. Somehow.") medical_record_text = "Patient demonstrates a high tolerance for alcohol." +/datum/quirk/alcohol_tolerance/check_quirk(datum/preferences/prefs) + if(prefs.pref_species && (NOMOUTH in prefs.pref_species.species_traits)) // Cant drink + return "You don't have the ability to drink!" + return FALSE + /datum/quirk/apathetic name = "Apathetic" desc = "You just don't care as much as other people. That's nice to have in a place like this, I guess." @@ -46,6 +56,11 @@ lose_text = span_danger("You no longer feel like drinking would ease your pain.") medical_record_text = "Patient has unusually efficient liver metabolism and can slowly regenerate wounds by drinking alcoholic beverages." +/datum/quirk/drunkhealing/check_quirk(datum/preferences/prefs) + if(prefs.pref_species && (NOMOUTH in prefs.pref_species.species_traits)) // Cant drink + return "You don't have the ability to drink!" + return FALSE + /datum/quirk/empath name = "Empath" desc = "Whether it's a sixth sense or careful study of body language, it only takes you a quick glance at someone to understand how they feel." @@ -191,6 +206,11 @@ species.toxic_food = initial(species.toxic_food) species.liked_food = initial(species.liked_food) +/datum/quirk/toxic_tastes/check_quirk(datum/preferences/prefs) + if(prefs.pref_species && (NOMOUTH in prefs.pref_species.species_traits)) // Cant eat + return "You don't have the ability to eat!" + return FALSE + /datum/quirk/tagger name = "Tagger" desc = "You're an experienced artist. While drawing graffiti, you can get twice as many uses out of drawing supplies." @@ -216,6 +236,11 @@ lose_text = span_danger("You no longer feel HONGRY.") medical_record_text = "Patient demonstrates a disturbing capacity for eating." +/datum/quirk/voracious/check_quirk(datum/preferences/prefs) + if(prefs.pref_species && (NOMOUTH in prefs.pref_species.species_traits)) // Cant eat + return "You don't have the ability to eat!" + return FALSE + /datum/quirk/efficient_metabolism //about 25% slower hunger name = "Efficient Metabolism" desc = "Your metabolism is unusually efficient, allowing you to better process your food and go longer periods without eating." @@ -262,3 +287,8 @@ /datum/quirk/cyberorgan/post_add() to_chat(quirk_holder, "Your [slot_string] has been replaced with an upgraded cybernetic variant.") + +/datum/quirk/cyberorgan/check_quirk(datum/preferences/prefs) + if(prefs.pref_species && istype(prefs.pref_species, /datum/species/ipc)) // IPCs are already cybernetic + return "You already have cybebrnetic organs!" + return FALSE diff --git a/code/datums/traits/negative.dm b/code/datums/traits/negative.dm index 53c543b09793..c5dfd99888c2 100644 --- a/code/datums/traits/negative.dm +++ b/code/datums/traits/negative.dm @@ -256,6 +256,11 @@ lose_text = span_danger("You're no longer severely affected by alcohol.") medical_record_text = "Patient demonstrates a low tolerance for alcohol. (Wimp)" +/datum/quirk/light_drinker/check_quirk(datum/preferences/prefs) + if(prefs.pref_species && (NOMOUTH in prefs.pref_species.species_traits)) // Cant drink + return "You don't have the ability to drink!" + return FALSE + /datum/quirk/nearsighted //t. errorage name = "Nearsighted" desc = "You are nearsighted without prescription glasses, but spawn with a pair." @@ -623,6 +628,11 @@ reagent_instance = new reagent_type() H.reagents.addiction_list.Add(reagent_instance) +/datum/quirk/junkie/check_quirk(datum/preferences/prefs) + if(prefs.pref_species && (prefs.pref_species.reagent_tag == PROCESS_SYNTHETIC)) //can't lose blood if your species doesn't have any + return "You dont process normal chemicals!" + return FALSE + /datum/quirk/junkie/smoker name = "Smoker" desc = "Sometimes you just really want a smoke. Probably not great for your lungs." @@ -728,6 +738,11 @@ cooldown = TRUE addtimer(VARSET_CALLBACK(src, cooldown, FALSE), cooldown_time) +/datum/quirk/allergic/check_quirk(datum/preferences/prefs) + if(prefs.pref_species && (prefs.pref_species.reagent_tag == PROCESS_SYNTHETIC)) //can't lose blood if your species doesn't have any + return "You dont process normal chemicals!" + return FALSE + /datum/quirk/kleptomaniac name = "Kleptomaniac" desc = "You have an uncontrollable urge to pick up things you see. Even things that don't belong to you." diff --git a/code/datums/traits/neutral.dm b/code/datums/traits/neutral.dm index be584d0fd266..7d89081574bb 100644 --- a/code/datums/traits/neutral.dm +++ b/code/datums/traits/neutral.dm @@ -24,6 +24,11 @@ if(!(initial(species.disliked_food) & MEAT)) species.disliked_food &= ~MEAT +/datum/quirk/vegetarian/check_quirk(datum/preferences/prefs) + if(prefs.pref_species && (NOMOUTH in prefs.pref_species.species_traits)) // Cant eat + return "You don't have the ability to eat!" + return FALSE + /datum/quirk/pineapple_liker name = "Ananas Affinity" desc = "You find yourself greatly enjoying fruits of the ananas genus. You can't seem to ever get enough of their sweet goodness!" @@ -43,6 +48,11 @@ var/datum/species/species = H.dna.species species.liked_food &= ~PINEAPPLE +/datum/quirk/pineapple_liker/check_quirk(datum/preferences/prefs) + if(prefs.pref_species && (NOMOUTH in prefs.pref_species.species_traits)) // Cant eat + return "You don't have the ability to eat!" + return FALSE + /datum/quirk/pineapple_hater name = "Ananas Aversion" desc = "You find yourself greatly detesting fruits of the ananas genus. Serious, how the hell can anyone say these things are good? And what kind of madman would even dare putting it on a pizza!?" @@ -62,6 +72,11 @@ var/datum/species/species = H.dna.species species.disliked_food &= ~PINEAPPLE +/datum/quirk/pineapple_hater/check_quirk(datum/preferences/prefs) + if(prefs.pref_species && (NOMOUTH in prefs.pref_species.species_traits)) // Cant eat + return "You don't have the ability to eat!" + return FALSE + /datum/quirk/deviant_tastes name = "Deviant Tastes" desc = "You dislike food that most people enjoy, and find delicious what they don't." @@ -84,6 +99,11 @@ species.liked_food = initial(species.liked_food) species.disliked_food = initial(species.disliked_food) +/datum/quirk/deviant_tastes/check_quirk(datum/preferences/prefs) + if(prefs.pref_species && (NOMOUTH in prefs.pref_species.species_traits)) // Cant eat + return "You don't have the ability to eat!" + return FALSE + /datum/quirk/monochromatic name = "Monochromacy" desc = "You suffer from full colorblindness, and perceive nearly the entire world in blacks and whites." @@ -147,3 +167,8 @@ SEND_SIGNAL(H.back, COMSIG_TRY_STORAGE_SHOW, H) to_chat(quirk_holder, span_boldnotice("Your bottle of hair dye spray is [where].")) + +/datum/quirk/colorist/check_quirk(datum/preferences/prefs) + if(prefs.pref_species && !(HAIR in prefs.pref_species.species_traits)) // No Hair + return "You don't have hair!" + return FALSE diff --git a/code/game/gamemodes/objective.dm b/code/game/gamemodes/objective.dm index 116be4f4bdb2..96e3ee14accf 100644 --- a/code/game/gamemodes/objective.dm +++ b/code/game/gamemodes/objective.dm @@ -35,6 +35,9 @@ GLOBAL_LIST_EMPTY(objectives) /datum/objective/proc/admin_edit(mob/admin) return +/datum/objective/proc/is_valid_target(possible_target) + return TRUE + //Shared by few objective types /datum/objective/proc/admin_simple_target_pick(mob/admin) var/list/possible_targets = list("Free objective","Random") @@ -125,7 +128,7 @@ GLOBAL_LIST_EMPTY(objectives) if(O.late_joiner) try_target_late_joiners = TRUE for(var/datum/mind/possible_target in get_crewmember_minds()) - if(!(possible_target in owners) && ishuman(possible_target.current) && (possible_target.current.stat != DEAD) && is_unique_objective(possible_target,dupe_search_range)) + if(is_valid_target(possible_target) && !(possible_target in owners) && ishuman(possible_target.current) && (possible_target.current.stat != DEAD) && is_unique_objective(possible_target,dupe_search_range)) //yogs start -- Quiet Rounds var/mob/living/carbon/human/guy = possible_target.current if(possible_target.antag_datums || !(guy.client && (guy.client.prefs.yogtoggles & QUIET_ROUND))) @@ -220,7 +223,8 @@ GLOBAL_LIST_EMPTY(objectives) if(target && target.current) if(ishuman(target.current)) var/mob/living/carbon/human/H = target.current - explanation_text = "Assassinate [target.name], the [lowertext(H.dna.species.name)] [!target_role_type ? target.assigned_role : target.special_role]." + // This should just check for an uppercase flag + explanation_text = "Assassinate [target.name], the [isipc(H) ? H.dna.species.name : lowertext(H.dna.species.name)] [!target_role_type ? target.assigned_role : target.special_role]." else explanation_text = "Assassinate [target.name], the [!target_role_type ? target.assigned_role : target.special_role]." else @@ -515,6 +519,18 @@ GLOBAL_LIST_EMPTY(objectives) return FALSE return TRUE +/datum/objective/escape/escape_with_identity/is_valid_target(possible_target) + var/list/datum/mind/owners = get_owners() + for(var/datum/mind/M in owners) + if(!M) + continue + if(!M.has_antag_datum(/datum/antagonist/changeling)) + continue + var/datum/mind/T = possible_target + if(!istype(T) || isipc(T.current)) + return FALSE + return TRUE + /datum/objective/escape/escape_with_identity name = "escape with identity" var/target_real_name // Has to be stored because the target's real_name can change over the course of the round @@ -1093,6 +1109,8 @@ GLOBAL_LIST_EMPTY(possible_items_special) for(var/datum/mind/M in SSticker.minds) if(M in lings) continue + if(isipc(M.current)) + continue if(department_head in get_department_heads(M.assigned_role)) if(ling_count) ling_count-- @@ -1122,6 +1140,8 @@ GLOBAL_LIST_EMPTY(possible_items_special) for(var/datum/mind/head in heads) if(head in lings) //Looking at you HoP. continue + if(isipc(head.current)) + continue if(needed_heads) department_minds += head department_real_names += head.current.real_name diff --git a/code/game/machinery/computer/cloning.dm b/code/game/machinery/computer/cloning.dm index 74d2f63e2031..27d2e59b96f1 100644 --- a/code/game/machinery/computer/cloning.dm +++ b/code/game/machinery/computer/cloning.dm @@ -611,7 +611,10 @@ has_bank_account = I.registered_account if(isbrain(mob_occupant)) dna = B.stored_dna - + + if(HAS_TRAIT(mob_occupant, TRAIT_NOCLONE)) + scantemp = "Unable to locate valid genetic data." + return FALSE if(!istype(dna)) scantemp = "Unable to locate valid genetic data." return FALSE diff --git a/code/game/mecha/mech_fabricator.dm b/code/game/mecha/mech_fabricator.dm index cddd27e003be..fe88ff263b97 100644 --- a/code/game/mecha/mech_fabricator.dm +++ b/code/game/mecha/mech_fabricator.dm @@ -57,6 +57,7 @@ "Cybernetics", "Implants", "Control Interfaces", + "IPC Components", "Misc" ) diff --git a/code/game/objects/items/defib.dm b/code/game/objects/items/defib.dm index 7be70c35a519..202e8341007e 100644 --- a/code/game/objects/items/defib.dm +++ b/code/game/objects/items/defib.dm @@ -579,18 +579,20 @@ failed = span_warning("[req_defib ? "[defib]" : "[src]"] buzzes: Resuscitation failed - Recovery of patient impossible. Further attempts futile.") else if (H.hellbound) failed = span_warning("[req_defib ? "[defib]" : "[src]"] buzzes: Resuscitation failed - Patient's soul unrecoverable. Further attempts futile.") - else if (tplus > tlimit) - failed = span_warning("[req_defib ? "[defib]" : "[src]"] buzzes: Resuscitation failed - Body has decayed for too long. Further attempts futile.") - else if (!heart) - failed = span_boldnotice("[req_defib ? "[defib]" : "[src]"] buzzes: Resuscitation failed - Patient's heart is missing. A replacement may allow for successful resuscitation.") - else if (heart.organ_flags & ORGAN_FAILING) - failed = span_boldnotice("[req_defib ? "[defib]" : "[src]"] buzzes: Resuscitation failed - Patient's heart too damaged. A coronary bypass or replacement may allow for successful resuscitation.") else if(total_burn >= MAX_REVIVE_FIRE_DAMAGE || total_brute >= MAX_REVIVE_BRUTE_DAMAGE) failed = span_boldnotice("[req_defib ? "[defib]" : "[src]"] buzzes: Resuscitation failed - Severe tissue damage makes recovery of patient impossible via defibrillator. Surgical repair may allow for successful resuscitation.") else if(HAS_TRAIT(H, TRAIT_HUSK)) failed = span_boldnotice("[req_defib ? "[defib]" : "[src]"] buzzes: Restucitation failed - Lack of important fluids has rendered the patient body unsurvivable. Application of experimental DNA recovery surgery or an upgraded cloner recommended.") else if(H.get_ghost()) failed = span_boldnotice("[req_defib ? "[defib]" : "[src]"] buzzes: Resuscitation failed - No activity in patient's brain. Further attempts may be successful.") + else if (HAS_TRAIT(H, TRAIT_NODEFIB)) + failed = span_boldnotice("[req_defib ? "[defib]" : "[src]"] buzzes: Resuscitation failed - Biology incompatiable.") + else if (tplus > tlimit) + failed = span_warning("[req_defib ? "[defib]" : "[src]"] buzzes: Resuscitation failed - Body has decayed for too long. Further attempts futile.") + else if (!heart) + failed = span_boldnotice("[req_defib ? "[defib]" : "[src]"] buzzes: Resuscitation failed - Patient's heart is missing. A replacement may allow for successful resuscitation.") + else if (heart.organ_flags & ORGAN_FAILING) + failed = span_boldnotice("[req_defib ? "[defib]" : "[src]"] buzzes: Resuscitation failed - Patient's heart too damaged. A coronary bypass or replacement may allow for successful resuscitation.") else var/obj/item/organ/brain/BR = H.getorgan(/obj/item/organ/brain) if(BR) diff --git a/code/game/objects/items/miscellaneous.dm b/code/game/objects/items/miscellaneous.dm index 7cf1488f66d4..d4210ef2f0f7 100644 --- a/code/game/objects/items/miscellaneous.dm +++ b/code/game/objects/items/miscellaneous.dm @@ -153,3 +153,37 @@ user.gib() playsound(src, 'sound/items/eatfood.ogg', 50, 1, -1) return MANUAL_SUICIDE + +/obj/item/ipcrevive // Doesnt do much beside be cosmetic + name = "IPC Revival Board" + desc = "Used to revive an IPC once fixed." + icon = 'icons/obj/module.dmi' + icon_state = "cyborg_upgrade1" + +/obj/item/ipcrevive/attack(mob/living/M, mob/living/user) + if(user.a_intent != INTENT_HELP) + return ..() + if(!isipc(M)) + to_chat(user, span_warning("This is not an IPC")) + return TRUE + var/mob/living/carbon/human/H = M + if(H.stat != DEAD) + to_chat(user, span_warning("This unit is not dead!")) + return TRUE + var/obj/item/organ/brain/BR = H.getorgan(/obj/item/organ/brain) + if(BR) + if(BR.suicided || BR.brainmob?.suiciding) + to_chat(user, span_warning("This units personality matrix is gone.")) + return TRUE + if(H.health < 0) + to_chat(user, span_warning("You have to repair the IPC before using this module!")) + return TRUE + to_chat(user, span_warning("You start restarting the IPC's internal circuitry.")) + if(!do_after(user, 5 SECONDS, H)) + return TRUE + if(H.mind) + H.mind.grab_ghost() + to_chat(user, span_notice("You reset the IPC's internal circuitry - reviving them!")) + H.revive() + qdel(src) + return TRUE diff --git a/code/modules/admin/view_variables/topic.dm b/code/modules/admin/view_variables/topic.dm index 4425a39f200b..ea5d217c35dc 100644 --- a/code/modules/admin/view_variables/topic.dm +++ b/code/modules/admin/view_variables/topic.dm @@ -75,10 +75,10 @@ var/newamt switch(Text) if(BRUTE) - L.adjustBruteLoss(amount) + L.adjustBruteLoss(amount, TRUE, TRUE) newamt = L.getBruteLoss() if(BURN) - L.adjustFireLoss(amount) + L.adjustFireLoss(amount, TRUE, TRUE) newamt = L.getFireLoss() if(TOX) L.adjustToxLoss(amount) diff --git a/code/modules/antagonists/cult/cult.dm b/code/modules/antagonists/cult/cult.dm index a41f9abea83d..2da1065c27bc 100644 --- a/code/modules/antagonists/cult/cult.dm +++ b/code/modules/antagonists/cult/cult.dm @@ -434,6 +434,12 @@ var/sacced = FALSE var/sac_image +/datum/objective/sacrifice/is_valid_target(possible_target) + . = ..() + var/datum/mind/M = possible_target + if(istype(M) && isipc(M.current)) + return FALSE + /datum/objective/sacrifice/check_completion() return sacced || completed diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm index 191c8888c762..8bd222c9b9a5 100644 --- a/code/modules/client/preferences.dm +++ b/code/modules/client/preferences.dm @@ -72,7 +72,7 @@ GLOBAL_LIST_EMPTY(preferences_datums) var/skin_tone = "caucasian1" //Skin color var/eye_color = "000" //Eye color var/datum/species/pref_species = new /datum/species/human() //Mutant race - var/list/features = list("mcolor" = "FFF", "gradientstyle" = "None", "gradientcolor" = "000", "ethcolor" = "9c3030", "tail_lizard" = "Smooth", "tail_human" = "None", "snout" = "Round", "horns" = "None", "ears" = "None", "wings" = "None", "frills" = "None", "spines" = "None", "body_markings" = "None", "legs" = "Normal Legs", "moth_wings" = "Plain", "tail_polysmorph" = "Polys", "teeth" = "None", "dome" = "None", "dorsal_tubes" = "No", "ethereal_mark" = "None", "pod_hair" = "Cabbage", "pod_flower" = "Cabbage") + var/list/features = list("mcolor" = "FFF", "gradientstyle" = "None", "gradientcolor" = "000", "ethcolor" = "9c3030", "tail_lizard" = "Smooth", "tail_human" = "None", "snout" = "Round", "horns" = "None", "ears" = "None", "wings" = "None", "frills" = "None", "spines" = "None", "body_markings" = "None", "legs" = "Normal Legs", "moth_wings" = "Plain", "tail_polysmorph" = "Polys", "teeth" = "None", "dome" = "None", "dorsal_tubes" = "No", "ethereal_mark" = "None", "pod_hair" = "Cabbage", "pod_flower" = "Cabbage", "ipc_screen" = "Blue", "ipc_antenna" = "None", "ipc_chassis" = "Morpheus Cyberkinetics(Greyscale)") var/list/genders = list(MALE, FEMALE, PLURAL) var/list/friendlyGenders = list("Male" = "male", "Female" = "female", "Other" = "plural") @@ -611,6 +611,49 @@ GLOBAL_LIST_EMPTY(preferences_datums) dat += "" mutant_category = 0 + if("ipc_screen" in pref_species.mutant_bodyparts) + if(!mutant_category) + dat += APPEARANCE_CATEGORY_COLUMN + + dat += "

Screen Style

" + + dat += "[features["ipc_screen"]]
" + + dat += "    Change
" + + mutant_category++ + if(mutant_category >= MAX_MUTANT_ROWS) + dat += "" + mutant_category = 0 + + if("ipc_antenna" in pref_species.mutant_bodyparts) + if(!mutant_category) + dat += APPEARANCE_CATEGORY_COLUMN + + dat += "

Antenna Style

" + + dat += "[features["ipc_antenna"]]
" + + dat += "    Change
" + + mutant_category++ + if(mutant_category >= MAX_MUTANT_ROWS) + dat += "" + mutant_category = 0 + + if("ipc_chassis" in pref_species.mutant_bodyparts) + if(!mutant_category) + dat += APPEARANCE_CATEGORY_COLUMN + + dat += "

Chassis Style

" + + dat += "[features["ipc_chassis"]]
" + + mutant_category++ + if(mutant_category >= MAX_MUTANT_ROWS) + dat += "" + mutant_category = 0 + if("ears" in pref_species.default_features) if(!mutant_category) dat += APPEARANCE_CATEGORY_COLUMN @@ -1486,7 +1529,7 @@ GLOBAL_LIST_EMPTY(preferences_datums) if("name") var/new_name = input(user, "Choose your character's name:", "Character Preference") as text|null if(new_name) - new_name = reject_bad_name(new_name) + new_name = reject_bad_name(new_name, pref_species.allow_numbers_in_name) if(new_name) real_name = new_name else @@ -1773,6 +1816,29 @@ GLOBAL_LIST_EMPTY(preferences_datums) facial_hair_color = sanitize_hexcolor(new_facial) else to_chat(user, span_danger("Invalid head flower color. Your color is not bright enough.")) + if("ipc_screen") + var/new_ipc_screen + + new_ipc_screen = input(user, "Choose your character's screen:", "Character Preference") as null|anything in GLOB.ipc_screens_list + + if(new_ipc_screen) + features["ipc_screen"] = new_ipc_screen + + if("ipc_antenna") + var/new_ipc_antenna + + new_ipc_antenna = input(user, "Choose your character's antenna:", "Character Preference") as null|anything in GLOB.ipc_antennas_list + + if(new_ipc_antenna) + features["ipc_antenna"] = new_ipc_antenna + + if("ipc_chassis") + var/new_ipc_chassis + + new_ipc_chassis = input(user, "Choose your character's chassis:", "Character Preference") as null|anything in GLOB.ipc_chassis_list + + if(new_ipc_chassis) + features["ipc_chassis"] = new_ipc_chassis if("s_tone") var/new_s_tone = input(user, "Choose your character's skin-tone:", "Character Preference") as null|anything in GLOB.skin_tones if(new_s_tone) diff --git a/code/modules/client/preferences_savefile.dm b/code/modules/client/preferences_savefile.dm index 857f41d5c0ab..3d4992251471 100644 --- a/code/modules/client/preferences_savefile.dm +++ b/code/modules/client/preferences_savefile.dm @@ -5,7 +5,7 @@ // You do not need to raise this if you are adding new values that have sane defaults. // Only raise this value when changing the meaning/format/name/layout of an existing value // where you would want the updater procs below to run -#define SAVEFILE_VERSION_MAX 37 +#define SAVEFILE_VERSION_MAX 38 /* SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Carn @@ -453,6 +453,9 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car READ_FILE(S["feature_ethereal_mark"], features["ethereal_mark"]) READ_FILE(S["feature_pod_hair"], features["pod_hair"]) READ_FILE(S["feature_pod_flower"], features["pod_flower"]) + READ_FILE(S["feature_ipc_screen"], features["ipc_screen"]) + READ_FILE(S["feature_ipc_antenna"], features["ipc_antenna"]) + READ_FILE(S["feature_ipc_chassis"], features["ipc_chassis"]) READ_FILE(S["persistent_scars"], persistent_scars) if(!CONFIG_GET(flag/join_with_mutant_humans)) @@ -489,7 +492,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car //Sanitize - real_name = reject_bad_name(real_name) + real_name = reject_bad_name(real_name, pref_species.allow_numbers_in_name) gender = sanitize_gender(gender) if(!real_name) real_name = random_unique_name(gender) @@ -556,6 +559,9 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car features["ethereal_mark"] = sanitize_inlist(features["ethereal_mark"], GLOB.ethereal_mark_list) features["pod_hair"] = sanitize_inlist(features["pod_hair"], GLOB.pod_hair_list) features["pod_flower"] = sanitize_inlist(features["pod_flower"], GLOB.pod_flower_list) + features["ipc_screen"] = sanitize_inlist(features["ipc_screen"], GLOB.ipc_screens_list) + features["ipc_antenna"] = sanitize_inlist(features["ipc_antenna"], GLOB.ipc_antennas_list) + features["ipc_chassis"] = sanitize_inlist(features["ipc_chassis"], GLOB.ipc_chassis_list) persistent_scars = sanitize_integer(persistent_scars) @@ -620,6 +626,9 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car WRITE_FILE(S["feature_pod_hair"] , features["pod_hair"]) WRITE_FILE(S["feature_pod_flower"] , features["pod_flower"]) WRITE_FILE(S["persistent_scars"] , persistent_scars) + WRITE_FILE(S["feature_ipc_screen"] , features["ipc_screen"]) + WRITE_FILE(S["feature_ipc_antenna"] , features["ipc_antenna"]) + WRITE_FILE(S["feature_ipc_chassis"] , features["ipc_chassis"]) //Custom names for(var/custom_name_id in GLOB.preferences_custom_names) diff --git a/code/modules/mob/dead/new_player/sprite_accessories.dm b/code/modules/mob/dead/new_player/sprite_accessories.dm index 953e087fc680..41bc228806eb 100644 --- a/code/modules/mob/dead/new_player/sprite_accessories.dm +++ b/code/modules/mob/dead/new_player/sprite_accessories.dm @@ -59,6 +59,7 @@ var/locked = FALSE //Is this part locked from roundstart selection? Used for parts that apply effects var/dimension_x = 32 var/dimension_y = 32 + var/limbs_id // The limbs id supplied for full-body replacing features. var/center = FALSE //Should we center the sprite? ////////////////////// @@ -851,6 +852,267 @@ name = "Spiked Wavy" icon_state = "wavy_spiked" +// IPC accessories. + +/datum/sprite_accessory/ipc_screens + icon = 'icons/mob/ipc_accessories.dmi' + color_src = EYECOLOR + +/datum/sprite_accessory/ipc_screens/blue + name = "Blue" + icon_state = "blue" + color_src = 0 + +/datum/sprite_accessory/ipc_screens/bsod + name = "BSOD" + icon_state = "bsod" + color_src = 0 + +/datum/sprite_accessory/ipc_screens/breakout + name = "Breakout" + icon_state = "breakout" + +/datum/sprite_accessory/ipc_screens/blank + name = "Null" + icon_state = "blank" + +/datum/sprite_accessory/ipc_screens/console + name = "Console" + icon_state = "console" + +/datum/sprite_accessory/ipc_screens/ecgwave + name = "ECG Wave" + icon_state = "ecgwave" + +/datum/sprite_accessory/ipc_screens/eight + name = "Eight" + icon_state = "eight" + +/datum/sprite_accessory/ipc_screens/eyes + name = "Eyes" + icon_state = "eyes" + +/datum/sprite_accessory/ipc_screens/eyestall + name = "Tall Eyes" + icon_state = "eyestall" + +/datum/sprite_accessory/ipc_screens/eyesangry + name = "Angry Eyes" + icon_state = "eyesangry" + +/datum/sprite_accessory/ipc_screens/glider + name = "Glider" + icon_state = "glider" + +/datum/sprite_accessory/ipc_screens/goggles + name = "Goggles" + icon_state = "goggles" + +/datum/sprite_accessory/ipc_screens/exclamation + name = "Exclamation Point" + icon_state = "excla" + +/datum/sprite_accessory/ipc_screens/heart + name = "Heart" + icon_state = "heart" + +/datum/sprite_accessory/ipc_screens/L + name = "L" + icon_state = "l" + +/datum/sprite_accessory/ipc_screens/loading + name = "Loading" + icon_state = "loading" + +/datum/sprite_accessory/ipc_screens/monoeye + name = "Mono-eye" + icon_state = "monoeye" + +/datum/sprite_accessory/ipc_screens/nature + name = "Nature" + icon_state = "nature" + +/datum/sprite_accessory/ipc_screens/orange + name = "Orange" + icon_state = "orange" + +/datum/sprite_accessory/ipc_screens/pink + name = "Pink" + icon_state = "pink" + +/datum/sprite_accessory/ipc_screens/question + name = "Question Mark" + icon_state = "question" + +/datum/sprite_accessory/ipc_screens/ring + name = "Ring" + icon_state = "ring" + +/datum/sprite_accessory/ipc_screens/rainbow + name = "Rainbow" + icon_state = "rainbow" + color_src = 0 + +/datum/sprite_accessory/ipc_screens/rainbowtwo + name = "Rainbow (Diagonal)" + icon_state = "rainbowdiag" + color_src = 0 + +/datum/sprite_accessory/ipc_screens/redtext + name = "Red Text" + icon_state = "redtext" + color_src = 0 + +/datum/sprite_accessory/ipc_screens/rgb + name = "RGB" + icon_state = "rgb" + +/datum/sprite_accessory/ipc_screens/scroll + name = "Scanline" + icon_state = "scroll" + +/datum/sprite_accessory/ipc_screens/shower + name = "Shower" + icon_state = "shower" + +/datum/sprite_accessory/ipc_screens/sinewave + name = "Sinewave" + icon_state = "sinewave" + +/datum/sprite_accessory/ipc_screens/squarewave + name = "Square wave" + icon_state = "squarewave" + +/datum/sprite_accessory/ipc_screens/static_screen + name = "Static" + icon_state = "static" + +/datum/sprite_accessory/ipc_screens/stars + name = "Stars" + icon_state = "stars" + +/datum/sprite_accessory/ipc_screens/sad + name = "Sad" + icon_state = "sad" + +/datum/sprite_accessory/ipc_screens/smiley + name = "Smiley" + icon_state = "smile" + +/datum/sprite_accessory/ipc_screens/tetris + name = "Tetris" + icon_state = "tetris" + +/datum/sprite_accessory/ipc_screens/tv + name = "Color Test" + icon_state = "tv" + +/datum/sprite_accessory/ipc_screens/textdrop + name = "Text drop" + icon_state = "textdrop" + +/datum/sprite_accessory/ipc_screens/windowsxp + name = "Windows XP" + icon_state = "windowsxp" + +/datum/sprite_accessory/ipc_screens/yellow + name = "Yellow" + icon_state = "yellow" + +/datum/sprite_accessory/ipc_antennas + icon = 'icons/mob/ipc_accessories.dmi' + color_src = HAIR + +/datum/sprite_accessory/ipc_antennas/none + name = "None" + icon_state = "None" + +/datum/sprite_accessory/ipc_antennas/angled + name = "Angled" + icon_state = "antennae" + +/datum/sprite_accessory/ipc_antennas/antlers + name = "Antlers" + icon_state = "antlers" + +/datum/sprite_accessory/ipc_antennas/crowned + name = "Crowned" + icon_state = "crowned" + +/datum/sprite_accessory/ipc_antennas/cyberhead + name = "Cyberhead" + icon_state = "cyberhead" + +/datum/sprite_accessory/ipc_antennas/droneeyes + name = "Drone Eyes" + icon_state = "droneeyes" + +/datum/sprite_accessory/ipc_antennas/brokenlight + name = "Broken Light" + icon_state = "lightb" + +/datum/sprite_accessory/ipc_antennas/light + name = "Light" + icon_state = "light" + +/datum/sprite_accessory/ipc_antennas/sidelights + name = "Sidelights" + icon_state = "sidelights" + +/datum/sprite_accessory/ipc_antennas/tesla + name = "Tesla" + icon_state = "tesla" + +/datum/sprite_accessory/ipc_antennas/tv + name = "TV Antenna" + icon_state = "tvantennae" + +/datum/sprite_accessory/ipc_chassis // Used for changing limb icons, doesn't need to hold the actual icon. That's handled in ipc.dm + icon = null + icon_state = "who cares fuck you" // In order to pull the chassis correctly, we need AN icon_state(see line 36-39). It doesn't have to be useful, because it isn't used. + color_src = 0 + +/datum/sprite_accessory/ipc_chassis/mcgreyscale + name = "Morpheus Cyberkinetics(Greyscale)" + limbs_id = "mcgipc" + color_src = MUTCOLORS + +/datum/sprite_accessory/ipc_chassis/bishopcyberkinetics + name = "Bishop Cyberkinetics" + limbs_id = "bshipc" + +/datum/sprite_accessory/ipc_chassis/bishopcyberkinetics2 + name = "Bishop Cyberkinetics 2.0" + limbs_id = "bs2ipc" + +/datum/sprite_accessory/ipc_chassis/hephaestussindustries + name = "Hephaestus Industries" + limbs_id = "hsiipc" + +/datum/sprite_accessory/ipc_chassis/hephaestussindustries2 + name = "Hephaestus Industries 2.0" + limbs_id = "hi2ipc" + +/datum/sprite_accessory/ipc_chassis/shellguardmunitions + name = "Shellguard Munitions Standard Series" + limbs_id = "sgmipc" + +/datum/sprite_accessory/ipc_chassis/wardtakahashimanufacturing + name = "Ward-Takahashi Manufacturing" + limbs_id = "wtmipc" + +/datum/sprite_accessory/ipc_chassis/xionmanufacturinggroup + name = "Xion Manufacturing Group" + limbs_id = "xmgipc" + +/datum/sprite_accessory/ipc_chassis/xionmanufacturinggroup2 + name = "Xion Manufacturing Group 2.0" + limbs_id = "xm2ipc" + +/datum/sprite_accessory/ipc_chassis/zenghupharmaceuticals + name = "Zeng-Hu Pharmaceuticals" + limbs_id = "zhpipc" + ///////////////////////////// // Facial Hair Definitions // ///////////////////////////// diff --git a/code/modules/mob/living/brain/brain_item.dm b/code/modules/mob/living/brain/brain_item.dm index b44baf473c59..b1e8c0fbdfe4 100644 --- a/code/modules/mob/living/brain/brain_item.dm +++ b/code/modules/mob/living/brain/brain_item.dm @@ -247,6 +247,25 @@ desc = "We barely understand the brains of terrestial animals. Who knows what we may find in the brain of such an advanced species?" icon_state = "brain-x" +/obj/item/organ/brain/positron + name = "positronic brain" + slot = "brain" + zone = "chest" + status = ORGAN_ROBOTIC + desc = "A cube of shining metal, four inches to a side and covered in shallow grooves. It has an IPC serial number engraved on the top. In order for this Posibrain to be used as a newly built Positronic Brain, it must be coupled with an MMI." + icon = 'icons/obj/assemblies.dmi' + icon_state = "posibrain-ipc" + organ_flags = ORGAN_SYNTHETIC + +/obj/item/organ/brain/positron/emp_act(severity) + switch(severity) + if(1) + owner.adjustOrganLoss(ORGAN_SLOT_BRAIN, 60) + to_chat(owner, "Alert: Posibrain heavily damaged.") + if(2) + owner.adjustOrganLoss(ORGAN_SLOT_BRAIN, 25) + to_chat(owner, "Alert: Posibrain damaged.") + ////////////////////////////////////TRAUMAS//////////////////////////////////////// diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm index dd22356ae1fa..56025513d0e7 100644 --- a/code/modules/mob/living/carbon/carbon.dm +++ b/code/modules/mob/living/carbon/carbon.dm @@ -498,6 +498,9 @@ if(istype(src.loc, /obj/effect/dummy)) //cannot vomit while phasing/vomitcrawling return TRUE + if(!has_mouth()) + return TRUE + if(nutrition < 100 && !blood) if(message) visible_message(span_warning("[src] dry heaves!"), \ @@ -540,6 +543,11 @@ break return TRUE +/mob/living/carbon/has_mouth() + for(var/obj/item/bodypart/head/head in bodyparts) + if(head.mouth) + return TRUE + /mob/living/carbon/proc/spew_organ(power = 5, amt = 1) for(var/i in 1 to amt) if(!internal_organs.len) diff --git a/code/modules/mob/living/carbon/damage_procs.dm b/code/modules/mob/living/carbon/damage_procs.dm index 00fdba55682e..d8792357e901 100644 --- a/code/modules/mob/living/carbon/damage_procs.dm +++ b/code/modules/mob/living/carbon/damage_procs.dm @@ -66,7 +66,9 @@ if(amount > 0) take_overall_damage(amount, 0, 0, updating_health, required_status) else - heal_overall_damage(abs(amount), 0, 0, required_status ? required_status : BODYPART_ORGANIC, updating_health) + if(!required_status) + required_status = forced ? null : BODYPART_ORGANIC + heal_overall_damage(abs(amount), 0, 0, required_status, updating_health) return amount /mob/living/carbon/adjustFireLoss(amount, updating_health = TRUE, forced = FALSE, required_status) @@ -75,7 +77,9 @@ if(amount > 0) take_overall_damage(0, amount, 0, updating_health, required_status) else - heal_overall_damage(0, abs(amount), 0, required_status ? required_status : BODYPART_ORGANIC, updating_health) + if(!required_status) + required_status = forced ? null : BODYPART_ORGANIC + heal_overall_damage(0, abs(amount), 0, required_status, updating_health) return amount /mob/living/carbon/adjustToxLoss(amount, updating_health = TRUE, forced = FALSE) diff --git a/code/modules/mob/living/carbon/human/damage_procs.dm b/code/modules/mob/living/carbon/human/damage_procs.dm index 9e00ed48f476..560e56e273b0 100644 --- a/code/modules/mob/living/carbon/human/damage_procs.dm +++ b/code/modules/mob/living/carbon/human/damage_procs.dm @@ -2,3 +2,8 @@ /mob/living/carbon/human/apply_damage(damage = 0, damagetype = BRUTE, def_zone = null, blocked = FALSE, forced = FALSE, wound_bonus = 0, bare_wound_bonus = 0, sharpness = SHARP_NONE) return dna.species.apply_damage(damage, damagetype, def_zone, blocked, src, wound_bonus, bare_wound_bonus, sharpness) + +/mob/living/carbon/human/revive(full_heal = 0, admin_revive = 0) + if(..()) + if(dna && dna.species) + dna.species.spec_revival(src, admin_revive) diff --git a/code/modules/mob/living/carbon/human/death.dm b/code/modules/mob/living/carbon/human/death.dm index 2d486b5b20e5..906258d53dcd 100644 --- a/code/modules/mob/living/carbon/human/death.dm +++ b/code/modules/mob/living/carbon/human/death.dm @@ -1,20 +1,40 @@ /mob/living/carbon/human/gib_animation() - new /obj/effect/temp_visual/gib_animation(loc, "gibbed-h") + switch(dna.species.species_gibs) + if("human") + new /obj/effect/temp_visual/gib_animation(loc, "gibbed-h") + if("robotic") + new /obj/effect/temp_visual/gib_animation(loc, "gibbed-r") /mob/living/carbon/human/dust_animation() - new /obj/effect/temp_visual/dust_animation(loc, "dust-h") + switch(dna.species.species_gibs) + if("human") + new /obj/effect/temp_visual/dust_animation(loc, "dust-h") + if("robotic") + new /obj/effect/temp_visual/dust_animation(loc, "dust-r") /mob/living/carbon/human/spawn_gibs(with_bodyparts) if(with_bodyparts) - new /obj/effect/gibspawner/human(drop_location(), src, get_static_viruses()) + switch(dna.species.species_gibs) + if("human") + new /obj/effect/gibspawner/human(get_turf(src), dna, get_static_viruses()) + if("robotic") + new /obj/effect/gibspawner/robot(get_turf(src)) else - new /obj/effect/gibspawner/human/bodypartless(drop_location(), src, get_static_viruses()) + switch(dna.species.species_gibs) + if("human") + new /obj/effect/gibspawner/human(get_turf(src), dna, get_static_viruses()) + if("robotic") + new /obj/effect/gibspawner/robot(get_turf(src)) /mob/living/carbon/human/spawn_dust(just_ash = FALSE) if(just_ash) new /obj/effect/decal/cleanable/ash(loc) else - new /obj/effect/decal/remains/human(loc) + switch(dna.species.species_gibs) + if("human") + new /obj/effect/decal/remains/human(loc) + if("robotic") + new /obj/effect/decal/remains/robot(loc) /mob/living/carbon/human/death(gibbed) if(stat == DEAD) diff --git a/code/modules/mob/living/carbon/human/emote.dm b/code/modules/mob/living/carbon/human/emote.dm index 0298a3c7bf7f..0df8b5cdca74 100644 --- a/code/modules/mob/living/carbon/human/emote.dm +++ b/code/modules/mob/living/carbon/human/emote.dm @@ -217,3 +217,76 @@ if(isturf(loc)) var/turf/T = loc T.Entered(src) + +/datum/emote/living/carbon/human/robot_tongue/can_run_emote(mob/user, status_check = TRUE , intentional) + if(!..()) + return FALSE + var/obj/item/organ/tongue/T = user.getorganslot("tongue") + if(T.status == ORGAN_ROBOTIC) + return TRUE + +/datum/emote/living/carbon/human/robot_tongue/beep + key = "beep" + key_third_person = "beeps" + message = "beeps." + message_param = "beeps at %t." + +/datum/emote/living/carbon/human/robot_tongue/beep/get_sound(mob/living/user) + return 'sound/machines/twobeep.ogg' + +/datum/emote/living/carbon/human/robot_tongue/buzz + key = "buzz" + key_third_person = "buzzes" + message = "buzzes." + message_param = "buzzes at %t." + +/datum/emote/living/carbon/human/robot_tongue/buzz/get_sound(mob/living/user) + return 'sound/machines/buzz-sigh.ogg' + +/datum/emote/living/carbon/human/robot_tongue/buzz2 + key = "buzz2" + message = "buzzes twice." + +/datum/emote/living/carbon/human/robot_tongue/buzz2/get_sound(mob/living/user) + return 'sound/machines/buzz-two.ogg' + +/datum/emote/living/carbon/human/robot_tongue/chime + key = "chime" + key_third_person = "chimes" + message = "chimes." + +/datum/emote/living/carbon/human/robot_tongue/chime/get_sound(mob/living/user) + return 'sound/machines/chime.ogg' + +/datum/emote/living/carbon/human/robot_tongue/ping + key = "ping" + key_third_person = "pings" + message = "pings." + message_param = "pings at %t." + +/datum/emote/living/carbon/human/robot_tongue/ping/get_sound(mob/living/user) + return 'sound/machines/ping.ogg' + + // Clown Robotic Tongue ONLY. Henk. + +/datum/emote/living/carbon/human/robot_tongue/clown/can_run_emote(mob/user, status_check = TRUE , intentional) + if(!..()) + return FALSE + if(user.mind.assigned_role == "Clown") + return TRUE + +/datum/emote/living/carbon/human/robot_tongue/clown/honk + key = "honk" + key_third_person = "honks" + message = "honks." + +/datum/emote/living/carbon/human/robot_tongue/clown/honk/get_sound(mob/living/user) + return 'sound/items/bikehorn.ogg' + +/datum/emote/living/carbon/human/robot_tongue/clown/sad + key = "sad" + key_third_person = "plays a sad trombone..." + message = "plays a sad trombone..." + +/datum/emote/living/carbon/human/robot_tongue/clown/sad/run_emote(mob/living/user) + return 'sound/misc/sadtrombone.ogg' diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index 85fc9056735c..3f5cbee2575f 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -1244,6 +1244,9 @@ /mob/living/carbon/human/species/mush race = /datum/species/mush +/mob/living/carbon/human/species/ipc + race = /datum/species/ipc + /mob/living/carbon/human/species/plasma race = /datum/species/plasmaman diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm index fb00847de8ba..67faae40f388 100644 --- a/code/modules/mob/living/carbon/human/human_defense.dm +++ b/code/modules/mob/living/carbon/human/human_defense.dm @@ -192,6 +192,12 @@ zone_hit_chance += 10 affecting = get_bodypart(ran_zone(user.zone_selected, zone_hit_chance)) var/target_area = parse_zone(check_zone(user.zone_selected)) //our intended target + if(affecting) + if(I.force && I.damtype != STAMINA && affecting.status == BODYPART_ROBOTIC) // Bodpart_robotic sparks when hit, but only when it does real damage + if(I.force >= 5) + do_sparks(1, FALSE, loc) + if(prob(25)) + new /obj/effect/decal/cleanable/oil(loc) SEND_SIGNAL(I, COMSIG_ITEM_ATTACK_ZONE, src, user, affecting) @@ -550,6 +556,10 @@ L.receive_damage(0,5) Paralyze(100) + if((TRAIT_EASYDISMEMBER in L.owner.dna.species.species_traits) && L.body_zone != "chest") + if(prob(20)) + L.dismember(BRUTE) + /mob/living/carbon/human/acid_act(acidpwr, acid_volume, bodyzone_hit) //todo: update this to utilize check_obscured_slots() //and make sure it's check_obscured_slots(TRUE) to stop aciding through visors etc var/list/damaged = list() var/list/inventory_items_to_kill = list() diff --git a/code/modules/mob/living/carbon/human/species.dm b/code/modules/mob/living/carbon/human/species.dm index efba5abde66f..6ee08e47b21b 100644 --- a/code/modules/mob/living/carbon/human/species.dm +++ b/code/modules/mob/living/carbon/human/species.dm @@ -79,6 +79,14 @@ GLOBAL_LIST_EMPTY(mentor_races) var/acidmod = 1 /// multiplier for stun duration var/stunmod = 1 + /// multiplier for oxyloss + var/oxymod = 1 + /// multiplier for cloneloss + var/clonemod = 1 + /// multiplier for toxloss + var/toxmod = 1 + /// multiplier for stun duration + var/staminamod = 1 /// multiplier for money paid at payday, species dependent var/payday_modifier = 1 ///Type of damage attack does @@ -111,6 +119,12 @@ GLOBAL_LIST_EMPTY(mentor_races) var/datum/action/innate/flight/fly ///the icon used for the wings var/wings_icon = "Angel" + /// Used for metabolizing reagents. We're going to assume you're a meatbag unless you say otherwise. + var/reagent_tag = PROCESS_ORGANIC + /// What kind of gibs to spawn + var/species_gibs = "human" + /// Can this species use numbers in its name? + var/allow_numbers_in_name = FALSE /// species-only traits. Can be found in DNA.dm var/list/species_traits = list() @@ -354,6 +368,12 @@ GLOBAL_LIST_EMPTY(mentor_races) if(I) I.Remove(C) QDEL_NULL(I) + else + for(var/mutantorgan in mutant_organs) + var/obj/item/organ/I = C.getorgan(mutantorgan) + if(I) + I.Remove(C) + QDEL_NULL(I) for(var/path in mutant_organs) var/obj/item/organ/I = new path() @@ -403,6 +423,15 @@ GLOBAL_LIST_EMPTY(mentor_races) else //Entries in the list should only ever be items or null, so if it's not an item, we can assume it's an empty hand C.put_in_hands(new mutanthands()) + if(ROBOTIC_LIMBS in species_traits) + for(var/obj/item/bodypart/B in C.bodyparts) + B.change_bodypart_status(BODYPART_ROBOTIC) // Makes all Bodyparts robotic. + B.render_like_organic = TRUE + + if(NOMOUTH in species_traits) + for(var/obj/item/bodypart/head/head in C.bodyparts) + head.mouth = FALSE + for(var/X in inherent_traits) ADD_TRAIT(C, X, SPECIES_TRAIT) @@ -424,6 +453,13 @@ GLOBAL_LIST_EMPTY(mentor_races) C.dna.blood_type = random_blood_type() if(DIGITIGRADE in species_traits) C.Digitigrade_Leg_Swap(TRUE) + if(ROBOTIC_LIMBS in species_traits) + for(var/obj/item/bodypart/B in C.bodyparts) + B.change_bodypart_status(BODYPART_ORGANIC, FALSE, TRUE) + B.render_like_organic = FALSE + if(NOMOUTH in species_traits) + for(var/obj/item/bodypart/head/head in C.bodyparts) + head.mouth = TRUE for(var/X in inherent_traits) REMOVE_TRAIT(C, X, SPECIES_TRAIT) @@ -795,6 +831,14 @@ GLOBAL_LIST_EMPTY(mentor_races) bodyparts_to_add -= "wings_open" else if ("wings" in mutant_bodyparts) bodyparts_to_add -= "wings_open" + + if("ipc_screen" in mutant_bodyparts) + if(!H.dna.features["ipc_screen"] || H.dna.features["ipc_screen"] == "None" || (H.wear_mask && (H.wear_mask.flags_inv & HIDEEYES)) || !HD) + bodyparts_to_add -= "ipc_screen" + + if("ipc_antenna" in mutant_bodyparts) + if(!H.dna.features["ipc_antenna"] || H.dna.features["ipc_antenna"] == "None" || H.head && (H.head.flags_inv & HIDEHAIR) || (H.wear_mask && (H.wear_mask.flags_inv & HIDEHAIR)) || !HD) + bodyparts_to_add -= "ipc_antenna" if("teeth" in mutant_bodyparts) if((H.wear_mask && (H.wear_mask.flags_inv & HIDEFACE)) || (H.head && (H.head.flags_inv & HIDEFACE)) || !HD || HD.status == BODYPART_ROBOTIC) @@ -912,6 +956,12 @@ GLOBAL_LIST_EMPTY(mentor_races) S = GLOB.dorsal_tubes_list[H.dna.features["dorsal_tubes"]] if("ethereal_mark") S = GLOB.ethereal_mark_list[H.dna.features["ethereal_mark"]] + if("ipc_screen") + S = GLOB.ipc_screens_list[H.dna.features["ipc_screen"]] + if("ipc_antenna") + S = GLOB.ipc_antennas_list[H.dna.features["ipc_antenna"]] + if("ipc_chassis") + S = GLOB.ipc_chassis_list[H.dna.features["ipc_chassis"]] if(!S || S.icon_state == "none") continue @@ -1209,11 +1259,24 @@ GLOBAL_LIST_EMPTY(mentor_races) /datum/species/proc/after_equip_job(datum/job/J, mob/living/carbon/human/H) H.update_mutant_bodyparts() +// Do species-specific reagent handling here +// Return 1 if it should do normal processing too +// Return 0 if it shouldn't deplete and do its normal effect +// Other return values will cause weird badness /datum/species/proc/handle_chemicals(datum/reagent/chem, mob/living/carbon/human/H) if(chem.type == exotic_blood) H.blood_volume = min(H.blood_volume + round(chem.volume, 0.1), BLOOD_VOLUME_MAXIMUM(H)) H.reagents.del_reagent(chem.type) - return 1 + return TRUE + //This handles dumping unprocessable reagents. + var/dump_reagent = TRUE + if((chem.process_flags & SYNTHETIC) && (H.dna.species.reagent_tag & PROCESS_SYNTHETIC)) //SYNTHETIC-oriented reagents require PROCESS_SYNTHETIC + dump_reagent = FALSE + if((chem.process_flags & ORGANIC) && (H.dna.species.reagent_tag & PROCESS_ORGANIC)) //ORGANIC-oriented reagents require PROCESS_ORGANIC + dump_reagent = FALSE + if(dump_reagent) + chem.holder.remove_reagent(chem.type, chem.metabolization_rate) + return TRUE return FALSE /datum/species/proc/check_species_weakness(obj/item, mob/living/attacker) @@ -1291,6 +1354,9 @@ GLOBAL_LIST_EMPTY(mentor_races) to_chat(H, span_notice("You no longer feel vigorous.")) H.metabolism_efficiency = 1 + get_hunger_alert(H) + +/datum/species/proc/get_hunger_alert(mob/living/carbon/human/H) switch(H.nutrition) if(NUTRITION_LEVEL_FULL to INFINITY) H.throw_alert("nutrition", /obj/screen/alert/fat) @@ -1796,14 +1862,14 @@ GLOBAL_LIST_EMPTY(mentor_races) else H.adjustFireLoss(damage * hit_percent * burnmod * H.physiology.burn_mod) if(TOX) - H.adjustToxLoss(damage * hit_percent * H.physiology.tox_mod) + H.adjustToxLoss(damage * hit_percent * toxmod * H.physiology.tox_mod) if(OXY) - H.adjustOxyLoss(damage * hit_percent * H.physiology.oxy_mod) + H.adjustOxyLoss(damage * hit_percent * oxymod * H.physiology.oxy_mod) if(CLONE) - H.adjustCloneLoss(damage * hit_percent * H.physiology.clone_mod) + H.adjustCloneLoss(damage * hit_percent * clonemod * H.physiology.clone_mod) if(STAMINA) if(BP) - if(BP.receive_damage(0, 0, damage * hit_percent * H.physiology.stamina_mod)) + if(BP.receive_damage(0, 0, damage * staminamod * hit_percent * H.physiology.stamina_mod)) H.update_stamina() else H.adjustStaminaLoss(damage * hit_percent * H.physiology.stamina_mod) @@ -2020,6 +2086,8 @@ GLOBAL_LIST_EMPTY(mentor_races) /datum/species/proc/ExtinguishMob(mob/living/carbon/human/H) return +/datum/species/proc/spec_revival(mob/living/carbon/human/H, admin_revive = FALSE) + return //////////// // Stun // diff --git a/code/modules/mob/living/carbon/human/species_types/IPC.dm b/code/modules/mob/living/carbon/human/species_types/IPC.dm new file mode 100644 index 000000000000..0621a1c412a6 --- /dev/null +++ b/code/modules/mob/living/carbon/human/species_types/IPC.dm @@ -0,0 +1,201 @@ +/datum/species/ipc // im fucking lazy mk2 and cant get sprites to normally work + name = "IPC" //inherited from the real species, for health scanners and things + id = "ipc" + say_mod = "states" //inherited from a user's real species + sexes = FALSE + species_traits = list(NOTRANSSTING,NOEYESPRITES,NO_DNA_COPY,TRAIT_EASYDISMEMBER,ROBOTIC_LIMBS,NOZOMBIE,MUTCOLORS,NOHUSK,NOMOUTH,AGENDER,NOBLOOD) + inherent_traits = list(TRAIT_RESISTCOLD,TRAIT_NOBREATH,TRAIT_RADIMMUNE,TRAIT_LIMBATTACHMENT,TRAIT_NOCRITDAMAGE,TRAIT_GENELESS,TRAIT_MEDICALIGNORE,TRAIT_NOCLONE,TRAIT_TOXIMMUNE,TRAIT_EASILY_WOUNDED,TRAIT_NODEFIB) + inherent_biotypes = list(MOB_ROBOTIC, MOB_HUMANOID) + mutant_brain = /obj/item/organ/brain/positron + mutant_heart = /obj/item/organ/heart/cybernetic/ipc + mutanteyes = /obj/item/organ/eyes/robotic + mutanttongue = /obj/item/organ/tongue/robot + mutantliver = /obj/item/organ/liver/cybernetic/upgraded/ipc + mutantstomach = /obj/item/organ/stomach/cell + mutantears = /obj/item/organ/ears/robot + mutant_organs = list(/obj/item/organ/cyberimp/arm/power_cord) + mutant_bodyparts = list("ipc_screen", "ipc_antenna", "ipc_chassis") + default_features = list("mcolor" = "#7D7D7D", "ipc_screen" = "Static", "ipc_antenna" = "None", "ipc_chassis" = "Morpheus Cyberkinetics(Greyscale)") + meat = /obj/item/stack/sheet/plasteel{amount = 5} + skinned_type = /obj/item/stack/sheet/metal{amount = 10} + exotic_blood = /datum/reagent/oil + damage_overlay_type = "synth" + limbs_id = "synth" + payday_modifier = 0.6 //Mass producible labor + burnmod = 1.5 + heatmod = 1 + brutemod = 1 + toxmod = 0 + clonemod = 0 + staminamod = 0.8 + siemens_coeff = 1.75 + reagent_tag = PROCESS_SYNTHETIC + species_gibs = "robotic" + attack_sound = 'sound/items/trayhit1.ogg' + allow_numbers_in_name = TRUE + deathsound = "sound/voice/borg_deathsound.ogg" + var/saved_screen //for saving the screen when they die + changesource_flags = MIRROR_BADMIN | WABBAJACK + + var/datum/action/innate/change_screen/change_screen + +/datum/species/ipc/random_name(unique) + var/ipc_name = "[pick(GLOB.posibrain_names)]-[rand(100, 999)]" + return ipc_name + +/datum/species/ipc/on_species_gain(mob/living/carbon/C) // Let's make that IPC actually robotic. + . = ..() + var/obj/item/organ/appendix/A = C.getorganslot(ORGAN_SLOT_APPENDIX) // Easiest way to remove it. + if(A) + A.Remove(C) + QDEL_NULL(A) + var/obj/item/organ/lungs/L = C.getorganslot(ORGAN_SLOT_LUNGS) + if(L) + L.Remove(C) + QDEL_NULL(L) + if(ishuman(C) && !change_screen) + change_screen = new + change_screen.Grant(C) + for(var/obj/item/bodypart/O in C.bodyparts) + O.render_like_organic = TRUE // Makes limbs render like organic limbs instead of augmented limbs, check bodyparts.dm + var/chassis = C.dna.features["ipc_chassis"] + var/datum/sprite_accessory/ipc_chassis/chassis_of_choice = GLOB.ipc_chassis_list[chassis] + C.dna.species.limbs_id = chassis_of_choice.limbs_id + if(chassis_of_choice.color_src == MUTCOLORS && !(MUTCOLORS in C.dna.species.species_traits)) // If it's a colorable(Greyscale) chassis, we use MUTCOLORS. + C.dna.species.species_traits += MUTCOLORS + else if(MUTCOLORS in C.dna.species.species_traits) + C.dna.species.species_traits -= MUTCOLORS + +datum/species/ipc/on_species_loss(mob/living/carbon/C) + . = ..() + if(change_screen) + change_screen.Remove(C) + +/datum/species/ipc/proc/handle_speech(datum/source, list/speech_args) + speech_args[SPEECH_SPANS] |= SPAN_ROBOT + +/datum/species/ipc/after_equip_job(datum/job/J, mob/living/carbon/human/H) + H.grant_language(/datum/language/machine) + +/datum/species/ipc/spec_death(gibbed, mob/living/carbon/C) + saved_screen = C.dna.features["ipc_screen"] + C.dna.features["ipc_screen"] = "BSOD" + C.update_body() + addtimer(CALLBACK(src, .proc/post_death, C), 5 SECONDS) + +/datum/species/ipc/proc/post_death(mob/living/carbon/C) + if(C.stat < DEAD) + return + C.dna.features["ipc_screen"] = null //Turns off screen on death + C.update_body() + +/datum/action/innate/change_screen + name = "Change Display" + check_flags = AB_CHECK_CONSCIOUS + icon_icon = 'icons/mob/actions/actions_silicon.dmi' + button_icon_state = "drone_vision" + +/datum/action/innate/change_screen/Activate() + var/screen_choice = input(usr, "Which screen do you want to use?", "Screen Change") as null | anything in GLOB.ipc_screens_list + var/color_choice = input(usr, "Which color do you want your screen to be?", "Color Change") as null | color + if(!screen_choice) + return + if(!color_choice) + return + if(!ishuman(owner)) + return + var/mob/living/carbon/human/H = owner + H.dna.features["ipc_screen"] = screen_choice + H.eye_color = sanitize_hexcolor(color_choice) + H.update_body() + +/obj/item/apc_powercord + name = "power cord" + desc = "An internal power cord hooked up to a battery. Useful if you run on electricity. Not so much otherwise." + icon = 'icons/obj/power.dmi' + icon_state = "wire1" + +/obj/item/apc_powercord/afterattack(atom/target, mob/user, proximity_flag, click_parameters) + if(!istype(target, /obj/machinery/power/apc) || !ishuman(user) || !proximity_flag) + return ..() + user.changeNext_move(CLICK_CD_MELEE) + var/obj/machinery/power/apc/A = target + var/mob/living/carbon/human/H = user + var/obj/item/organ/stomach/cell/cell = locate(/obj/item/organ/stomach/cell) in H.internal_organs + if(!cell) + to_chat(H, "You try to siphon energy from the [A], but your power cell is gone!") + return + + if(A.cell && A.cell.charge > 0) + if(H.nutrition >= NUTRITION_LEVEL_MOSTLY_FULL) + to_chat(user, "You are already fully charged!") + return + else + powerdraw_loop(A, H) + return + + to_chat(user, "There is no charge to draw from that APC.") + +/obj/item/apc_powercord/proc/powerdraw_loop(obj/machinery/power/apc/A, mob/living/carbon/human/H) + H.visible_message("[H] inserts a power connector into the [A].", "You begin to draw power from the [A].") + while(do_after(H, 1 SECONDS, target = A)) + if(loc != H) + to_chat(H, "You must keep your connector out while charging!") + break + if(A.cell.charge == 0) + to_chat(H, "The [A] doesn't have enough charge to spare.") + break + if(H.nutrition > NUTRITION_LEVEL_MOSTLY_FULL) + to_chat(H, "You are now fully charged.") + break + A.charging = 1 + if(A.cell.charge >= 500) + H.nutrition += 50 + A.cell.charge -= 250 + to_chat(H, "You siphon off some of the stored charge for your own use.") + else + H.nutrition += A.cell.charge/10 + A.cell.charge = 0 + to_chat(H, "You siphon off as much as the [A] can spare.") + break + H.visible_message("[H] unplugs from the [A].", "You unplug from the [A].") + +/datum/species/ipc/get_hunger_alert(mob/living/carbon/human/H) + switch(H.nutrition) + if(NUTRITION_LEVEL_FED to INFINITY) + H.clear_alert("nutrition") + if(NUTRITION_LEVEL_HUNGRY to NUTRITION_LEVEL_FED) + H.throw_alert("nutrition", /obj/screen/alert/lowcell, 2) + if(NUTRITION_LEVEL_STARVING to NUTRITION_LEVEL_HUNGRY) + H.throw_alert("nutrition", /obj/screen/alert/lowcell, 3) + if(0 to NUTRITION_LEVEL_STARVING) + H.throw_alert("nutrition", /obj/screen/alert/emptycell) + +/datum/species/ipc/spec_revival(mob/living/carbon/human/H, admin_revive) + if(admin_revive) + return ..() + to_chat(H, span_notice("You do not remember your death, how you died, or who killed you. See rule 1.7.")) + H.Stun(9 SECONDS) // No moving either + H.dna.features["ipc_screen"] = "BSOD" + H.update_body() + addtimer(CALLBACK(src, .proc/afterrevive, H), 0) + return + +/datum/species/ipc/proc/afterrevive(mob/living/carbon/human/H) + H.say("Reactivating [pick("core systems", "central subroutines", "key functions")]...") + sleep(3 SECONDS) + H.say("Reinitializing [pick("personality matrix", "behavior logic", "morality subsystems")]...") + sleep(3 SECONDS) + H.say("Finalizing setup...") + sleep(3 SECONDS) + H.say("Unit [H.real_name] is fully functional. Have a nice day.") + H.dna.features["ipc_screen"] = saved_screen + H.update_body() + +/datum/species/ipc/spec_life(mob/living/carbon/human/H) + . = ..() + if(H.health <= HEALTH_THRESHOLD_FULLCRIT && H.stat != DEAD) // So they die eventually instead of being stuck in crit limbo. + H.adjustFireLoss(6) // After bodypart_robotic resistance this is ~2/second + if(prob(5)) + to_chat(H, "Alert: Internal temperature regulation systems offline; thermal damage sustained. Shutdown imminent.") + H.visible_message("[H]'s cooling system fans stutter and stall. There is a faint, yet rapid beeping coming from inside their chassis.") diff --git a/code/modules/mob/living/carbon/human/species_types/flypeople.dm b/code/modules/mob/living/carbon/human/species_types/flypeople.dm index e642bf54a83e..4ccd49169019 100644 --- a/code/modules/mob/living/carbon/human/species_types/flypeople.dm +++ b/code/modules/mob/living/carbon/human/species_types/flypeople.dm @@ -15,11 +15,8 @@ /datum/species/fly/handle_chemicals(datum/reagent/chem, mob/living/carbon/human/H) if(chem.type == /datum/reagent/toxin/pestkiller) H.adjustToxLoss(3) - H.reagents.remove_reagent(chem.type, REAGENTS_METABOLISM) - return 1 - - -/datum/species/fly/handle_chemicals(datum/reagent/chem, mob/living/carbon/human/H) + H.reagents.remove_reagent(chem.type, chem.metabolization_rate) + return TRUE if(istype(chem, /datum/reagent/consumable)) var/datum/reagent/consumable/nutri_check = chem if(nutri_check.nutriment_factor > 0) @@ -28,7 +25,7 @@ playsound(pos, 'sound/effects/splat.ogg', 50, 1) H.visible_message(span_danger("[H] vomits on the floor!"), \ span_userdanger("You throw up on the floor!")) - ..() + return ..() /datum/species/fly/check_species_weakness(obj/item/weapon, mob/living/attacker) if(istype(weapon, /obj/item/melee/flyswatter)) diff --git a/code/modules/mob/living/carbon/human/species_types/golems.dm b/code/modules/mob/living/carbon/human/species_types/golems.dm index 75de5f595fea..153299b2a3f6 100644 --- a/code/modules/mob/living/carbon/human/species_types/golems.dm +++ b/code/modules/mob/living/carbon/human/species_types/golems.dm @@ -319,8 +319,9 @@ /datum/species/golem/wood/handle_chemicals(datum/reagent/chem, mob/living/carbon/human/H) if(chem.type == /datum/reagent/toxin/plantbgone) H.adjustToxLoss(3) - H.reagents.remove_reagent(chem.type, REAGENTS_METABOLISM) - return 1 + H.reagents.remove_reagent(chem.type, chem.metabolization_rate) + return TRUE + return ..() /datum/species/golem/wood/holy //slightly upgraded wood golem, for the plant sect id = "holy wood golem" @@ -666,12 +667,15 @@ /datum/species/golem/runic/handle_chemicals(datum/reagent/chem, mob/living/carbon/human/H) if(istype(chem, /datum/reagent/water/holywater)) H.adjustFireLoss(4) - H.reagents.remove_reagent(chem.type, REAGENTS_METABOLISM) + H.reagents.remove_reagent(chem.type, chem.metabolization_rate) + return TRUE if(chem.type == /datum/reagent/fuel/unholywater) H.adjustBruteLoss(-4) H.adjustFireLoss(-4) - H.reagents.remove_reagent(chem.type, REAGENTS_METABOLISM) + H.reagents.remove_reagent(chem.type, chem.metabolization_rate) + return TRUE + return ..() /datum/species/golem/clockwork diff --git a/code/modules/mob/living/carbon/human/species_types/mothmen.dm b/code/modules/mob/living/carbon/human/species_types/mothmen.dm index ed5d40d9ab6b..2dd46069b039 100644 --- a/code/modules/mob/living/carbon/human/species_types/mothmen.dm +++ b/code/modules/mob/living/carbon/human/species_types/mothmen.dm @@ -52,10 +52,11 @@ handle_mutant_bodyparts(H) /datum/species/moth/handle_chemicals(datum/reagent/chem, mob/living/carbon/human/H) - . = ..() if(chem.type == /datum/reagent/toxin/pestkiller) H.adjustToxLoss(3) - H.reagents.remove_reagent(chem.type, REAGENTS_METABOLISM) + H.reagents.remove_reagent(chem.type, chem.metabolization_rate) + return FALSE + return ..() /datum/species/moth/check_species_weakness(obj/item/weapon, mob/living/attacker) if(istype(weapon, /obj/item/melee/flyswatter)) diff --git a/code/modules/mob/living/carbon/human/species_types/mushpeople.dm b/code/modules/mob/living/carbon/human/species_types/mushpeople.dm index 55a866552229..13761a2d60b5 100644 --- a/code/modules/mob/living/carbon/human/species_types/mushpeople.dm +++ b/code/modules/mob/living/carbon/human/species_types/mushpeople.dm @@ -53,6 +53,7 @@ H.adjustToxLoss(3) H.reagents.remove_reagent(chem.type, REAGENTS_METABOLISM) return TRUE + return ..() /datum/species/mush/handle_mutant_bodyparts(mob/living/carbon/human/H, forced_colour) forced_colour = FALSE diff --git a/code/modules/mob/living/carbon/human/species_types/podpeople.dm b/code/modules/mob/living/carbon/human/species_types/podpeople.dm index e069593fe10b..21909d3dac9c 100644 --- a/code/modules/mob/living/carbon/human/species_types/podpeople.dm +++ b/code/modules/mob/living/carbon/human/species_types/podpeople.dm @@ -60,8 +60,9 @@ DISREGUARD THIS FILE IF YOU'RE INTENDING TO CHANGE ASPECTS OF PLAYER CONTROLLED /datum/species/pod/handle_chemicals(datum/reagent/chem, mob/living/carbon/human/H) if(chem.type == /datum/reagent/toxin/plantbgone) H.adjustToxLoss(3) - H.reagents.remove_reagent(chem.type, REAGENTS_METABOLISM) - return 1 + H.reagents.remove_reagent(chem.type, chem.metabolization_rate) + return TRUE + return ..() /datum/species/pod/on_hit(obj/item/projectile/P, mob/living/carbon/human/H) switch(P.type) diff --git a/code/modules/mob/living/carbon/human/species_types/skeletons.dm b/code/modules/mob/living/carbon/human/species_types/skeletons.dm index c69328592f05..00aea5c35357 100644 --- a/code/modules/mob/living/carbon/human/species_types/skeletons.dm +++ b/code/modules/mob/living/carbon/human/species_types/skeletons.dm @@ -5,7 +5,7 @@ say_mod = "rattles" sexes = 0 meat = /obj/item/reagent_containers/food/snacks/meat/slab/human/mutant/skeleton - species_traits = list(NOBLOOD,HAS_BONE, NO_DNA_COPY, NOTRANSSTING) + species_traits = list(NOBLOOD,HAS_BONE, NO_DNA_COPY, NOTRANSSTING, NOHUSK) inherent_traits = list(TRAIT_RESISTHEAT,TRAIT_NOBREATH,TRAIT_RESISTCOLD,TRAIT_RESISTHIGHPRESSURE,TRAIT_RESISTLOWPRESSURE,TRAIT_RADIMMUNE,TRAIT_GENELESS,TRAIT_PIERCEIMMUNE,TRAIT_NOHUNGER,TRAIT_EASYDISMEMBER,TRAIT_LIMBATTACHMENT,TRAIT_FAKEDEATH, TRAIT_CALCIUM_HEALER) inherent_biotypes = list(MOB_UNDEAD, MOB_HUMANOID) mutanttongue = /obj/item/organ/tongue/bone diff --git a/code/modules/mob/living/carbon/human/species_types/snail.dm b/code/modules/mob/living/carbon/human/species_types/snail.dm index 0e59b5c1a3a9..c53f045b9e7c 100644 --- a/code/modules/mob/living/carbon/human/species_types/snail.dm +++ b/code/modules/mob/living/carbon/human/species_types/snail.dm @@ -24,8 +24,9 @@ if(istype(chem,/datum/reagent/consumable/sodiumchloride)) H.adjustFireLoss(2) playsound(H, 'sound/weapons/sear.ogg', 30, 1) - H.reagents.remove_reagent(chem.type, REAGENTS_METABOLISM) - return 1 + H.reagents.remove_reagent(chem.type, chem.metabolization_rate) + return TRUE + return ..() /datum/species/snail/on_species_gain(mob/living/carbon/C, datum/species/old_species, pref_load) . = ..() diff --git a/code/modules/mob/living/carbon/human/status_procs.dm b/code/modules/mob/living/carbon/human/status_procs.dm index 96c48b4695e9..bba496986004 100644 --- a/code/modules/mob/living/carbon/human/status_procs.dm +++ b/code/modules/mob/living/carbon/human/status_procs.dm @@ -32,7 +32,7 @@ update_hair() /mob/living/carbon/human/become_husk(source) - if(istype(dna.species, /datum/species/skeleton)) //skeletons shouldn't be husks. + if(NOHUSK in dna.species.species_traits) cure_husk() return . = ..() diff --git a/code/modules/mob/living/emote.dm b/code/modules/mob/living/emote.dm index 705c3b3ace1d..4b257165b7bb 100644 --- a/code/modules/mob/living/emote.dm +++ b/code/modules/mob/living/emote.dm @@ -78,6 +78,7 @@ message_alien = "lets out a waning guttural screech, green blood bubbling from its maw..." message_larva = "lets out a sickly hiss of air and falls limply to the floor..." message_monkey = "lets out a faint chimper as it collapses and stops moving..." + message_ipc = "gives one shrill beep before falling limp, their monitor flashing blue before completely shutting off..." message_simple = "stops moving..." stat_allowed = UNCONSCIOUS cooldown = 3.4 SECONDS diff --git a/code/modules/mob/living/simple_animal/bot/medbot.dm b/code/modules/mob/living/simple_animal/bot/medbot.dm index 9275b22e9da2..bbfce8b02cc1 100644 --- a/code/modules/mob/living/simple_animal/bot/medbot.dm +++ b/code/modules/mob/living/simple_animal/bot/medbot.dm @@ -454,12 +454,23 @@ if(C.suiciding) return FALSE //Kevorkian school of robotic medical assistants. + if(istype(C.dna.species, /datum/species/ipc)) + return FALSE + if(emagged == 2) //Everyone needs our medicine. (Our medicine is toxins) return TRUE if(HAS_TRAIT(C,TRAIT_MEDICALIGNORE)) return FALSE + var/can_inject = FALSE + for(var/X in C.bodyparts) + var/obj/item/bodypart/part = X + if(part.status == BODYPART_ORGANIC) + can_inject = TRUE + if(!can_inject) + return FALSE + if(ishuman(C)) var/mob/living/carbon/human/H = C if (H.wear_suit && H.head && istype(H.wear_suit, /obj/item/clothing) && istype(H.head, /obj/item/clothing)) diff --git a/code/modules/mob/living/simple_animal/slime/powers.dm b/code/modules/mob/living/simple_animal/slime/powers.dm index 288bfcad28d8..fb8dd65339bf 100644 --- a/code/modules/mob/living/simple_animal/slime/powers.dm +++ b/code/modules/mob/living/simple_animal/slime/powers.dm @@ -82,6 +82,12 @@ to_chat(src, span_warning("I can't latch onto another slime...")) return FALSE + if(isipc(M)) + if(silent) + return FALSE + to_chat(src, "This subject does not have life energy...") + return FALSE + if(docile) if(silent) return FALSE diff --git a/code/modules/mob/living/taste.dm b/code/modules/mob/living/taste.dm index 16140749c7b9..e8e8ce1fd808 100644 --- a/code/modules/mob/living/taste.dm +++ b/code/modules/mob/living/taste.dm @@ -1,4 +1,5 @@ #define DEFAULT_TASTE_SENSITIVITY 15 +#define NO_TASTE_SENSITIVITY -1 /mob/living var/last_taste_time @@ -12,7 +13,7 @@ if(istype(tongue) && !HAS_TRAIT(src, TRAIT_AGEUSIA)) . = tongue.taste_sensitivity else - . = 101 // can't taste anything without a tongue + . = NO_TASTE_SENSITIVITY // can't taste anything without a tongue // non destructively tastes a reagent container /mob/living/proc/taste(datum/reagents/from) @@ -25,7 +26,7 @@ text_output = pick("spiders","dreams","nightmares","the future","the past","victory",\ "defeat","pain","bliss","revenge","poison","time","space","death","life","truth","lies","justice","memory",\ "regrets","your soul","suffering","music","noise","blood","hunger","the american way") - if(text_output != last_taste_text || last_taste_time + 100 < world.time) + if((text_output != last_taste_text || last_taste_time + 100 < world.time) && (taste_sensitivity != NO_TASTE_SENSITIVITY)) to_chat(src, span_notice("You can taste [text_output].")) // "something indescribable" -> too many tastes, not enough flavor. diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm index 7ea7e13d9ce0..5195a60f6f0e 100644 --- a/code/modules/mob/mob_helpers.dm +++ b/code/modules/mob/mob_helpers.dm @@ -563,6 +563,9 @@ /mob/proc/can_hear() . = TRUE +/mob/proc/has_mouth() + return FALSE + /** * Examine text for traits shared by multiple types. * diff --git a/code/modules/power/apc.dm b/code/modules/power/apc.dm index bb7b0efc8cd7..b0a2ca6165f0 100644 --- a/code/modules/power/apc.dm +++ b/code/modules/power/apc.dm @@ -749,6 +749,8 @@ locked = FALSE update_icon() return + else if(istype(W, /obj/item/apc_powercord)) + return //because we put our fancy code in the right places, and this is all in the powercord's afterattack() else if(panel_open && !opened && is_wire_tool(W)) wires.interact(user) else diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm index ca2cd87abf4c..3f1dcf0b6ec6 100644 --- a/code/modules/projectiles/projectile.dm +++ b/code/modules/projectiles/projectile.dm @@ -183,6 +183,11 @@ new /obj/effect/temp_visual/dir_setting/bloodsplatter(target_loca, splatter_dir) else new /obj/effect/temp_visual/dir_setting/bloodsplatter/genericsplatter(target_loca, splatter_dir) + var/obj/item/bodypart/B = L.get_bodypart(def_zone) + if(B.status == BODYPART_ROBOTIC) // So if you hit a robotic, it sparks instead of bloodspatters + do_sparks(2, FALSE, target.loc) + if(prob(25)) + new /obj/effect/decal/cleanable/oil(target_loca) if(prob(33)) L.add_splatter_floor(target_loca) else if(impact_effect_type && !hitscan) diff --git a/code/modules/reagents/chemistry/holder.dm b/code/modules/reagents/chemistry/holder.dm index 019525bc6f93..9e616ab30b47 100644 --- a/code/modules/reagents/chemistry/holder.dm +++ b/code/modules/reagents/chemistry/holder.dm @@ -322,12 +322,13 @@ continue if(!C) C = R.holder.my_atom + //If you got this far, that means we can process whatever reagent this iteration is for. Handle things normally from here. if(!R.metabolizing) R.metabolizing = TRUE R.on_mob_metabolize(C) if(C && R) - if(C.reagent_check(R) != 1) + if(C.reagent_check(R) != 1) //Most relevant to Humans, this handles species-specific chem interactions. if(can_overdose) if(R.overdose_threshold) if(R.volume >= R.overdose_threshold && !R.overdosed) @@ -588,6 +589,22 @@ del_reagent(R.type) return 0 +/datum/reagents/proc/reaction_check(mob/living/M, datum/reagent/R) + var/can_process = FALSE + if(ishuman(M)) + var/mob/living/carbon/human/H = M + //Check if this mob's species is set and can process this type of reagent + if(H.dna && H.dna.species.reagent_tag) + if((R.process_flags & SYNTHETIC) && (H.dna.species.reagent_tag & PROCESS_SYNTHETIC)) //SYNTHETIC-oriented reagents require PROCESS_SYNTHETIC + can_process = TRUE + if((R.process_flags & ORGANIC) && (H.dna.species.reagent_tag & PROCESS_ORGANIC)) //ORGANIC-oriented reagents require PROCESS_ORGANIC + can_process = TRUE + //We'll assume that non-human mobs lack the ability to process synthetic-oriented reagents (adjust this if we need to change that assumption) + else + if(R.process_flags != SYNTHETIC) + can_process = TRUE + return can_process + /** * Applies the relevant reaction_ proc for every reagent in this holder * * [/datum/reagent/proc/reaction_mob] @@ -612,6 +629,9 @@ var/datum/reagent/R = reagent switch(react_type) if("LIVING") + var/check = reaction_check(A, R) + if(!check) + continue var/touch_protection = 0 if(method == VAPOR) var/mob/living/L = A diff --git a/code/modules/reagents/chemistry/reagents.dm b/code/modules/reagents/chemistry/reagents.dm index e706943e53f4..9dacc3cef808 100644 --- a/code/modules/reagents/chemistry/reagents.dm +++ b/code/modules/reagents/chemistry/reagents.dm @@ -58,6 +58,8 @@ GLOBAL_LIST_INIT(name2reagent, build_name2reagent()) var/addiction_threshold = 0 /// increases as addiction gets worse var/addiction_stage = 0 + /// What can process this? ORGANIC, SYNTHETIC, or ORGANIC | SYNTHETIC?. We'll assume by default that it affects organics. + var/process_flags = ORGANIC /// You fucked up and this is now triggering its overdose effects, purge that shit quick. var/overdosed = 0 ///if false stops metab in liverless mobs diff --git a/code/modules/reagents/chemistry/reagents/medicine_reagents.dm b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm index be804d348590..fb439869ff35 100644 --- a/code/modules/reagents/chemistry/reagents/medicine_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm @@ -433,6 +433,39 @@ M.reagents.remove_reagent(R.type,1) ..() +/datum/reagent/medicine/system_cleaner + name = "System Cleaner" + description = "Neutralizes harmful chemical compounds inside synthetic systems." + reagent_state = LIQUID + color = "#F1C40F" + metabolization_rate = 0.5 * REAGENTS_METABOLISM + process_flags = SYNTHETIC + +/datum/reagent/medicine/system_cleaner/on_mob_life(mob/living/M) + M.adjustToxLoss(-2*REM, 0) + . = 1 + for(var/datum/reagent/R in M.reagents.reagent_list) + if(R != src) + M.reagents.remove_reagent(R.type,1) + ..() + +/datum/reagent/medicine/liquid_solder + name = "Liquid Solder" + description = "Repairs brain damage in synthetics." + color = "#727272" + taste_description = "metallic" + process_flags = SYNTHETIC + +/datum/reagent/medicine/liquid_solder/on_mob_life(mob/living/M) + M.adjustOrganLoss(ORGAN_SLOT_BRAIN, -3*REM) + if(iscarbon(M)) + var/mob/living/carbon/C = M + if(prob(10) && C.has_trauma_type(BRAIN_TRAUMA_SPECIAL)) + C.cure_trauma_type(BRAIN_TRAUMA_SPECIAL) + else if(prob(10) && C.has_trauma_type(BRAIN_TRAUMA_MILD)) + C.cure_trauma_type(BRAIN_TRAUMA_MILD) + ..() + /datum/reagent/medicine/omnizine name = "Omnizine" description = "Slowly heals all damage types. Overdose will cause damage in all types instead." @@ -441,6 +474,7 @@ metabolization_rate = 0.25 * REAGENTS_METABOLISM var/healing = 0.5 overdose_threshold = 30 + process_flags = ORGANIC | SYNTHETIC /datum/reagent/medicine/omnizine/on_mob_life(mob/living/carbon/M) M.adjustToxLoss(-healing*REM, 0) diff --git a/code/modules/reagents/chemistry/reagents/other_reagents.dm b/code/modules/reagents/chemistry/reagents/other_reagents.dm index bb8335b8680d..0969f8b266a8 100644 --- a/code/modules/reagents/chemistry/reagents/other_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/other_reagents.dm @@ -122,6 +122,7 @@ glass_name = "glass of water" glass_desc = "The father of all refreshments." shot_glass_icon_state = "shotglassclear" + process_flags = ORGANIC | SYNTHETIC /* * Water reaction to turf @@ -331,6 +332,7 @@ name = "Hell Water" description = "YOUR FLESH! IT BURNS!" taste_description = "burning" + process_flags = ORGANIC | SYNTHETIC /datum/reagent/hellwater/on_mob_life(mob/living/carbon/M) M.fire_stacks = min(5,M.fire_stacks + 3) @@ -503,6 +505,7 @@ taste_description = "slime" var/datum/species/race = /datum/species/human var/mutationtext = span_danger("The pain subsides. You feel... human.") + process_flags = ORGANIC | SYNTHETIC /datum/reagent/mutationtoxin/on_mob_life(mob/living/carbon/human/H) ..() @@ -964,6 +967,7 @@ color = "#B8B8C0" // rgb: 184, 184, 192 taste_description = "the inside of a reactor" var/irradiation_level = 1 + process_flags = ORGANIC | SYNTHETIC /datum/reagent/uranium/on_mob_life(mob/living/carbon/M) M.apply_effect(irradiation_level/M.metabolism_efficiency,EFFECT_IRRADIATE,0) @@ -984,6 +988,7 @@ color = "#C7C7C7" // rgb: 199,199,199 taste_description = "the colour blue and regret" irradiation_level = 2*REM + process_flags = ORGANIC | SYNTHETIC /datum/reagent/bluespace name = "Bluespace Dust" @@ -991,6 +996,7 @@ reagent_state = SOLID color = "#0000CC" taste_description = "fizzling blue" + process_flags = ORGANIC | SYNTHETIC /datum/reagent/bluespace/reaction_mob(mob/living/M, method=TOUCH, reac_volume) if(method == TOUCH || method == VAPOR) @@ -1030,6 +1036,7 @@ glass_icon_state = "dr_gibb_glass" glass_name = "glass of welder fuel" glass_desc = "Unless you're an industrial tool, this is probably not safe for consumption." + process_flags = ORGANIC | SYNTHETIC /datum/reagent/fuel/reaction_mob(mob/living/M, method=TOUCH, reac_volume)//Splashing people with welding fuel to make them easy to ignite! if(method == TOUCH || method == VAPOR) @@ -1529,6 +1536,7 @@ color = "#C8A5DC" taste_description = "bitterness" taste_mult = 1.5 + process_flags = ORGANIC | SYNTHETIC /datum/reagent/stable_plasma/on_mob_life(mob/living/carbon/C) C.adjustPlasma(10) diff --git a/code/modules/reagents/chemistry/reagents/pyrotechnic_reagents.dm b/code/modules/reagents/chemistry/reagents/pyrotechnic_reagents.dm index 0d7677513e63..48258ac5f3ee 100644 --- a/code/modules/reagents/chemistry/reagents/pyrotechnic_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/pyrotechnic_reagents.dm @@ -5,6 +5,7 @@ reagent_state = SOLID color = "#550000" taste_description = "sweet tasting metal" + process_flags = ORGANIC | SYNTHETIC /datum/reagent/thermite/reaction_turf(turf/T, reac_volume) if(reac_volume >= 1) @@ -35,6 +36,7 @@ color = "#FFC8C8" metabolization_rate = 4 taste_description = "burning" + process_flags = ORGANIC | SYNTHETIC /datum/reagent/clf3/on_mob_life(mob/living/carbon/M) M.adjust_fire_stacks(2) @@ -135,6 +137,7 @@ color = "#FA00AF" taste_description = "burning" self_consuming = TRUE + process_flags = ORGANIC | SYNTHETIC /datum/reagent/phlogiston/reaction_mob(mob/living/M, method=TOUCH, reac_volume) M.adjust_fire_stacks(1) @@ -157,6 +160,7 @@ color = "#FA00AF" taste_description = "burning" self_consuming = TRUE + process_flags = ORGANIC | SYNTHETIC /datum/reagent/napalm/on_mob_life(mob/living/carbon/M) M.adjust_fire_stacks(1) @@ -174,6 +178,7 @@ metabolization_rate = 0.5 * REAGENTS_METABOLISM taste_description = "bitterness" self_consuming = TRUE + process_flags = ORGANIC | SYNTHETIC /datum/reagent/cryostylane/on_mob_life(mob/living/carbon/M) //TODO: code freezing into an ice cube diff --git a/code/modules/reagents/chemistry/reagents/toxin_reagents.dm b/code/modules/reagents/chemistry/reagents/toxin_reagents.dm index d3807053a69a..73fca550a592 100644 --- a/code/modules/reagents/chemistry/reagents/toxin_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/toxin_reagents.dm @@ -58,6 +58,7 @@ taste_mult = 1.5 color = "#8228A0" toxpwr = 3 + process_flags = ORGANIC | SYNTHETIC /datum/reagent/toxin/plasma/on_mob_life(mob/living/carbon/C) if(holder.has_reagent(/datum/reagent/medicine/epinephrine)) @@ -393,6 +394,7 @@ color = "#787878" metabolization_rate = 0.125 * REAGENTS_METABOLISM toxpwr = 0 + process_flags = ORGANIC | SYNTHETIC /datum/reagent/toxin/polonium/on_mob_life(mob/living/carbon/M) M.radiation += 40 @@ -742,6 +744,7 @@ metabolization_rate = 1.2 * REAGENTS_METABOLISM toxpwr = 0.5 taste_description = "spinning" + process_flags = ORGANIC | SYNTHETIC /datum/reagent/toxin/rotatium/on_mob_life(mob/living/carbon/M) if(M.hud_used) @@ -790,6 +793,7 @@ var/acidpwr = 10 //the amount of protection removed from the armour taste_description = "acid" self_consuming = TRUE + process_flags = ORGANIC | SYNTHETIC /datum/reagent/toxin/acid/reaction_mob(mob/living/carbon/C, method=TOUCH, reac_volume) if(!istype(C)) diff --git a/code/modules/reagents/chemistry/recipes/medicine.dm b/code/modules/reagents/chemistry/recipes/medicine.dm index f52bab751cac..cb3c405c09ef 100644 --- a/code/modules/reagents/chemistry/recipes/medicine.dm +++ b/code/modules/reagents/chemistry/recipes/medicine.dm @@ -276,6 +276,20 @@ results = list(/datum/reagent/medicine/psicodine = 5) required_reagents = list( /datum/reagent/medicine/mannitol = 2, /datum/reagent/water = 2, /datum/reagent/impedrezene = 1) +/datum/chemical_reaction/system_cleaner + name = "System Cleaner" + id = /datum/reagent/medicine/system_cleaner + results = list(/datum/reagent/medicine/system_cleaner = 5) + required_reagents = list(/datum/reagent/consumable/ethanol = 1, /datum/reagent/chlorine = 1, /datum/reagent/phenol = 2, /datum/reagent/potassium = 1) + +/datum/chemical_reaction/liquid_solder + name = "Liquid Solder" + id = /datum/reagent/medicine/liquid_solder + results = list(/datum/reagent/medicine/liquid_solder = 3) + required_reagents = list( /datum/reagent/consumable/ethanol = 1, /datum/reagent/copper = 1, /datum/reagent/silver = 1) + required_temp = 370 + mix_message = "The mixture becomes a metallic slurry." + /datum/chemical_reaction/burnmix // Chemistry recipe name = "BurnMix" required_temp = 700 diff --git a/code/modules/reagents/reagent_containers.dm b/code/modules/reagents/reagent_containers.dm index b996701eaf17..3e22aeaaf950 100644 --- a/code/modules/reagents/reagent_containers.dm +++ b/code/modules/reagents/reagent_containers.dm @@ -58,8 +58,14 @@ if(covered) var/who = (isnull(user) || eater == user) ? "your" : "[eater.p_their()]" to_chat(user, span_warning("You have to remove [who] [covered] first!")) - return 0 - return 1 + return FALSE + if(!eater.has_mouth()) + if(eater == user) + to_chat(eater, "You have no mouth, and cannot eat.") + else + to_chat(user, "You can't feed [eater], because they have no mouth!") + return FALSE + return TRUE /obj/item/reagent_containers/ex_act() if(reagents) diff --git a/code/modules/research/designs/mechfabricator_designs.dm b/code/modules/research/designs/mechfabricator_designs.dm index 342fdfc75ce3..54b3a99e4699 100644 --- a/code/modules/research/designs/mechfabricator_designs.dm +++ b/code/modules/research/designs/mechfabricator_designs.dm @@ -835,3 +835,68 @@ construction_time = 100 build_path = /obj/item/assembly/flash/handheld category = list("Misc") + +// IPC Replacement Parts + +/datum/design/robotic_liver + name = "Substance Processor" + id = "robotic_liver" + build_type = MECHFAB + build_path = /obj/item/organ/liver/cybernetic/upgraded/ipc + materials = list(/datum/material/iron = 2000, /datum/material/glass = 1000) + construction_time = 100 + category = list("IPC Components") + +/datum/design/robotic_eyes + name = "Basic Robotic Eyes" + id = "robotic_eyes" + build_type = MECHFAB + build_path = /obj/item/organ/eyes/robotic + materials = list(/datum/material/iron = 1000, /datum/material/glass = 2000) + construction_time = 100 + category = list("IPC Components") + +/datum/design/robotic_tongue + name = "Robotic Voicebox" + id = "robotic_tongue" + build_type = MECHFAB + build_path = /obj/item/organ/tongue/robot + materials = list(/datum/material/iron = 2000, /datum/material/glass = 1000) + construction_time = 100 + category = list("IPC Components") + +/datum/design/robotic_stomach + name = "Micro-cell" + id = "robotic_stomach" + build_type = MECHFAB + build_path = /obj/item/organ/stomach/cell + materials = list(/datum/material/iron = 2000, /datum/material/glass = 2000, /datum/material/plasma = 200) + construction_time = 100 + category = list("IPC Components") + +/datum/design/robotic_ears + name = "Auditory Sensors" + id = "robotic_ears" + build_type = MECHFAB + build_path = /obj/item/organ/ears/robot + materials = list(/datum/material/iron = 2000, /datum/material/glass = 1000) + construction_time = 100 + category = list("IPC Components") + +/datum/design/power_cord + name = "Recharging Electronics" + id = "power_cord" + build_type = MECHFAB + build_path = /obj/item/organ/cyberimp/arm/power_cord + materials = list(/datum/material/iron = 2000, /datum/material/glass = 1000) + construction_time = 100 + category = list("IPC Components") + +/datum/design/ipcrevive + name = "IPC Revival Board" + id = "ipcrevive" + build_type = MECHFAB + build_path = /obj/item/ipcrevive + materials = list(/datum/material/iron = 20000 , /datum/material/glass = 5000) + construction_time = 120 + category = list("IPC Components") diff --git a/code/modules/research/techweb/all_nodes.dm b/code/modules/research/techweb/all_nodes.dm index dafd0d5bc829..ed4a83d46edc 100644 --- a/code/modules/research/techweb/all_nodes.dm +++ b/code/modules/research/techweb/all_nodes.dm @@ -542,6 +542,15 @@ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 1500) export_price = 5000 +/datum/techweb_node/ipc_organs + id = "ipc_organs" + display_name = "IPC Parts" + description = "We have the technology to replace him." + prereq_ids = list("cyber_organs","robotics") + design_ids = list("robotic_liver", "robotic_eyes", "robotic_tongue", "robotic_stomach", "robotic_ears", "power_cord", "ipcrevive") + research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 1500) + export_price = 5000 + /datum/techweb_node/cyber_implants id = "cyber_implants" display_name = "Cybernetic Implants" diff --git a/code/modules/research/techweb/layout.dm b/code/modules/research/techweb/layout.dm index 3f2a0c33c30a..68a99f15b48a 100644 --- a/code/modules/research/techweb/layout.dm +++ b/code/modules/research/techweb/layout.dm @@ -119,6 +119,10 @@ ui_x = 192 ui_y = -320 +/datum/techweb_node/ipc_organs + ui_x = -160 + ui_y = 32 + /datum/techweb_node/cyber_implants ui_x = 352 ui_y = -32 diff --git a/code/modules/surgery/advanced/brainwashing.dm b/code/modules/surgery/advanced/brainwashing.dm index e389bdb04d5e..933c20340f19 100644 --- a/code/modules/surgery/advanced/brainwashing.dm +++ b/code/modules/surgery/advanced/brainwashing.dm @@ -20,6 +20,7 @@ target_mobtypes = list(/mob/living/carbon/human) possible_locs = list(BODY_ZONE_HEAD) + requires_bodypart_type = FALSE /datum/surgery/advanced/brainwashing/can_start(mob/user, mob/living/carbon/target) if(!..()) diff --git a/code/modules/surgery/advanced/revival.dm b/code/modules/surgery/advanced/revival.dm index abf149453c09..7ae4fb9c83b0 100644 --- a/code/modules/surgery/advanced/revival.dm +++ b/code/modules/surgery/advanced/revival.dm @@ -16,6 +16,8 @@ /datum/surgery/advanced/revival/can_start(mob/user, mob/living/carbon/target) if(!..()) return FALSE + if(HAS_TRAIT(target, TRAIT_NODEFIB)) + return FALSE if(target.stat != DEAD) return FALSE if(target.suiciding || target.hellbound || HAS_TRAIT(target, TRAIT_HUSK)) diff --git a/code/modules/surgery/bodyparts/_bodyparts.dm b/code/modules/surgery/bodyparts/_bodyparts.dm index 42eb7df14eae..e2c2dee58d3f 100644 --- a/code/modules/surgery/bodyparts/_bodyparts.dm +++ b/code/modules/surgery/bodyparts/_bodyparts.dm @@ -20,6 +20,7 @@ var/use_digitigrade = NOT_DIGITIGRADE //Used for alternate legs, useless elsewhere var/list/embedded_objects = list() var/held_index = 0 //are we a hand? if so, which one! + var/render_like_organic = FALSE // TRUE is for when you want a BODYPART_ROBOTIC to pretend to be a BODYPART_ORGANIC. var/is_pseudopart = FALSE //For limbs that don't really exist, eg chainsaws ///If disabled, limb is as good as missing. @@ -131,10 +132,16 @@ var/mob/living/carbon/human/H = C if(HAS_TRAIT(C, TRAIT_LIMBATTACHMENT)) if(!H.get_bodypart(body_zone) && !animal_origin) + if(iscarbon(user)) + var/mob/living/carbon/target = user + if(target.dna && target.dna.species && (ROBOTIC_LIMBS in target.dna.species.species_traits) && src.status != BODYPART_ROBOTIC) + if(H == user) + to_chat(H, "You try to force [src] into your empty socket, but it doesn't fit") + else + to_chat(user, "You try to force [src] into [H.p_their()] empty socket, but it doesn't fit") + return user.temporarilyRemoveItemFromInventory(src, TRUE) - if(!attach_limb(C)) - to_chat(user, span_warning("[H]'s body rejects [src]!")) - forceMove(H.loc) + attach_limb(C) if(H == user) H.visible_message(span_warning("[H] jams [src] into [H.p_their()] empty socket!"),\ span_notice("You force [src] into your empty socket, and it locks into place!")) @@ -818,7 +825,7 @@ if((body_zone != BODY_ZONE_HEAD && body_zone != BODY_ZONE_CHEST)) should_draw_gender = FALSE - if(is_organic_limb()) + if(status == BODYPART_ORGANIC || (status == BODYPART_ROBOTIC && render_like_organic == TRUE)) // So IPC augments can be colorful without disrupting normal BODYPART_ROBOTIC render code. if(should_draw_greyscale) limb.icon = 'icons/mob/human_parts_greyscale.dmi' if(should_draw_gender) diff --git a/code/modules/surgery/bodyparts/dismemberment.dm b/code/modules/surgery/bodyparts/dismemberment.dm index 9e5d282cc961..ef5f4e81d22b 100644 --- a/code/modules/surgery/bodyparts/dismemberment.dm +++ b/code/modules/surgery/bodyparts/dismemberment.dm @@ -361,6 +361,8 @@ LAZYADD(C.all_scars, thing) update_bodypart_damage_state() + if(C.dna && C.dna.species && (ROBOTIC_LIMBS in C.dna.species.species_traits) && src.status == BODYPART_ROBOTIC) + src.render_like_organic = TRUE C.updatehealth() C.update_body() @@ -368,7 +370,6 @@ C.update_damage_overlays() C.update_mobility() - /obj/item/bodypart/head/attach_limb(mob/living/carbon/C, special) //Transfer some head appearance vars over if(brain) @@ -438,6 +439,16 @@ L.brutestate = 0 L.burnstate = 0 + if(ishuman(src)) + var/mob/living/carbon/human/H = src + if(H.dna && H.dna.species && (ROBOTIC_LIMBS in H.dna.species.species_traits)) + L.change_bodypart_status(BODYPART_ROBOTIC) + L.render_like_organic = TRUE + if(limb_zone == "head" && H.dna && H.dna.species && (NOMOUTH in H.dna.species.species_traits)) + var/obj/item/bodypart/head/head = L + if(head) + head.mouth = FALSE + L.attach_limb(src, 1) var/datum/scar/scaries = new var/datum/wound/loss/phantom_loss = new // stolen valor, really diff --git a/code/modules/surgery/bodyparts/head.dm b/code/modules/surgery/bodyparts/head.dm index 3a2defe86c9d..69e6b841780c 100644 --- a/code/modules/surgery/bodyparts/head.dm +++ b/code/modules/surgery/bodyparts/head.dm @@ -36,6 +36,7 @@ var/lip_style = null var/lip_color = "white" + var/mouth = TRUE /obj/item/bodypart/head/Destroy() @@ -94,6 +95,8 @@ /obj/item/bodypart/head/can_dismember(obj/item/I) + if(owner && isipc(owner)) + return TRUE if(owner && !((owner.stat == DEAD) || owner.InFullCritical())) return FALSE return ..() @@ -133,6 +136,10 @@ else C = owner + if(isipc(C)) + max_damage = 50 + disabled_wound_penalty = 250 // 200 Makes it possible to delimb so 250 for some better chance + real_name = C.real_name if(HAS_TRAIT(C, TRAIT_HUSK)) real_name = "Unknown" @@ -176,7 +183,10 @@ hair_alpha = S.hair_alpha else hair_style = "Bald" - hair_color = "000" + if(H && H.hair_color) + hair_color = H.hair_color + else + hair_color = "000" hair_alpha = initial(hair_alpha) // lipstick if(H.lip_style && (LIPS in S.species_traits)) diff --git a/code/modules/surgery/organ_manipulation.dm b/code/modules/surgery/organ_manipulation.dm index 1309b5c59cd4..f4fa5f864f00 100644 --- a/code/modules/surgery/organ_manipulation.dm +++ b/code/modules/surgery/organ_manipulation.dm @@ -94,6 +94,25 @@ to_chat(user, span_notice("You cannot put [I] into [target]'s [parse_zone(target_zone)]!")) return -1 tool = I + + if(isipc(target)) + if(istype(tool, /obj/item/organ/brain/positron)) + var/obj/item/bodypart/affected = target.get_bodypart(check_zone(target_zone)) + if(!affected) + return -1 + if(affected.status != ORGAN_ROBOTIC) + to_chat(user, "You can't put [tool] into a meat enclosure!") + return -1 + if(target_zone != BODY_ZONE_CHEST) + to_chat(user, "You have to install [tool] in [target]'s chest!") + return -1 + if(target.getorganslot(ORGAN_SLOT_BRAIN)) + to_chat(user, "[target] already has a brain! You'd rather not find out what would happen with two in there.") + return -1 + else if(istype(tool, /obj/item/organ/brain)) + to_chat(user, "[target] does not have the proper connectors to interface with [tool].") + return -1 + if(isorgan(tool)) current_type = "insert" preop_sound = initial(preop_sound) diff --git a/code/modules/surgery/organs/augments_arms.dm b/code/modules/surgery/organs/augments_arms.dm index 2190841b0d4a..ea1fc90b7c55 100644 --- a/code/modules/surgery/organs/augments_arms.dm +++ b/code/modules/surgery/organs/augments_arms.dm @@ -386,3 +386,9 @@ /obj/item/organ/cyberimp/arm/syndie_mantis/l zone = BODY_ZONE_L_ARM syndicate_implant = TRUE + +/obj/item/organ/cyberimp/arm/power_cord + name = "power cord implant" + desc = "An internal power cord hooked up to a battery. Useful if you run on volts." + contents = newlist(/obj/item/apc_powercord) + zone = "l_arm" diff --git a/code/modules/surgery/organs/ears.dm b/code/modules/surgery/organs/ears.dm index 07e041f1bbf6..f2166636c754 100644 --- a/code/modules/surgery/organs/ears.dm +++ b/code/modules/surgery/organs/ears.dm @@ -140,3 +140,27 @@ desc = "The robust ears of a bronze golem. " damage_multiplier = 0.1 //STRONK bang_protect = 1 //Fear me weaklings. + +/obj/item/organ/ears/robot + name = "auditory sensors" + icon_state = "robotic_ears" + desc = "A pair of microphones intended to be installed in an IPC head, that grant the ability to hear." + zone = "head" + slot = "ears" + gender = PLURAL + status = ORGAN_ROBOTIC + organ_flags = ORGAN_SYNTHETIC + +/obj/item/organ/ears/robot/emp_act(severity) + switch(severity) + if(1) + owner.Jitter(30) + owner.Dizzy(30) + owner.Knockdown(200) + deaf = 30 + to_chat(owner, "Your robotic ears are ringing, uselessly.") + if(2) + owner.Jitter(15) + owner.Dizzy(15) + owner.Knockdown(100) + to_chat(owner, "Your robotic ears buzz.") diff --git a/code/modules/surgery/organs/heart.dm b/code/modules/surgery/organs/heart.dm index 1a72126da17f..890ba1ffee45 100644 --- a/code/modules/surgery/organs/heart.dm +++ b/code/modules/surgery/organs/heart.dm @@ -251,6 +251,14 @@ . = ..() addtimer(CALLBACK(src, .proc/Restart), 8 SECONDS) //Can restart itself after an EMP so it isnt an insta death +/obj/item/organ/heart/cybernetic/ipc + desc = "An electronic device that appears to mimic the functions of an organic heart." + +/obj/item/organ/heart/cybernetic/ipc/emp_act() + . = ..() + to_chat(owner, "Alert: Cybernetic heart failed one heartbeat") + addtimer(CALLBACK(src, .proc/Restart), 10 SECONDS) + /obj/item/organ/heart/freedom name = "heart of freedom" desc = "This heart pumps with the passion to give... something freedom." diff --git a/code/modules/surgery/organs/liver.dm b/code/modules/surgery/organs/liver.dm index 592c5a5bc5ef..4669301e9496 100755 --- a/code/modules/surgery/organs/liver.dm +++ b/code/modules/surgery/organs/liver.dm @@ -117,3 +117,21 @@ damage+=100 if(2) damage+=50 + +/obj/item/organ/liver/cybernetic/upgraded/ipc + name = "substance processor" + icon_state = "substance_processor" + attack_verb = list("processed") + desc = "A machine component, installed in the chest. This grants the Machine the ability to process chemicals that enter its systems." + alcohol_tolerance = 0 + toxTolerance = -1 + toxLethality = 0 + status = ORGAN_ROBOTIC + +/obj/item/organ/liver/cybernetic/upgraded/ipc/emp_act(severity) + to_chat(owner, "Alert: Your Substance Processor has been damaged. An internal chemical leak is affecting performance.") + switch(severity) + if(1) + owner.toxloss += 15 + if(2) + owner.toxloss += 5 diff --git a/code/modules/surgery/organs/stomach.dm b/code/modules/surgery/organs/stomach.dm index 8e86dc0789b6..364cf42f92b3 100644 --- a/code/modules/surgery/organs/stomach.dm +++ b/code/modules/surgery/organs/stomach.dm @@ -124,6 +124,37 @@ icon_state = "stomach-p" desc = "A strange crystal that is responsible for metabolizing the unseen energy force that feeds plasmamen." +/obj/item/organ/stomach/cell + name = "micro-cell" + icon_state = "microcell" + w_class = WEIGHT_CLASS_NORMAL + zone = "chest" + slot = "stomach" + attack_verb = list("assault and battery'd") + desc = "A micro-cell, for IPC use only. Do not swallow." + status = ORGAN_ROBOTIC + organ_flags = ORGAN_SYNTHETIC + +/obj/item/organ/stomach/cell/emp_act(severity) + switch(severity) + if(1) + owner.nutrition = 50 + to_chat(owner, "Alert: Heavy EMP Detected. Rebooting power cell to prevent damage.") + if(2) + owner.nutrition = 250 + to_chat(owner, "Alert: EMP Detected. Cycling battery.") + +/obj/item/organ/stomach/cell/Insert(mob/living/carbon/M, special, drop_if_replaced) + . = ..() + RegisterSignal(owner, COMSIG_PROCESS_BORGCHARGER_OCCUPANT, .proc/charge) + +/obj/item/organ/stomach/cell/Remove(mob/living/carbon/M, special) + . = ..() + UnregisterSignal(owner, COMSIG_PROCESS_BORGCHARGER_OCCUPANT) + +/obj/item/organ/stomach/cell/proc/charge(datum/source, amount, repairs) + owner.nutrition = clamp(owner.nutrition + (amount/100), 0, NUTRITION_LEVEL_FULL) // no fat ipcs + /obj/item/organ/stomach/ethereal name = "biological battery" icon_state = "stomach-p" //Welp. At least it's more unique in functionaliy. diff --git a/code/modules/surgery/organs/tongue.dm b/code/modules/surgery/organs/tongue.dm index 00052362c8a1..3680028815d4 100644 --- a/code/modules/surgery/organs/tongue.dm +++ b/code/modules/surgery/organs/tongue.dm @@ -121,7 +121,7 @@ desc = "A mysterious structure that allows for instant communication between users. Pretty impressive until you need to eat something." icon_state = "tongueayylmao" say_mod = "gibbers" - taste_sensitivity = 101 // ayys cannot taste anything. + taste_sensitivity = NO_TASTE_SENSITIVITY // ayys cannot taste anything. modifies_speech = TRUE var/mothership @@ -222,7 +222,7 @@ icon_state = "tonguebone" say_mod = "rattles" attack_verb = list("bitten", "chattered", "chomped", "enamelled", "boned") - taste_sensitivity = 101 // skeletons cannot taste anything + taste_sensitivity = NO_TASTE_SENSITIVITY // skeletons cannot taste anything modifies_speech = TRUE var/chattering = FALSE var/phomeme_type = "sans" @@ -252,11 +252,17 @@ name = "robotic voicebox" desc = "A voice synthesizer that can interface with organic lifeforms." status = ORGAN_ROBOTIC + organ_flags = ORGAN_SYNTHETIC icon_state = "tonguerobot" say_mod = "states" attack_verb = list("beeped", "booped") modifies_speech = TRUE - taste_sensitivity = 25 // not as good as an organic tongue + taste_sensitivity = NO_TASTE_SENSITIVITY // not as good as an organic tongue + +/obj/item/organ/tongue/robot/emp_act(severity) + owner.apply_effect(EFFECT_STUTTER, 120) + owner.emote("scream") + to_chat(owner, "Alert: Vocal cords are malfunctioning.") /obj/item/organ/tongue/robot/can_speak_language(language) return TRUE // THE MAGIC OF ELECTRONICS diff --git a/code/modules/surgery/prosthetic_replacement.dm b/code/modules/surgery/prosthetic_replacement.dm index ef940fe3e2fb..6bbe3d6bc681 100644 --- a/code/modules/surgery/prosthetic_replacement.dm +++ b/code/modules/surgery/prosthetic_replacement.dm @@ -51,6 +51,9 @@ tool = I if(istype(tool, /obj/item/bodypart)) var/obj/item/bodypart/BP = tool + if(target.dna && target.dna.species && (ROBOTIC_LIMBS in target.dna.species.species_traits) && BP.status != BODYPART_ROBOTIC) + to_chat(user, "[BP] doesn't match the patient's morphology.") + return -1 if(ismonkey(target))// monkey patient only accept organic monkey limbs if(BP.status == BODYPART_ROBOTIC || BP.animal_origin != MONKEY_BODYPART) to_chat(user, span_warning("[BP] doesn't match the patient's morphology.")) @@ -89,6 +92,9 @@ if(istype(tool, /obj/item/bodypart) && user.temporarilyRemoveItemFromInventory(tool)) var/obj/item/bodypart/L = tool L.attach_limb(target) + if(target.dna && target.dna.species && (ROBOTIC_LIMBS in target.dna.species.species_traits)) + if(L.status == BODYPART_ROBOTIC) + L.render_like_organic = TRUE if(organ_rejection_dam) target.adjustToxLoss(organ_rejection_dam) display_results(user, target, span_notice("You succeed in replacing [target]'s [parse_zone(target_zone)]."), diff --git a/config/game_options.txt b/config/game_options.txt index 52aebab2338e..8b021a077719 100644 --- a/config/game_options.txt +++ b/config/game_options.txt @@ -536,6 +536,7 @@ ROUNDSTART_RACES plasmaman ROUNDSTART_RACES preternis ROUNDSTART_RACES polysmorph #ROUNDSTART_RACES snail +ROUNDSTART_RACES ipc ## Races that are better than humans in some ways, but worse in others ROUNDSTART_RACES ethereal diff --git a/icons/mob/human_parts.dmi b/icons/mob/human_parts.dmi index b0beb21017c9..dfee730e05b9 100644 Binary files a/icons/mob/human_parts.dmi and b/icons/mob/human_parts.dmi differ diff --git a/icons/mob/human_parts_greyscale.dmi b/icons/mob/human_parts_greyscale.dmi index 830b71634de1..75c34dd41b5d 100644 Binary files a/icons/mob/human_parts_greyscale.dmi and b/icons/mob/human_parts_greyscale.dmi differ diff --git a/icons/mob/ipc_accessories.dmi b/icons/mob/ipc_accessories.dmi new file mode 100644 index 000000000000..ba002c1b6a7c Binary files /dev/null and b/icons/mob/ipc_accessories.dmi differ diff --git a/icons/obj/assemblies.dmi b/icons/obj/assemblies.dmi index b65355e61772..25d4a2a9cac0 100644 Binary files a/icons/obj/assemblies.dmi and b/icons/obj/assemblies.dmi differ diff --git a/icons/obj/surgery.dmi b/icons/obj/surgery.dmi index 8cffaff8b48a..d223395c2ff7 100755 Binary files a/icons/obj/surgery.dmi and b/icons/obj/surgery.dmi differ diff --git a/yogstation.dme b/yogstation.dme index 9ade5c068bfe..42575546b654 100644 --- a/yogstation.dme +++ b/yogstation.dme @@ -2387,6 +2387,7 @@ #include "code\modules\mob\living\carbon\human\species_types\flypeople.dm" #include "code\modules\mob\living\carbon\human\species_types\golems.dm" #include "code\modules\mob\living\carbon\human\species_types\humans.dm" +#include "code\modules\mob\living\carbon\human\species_types\IPC.dm" #include "code\modules\mob\living\carbon\human\species_types\jellypeople.dm" #include "code\modules\mob\living\carbon\human\species_types\lizardpeople.dm" #include "code\modules\mob\living\carbon\human\species_types\mothmen.dm" diff --git a/yogstation/icons/mob/human_parts.dmi b/yogstation/icons/mob/human_parts.dmi index 757ba9aa420d..2a3a63834a64 100644 Binary files a/yogstation/icons/mob/human_parts.dmi and b/yogstation/icons/mob/human_parts.dmi differ