From d4c8a4ed9e1c8495c1d8e224ebc742a4a2e6745c Mon Sep 17 00:00:00 2001 From: SkyratBot <59378654+SkyratBot@users.noreply.github.com> Date: Sun, 26 Mar 2023 19:28:28 +0200 Subject: [PATCH] [MIRROR] Resolves mutant species keeping old organs they shouldn't have [MDB IGNORE] (#19958) * Resolves mutant species keeping old organs they shouldn't have (#73728) ## About The Pull Request regenerate organs never removed invalid organs when regenerating ## Why It's Good For The Game Fixes https://github.com/tgstation/tgstation/issues/73640 adds a unit test to prevent regressions in the future ## Changelog :cl: fix: Plasmamen don't have hearts, again /:cl: * Resolves mutant species keeping old organs they shouldn't have * https://github.com/tgstation/tgstation/pull/68168 * Now everything should pass, yes, even appendixes for synths --------- Co-authored-by: Zephyr <12817816+ZephyrTFA@users.noreply.github.com> Co-authored-by: GoldenAlpharex Co-authored-by: Coffee --- code/__DEFINES/surgery.dm | 4 ++ .../mutant_bodypart_overlay.dm | 12 +++- .../mob/living/carbon/human/species.dm | 11 ++-- .../carbon/human/species_types/golems.dm | 4 +- .../organs/external/_external_organs.dm | 9 ++- code/modules/unit_tests/_unit_tests.dm | 1 + code/modules/unit_tests/mutant_organs.dm | 65 +++++++++++++++++++ .../modules/surgery/organs/vox.dm | 4 +- .../modules/synths/code/bodyparts/brain.dm | 2 +- .../modules/synths/code/bodyparts/ears.dm | 2 +- .../modules/synths/code/bodyparts/eyes.dm | 2 +- .../modules/synths/code/bodyparts/heart.dm | 2 +- .../modules/synths/code/bodyparts/liver.dm | 2 +- .../modules/synths/code/bodyparts/lungs.dm | 2 +- .../modules/synths/code/bodyparts/stomach.dm | 2 +- .../modules/synths/code/bodyparts/tongue.dm | 2 +- .../modules/synths/code/species/synthetic.dm | 5 +- 17 files changed, 106 insertions(+), 25 deletions(-) create mode 100644 code/modules/unit_tests/mutant_organs.dm diff --git a/code/__DEFINES/surgery.dm b/code/__DEFINES/surgery.dm index 07e9dcc8a51..1b16f9e0de7 100644 --- a/code/__DEFINES/surgery.dm +++ b/code/__DEFINES/surgery.dm @@ -13,6 +13,10 @@ #define ORGAN_SYNTHETIC_EMP (1<<5) // //Can't be removed using surgery #define ORGAN_UNREMOVABLE (1<<6) +// SKYRAT EDIT START - Customization +/// Synthetic organ granted by a species (for use for organ replacements between species) +#define ORGAN_SYNTHETIC_FROM_SPECIES (1<<7) +// SKYRAT EDIT END /// When the surgery step fails :( #define SURGERY_STEP_FAIL -1 diff --git a/code/datums/bodypart_overlays/mutant_bodypart_overlay.dm b/code/datums/bodypart_overlays/mutant_bodypart_overlay.dm index 6e72897d12c..ea1d94ce790 100644 --- a/code/datums/bodypart_overlays/mutant_bodypart_overlay.dm +++ b/code/datums/bodypart_overlays/mutant_bodypart_overlay.dm @@ -13,6 +13,10 @@ ///Take on the dna/preference from whoever we're gonna be inserted in var/imprint_on_next_insertion = TRUE +/datum/bodypart_overlay/mutant/get_overlay(layer, obj/item/bodypart/limb) + inherit_color(limb) // If draw_color is not set yet, go ahead and do that + return ..() + ///Completely random image and color generation (obeys what a player can choose from) /datum/bodypart_overlay/mutant/proc/randomize_appearance() randomize_sprite() @@ -90,8 +94,13 @@ ///Give the organ its color. Force will override the existing one. /datum/bodypart_overlay/mutant/proc/inherit_color(obj/item/bodypart/ownerlimb, force) + if(isnull(ownerlimb)) + draw_color = null + return TRUE + if(draw_color && !force) - return + return FALSE + switch(color_source) if(ORGAN_COLOR_OVERRIDE) draw_color = override_color(ownerlimb.draw_color) @@ -102,6 +111,7 @@ return var/mob/living/carbon/human/human_owner = ownerlimb.owner draw_color = human_owner.hair_color + return TRUE ///Sprite accessories are singletons, stored list("Big Snout" = instance of /datum/sprite_accessory/snout/big), so here we get that singleton diff --git a/code/modules/mob/living/carbon/human/species.dm b/code/modules/mob/living/carbon/human/species.dm index 00cb441472e..a248403b94e 100644 --- a/code/modules/mob/living/carbon/human/species.dm +++ b/code/modules/mob/living/carbon/human/species.dm @@ -172,9 +172,9 @@ GLOBAL_LIST_EMPTY(features_by_species) ///What anim to use for gibbing var/gib_anim = "gibbed-h" - - //Do NOT remove by setting to null. use OR make an ASSOCIATED TRAIT. - //why does it work this way? because traits also disable the downsides of not having an organ, removing organs but not having the trait will make your species die + // Prefer anything other than setting these to null, such as TRAITS + // why? + // because traits also disable the downsides of not having an organ, removing organs but not having the trait or logic will make your species die ///Replaces default brain with a different organ var/obj/item/organ/internal/brain/mutantbrain = /obj/item/organ/internal/brain @@ -350,6 +350,9 @@ GLOBAL_LIST_EMPTY(features_by_species) var/obj/item/organ/oldorgan = C.getorganslot(slot) //used in removing var/obj/item/organ/neworgan = slot_mutantorgans[slot] //used in adding if(!neworgan) //these can be null, if so we shouldn't regenerate + if(oldorgan) // although we also need to remove the old organ if it exists + oldorgan.Remove(C, special=TRUE) + qdel(oldorgan) continue if(visual_only && !initial(neworgan.visual)) @@ -367,7 +370,7 @@ GLOBAL_LIST_EMPTY(features_by_species) * - The replaced organ is not in an excluded zone * - The replaced organ is not unremovable or synthetic (an implant) */ - if(oldorgan && (!should_have || replace_current) && !(oldorgan.zone in excluded_zones) && !(oldorgan.organ_flags & (ORGAN_UNREMOVABLE|ORGAN_SYNTHETIC))) + if(oldorgan && (!should_have || replace_current) && !(oldorgan.zone in excluded_zones) && (!(oldorgan.organ_flags & (ORGAN_UNREMOVABLE|ORGAN_SYNTHETIC)) || (oldorgan.organ_flags & ORGAN_SYNTHETIC_FROM_SPECIES))) // SKYRAT EDIT - Customization - ORIGINAL: if(oldorgan && (!should_have || replace_current) && !(oldorgan.zone in excluded_zones) && !(oldorgan.organ_flags & (ORGAN_UNREMOVABLE|ORGAN_SYNTHETIC))) if(slot == ORGAN_SLOT_BRAIN) var/obj/item/organ/internal/brain/brain = oldorgan if(!brain.decoy_override)//"Just keep it if it's fake" - confucius, probably 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 d371ceb806c..e0dd9694180 100644 --- a/code/modules/mob/living/carbon/human/species_types/golems.dm +++ b/code/modules/mob/living/carbon/human/species_types/golems.dm @@ -25,7 +25,6 @@ mutantlungs = null inherent_biotypes = MOB_HUMANOID|MOB_MINERAL mutant_organs = list(/obj/item/organ/internal/adamantine_resonator) - mutanttongue = /obj/item/organ/internal/vocal_cords/adamantine speedmod = 2 payday_modifier = 0.75 armor = 55 @@ -88,8 +87,7 @@ name = "Adamantine Golem" id = SPECIES_GOLEM_ADAMANTINE meat = /obj/item/food/meat/slab/human/mutant/golem/adamantine - mutant_organs = list(/obj/item/organ/internal/adamantine_resonator) - mutanttongue = /obj/item/organ/internal/vocal_cords/adamantine + mutant_organs = list(/obj/item/organ/internal/adamantine_resonator, /obj/item/organ/internal/vocal_cords/adamantine) fixed_mut_color = "#44eedd" info_text = "As an Adamantine Golem, you possess special vocal cords allowing you to \"resonate\" messages to all golems. Your unique mineral makeup makes you immune to most types of magic." prefix = "Adamantine" diff --git a/code/modules/surgery/organs/external/_external_organs.dm b/code/modules/surgery/organs/external/_external_organs.dm index a7f484f81ae..e520fcb921e 100644 --- a/code/modules/surgery/organs/external/_external_organs.dm +++ b/code/modules/surgery/organs/external/_external_organs.dm @@ -168,7 +168,7 @@ //Build the mob sprite and use it as our overlay for(var/external_layer in bodypart_overlay.all_layers) if(bodypart_overlay.layers & external_layer) - . += bodypart_overlay.get_overlay(external_layer, limb = null) + . += bodypart_overlay.get_overlay(external_layer, ownerlimb) ///The horns of a lizard! /obj/item/organ/external/horns @@ -366,8 +366,11 @@ if(draw_layer != bitflag_to_layer(color_swapped_layer)) return ..() - var/list/rgb_list = rgb2num(draw_color) - overlay.color = rgb(color_inverse_base - rgb_list[1], color_inverse_base - rgb_list[2], color_inverse_base - rgb_list[3]) //inversa da color + if(draw_color) // can someone explain to me why draw_color is allowed to EVER BE AN EMPTY STRING + var/list/rgb_list = rgb2num(draw_color) + overlay.color = rgb(color_inverse_base - rgb_list[1], color_inverse_base - rgb_list[2], color_inverse_base - rgb_list[3]) //inversa da color + else + overlay.color = null /datum/bodypart_overlay/mutant/pod_hair/can_draw_on_bodypart(mob/living/carbon/human/human) if((human.head?.flags_inv & HIDEHAIR) || (human.wear_mask?.flags_inv & HIDEHAIR)) diff --git a/code/modules/unit_tests/_unit_tests.dm b/code/modules/unit_tests/_unit_tests.dm index 22c14726ad9..fe78f924937 100644 --- a/code/modules/unit_tests/_unit_tests.dm +++ b/code/modules/unit_tests/_unit_tests.dm @@ -156,6 +156,7 @@ #include "monkey_business.dm" #include "mouse_bite_cable.dm" #include "mutant_hands_consistency.dm" +#include "mutant_organs.dm" #include "novaflower_burn.dm" #include "ntnetwork_tests.dm" #include "nuke_cinematic.dm" diff --git a/code/modules/unit_tests/mutant_organs.dm b/code/modules/unit_tests/mutant_organs.dm new file mode 100644 index 00000000000..b61dacdb477 --- /dev/null +++ b/code/modules/unit_tests/mutant_organs.dm @@ -0,0 +1,65 @@ +/** + * Iterates over all species to ensure that organs are valid after being set to a mutant species + */ +/datum/unit_test/mutant_organs + +/datum/unit_test/mutant_organs/Run() + var/mob/living/carbon/human/consistent/dummy = allocate(/mob/living/carbon/human/consistent) + var/list/ignore = list( + /datum/species/dullahan, + ) + var/list/species = subtypesof(/datum/species) - ignore + var/static/list/organs_we_care_about = list( + ORGAN_SLOT_BRAIN, + ORGAN_SLOT_HEART, + ORGAN_SLOT_LUNGS, + ORGAN_SLOT_EYES, + ORGAN_SLOT_EARS, + ORGAN_SLOT_TONGUE, + ORGAN_SLOT_LIVER, + ORGAN_SLOT_STOMACH, + ORGAN_SLOT_APPENDIX, + ) + + for(var/datum/species/species_type as anything in species) + // change them to the species + dummy.set_species(species_type) + + // check all their organs + for(var/organ_slot in organs_we_care_about) + var/expected_type = slot_to_species_organ_type(organ_slot, species_type) + var/obj/item/organ/actual_organ = dummy.getorganslot(organ_slot) + if(isnull(actual_organ)) + if(!isnull(expected_type)) + TEST_FAIL("[species_type] did not update their [organ_slot] organ to [expected_type], no organ was found") + continue + else + if(isnull(expected_type)) + TEST_FAIL("[species_type] did not remove their [organ_slot] organ") + continue + + if(actual_organ.type != expected_type) + TEST_FAIL("[species_type] did not update their [organ_slot] organ to [expected_type], instead it was [actual_organ.type]") + continue + +/datum/unit_test/mutant_organs/proc/slot_to_species_organ_type(slot, datum/species/species) + switch(slot) + if(ORGAN_SLOT_BRAIN) + return initial(species.mutantbrain) + if(ORGAN_SLOT_HEART) + return initial(species.mutantheart) + if(ORGAN_SLOT_LUNGS) + return initial(species.mutantlungs) + if(ORGAN_SLOT_EYES) + return initial(species.mutanteyes) + if(ORGAN_SLOT_EARS) + return initial(species.mutantears) + if(ORGAN_SLOT_TONGUE) + return initial(species.mutanttongue) + if(ORGAN_SLOT_LIVER) + return initial(species.mutantliver) + if(ORGAN_SLOT_STOMACH) + return initial(species.mutantstomach) + if(ORGAN_SLOT_APPENDIX) + return initial(species.mutantappendix) + CRASH("Invalid organ slot [slot]") // just incase someone adds an organ we care about and forgets to add it here diff --git a/modular_skyrat/modules/customization/modules/surgery/organs/vox.dm b/modular_skyrat/modules/customization/modules/surgery/organs/vox.dm index e0f505f1904..ff9c7e79db4 100644 --- a/modular_skyrat/modules/customization/modules/surgery/organs/vox.dm +++ b/modular_skyrat/modules/customization/modules/surgery/organs/vox.dm @@ -17,12 +17,12 @@ cold_level_2_threshold = 0 cold_level_3_threshold = 0 status = ORGAN_ROBOTIC - organ_flags = ORGAN_SYNTHETIC + organ_flags = ORGAN_SYNTHETIC | ORGAN_SYNTHETIC_FROM_SPECIES /obj/item/organ/internal/brain/vox name = "vox brain" status = ORGAN_ROBOTIC - organ_flags = ORGAN_SYNTHETIC + organ_flags = ORGAN_SYNTHETIC | ORGAN_SYNTHETIC_FROM_SPECIES /obj/item/organ/internal/brain/vox/emp_act(severity) . = ..() diff --git a/modular_skyrat/modules/synths/code/bodyparts/brain.dm b/modular_skyrat/modules/synths/code/bodyparts/brain.dm index bc969998150..05269f84f26 100644 --- a/modular_skyrat/modules/synths/code/bodyparts/brain.dm +++ b/modular_skyrat/modules/synths/code/bodyparts/brain.dm @@ -8,7 +8,7 @@ slot = ORGAN_SLOT_BRAIN zone = BODY_ZONE_CHEST status = ORGAN_ROBOTIC - organ_flags = ORGAN_SYNTHETIC + organ_flags = ORGAN_SYNTHETIC | ORGAN_SYNTHETIC_FROM_SPECIES 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. It is usually slotted into the chest of synthetic crewmembers." icon = 'modular_skyrat/master_files/icons/obj/surgery.dmi' icon_state = "posibrain-ipc" diff --git a/modular_skyrat/modules/synths/code/bodyparts/ears.dm b/modular_skyrat/modules/synths/code/bodyparts/ears.dm index eca525a7f5f..d0f57250cc6 100644 --- a/modular_skyrat/modules/synths/code/bodyparts/ears.dm +++ b/modular_skyrat/modules/synths/code/bodyparts/ears.dm @@ -12,7 +12,7 @@ slot = ORGAN_SLOT_EARS gender = PLURAL status = ORGAN_ROBOTIC - organ_flags = ORGAN_SYNTHETIC + organ_flags = ORGAN_SYNTHETIC | ORGAN_SYNTHETIC_FROM_SPECIES /obj/item/organ/internal/ears/synth/emp_act(severity) . = ..() diff --git a/modular_skyrat/modules/synths/code/bodyparts/eyes.dm b/modular_skyrat/modules/synths/code/bodyparts/eyes.dm index 6d6c1691376..3488797ced8 100644 --- a/modular_skyrat/modules/synths/code/bodyparts/eyes.dm +++ b/modular_skyrat/modules/synths/code/bodyparts/eyes.dm @@ -3,7 +3,7 @@ icon_state = "cybernetic_eyeballs" desc = "A very basic set of optical sensors with no extra vision modes or functions." status = ORGAN_ROBOTIC - organ_flags = ORGAN_SYNTHETIC + organ_flags = ORGAN_SYNTHETIC | ORGAN_SYNTHETIC_FROM_SPECIES /obj/item/organ/internal/eyes/synth/emp_act(severity) . = ..() diff --git a/modular_skyrat/modules/synths/code/bodyparts/heart.dm b/modular_skyrat/modules/synths/code/bodyparts/heart.dm index 50d13505a8f..bb1f4a4dc85 100644 --- a/modular_skyrat/modules/synths/code/bodyparts/heart.dm +++ b/modular_skyrat/modules/synths/code/bodyparts/heart.dm @@ -1,7 +1,7 @@ /obj/item/organ/internal/heart/synth name = "hydraulic pump engine" desc = "An electronic device that handles the hydraulic pumps, powering one's robotic limbs. Without this, synthetics are unable to move." - organ_flags = ORGAN_SYNTHETIC + organ_flags = ORGAN_SYNTHETIC | ORGAN_SYNTHETIC_FROM_SPECIES status = ORGAN_ROBOTIC icon = 'modular_skyrat/master_files/icons/obj/surgery.dmi' icon_state = "heart-ipc" diff --git a/modular_skyrat/modules/synths/code/bodyparts/liver.dm b/modular_skyrat/modules/synths/code/bodyparts/liver.dm index 9353eda7d6a..71106c9602a 100644 --- a/modular_skyrat/modules/synths/code/bodyparts/liver.dm +++ b/modular_skyrat/modules/synths/code/bodyparts/liver.dm @@ -1,7 +1,7 @@ /obj/item/organ/internal/liver/synth name = "reagent processing unit" desc = "An electronic device that processes the beneficial chemicals for the synthetic user." - organ_flags = ORGAN_SYNTHETIC + organ_flags = ORGAN_SYNTHETIC | ORGAN_SYNTHETIC_FROM_SPECIES status = ORGAN_ROBOTIC icon = 'modular_skyrat/master_files/icons/obj/surgery.dmi' icon_state = "liver-ipc" diff --git a/modular_skyrat/modules/synths/code/bodyparts/lungs.dm b/modular_skyrat/modules/synths/code/bodyparts/lungs.dm index 6aeaacc3870..5791f03eb16 100644 --- a/modular_skyrat/modules/synths/code/bodyparts/lungs.dm +++ b/modular_skyrat/modules/synths/code/bodyparts/lungs.dm @@ -15,7 +15,7 @@ safe_oxygen_min = 0 //What are you doing man, dont breathe with those! safe_oxygen_max = 0 status = ORGAN_ROBOTIC - organ_flags = ORGAN_SYNTHETIC + organ_flags = ORGAN_SYNTHETIC | ORGAN_SYNTHETIC_FROM_SPECIES /obj/item/organ/internal/lungs/synth/emp_act(severity) . = ..() diff --git a/modular_skyrat/modules/synths/code/bodyparts/stomach.dm b/modular_skyrat/modules/synths/code/bodyparts/stomach.dm index 1a2ea8c9aca..bad2ab85d9a 100644 --- a/modular_skyrat/modules/synths/code/bodyparts/stomach.dm +++ b/modular_skyrat/modules/synths/code/bodyparts/stomach.dm @@ -10,7 +10,7 @@ slot = "stomach" desc = "A specialised cell, for synthetic use only. Has a low-power mode. Without this, synthetics are unable to stay powered." status = ORGAN_ROBOTIC - organ_flags = ORGAN_SYNTHETIC + organ_flags = ORGAN_SYNTHETIC | ORGAN_SYNTHETIC_FROM_SPECIES /obj/item/organ/internal/stomach/synth/emp_act(severity) . = ..() diff --git a/modular_skyrat/modules/synths/code/bodyparts/tongue.dm b/modular_skyrat/modules/synths/code/bodyparts/tongue.dm index 93c1f094f47..75beccaf1cc 100644 --- a/modular_skyrat/modules/synths/code/bodyparts/tongue.dm +++ b/modular_skyrat/modules/synths/code/bodyparts/tongue.dm @@ -8,7 +8,7 @@ modifies_speech = TRUE taste_sensitivity = 25 // not as good as an organic tongue maxHealth = 100 //RoboTongue! - organ_flags = ORGAN_SYNTHETIC + organ_flags = ORGAN_SYNTHETIC | ORGAN_SYNTHETIC_FROM_SPECIES /obj/item/organ/internal/tongue/synth/handle_speech(datum/source, list/speech_args) speech_args[SPEECH_SPANS] |= SPAN_ROBOT diff --git a/modular_skyrat/modules/synths/code/species/synthetic.dm b/modular_skyrat/modules/synths/code/species/synthetic.dm index a1d64d8ccb9..00ab34b5824 100644 --- a/modular_skyrat/modules/synths/code/species/synthetic.dm +++ b/modular_skyrat/modules/synths/code/species/synthetic.dm @@ -51,6 +51,7 @@ mutantlungs = /obj/item/organ/internal/lungs/synth mutantheart = /obj/item/organ/internal/heart/synth mutantliver = /obj/item/organ/internal/liver/synth + mutantappendix = null exotic_blood = /datum/reagent/fuel/oil bodypart_overrides = list( BODY_ZONE_HEAD = /obj/item/bodypart/head/robot/synth, @@ -94,10 +95,6 @@ /datum/species/synthetic/on_species_gain(mob/living/carbon/human/transformer) . = ..() - var/obj/item/organ/internal/appendix/appendix = transformer.getorganslot(ORGAN_SLOT_APPENDIX) - if(appendix) - appendix.Remove(transformer) - qdel(appendix) var/screen_mutant_bodypart = transformer.dna.mutant_bodyparts[MUTANT_SYNTH_SCREEN] var/obj/item/organ/internal/eyes/eyes = transformer.getorganslot(ORGAN_SLOT_EYES)