From 0d983be692fb94f59c6d570a5edae9202edac8f3 Mon Sep 17 00:00:00 2001 From: lessthanthree <83487515+lessthnthree@users.noreply.github.com> Date: Fri, 17 Mar 2023 15:29:15 -0700 Subject: [PATCH] [MANUAL MIRROR] Fix: DNA Infuser & Unit Tests, Organs Bugfixes (#19211) * Fix: DNA Infuser & Unit Tests, Organs Bugfixes (#73003) >_"I don't remember buying tickets to Mutants on Ice."_ >-Duke Nukem This PR is (hopefully the final) part of a series of my continuing refactors of the DNA Infuser. This PR represents a "quality pass" which should also iron-out the rest of the most impactful bugs. Granular list of changes: - This PR adds unit tests for the DNA Infuser organs and `/datum/status_effect/organ_set_bonus` as recommended by @AnturK - I noticed that the base `/datum/infuser_entry` was being used in the machine for the Fly and "rejected" infusions, whereas usually we would expect it to be a base type used only as a development template. I corrected this issue and created `/datum/infuser_entry/fly` to be used for that use-case instead. - Added `/mob/proc/can_mutate()` and `/mob/living/carbon/can_mutate()` to replace a few copied lines across several files. The proc is normally used in the context of mutating a Human via their DNA. - I fixed a ton of typos in organ-related code, specifically where "receiver" was typo'd as "reciever". There are far more of those typos, but I limited the scope of my changes to organs. - I noticed a bug in `/datum/species/proc/regenerate_organs` wherein a race condition caused an organ to remove itself before it's done inserting itself. This happens because the Fly organ set bonus runs `regenerate_organs` which calls `Remove` on the organ while `Insert` is still in the call-stack. I added `INVOKE_ASYNC` as a workaround, and also changed the order the signals are emitted to prevent future bugs. This bug primarily only impacted the flyperson species transformation, which was part of the DNA Infuser's flyperson infusion organ set bonus. - In my last refactor PR #72745 I also introduced a bug in `/obj/machinery/dna_infuser/proc/infuse_organ` wherein I forgot to add the usage of `new` when attempting to implant new organs, and this PR fixes the erroneous code. - Fxed a bug which causes the organ set bonus to activate when mixing organs from different sources, which is caused by a developer oversight wherein all `/datum/status_effect/organ_set_bonus` had identical IDs. - Added a cleaner `replacetext`-based way of handling pronouns in `/datum/element/noticable_organ/proc/on_receiver_examine`, using custom macros `%PRONOUN_S` and `%PRONOUN_ES` as advised by @MrMelbert - This PR also fixes #72767 With the changes in this PR the machine will finally work as we expect it to. By adding unit tests we will also be able to ensure that it works as expected from now on. I feel confident saying that the completeness, algorithmic correctness, and code health of the DNA Infuser is much better than it was before. :cl: A.C.M.O. fix: Fully fixed the DNA Infuser, which will now infuse organs as expected. fix: Fixed flyperson species transformation and organ set bonus, which was throwing a runtime. fix: Fixed many typos in organ-related source code. /:cl: --------- Co-authored-by: Time-Green * *shrug * Uncommented the line Gandalf wanted uncommented * Properly fixes CI on this one, hopefully. --------- Co-authored-by: Dani Glore Co-authored-by: Time-Green Co-authored-by: Tom <8881105+tf-4@users.noreply.github.com> Co-authored-by: GoldenAlpharex --- code/datums/dna.dm | 10 + code/datums/elements/organ_set_bonus.dm | 34 +++- .../weather/weather_types/radiation_storm.dm | 2 +- .../game/machinery/dna_infuser/dna_infuser.dm | 19 +- .../dna_infuser/organ_sets/carp_organs.dm | 29 +-- .../dna_infuser/organ_sets/fly_organs.dm | 33 ++-- .../dna_infuser/organ_sets/fox_organs.dm | 9 +- .../dna_infuser/organ_sets/goliath_organs.dm | 34 +--- .../dna_infuser/organ_sets/gondola_organs.dm | 2 +- .../dna_infuser/organ_sets/rat_organs.dm | 69 +++---- code/game/objects/items/dna_injector.dm | 171 +++++++++--------- .../chemistry/reagents/toxin_reagents.dm | 2 +- code/modules/surgery/organs/_organ.dm | 29 +-- code/modules/surgery/organs/ears.dm | 8 +- .../organs/external/_external_organs.dm | 22 +-- code/modules/surgery/organs/external/tails.dm | 18 +- .../organs/external/wings/functional_wings.dm | 4 +- .../organs/external/wings/moth_wings.dm | 8 +- code/modules/surgery/organs/organ_internal.dm | 4 +- code/modules/unit_tests/_unit_tests.dm | 1 + code/modules/unit_tests/organ_set_bonus.dm | 86 +++++++++ .../wiremod/shell/brain_computer_interface.dm | 4 +- 22 files changed, 344 insertions(+), 254 deletions(-) create mode 100644 code/modules/unit_tests/organ_set_bonus.dm diff --git a/code/datums/dna.dm b/code/datums/dna.dm index 6c41355d398..ceb9c873dfd 100644 --- a/code/datums/dna.dm +++ b/code/datums/dna.dm @@ -561,6 +561,16 @@ GLOBAL_LIST_INIT(total_uf_len_by_block, populate_total_uf_len_by_block()) /mob/living/carbon/has_dna() return dna +/// Returns TRUE if the mob is allowed to mutate via its DNA, or FALSE if otherwise. +/// Only an organic Carbon with valid DNA may mutate; not robots, AIs, aliens, Ians, or other mobs. +/mob/proc/can_mutate() + return FALSE + +/mob/living/carbon/can_mutate() + if(!(mob_biotypes & MOB_ORGANIC)) + return FALSE + if(has_dna() && !HAS_TRAIT(src, TRAIT_GENELESS) && !HAS_TRAIT(src, TRAIT_BADDNA)) + return TRUE /// Sets the DNA of the mob to the given DNA. /mob/living/carbon/human/proc/hardset_dna(unique_identity, list/mutation_index, list/default_mutation_genes, newreal_name, newblood_type, datum/species/mrace, newfeatures, list/mutations, force_transfer_mutations) diff --git a/code/datums/elements/organ_set_bonus.dm b/code/datums/elements/organ_set_bonus.dm index fb90c3f4640..290e9865798 100644 --- a/code/datums/elements/organ_set_bonus.dm +++ b/code/datums/elements/organ_set_bonus.dm @@ -6,17 +6,15 @@ /datum/element/organ_set_bonus element_flags = ELEMENT_BESPOKE argument_hash_start_idx = 2 - ///status effect type to apply/update! - var/bonus_type + /// Status Effect typepath to instantiate and apply to the mob. + var/datum/status_effect/organ_set_bonus/bonus_type /datum/element/organ_set_bonus/Attach(datum/target, bonus_type) . = ..() if(!isorgan(target)) return ELEMENT_INCOMPATIBLE - src.bonus_type = bonus_type - RegisterSignal(target, COMSIG_ORGAN_IMPLANTED, PROC_REF(on_implanted)) RegisterSignal(target, COMSIG_ORGAN_REMOVED, PROC_REF(on_removed)) @@ -50,11 +48,15 @@ ///how many organs the carbon with this has in the set var/organs = 0 ///how many organs in the set you need to enable the bonus - var/organs_needed = 5 + var/organs_needed = 0 ///if the bonus is active var/bonus_active = FALSE var/bonus_activate_text = span_notice("??? DNA is deeply infused with you! You've learned how to make error reports!") var/bonus_deactivate_text = span_notice("Your DNA is no longer majority ???. You did make an issue report, right?") + /// Required mob bio-type. Also checks DNA validity it's set to MOB_ORGANIC. + var/required_biotype = MOB_ORGANIC + /// A Trait or list of Traits added to the mob upon bonus activation. + var/bonus_traits /datum/status_effect/organ_set_bonus/proc/set_organs(new_value) organs = new_value @@ -62,18 +64,36 @@ qdel(src) if(organs >= organs_needed) if(!bonus_active) - enable_bonus() + INVOKE_ASYNC(src, PROC_REF(enable_bonus)) else if(bonus_active) - disable_bonus() + INVOKE_ASYNC(src, PROC_REF(disable_bonus)) /datum/status_effect/organ_set_bonus/proc/enable_bonus() SHOULD_CALL_PARENT(TRUE) + if(required_biotype) + if(!(owner.mob_biotypes & required_biotype)) + return FALSE + if((required_biotype == MOB_ORGANIC) && !owner.can_mutate()) + return FALSE bonus_active = TRUE + if(bonus_traits) + if(islist(bonus_traits)) + for(var/trait in bonus_traits) + ADD_TRAIT(owner, trait, REF(src)) + else + ADD_TRAIT(owner, bonus_traits, REF(src)) if(bonus_activate_text) to_chat(owner, bonus_activate_text) + return TRUE /datum/status_effect/organ_set_bonus/proc/disable_bonus() SHOULD_CALL_PARENT(TRUE) bonus_active = FALSE + if(bonus_traits) + if(islist(bonus_traits)) + for(var/trait in bonus_traits) + REMOVE_TRAIT(owner, trait, REF(src)) + else + REMOVE_TRAIT(owner, bonus_traits, REF(src)) if(bonus_deactivate_text) to_chat(owner, bonus_deactivate_text) diff --git a/code/datums/weather/weather_types/radiation_storm.dm b/code/datums/weather/weather_types/radiation_storm.dm index b47b1bff7b4..4736ff0f490 100644 --- a/code/datums/weather/weather_types/radiation_storm.dm +++ b/code/datums/weather/weather_types/radiation_storm.dm @@ -38,7 +38,7 @@ return var/mob/living/carbon/human/H = L - if(!H.dna || HAS_TRAIT(H, TRAIT_GENELESS) || H.status_flags & GODMODE) + if(!H.can_mutate() || H.status_flags & GODMODE) return if(HAS_TRAIT(H, TRAIT_RADIMMUNE)) diff --git a/code/game/machinery/dna_infuser/dna_infuser.dm b/code/game/machinery/dna_infuser/dna_infuser.dm index 05be920a49d..3c1f5d74e30 100644 --- a/code/game/machinery/dna_infuser/dna_infuser.dm +++ b/code/game/machinery/dna_infuser/dna_infuser.dm @@ -127,11 +127,13 @@ // TODO: In the future, this should have more logic: // - Replace non-mutant organs before mutant ones. /obj/machinery/dna_infuser/proc/infuse_organ(mob/living/carbon/human/target) - if(!ishuman(target) || !infusing_into) + if(!ishuman(target)) return FALSE var/obj/item/organ/new_organ = pick_organ(target) if(!new_organ) return FALSE + // Valid organ successfully picked. + new_organ = new new_organ() if(!istype(new_organ, /obj/item/organ/internal/brain)) // Organ ISN'T brain, insert normally. new_organ.Insert(target, special = TRUE, drop_if_replaced = FALSE) @@ -148,14 +150,17 @@ return TRUE /// Picks a random mutated organ from the infuser entry which is also compatible with the target mob. -/// Tries to return a valid mutant organ if all of the following criteria are true: +/// Tries to return a typepath of a valid mutant organ if all of the following criteria are true: /// 1. Target must have a pre-existing organ in the same organ slot as the new organ; /// - or the new organ must be external. /// 2. Target's pre-existing organ must be organic / not robotic. /// 3. Target must not have the same/identical organ. /obj/machinery/dna_infuser/proc/pick_organ(mob/living/carbon/human/target) + if(!infusing_into) + return FALSE var/list/obj/item/organ/potential_new_organs = infusing_into.output_organs.Copy() - for(var/obj/item/organ/new_organ in infusing_into.output_organs) + // Remove organ typepaths from the list if they're incompatible with target. + for(var/obj/item/organ/new_organ as anything in infusing_into.output_organs) var/obj/item/organ/old_organ = target.getorganslot(initial(new_organ.slot)) if(old_organ) if((old_organ.type != new_organ) && (old_organ.status != ORGAN_ROBOTIC)) @@ -262,14 +267,14 @@ /// Verify that the occupant/target is organic, and has mutable DNA. /obj/machinery/dna_infuser/proc/is_valid_occupant(mob/living/carbon/human/human_target, mob/user) - // Invalid: Occupant isn't Human, isn't organic, lacks DNA / has TRAIT_GENELESS. - if(!ishuman(human_target) || !(human_target.mob_biotypes & MOB_ORGANIC) || !human_target.has_dna() || HAS_TRAIT(human_target, TRAIT_GENELESS)) - balloon_alert(user, "dna is missing!") - return FALSE // Invalid: DNA is too damaged to mutate anymore / has TRAIT_BADDNA. if(HAS_TRAIT(human_target, TRAIT_BADDNA)) balloon_alert(user, "dna is corrupted!") return FALSE + // Invalid: Occupant isn't Human, isn't organic, lacks DNA / has TRAIT_GENELESS. + if(!ishuman(human_target) || !human_target.can_mutate()) + balloon_alert(user, "dna is missing!") + return FALSE // Valid: Occupant is an organic Human who has undamaged and mutable DNA. return TRUE diff --git a/code/game/machinery/dna_infuser/organ_sets/carp_organs.dm b/code/game/machinery/dna_infuser/organ_sets/carp_organs.dm index 3cc4fca59e6..0835ce5aa09 100644 --- a/code/game/machinery/dna_infuser/organ_sets/carp_organs.dm +++ b/code/game/machinery/dna_infuser/organ_sets/carp_organs.dm @@ -1,23 +1,15 @@ - #define CARP_ORGAN_COLOR "#4caee7" #define CARP_SCLERA_COLOR "#ffffff" #define CARP_PUPIL_COLOR "#00b1b1" - #define CARP_COLORS CARP_ORGAN_COLOR + CARP_SCLERA_COLOR + CARP_PUPIL_COLOR ///bonus of the carp: you can swim through space! /datum/status_effect/organ_set_bonus/carp + id = "organ_set_bonus_carp" organs_needed = 4 bonus_activate_text = span_notice("Carp DNA is deeply infused with you! You've learned how to propel yourself through space!") bonus_deactivate_text = span_notice("Your DNA is once again mostly yours, and so fades your ability to space-swim...") - -/datum/status_effect/organ_set_bonus/carp/enable_bonus() - . = ..() - ADD_TRAIT(owner, TRAIT_SPACEWALK, REF(src)) - -/datum/status_effect/organ_set_bonus/carp/disable_bonus() - . = ..() - REMOVE_TRAIT(owner, TRAIT_SPACEWALK, REF(src)) + bonus_traits = TRAIT_SPACEWALK ///Carp lungs! You can breathe in space! Oh... you can't breathe on the station, you need low oxygen environments. /// Inverts behavior of lungs. Bypasses suffocation due to space / lack of gas, but also allows Oxygen to suffocate. @@ -61,6 +53,8 @@ if(!ishuman(tongue_owner)) return var/mob/living/carbon/human/human_receiver = tongue_owner + if(!human_receiver.can_mutate()) + return var/datum/species/rec_species = human_receiver.dna.species rec_species.update_no_equip_flags(tongue_owner, rec_species.no_equip_flags | ITEM_SLOT_MASK) var/obj/item/bodypart/head/head = human_receiver.get_bodypart(BODY_ZONE_HEAD) @@ -73,6 +67,8 @@ if(!ishuman(tongue_owner)) return var/mob/living/carbon/human/human_receiver = tongue_owner + if(!human_receiver.can_mutate()) + return var/datum/species/rec_species = human_receiver.dna.species rec_species.update_no_equip_flags(tongue_owner, initial(rec_species.no_equip_flags)) var/obj/item/bodypart/head/head = human_receiver.get_bodypart(BODY_ZONE_HEAD) @@ -112,10 +108,7 @@ /obj/item/organ/internal/brain/carp/Initialize(mapload) . = ..() AddElement(/datum/element/organ_set_bonus, /datum/status_effect/organ_set_bonus/carp) - -/obj/item/organ/internal/brain/carp/Insert(mob/living/carbon/reciever, special, drop_if_replaced, no_id_transfer) - AddElement(/datum/element/noticable_organ, "seem[reciever.p_s()] unable to stay still.") - return ..() + AddElement(/datum/element/noticable_organ, "seem%PRONOUN_S unable to stay still.") /obj/item/organ/internal/brain/carp/Insert(mob/living/carbon/brain_owner, special, drop_if_replaced, no_id_transfer) . = ..() @@ -125,7 +118,7 @@ //technically you could get around the mood issue by extracting and reimplanting the brain but it will be far easier to just go one z there and back /obj/item/organ/internal/brain/carp/Remove(mob/living/carbon/brain_owner, special, no_id_transfer) . = ..() - UnregisterSignal(brain_owner) + UnregisterSignal(brain_owner, COMSIG_MOVABLE_Z_CHANGED) deltimer(cooldown_timer) /obj/item/organ/internal/brain/carp/get_attacking_limb(mob/living/carbon/human/target) @@ -153,14 +146,10 @@ /obj/item/organ/internal/heart/carp/Initialize(mapload) . = ..() + AddElement(/datum/element/noticable_organ, "skin has small patches of scales growing on it.", BODY_ZONE_CHEST) AddElement(/datum/element/organ_set_bonus, /datum/status_effect/organ_set_bonus/carp) -/obj/item/organ/internal/heart/carp/Insert(mob/living/carbon/reciever, special = FALSE, drop_if_replaced = TRUE) - AddElement(/datum/element/noticable_organ, "[reciever.p_have()] small patches of scales growing on [reciever.p_their()] skin...") - return ..() - #undef CARP_ORGAN_COLOR #undef CARP_SCLERA_COLOR #undef CARP_PUPIL_COLOR - #undef CARP_COLORS diff --git a/code/game/machinery/dna_infuser/organ_sets/fly_organs.dm b/code/game/machinery/dna_infuser/organ_sets/fly_organs.dm index e05e479524c..71bb89fdad4 100644 --- a/code/game/machinery/dna_infuser/organ_sets/fly_organs.dm +++ b/code/game/machinery/dna_infuser/organ_sets/fly_organs.dm @@ -1,5 +1,9 @@ +#define FLY_INFUSED_ORGAN_DESC "You have no idea what the hell this is, or how it manages to keep something alive in any capacity." +#define FLY_INFUSED_ORGAN_ICON pick("brain-x-d", "liver-x", "kidneys-x", "spinner-x", "lungs-x", "random_fly_1", "random_fly_2", "random_fly_3", "random_fly_4", "random_fly_5") + ///bonus of the fly: you... are a flyperson now. sorry. /datum/status_effect/organ_set_bonus/fly + id = "organ_set_bonus_fly" organs_needed = 4 //there are actually 7 fly organs that count, but you only need 4 to go full-flyperson. Be careful! bonus_activate_text = null bonus_deactivate_text = null @@ -64,12 +68,12 @@ return ..() + /datum/language/buzzwords /obj/item/organ/internal/heart/fly - desc = "You have no idea what the hell this is, or how it manages to keep something alive in any capacity." + desc = FLY_INFUSED_ORGAN_DESC /obj/item/organ/internal/heart/fly/Initialize(mapload) . = ..() name = odd_organ_name() - icon_state = pick("brain-x-d", "liver-x", "kidneys-x", "spinner-x", "lungs-x", "random_fly_1", "random_fly_2", "random_fly_3", "random_fly_4", "random_fly_5") + icon_state = FLY_INFUSED_ORGAN_ICON AddElement(/datum/element/organ_set_bonus, /datum/status_effect/organ_set_bonus/fly) /obj/item/organ/internal/heart/fly/update_icon_state() @@ -77,31 +81,31 @@ return //don't set icon thank you /obj/item/organ/internal/lungs/fly - desc = "You have no idea what the hell this is, or how it manages to keep something alive in any capacity." + desc = FLY_INFUSED_ORGAN_DESC /obj/item/organ/internal/lungs/fly/Initialize(mapload) . = ..() name = odd_organ_name() - icon_state = pick("brain-x-d", "liver-x", "kidneys-x", "spinner-x", "lungs-x", "random_fly_1", "random_fly_2", "random_fly_3", "random_fly_4", "random_fly_5") + icon_state = FLY_INFUSED_ORGAN_ICON AddElement(/datum/element/organ_set_bonus, /datum/status_effect/organ_set_bonus/fly) /obj/item/organ/internal/liver/fly - desc = "You have no idea what the hell this is, or how it manages to keep something alive in any capacity." + desc = FLY_INFUSED_ORGAN_DESC alcohol_tolerance = 0.007 //flies eat vomit, so a lower alcohol tolerance is perfect! /obj/item/organ/internal/liver/fly/Initialize(mapload) . = ..() name = odd_organ_name() - icon_state = pick("brain-x-d", "liver-x", "kidneys-x", "spinner-x", "lungs-x", "random_fly_1", "random_fly_2", "random_fly_3", "random_fly_4", "random_fly_5") + icon_state = FLY_INFUSED_ORGAN_ICON AddElement(/datum/element/organ_set_bonus, /datum/status_effect/organ_set_bonus/fly) /obj/item/organ/internal/stomach/fly - desc = "You have no idea what the hell this is, or how it manages to keep something alive in any capacity." + desc = FLY_INFUSED_ORGAN_DESC /obj/item/organ/internal/stomach/fly/Initialize(mapload) . = ..() name = odd_organ_name() - icon_state = pick("brain-x-d", "liver-x", "kidneys-x", "spinner-x", "lungs-x", "random_fly_1", "random_fly_2", "random_fly_3", "random_fly_4", "random_fly_5") + icon_state = FLY_INFUSED_ORGAN_ICON AddElement(/datum/element/organ_set_bonus, /datum/status_effect/organ_set_bonus/fly) /obj/item/organ/internal/stomach/fly/after_eat(edible) @@ -117,28 +121,29 @@ return ..() /obj/item/organ/internal/appendix/fly - desc = "You have no idea what the hell this is, or how it manages to keep something alive in any capacity." + desc = FLY_INFUSED_ORGAN_DESC /obj/item/organ/internal/appendix/fly/Initialize(mapload) . = ..() name = odd_organ_name() - icon_state = pick("brain-x-d", "liver-x", "kidneys-x", "spinner-x", "lungs-x", "random_fly_1", "random_fly_2", "random_fly_3", "random_fly_4", "random_fly_5") + icon_state = FLY_INFUSED_ORGAN_ICON AddElement(/datum/element/organ_set_bonus, /datum/status_effect/organ_set_bonus/fly) /obj/item/organ/internal/appendix/fly/update_appearance(updates=ALL) return ..(updates & ~(UPDATE_NAME|UPDATE_ICON)) //don't set name or icon thank you - - //useless organs we throw in just to fuck with surgeons a bit more. they aren't part of a bonus, just the (absolute) state of flies /obj/item/organ/internal/fly - desc = "You have no idea what the hell this is, or how it manages to keep something alive in any capacity." + desc = FLY_INFUSED_ORGAN_DESC visual = FALSE /obj/item/organ/internal/fly/Initialize(mapload) . = ..() name = odd_organ_name() - icon_state = pick("brain-x-d", "liver-x", "kidneys-x", "spinner-x", "lungs-x", "random_fly_1", "random_fly_2", "random_fly_3", "random_fly_4", "random_fly_5") + icon_state = FLY_INFUSED_ORGAN_ICON /obj/item/organ/internal/fly/groin //appendix is the only groin organ so we gotta have one of these too lol zone = BODY_ZONE_PRECISE_GROIN + +#undef FLY_INFUSED_ORGAN_DESC +#undef FLY_INFUSED_ORGAN_ICON diff --git a/code/game/machinery/dna_infuser/organ_sets/fox_organs.dm b/code/game/machinery/dna_infuser/organ_sets/fox_organs.dm index 04c48cfe9ac..0c7f482422c 100644 --- a/code/game/machinery/dna_infuser/organ_sets/fox_organs.dm +++ b/code/game/machinery/dna_infuser/organ_sets/fox_organs.dm @@ -1,4 +1,3 @@ - /obj/item/organ/internal/ears/fox name = "fox ears" icon = 'icons/obj/clothing/head/costume.dmi' @@ -10,16 +9,16 @@ //SKYRAT EDIT REMOVAL BEGIN - CUSTOMIZATION /* /obj/item/organ/internal/ears/fox/Insert(mob/living/carbon/human/ear_owner, special = 0, drop_if_replaced = TRUE) - ..() - if(istype(ear_owner)) + . = ..() + if(istype(ear_owner) && ear_owner.dna) color = ear_owner.hair_color ear_owner.dna.features["ears"] = ear_owner.dna.species.mutant_bodyparts["ears"] = "Fox" ear_owner.dna.update_uf_block(DNA_EARS_BLOCK) ear_owner.update_body() /obj/item/organ/internal/ears/fox/Remove(mob/living/carbon/human/ear_owner, special = 0) - ..() - if(istype(ear_owner)) + . = ..() + if(istype(ear_owner) && ear_owner.dna) color = ear_owner.hair_color ear_owner.dna.species.mutant_bodyparts -= "ears" ear_owner.update_body() diff --git a/code/game/machinery/dna_infuser/organ_sets/goliath_organs.dm b/code/game/machinery/dna_infuser/organ_sets/goliath_organs.dm index 48dd45c7985..66283804e77 100644 --- a/code/game/machinery/dna_infuser/organ_sets/goliath_organs.dm +++ b/code/game/machinery/dna_infuser/organ_sets/goliath_organs.dm @@ -1,23 +1,15 @@ - #define GOLIATH_ORGAN_COLOR "#875652" #define GOLIATH_SCLERA_COLOR "#ac0f32" #define GOLIATH_PUPIL_COLOR "#FF0000" - #define GOLIATH_COLORS GOLIATH_ORGAN_COLOR + GOLIATH_SCLERA_COLOR + GOLIATH_PUPIL_COLOR ///bonus of the goliath: you can swim through space! /datum/status_effect/organ_set_bonus/goliath + id = "organ_set_bonus_goliath" organs_needed = 4 bonus_activate_text = span_notice("goliath DNA is deeply infused with you! You can now endure walking on lava!") bonus_deactivate_text = span_notice("You feel your muscle mass shrink and the tendrils around your skin wither. Your Goliath DNA is mostly gone and so is your ability to survive lava.") - -/datum/status_effect/organ_set_bonus/goliath/enable_bonus() - . = ..() - ADD_TRAIT(owner, TRAIT_LAVA_IMMUNE, REF(src)) - -/datum/status_effect/organ_set_bonus/goliath/disable_bonus() - . = ..() - REMOVE_TRAIT(owner, TRAIT_LAVA_IMMUNE, REF(src)) + bonus_traits = TRAIT_LAVA_IMMUNE ///goliath eyes, simple night vision /obj/item/organ/internal/eyes/night_vision/goliath @@ -42,14 +34,6 @@ AddElement(/datum/element/noticable_organ, "eyes are blood red and stone-like.", BODY_ZONE_PRECISE_EYES) AddElement(/datum/element/organ_set_bonus, /datum/status_effect/organ_set_bonus/goliath) -/obj/item/organ/internal/eyes/night_vision/goliath/Insert(mob/living/carbon/eyes_owner, special, drop_if_replaced) - . = ..() - ADD_TRAIT(eyes_owner, TRAIT_UNNATURAL_RED_GLOWY_EYES, ORGAN_TRAIT) - -/obj/item/organ/internal/eyes/night_vision/goliath/Remove(mob/living/carbon/eyes_owner, special, drop_if_replaced) - REMOVE_TRAIT(eyes_owner, TRAIT_UNNATURAL_RED_GLOWY_EYES, ORGAN_TRAIT) - return ..() - ///goliath lungs! You can breathe lavaland air mix but can't breath pure O2 from a tank anymore. /obj/item/organ/internal/lungs/lavaland/goliath name = "mutated goliath-lungs" @@ -87,9 +71,10 @@ if(!ishuman(brain_owner)) return var/mob/living/carbon/human/human_receiver = brain_owner + if(!human_receiver.can_mutate()) + return var/datum/species/rec_species = human_receiver.dna.species rec_species.update_no_equip_flags(brain_owner, rec_species.no_equip_flags | ITEM_SLOT_GLOVES) - hammer = new/obj/item/goliath_infuser_hammer brain_owner.put_in_hands(hammer) @@ -99,12 +84,13 @@ if(!ishuman(brain_owner)) return var/mob/living/carbon/human/human_receiver = brain_owner + if(!human_receiver.can_mutate()) + return var/datum/species/rec_species = human_receiver.dna.species rec_species.update_no_equip_flags(brain_owner, initial(rec_species.no_equip_flags)) if(hammer) brain_owner.visible_message(span_warning("\The [hammer] disintegrates!")) QDEL_NULL(hammer) - return ..() /obj/item/goliath_infuser_hammer name = "tendril hammer" @@ -172,11 +158,11 @@ /obj/item/organ/internal/heart/goliath/Initialize(mapload) . = ..() + AddElement(/datum/element/noticable_organ, "skin has visible hard plates growing from within.", BODY_ZONE_CHEST) AddElement(/datum/element/organ_set_bonus, /datum/status_effect/organ_set_bonus/goliath) AddElement(/datum/element/update_icon_blocker) -/obj/item/organ/internal/heart/goliath/Insert(mob/living/carbon/reciever, special = FALSE, drop_if_replaced = TRUE) - AddElement(/datum/element/noticable_organ, "skin [reciever.p_have()] visible hard plates growing from within.", BODY_ZONE_CHEST) - return ..() - +#undef GOLIATH_ORGAN_COLOR +#undef GOLIATH_SCLERA_COLOR +#undef GOLIATH_PUPIL_COLOR #undef GOLIATH_COLORS diff --git a/code/game/machinery/dna_infuser/organ_sets/gondola_organs.dm b/code/game/machinery/dna_infuser/organ_sets/gondola_organs.dm index c7d2db6419f..805ae2290be 100644 --- a/code/game/machinery/dna_infuser/organ_sets/gondola_organs.dm +++ b/code/game/machinery/dna_infuser/organ_sets/gondola_organs.dm @@ -13,7 +13,7 @@ Fluoride Stare: After someone says 5 words, blah blah blah... organs_needed = 3 bonus_activate_text = span_notice("Gondola DNA is deeply infused with you! You are the ultimate observer, uncaring of the environment around you...") bonus_deactivate_text = span_notice("Your DNA is no longer serene and gondola-like, and so you begin remembering that breathing is like, important...") - //bonus_traits = list(TRAIT_RESISTHEAT, TRAIT_RESISTCOLD, TRAIT_NOBREATH, TRAIT_RESISTLOWPRESSURE, TRAIT_RESISTHIGHPRESSURE) SKYRAT EDIT REMOVE ME!!! + bonus_traits = list(TRAIT_RESISTHEAT, TRAIT_RESISTCOLD, TRAIT_NOBREATH, TRAIT_RESISTLOWPRESSURE, TRAIT_RESISTHIGHPRESSURE) /// makes you a pacifist and turns most mobs neutral towards you /obj/item/organ/internal/heart/gondola diff --git a/code/game/machinery/dna_infuser/organ_sets/rat_organs.dm b/code/game/machinery/dna_infuser/organ_sets/rat_organs.dm index 414ca0b1622..d107bf42534 100644 --- a/code/game/machinery/dna_infuser/organ_sets/rat_organs.dm +++ b/code/game/machinery/dna_infuser/organ_sets/rat_organs.dm @@ -1,25 +1,15 @@ - #define RAT_ORGAN_COLOR "#646464" #define RAT_SCLERA_COLOR "#f0e055" #define RAT_PUPIL_COLOR "#000000" - #define RAT_COLORS RAT_ORGAN_COLOR + RAT_SCLERA_COLOR + RAT_PUPIL_COLOR ///bonus of the rat: you can ventcrawl! /datum/status_effect/organ_set_bonus/rat + id = "organ_set_bonus_rat" organs_needed = 4 bonus_activate_text = span_notice("Rodent DNA is deeply infused with you! You've learned how to traverse ventilation!") bonus_deactivate_text = span_notice("Your DNA is no longer majority rat, and so fades your ventilation skills...") - -/datum/status_effect/organ_set_bonus/rat/enable_bonus() - . = ..() - ADD_TRAIT(owner, TRAIT_VENTCRAWLER_NUDE, REF(src)) - -/datum/status_effect/organ_set_bonus/rat/disable_bonus() - . = ..() - REMOVE_TRAIT(owner, TRAIT_VENTCRAWLER_NUDE, REF(src)) - - + bonus_traits = TRAIT_VENTCRAWLER_NUDE ///way better night vision, super sensitive. lotta things work like this, huh? /obj/item/organ/internal/eyes/night_vision/rat @@ -42,8 +32,6 @@ AddElement(/datum/element/noticable_organ, "eyes have deep, shifty black pupils, surrounded by a sickening yellow sclera.", BODY_ZONE_PRECISE_EYES) AddElement(/datum/element/organ_set_bonus, /datum/status_effect/organ_set_bonus/rat) - - ///increases hunger, disgust recovers quicker, expands what is defined as "food" /obj/item/organ/internal/stomach/rat name = "mutated rat-stomach" @@ -54,27 +42,29 @@ icon_state = "stomach" greyscale_config = /datum/greyscale_config/mutant_organ greyscale_colors = RAT_COLORS + /// Multiplier of [physiology.hunger_mod]. + var/hunger_mod = 10 /obj/item/organ/internal/stomach/rat/Initialize(mapload) . = ..() AddElement(/datum/element/organ_set_bonus, /datum/status_effect/organ_set_bonus/rat) + AddElement(/datum/element/noticable_organ, "mouth is drooling excessively.", BODY_ZONE_PRECISE_MOUTH) -/obj/item/organ/internal/stomach/rat/Insert(mob/living/carbon/reciever, special = FALSE, drop_if_replaced = TRUE) - AddElement(/datum/element/noticable_organ, "salivate[reciever.p_s()] excessively.", BODY_ZONE_HEAD) - return ..() - -/obj/item/organ/internal/stomach/rat/Insert(mob/living/carbon/reciever, special, drop_if_replaced) +/obj/item/organ/internal/stomach/rat/Insert(mob/living/carbon/receiver, special, drop_if_replaced) . = ..() - if(!ishuman(reciever)) + if(!ishuman(receiver)) + return + var/mob/living/carbon/human/human_holder = receiver + if(!human_holder.can_mutate()) return - var/mob/living/carbon/human/human_holder = reciever var/datum/species/species = human_holder.dna.species //mmm, cheese. doesn't especially like anything else species.liked_food = DAIRY //but a rat can eat anything without issue species.disliked_food = NONE species.toxic_food = NONE - human_holder.physiology.hunger_mod *= 10 + if(human_holder.physiology) + human_holder.physiology.hunger_mod *= hunger_mod RegisterSignal(human_holder, COMSIG_SPECIES_GAIN, PROC_REF(on_species_gain)) /obj/item/organ/internal/stomach/rat/proc/on_species_gain(datum/source, datum/species/new_species, datum/species/old_species) @@ -88,15 +78,16 @@ if(!ishuman(stomach_owner)) return var/mob/living/carbon/human/human_holder = stomach_owner + if(!human_holder.can_mutate()) + return var/datum/species/species = human_holder.dna.species species.liked_food = initial(species.liked_food) species.disliked_food = initial(species.disliked_food) species.toxic_food = initial(species.toxic_food) - human_holder.physiology.hunger_mod /= 10 + if(human_holder.physiology) + human_holder.physiology.hunger_mod /= hunger_mod UnregisterSignal(stomach_owner, COMSIG_SPECIES_GAIN) - - /// makes you smaller, walk over tables, and take 1.5x damage /obj/item/organ/internal/heart/rat name = "mutated rat-heart" @@ -110,29 +101,30 @@ /obj/item/organ/internal/heart/rat/Initialize(mapload) . = ..() AddElement(/datum/element/organ_set_bonus, /datum/status_effect/organ_set_bonus/rat) + AddElement(/datum/element/noticable_organ, "hunch%PRONOUN_ES over unnaturally!") -/obj/item/organ/internal/heart/rat/Insert(mob/living/carbon/reciever, special = FALSE, drop_if_replaced = TRUE) - AddElement(/datum/element/noticable_organ, "hunch[owner.p_es()] over unnaturally!") - return ..() - -/obj/item/organ/internal/heart/rat/Insert(mob/living/carbon/reciever, special, drop_if_replaced) +/obj/item/organ/internal/heart/rat/Insert(mob/living/carbon/receiver, special, drop_if_replaced) . = ..() - if(!ishuman(reciever)) + if(!ishuman(receiver)) return - var/mob/living/carbon/human/human_reciever = reciever - human_reciever.dna.add_mutation(/datum/mutation/human/dwarfism) + var/mob/living/carbon/human/human_receiver = receiver + if(!human_receiver.can_mutate()) + return + human_receiver.dna.add_mutation(/datum/mutation/human/dwarfism) //but 1.5 damage - human_reciever.physiology.damage_resistance -= 50 + if(human_receiver.physiology) + human_receiver.physiology.damage_resistance -= 50 /obj/item/organ/internal/heart/rat/Remove(mob/living/carbon/heartless, special) . = ..() if(!ishuman(heartless)) return var/mob/living/carbon/human/human_heartless = heartless + if(!human_heartless.can_mutate()) + return human_heartless.dna.remove_mutation(/datum/mutation/human/dwarfism) - human_heartless.physiology.damage_resistance += 50 - - + if(human_heartless.physiology) + human_heartless.physiology.damage_resistance += 50 /// you occasionally squeak, and have some rat related verbal tics /obj/item/organ/internal/tongue/rat @@ -148,7 +140,7 @@ /obj/item/organ/internal/tongue/rat/Initialize(mapload) . = ..() - AddElement(/datum/element/noticable_organ, "teeth are oddly shaped and yellowing", BODY_ZONE_HEAD) + AddElement(/datum/element/noticable_organ, "teeth are oddly shaped and yellowing", BODY_ZONE_PRECISE_MOUTH) AddElement(/datum/element/organ_set_bonus, /datum/status_effect/organ_set_bonus/rat) /obj/item/organ/internal/tongue/rat/modify_speech(datum/source, list/speech_args) @@ -184,5 +176,4 @@ #undef RAT_ORGAN_COLOR #undef RAT_SCLERA_COLOR #undef RAT_PUPIL_COLOR - #undef RAT_COLORS diff --git a/code/game/objects/items/dna_injector.dm b/code/game/objects/items/dna_injector.dm index a0c6f4c65bb..42ccd090483 100644 --- a/code/game/objects/items/dna_injector.dm +++ b/code/game/objects/items/dna_injector.dm @@ -41,30 +41,30 @@ return attack_hand(user, modifiers) /obj/item/dnainjector/proc/inject(mob/living/carbon/target, mob/user) - if(target.has_dna() && !HAS_TRAIT(target, TRAIT_GENELESS) && !HAS_TRAIT(target, TRAIT_BADDNA)) - for(var/removed_mutation in remove_mutations) - target.dna.remove_mutation(removed_mutation) - for(var/added_mutation in add_mutations) - if(added_mutation == /datum/mutation/human/race) - message_admins("[ADMIN_LOOKUPFLW(user)] injected [key_name_admin(target)] with the [name] [span_danger("(MONKEY)")]") - if(target.dna.mutation_in_sequence(added_mutation)) - target.dna.activate_mutation(added_mutation) - else - target.dna.add_mutation(added_mutation, MUT_EXTRA) - if(fields) - if(fields["name"] && fields["UE"] && fields["blood_type"]) - target.real_name = fields["name"] - target.dna.unique_enzymes = fields["UE"] - target.name = target.real_name - target.dna.blood_type = fields["blood_type"] - if(fields["UI"]) //UI+UE - target.dna.unique_identity = merge_text(target.dna.unique_identity, fields["UI"]) - if(fields["UF"]) - target.dna.unique_features = merge_text(target.dna.unique_features, fields["UF"]) - if(fields["UI"] || fields["UF"]) - target.updateappearance(mutcolor_update = TRUE, mutations_overlay_update = TRUE) - return TRUE - return FALSE + if(!target.can_mutate()) + return FALSE + for(var/removed_mutation in remove_mutations) + target.dna.remove_mutation(removed_mutation) + for(var/added_mutation in add_mutations) + if(added_mutation == /datum/mutation/human/race) + message_admins("[ADMIN_LOOKUPFLW(user)] injected [key_name_admin(target)] with the [name] [span_danger("(MONKEY)")]") + if(target.dna.mutation_in_sequence(added_mutation)) + target.dna.activate_mutation(added_mutation) + else + target.dna.add_mutation(added_mutation, MUT_EXTRA) + if(fields) + if(fields["name"] && fields["UE"] && fields["blood_type"]) + target.real_name = fields["name"] + target.dna.unique_enzymes = fields["UE"] + target.name = target.real_name + target.dna.blood_type = fields["blood_type"] + if(fields["UI"]) //UI+UE + target.dna.unique_identity = merge_text(target.dna.unique_identity, fields["UI"]) + if(fields["UF"]) + target.dna.unique_features = merge_text(target.dna.unique_features, fields["UF"]) + if(fields["UI"] || fields["UF"]) + target.updateappearance(mutcolor_update = TRUE, mutations_overlay_update = TRUE) + return TRUE /obj/item/dnainjector/attack(mob/target, mob/user) if(!ISADVANCEDTOOLUSER(user)) @@ -105,52 +105,50 @@ if(target.stat == DEAD) //prevents dead people from having their DNA changed to_chat(user, span_notice("You can't modify [target]'s DNA while [target.p_theyre()] dead.")) return FALSE - - if(target.has_dna() && !(HAS_TRAIT(target, TRAIT_BADDNA))) - var/endtime = world.time + duration - for(var/mutation in remove_mutations) - if(mutation == /datum/mutation/human/race) - if(!ismonkey(target)) - continue - target = target.dna.remove_mutation(mutation) - else - target.dna.remove_mutation(mutation) - for(var/mutation in add_mutations) - if(target.dna.get_mutation(mutation)) - continue //Skip permanent mutations we already have. - if(mutation == /datum/mutation/human/race && !ismonkey(target)) - message_admins("[ADMIN_LOOKUPFLW(user)] injected [key_name_admin(target)] with the [name] [span_danger("(MONKEY)")]") - target = target.dna.add_mutation(mutation, MUT_OTHER, endtime) - else - target.dna.add_mutation(mutation, MUT_OTHER, endtime) - if(fields) - if(fields["name"] && fields["UE"] && fields["blood_type"]) - if(!target.dna.previous["name"]) - target.dna.previous["name"] = target.real_name - if(!target.dna.previous["UE"]) - target.dna.previous["UE"] = target.dna.unique_enzymes - if(!target.dna.previous["blood_type"]) - target.dna.previous["blood_type"] = target.dna.blood_type - target.real_name = fields["name"] - target.dna.unique_enzymes = fields["UE"] - target.name = target.real_name - target.dna.blood_type = fields["blood_type"] - target.dna.temporary_mutations[UE_CHANGED] = endtime - if(fields["UI"]) //UI+UE - if(!target.dna.previous["UI"]) - target.dna.previous["UI"] = target.dna.unique_identity - target.dna.unique_identity = merge_text(target.dna.unique_identity, fields["UI"]) - target.dna.temporary_mutations[UI_CHANGED] = endtime - if(fields["UF"]) //UI+UE - if(!target.dna.previous["UF"]) - target.dna.previous["UF"] = target.dna.unique_features - target.dna.unique_features = merge_text(target.dna.unique_features, fields["UF"]) - target.dna.temporary_mutations[UF_CHANGED] = endtime - if(fields["UI"] || fields["UF"]) - target.updateappearance(mutcolor_update = TRUE, mutations_overlay_update = TRUE) - return TRUE - else + if(!target.can_mutate()) return FALSE + var/endtime = world.time + duration + for(var/mutation in remove_mutations) + if(mutation == /datum/mutation/human/race) + if(!ismonkey(target)) + continue + target = target.dna.remove_mutation(mutation) + else + target.dna.remove_mutation(mutation) + for(var/mutation in add_mutations) + if(target.dna.get_mutation(mutation)) + continue //Skip permanent mutations we already have. + if(mutation == /datum/mutation/human/race && !ismonkey(target)) + message_admins("[ADMIN_LOOKUPFLW(user)] injected [key_name_admin(target)] with the [name] [span_danger("(MONKEY)")]") + target = target.dna.add_mutation(mutation, MUT_OTHER, endtime) + else + target.dna.add_mutation(mutation, MUT_OTHER, endtime) + if(fields) + if(fields["name"] && fields["UE"] && fields["blood_type"]) + if(!target.dna.previous["name"]) + target.dna.previous["name"] = target.real_name + if(!target.dna.previous["UE"]) + target.dna.previous["UE"] = target.dna.unique_enzymes + if(!target.dna.previous["blood_type"]) + target.dna.previous["blood_type"] = target.dna.blood_type + target.real_name = fields["name"] + target.dna.unique_enzymes = fields["UE"] + target.name = target.real_name + target.dna.blood_type = fields["blood_type"] + target.dna.temporary_mutations[UE_CHANGED] = endtime + if(fields["UI"]) //UI+UE + if(!target.dna.previous["UI"]) + target.dna.previous["UI"] = target.dna.unique_identity + target.dna.unique_identity = merge_text(target.dna.unique_identity, fields["UI"]) + target.dna.temporary_mutations[UI_CHANGED] = endtime + if(fields["UF"]) //UI+UE + if(!target.dna.previous["UF"]) + target.dna.previous["UF"] = target.dna.unique_features + target.dna.unique_features = merge_text(target.dna.unique_features, fields["UF"]) + target.dna.temporary_mutations[UF_CHANGED] = endtime + if(fields["UI"] || fields["UF"]) + target.updateappearance(mutcolor_update = TRUE, mutations_overlay_update = TRUE) + return TRUE /obj/item/dnainjector/timed/hulk name = "\improper DNA injector (Hulk)" @@ -171,24 +169,23 @@ var/crispr_charge = FALSE // Look for viruses, look at symptoms, if research and Dormant DNA Activator or Viral Evolutionary Acceleration, set to true /obj/item/dnainjector/activator/inject(mob/living/carbon/target, mob/user) - if(target.has_dna() && !HAS_TRAIT(target, TRAIT_GENELESS) && !HAS_TRAIT(target, TRAIT_BADDNA)) - for(var/mutation in add_mutations) - var/datum/mutation/human/added_mutation = mutation - if(istype(added_mutation, /datum/mutation/human)) - mutation = added_mutation.type - if(!target.dna.activate_mutation(added_mutation)) - if(doitanyway) - target.dna.add_mutation(added_mutation, MUT_EXTRA) - else if(research && target.client) - filled = TRUE - - for(var/datum/disease/advance/disease in target.diseases) - for(var/datum/symptom/symp in disease.symptoms) - if((symp.type == /datum/symptom/genetic_mutation) || (symp.type == /datum/symptom/viralevolution)) - crispr_charge = TRUE - log_combat(user, target, "[!doitanyway ? "failed to inject" : "injected"]", "[src] ([mutation])[crispr_charge ? " with CRISPR charge" : ""]") - return TRUE - return FALSE + if(!target.can_mutate()) + return FALSE + for(var/mutation in add_mutations) + var/datum/mutation/human/added_mutation = mutation + if(istype(added_mutation, /datum/mutation/human)) + mutation = added_mutation.type + if(!target.dna.activate_mutation(added_mutation)) + if(doitanyway) + target.dna.add_mutation(added_mutation, MUT_EXTRA) + else if(research && target.client) + filled = TRUE + for(var/datum/disease/advance/disease in target.diseases) + for(var/datum/symptom/symp in disease.symptoms) + if((symp.type == /datum/symptom/genetic_mutation) || (symp.type == /datum/symptom/viralevolution)) + crispr_charge = TRUE + log_combat(user, target, "[!doitanyway ? "failed to inject" : "injected"]", "[src] ([mutation])[crispr_charge ? " with CRISPR charge" : ""]") + return TRUE /// DNA INJECTORS diff --git a/code/modules/reagents/chemistry/reagents/toxin_reagents.dm b/code/modules/reagents/chemistry/reagents/toxin_reagents.dm index 1b9ff451c83..0f76cfd2a2a 100644 --- a/code/modules/reagents/chemistry/reagents/toxin_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/toxin_reagents.dm @@ -52,7 +52,7 @@ /datum/reagent/toxin/mutagen/expose_mob(mob/living/carbon/exposed_mob, methods=TOUCH, reac_volume) . = ..() - if(!exposed_mob.has_dna() || HAS_TRAIT(exposed_mob, TRAIT_GENELESS) || HAS_TRAIT(exposed_mob, TRAIT_BADDNA)) + if(!exposed_mob.can_mutate()) return //No robots, AIs, aliens, Ians or other mobs should be affected by this. if(((methods & VAPOR) && prob(min(33, reac_volume))) || (methods & (INGEST|PATCH|INJECT))) exposed_mob.random_mutate_unique_identity() diff --git a/code/modules/surgery/organs/_organ.dm b/code/modules/surgery/organs/_organ.dm index c46134c8c3e..6252cb3ade7 100644 --- a/code/modules/surgery/organs/_organ.dm +++ b/code/modules/surgery/organs/_organ.dm @@ -62,34 +62,35 @@ INITIALIZE_IMMEDIATE(/obj/item/organ) /* * Insert the organ into the select mob. * - * reciever - the mob who will get our organ + * receiver - the mob who will get our organ * special - "quick swapping" an organ out - when TRUE, the mob will be unaffected by not having that organ for the moment * drop_if_replaced - if there's an organ in the slot already, whether we drop it afterwards */ -/obj/item/organ/proc/Insert(mob/living/carbon/reciever, special = FALSE, drop_if_replaced = TRUE) - if(!iscarbon(reciever) || owner == reciever) +/obj/item/organ/proc/Insert(mob/living/carbon/receiver, special = FALSE, drop_if_replaced = TRUE) + if(!iscarbon(receiver) || owner == receiver) return FALSE - var/obj/item/organ/replaced = reciever.getorganslot(slot) + var/obj/item/organ/replaced = receiver.getorganslot(slot) if(replaced) - replaced.Remove(reciever, special = TRUE) + replaced.Remove(receiver, special = TRUE) if(drop_if_replaced) - replaced.forceMove(get_turf(reciever)) + replaced.forceMove(get_turf(receiver)) else qdel(replaced) - reciever.internal_organs |= src - reciever.internal_organs_slot[slot] = src + receiver.internal_organs |= src + receiver.internal_organs_slot[slot] = src + owner = receiver - SEND_SIGNAL(src, COMSIG_ORGAN_IMPLANTED, reciever) - SEND_SIGNAL(reciever, COMSIG_CARBON_GAIN_ORGAN, src, special) - - owner = reciever moveToNullspace() RegisterSignal(owner, COMSIG_PARENT_EXAMINE, PROC_REF(on_owner_examine)) - update_organ_traits(reciever) + update_organ_traits(receiver) for(var/datum/action/action as anything in actions) - action.Grant(reciever) + action.Grant(receiver) + + SEND_SIGNAL(src, COMSIG_ORGAN_IMPLANTED, receiver) + SEND_SIGNAL(receiver, COMSIG_CARBON_GAIN_ORGAN, src, special) + return TRUE /* diff --git a/code/modules/surgery/organs/ears.dm b/code/modules/surgery/organs/ears.dm index b62ff383658..2f8d88d0c14 100644 --- a/code/modules/surgery/organs/ears.dm +++ b/code/modules/surgery/organs/ears.dm @@ -71,16 +71,16 @@ //SKYRAT EDIT REMOVAL BEGIN - CUSTOMIZATION /* /obj/item/organ/internal/ears/cat/Insert(mob/living/carbon/human/ear_owner, special = 0, drop_if_replaced = TRUE) - ..() - if(istype(ear_owner)) + . = ..() + if(istype(ear_owner) && ear_owner.dna) color = ear_owner.hair_color ear_owner.dna.features["ears"] = ear_owner.dna.species.mutant_bodyparts["ears"] = "Cat" ear_owner.dna.update_uf_block(DNA_EARS_BLOCK) ear_owner.update_body() /obj/item/organ/internal/ears/cat/Remove(mob/living/carbon/human/ear_owner, special = 0) - ..() - if(istype(ear_owner)) + . = ..() + if(istype(ear_owner) && ear_owner.dna) color = ear_owner.hair_color ear_owner.dna.species.mutant_bodyparts -= "ears" ear_owner.update_body() diff --git a/code/modules/surgery/organs/external/_external_organs.dm b/code/modules/surgery/organs/external/_external_organs.dm index 83c0ce9f3d9..ed9f8fee155 100644 --- a/code/modules/surgery/organs/external/_external_organs.dm +++ b/code/modules/surgery/organs/external/_external_organs.dm @@ -67,8 +67,8 @@ return ..() -/obj/item/organ/external/Insert(mob/living/carbon/reciever, special, drop_if_replaced) - var/obj/item/bodypart/limb = reciever.get_bodypart(deprecise_zone(zone)) +/obj/item/organ/external/Insert(mob/living/carbon/receiver, special, drop_if_replaced) + var/obj/item/bodypart/limb = receiver.get_bodypart(deprecise_zone(zone)) if(!limb) return FALSE @@ -80,12 +80,12 @@ if(bodypart_overlay.imprint_on_next_insertion) //We only want this set *once* - // SKYRAT EDIT - Customization - ORIGINAL: bodypart_overlay.set_appearance_from_name(reciever.dna.features[bodypart_overlay.feature_key]) - if(reciever.dna.features[bodypart_overlay.feature_key]) - bodypart_overlay.set_appearance_from_name(reciever.dna.features[bodypart_overlay.feature_key]) + // SKYRAT EDIT - Customization - ORIGINAL: bodypart_overlay.set_appearance_from_name(receiver.dna.features[bodypart_overlay.feature_key]) + if(receiver.dna.features[bodypart_overlay.feature_key]) + bodypart_overlay.set_appearance_from_name(receiver.dna.features[bodypart_overlay.feature_key]) else - bodypart_overlay.set_appearance_from_dna(reciever.dna) + bodypart_overlay.set_appearance_from_dna(receiver.dna) // SKYRAT EDIT END bodypart_overlay.imprint_on_next_insertion = FALSE @@ -93,9 +93,9 @@ add_to_limb(ownerlimb) if(external_bodytypes) - limb.synchronize_bodytypes(reciever) + limb.synchronize_bodytypes(receiver) - reciever.update_body_parts() + receiver.update_body_parts() /obj/item/organ/external/Remove(mob/living/carbon/organ_owner, special, moving) . = ..() @@ -273,11 +273,11 @@ ///Store our old datum here for if our antennae are healed var/original_sprite_datum -/obj/item/organ/external/antennae/Insert(mob/living/carbon/reciever, special, drop_if_replaced) +/obj/item/organ/external/antennae/Insert(mob/living/carbon/receiver, special, drop_if_replaced) . = ..() - RegisterSignal(reciever, COMSIG_HUMAN_BURNING, PROC_REF(try_burn_antennae)) - RegisterSignal(reciever, COMSIG_LIVING_POST_FULLY_HEAL, PROC_REF(heal_antennae)) + RegisterSignal(receiver, COMSIG_HUMAN_BURNING, PROC_REF(try_burn_antennae)) + RegisterSignal(receiver, COMSIG_LIVING_POST_FULLY_HEAL, PROC_REF(heal_antennae)) /obj/item/organ/external/antennae/Remove(mob/living/carbon/organ_owner, special, moving) . = ..() diff --git a/code/modules/surgery/organs/external/tails.dm b/code/modules/surgery/organs/external/tails.dm index 991af39e454..7b2d1da179d 100644 --- a/code/modules/surgery/organs/external/tails.dm +++ b/code/modules/surgery/organs/external/tails.dm @@ -17,19 +17,19 @@ ///The original owner of this tail var/original_owner //Yay, snowflake code! -/obj/item/organ/external/tail/Insert(mob/living/carbon/reciever, special, drop_if_replaced) +/obj/item/organ/external/tail/Insert(mob/living/carbon/receiver, special, drop_if_replaced) . = ..() if(.) - RegisterSignal(reciever, COMSIG_ORGAN_WAG_TAIL, PROC_REF(wag)) - original_owner ||= WEAKREF(reciever) + RegisterSignal(receiver, COMSIG_ORGAN_WAG_TAIL, PROC_REF(wag)) + original_owner ||= WEAKREF(receiver) - reciever.clear_mood_event("tail_lost") - reciever.clear_mood_event("tail_balance_lost") + receiver.clear_mood_event("tail_lost") + receiver.clear_mood_event("tail_balance_lost") - if(IS_WEAKREF_OF(reciever, original_owner)) - reciever.clear_mood_event("wrong_tail_regained") - else if(type in reciever.dna.species.external_organs) - reciever.add_mood_event("wrong_tail_regained", /datum/mood_event/tail_regained_wrong) + if(IS_WEAKREF_OF(receiver, original_owner)) + receiver.clear_mood_event("wrong_tail_regained") + else if(type in receiver.dna.species.external_organs) + receiver.add_mood_event("wrong_tail_regained", /datum/mood_event/tail_regained_wrong) /obj/item/organ/external/tail/Remove(mob/living/carbon/organ_owner, special, moving) if(wag_flags & WAG_WAGGING) diff --git a/code/modules/surgery/organs/external/wings/functional_wings.dm b/code/modules/surgery/organs/external/wings/functional_wings.dm index a1605f3d546..91eec796873 100644 --- a/code/modules/surgery/organs/external/wings/functional_wings.dm +++ b/code/modules/surgery/organs/external/wings/functional_wings.dm @@ -31,12 +31,12 @@ return TRUE // SKYRAT EDIT END -/obj/item/organ/external/wings/functional/Insert(mob/living/carbon/reciever, special, drop_if_replaced) +/obj/item/organ/external/wings/functional/Insert(mob/living/carbon/receiver, special, drop_if_replaced) . = ..() if(isnull(fly)) fly = new - fly.Grant(reciever) + fly.Grant(receiver) /obj/item/organ/external/wings/functional/Remove(mob/living/carbon/organ_owner, special, moving) . = ..() diff --git a/code/modules/surgery/organs/external/wings/moth_wings.dm b/code/modules/surgery/organs/external/wings/moth_wings.dm index 7c9a10934cd..715a22061c2 100644 --- a/code/modules/surgery/organs/external/wings/moth_wings.dm +++ b/code/modules/surgery/organs/external/wings/moth_wings.dm @@ -14,12 +14,12 @@ ///Store our old datum here for if our burned wings are healed var/original_sprite_datum -/obj/item/organ/external/wings/moth/Insert(mob/living/carbon/reciever, special, drop_if_replaced) +/obj/item/organ/external/wings/moth/Insert(mob/living/carbon/receiver, special, drop_if_replaced) . = ..() - RegisterSignal(reciever, COMSIG_HUMAN_BURNING, PROC_REF(try_burn_wings)) - RegisterSignal(reciever, COMSIG_LIVING_POST_FULLY_HEAL, PROC_REF(heal_wings)) - RegisterSignal(reciever, COMSIG_MOVABLE_PRE_MOVE, PROC_REF(update_float_move)) + RegisterSignal(receiver, COMSIG_HUMAN_BURNING, PROC_REF(try_burn_wings)) + RegisterSignal(receiver, COMSIG_LIVING_POST_FULLY_HEAL, PROC_REF(heal_wings)) + RegisterSignal(receiver, COMSIG_MOVABLE_PRE_MOVE, PROC_REF(update_float_move)) /obj/item/organ/external/wings/moth/Remove(mob/living/carbon/organ_owner, special, moving) . = ..() diff --git a/code/modules/surgery/organs/organ_internal.dm b/code/modules/surgery/organs/organ_internal.dm index 9bb3f42543f..640937e8be1 100644 --- a/code/modules/surgery/organs/organ_internal.dm +++ b/code/modules/surgery/organs/organ_internal.dm @@ -14,9 +14,9 @@ STOP_PROCESSING(SSobj, src) return ..() -/obj/item/organ/internal/Insert(mob/living/carbon/reciever, special = FALSE, drop_if_replaced = TRUE) +/obj/item/organ/internal/Insert(mob/living/carbon/receiver, special = FALSE, drop_if_replaced = TRUE) . = ..() - if(!.) + if(!. || !owner) return // internal_organs_slot must ALWAYS be ordered in the same way as organ_process_order diff --git a/code/modules/unit_tests/_unit_tests.dm b/code/modules/unit_tests/_unit_tests.dm index 869e2049920..22c14726ad9 100644 --- a/code/modules/unit_tests/_unit_tests.dm +++ b/code/modules/unit_tests/_unit_tests.dm @@ -162,6 +162,7 @@ #include "objectives.dm" #include "orderable_items.dm" #include "operating_table.dm" +#include "organ_set_bonus.dm" #include "outfit_sanity.dm" #include "paintings.dm" #include "pills.dm" diff --git a/code/modules/unit_tests/organ_set_bonus.dm b/code/modules/unit_tests/organ_set_bonus.dm new file mode 100644 index 00000000000..17620f0a393 --- /dev/null +++ b/code/modules/unit_tests/organ_set_bonus.dm @@ -0,0 +1,86 @@ +/// Tests the "organ set bonus" Elements and Status Effects, which are for the DNA Infuser. +/// Ensures the developers properly change IDs to be unique. +/datum/unit_test/organ_set_bonus_id + +/datum/unit_test/organ_set_bonus_id/Run() + var/list/bonus_effects = typesof(/datum/status_effect/organ_set_bonus) + var/list/existing_ids = list() + for(var/datum/status_effect/organ_set_bonus/bonus_effect as anything in bonus_effects) + var/effect_id = initial(bonus_effect.id) + var/existing_status = (effect_id in existing_ids) + if(existing_status) + TEST_FAIL("The ID of [bonus_effect] was duplicated in another status effect.") + else + existing_ids += effect_id + +/datum/unit_test/organ_set_bonus_sanity/proc/check_status_type(mob/living/carbon/human/lab_rat, datum/status_effect/status_type) + for(var/datum/status_effect/present_effect as anything in lab_rat.status_effects) + if(istype(present_effect, status_type)) + return present_effect + +/// Tests the "organ set bonus" Elements and Status Effects, which are for the DNA Infuser. +/// Ensures that each Element and Status Effect gets properly added/removed from mobs. +/datum/unit_test/organ_set_bonus_sanity + +/datum/unit_test/organ_set_bonus_sanity/Run() + // Fetch the globally instantiated DNA Infuser entries. + for(var/datum/infuser_entry/infuser_entry as anything in GLOB.infuser_entries) + var/output_organs = infuser_entry.output_organs + // Human which will reiceve organs. + var/mob/living/carbon/human/lab_rat = allocate(/mob/living/carbon/human/consistent) + lab_rat.dna.mutant_bodyparts["moth_antennae"] = list(MUTANT_INDEX_NAME = "Plain", MUTANT_INDEX_COLOR_LIST = list("#FFFFFF"), MUTANT_INDEX_EMISSIVE_LIST = list(FALSE)) // SKYRAT EDIT - Customization + var/list/obj/item/organ/inserted_organs = list() + for(var/obj/item/organ/organ as anything in output_organs) + organ = new organ() + var/implant_ok = organ.Insert(lab_rat, special = TRUE, drop_if_replaced = FALSE) + if(!implant_ok) + TEST_FAIL("The organ \"[organ.type]\" for \"[infuser_entry.type]\" was not inserted in the mob when expected.") + continue + inserted_organs += organ + + var/total_inserted = length(inserted_organs) + + // Search for added Status Effect. + var/datum/status_effect/organ_set_bonus/added_status = check_status_type(lab_rat, /datum/status_effect/organ_set_bonus) + + // Threshold description implies an organ set bonus. + // Without it, we'll assume there isn't a Status Effect to look for. + var/has_threshold = (infuser_entry.threshold_desc != DNA_INFUSION_NO_THRESHOLD) + var/total_organs_needed = added_status ? added_status.organs_needed : 0 + var/total_organs = length(infuser_entry.output_organs) + + // Found a Status Effect but no threshold description. + if(!has_threshold && added_status) + TEST_FAIL("The threshold_desc variable for \"[infuser_entry.type]\" was an empty string when a description was expected.") + + // Since threshold_desc is filled-in, we expect the organ set bonus. + if(has_threshold) + if(!added_status) + TEST_FAIL("The \"/datum/status_effect/organ_set_bonus\" for \"[infuser_entry.type]\" was not added to the mob when expected.") + else if(!total_organs_needed) + TEST_FAIL("The \"needed_organs\" variable for \"[added_status.type]\" was 0 or null, when a positive number was expected.") + else if(total_organs_needed > total_organs) + TEST_FAIL("The \"output_organs\" list for \"[infuser_entry.type]\" had a length of \"[length(infuser_entry.output_organs)]\" when a minimum of at least [total_organs_needed] organs was specified in \"[added_status.type]\".") + else if(!added_status.bonus_active) + TEST_FAIL("The \"[added_status.type]\" bonus was not activated after inserting [total_inserted] of the [total_organs_needed] required organs in the mob, when it was expected.") + + // Nothing to do. + if(!total_inserted) + continue + + // Bonus of the Fly mutation swaps out all the organs, making it rather permanent. + // As a result, the inserted_organs list is un-usable by this point. + if(istype(infuser_entry, /datum/infuser_entry/fly)) + continue + + // Remove all the organs which were just added. + var/total_removed = 0 + for(var/obj/item/organ/test_organ as anything in inserted_organs) + test_organ.Remove(lab_rat, special = TRUE) + total_removed += 1 + + added_status = check_status_type(lab_rat, /datum/status_effect/organ_set_bonus) + + // Search for added Status Effect. + if(added_status && added_status.bonus_active) + TEST_FAIL("The \"[added_status.type]\" bonus was not deactivated after removing [total_removed] of the [total_organs_needed] required organs from the mob, when it was expected.") diff --git a/code/modules/wiremod/shell/brain_computer_interface.dm b/code/modules/wiremod/shell/brain_computer_interface.dm index 62dd75c293b..7893a755b2c 100644 --- a/code/modules/wiremod/shell/brain_computer_interface.dm +++ b/code/modules/wiremod/shell/brain_computer_interface.dm @@ -17,11 +17,11 @@ new /obj/item/circuit_component/bci_core, ), SHELL_CAPACITY_SMALL, starting_circuit = circuit) -/obj/item/organ/internal/cyberimp/bci/Insert(mob/living/carbon/reciever, special, drop_if_replaced) +/obj/item/organ/internal/cyberimp/bci/Insert(mob/living/carbon/receiver, special, drop_if_replaced) . = ..() // Organs are put in nullspace, but this breaks circuit interactions - forceMove(reciever) + forceMove(receiver) /obj/item/organ/internal/cyberimp/bci/say(message, bubble_type, list/spans, sanitize, datum/language/language, ignore_spam, forced = null, filterproof = null, message_range = 7, datum/saymode/saymode = null) if (owner)