From 095f7e3b7058ce413fb580ec4a7f2cb7d2a24eb6 Mon Sep 17 00:00:00 2001 From: Time-Green <7501474+Time-Green@users.noreply.github.com> Date: Tue, 20 Aug 2024 16:33:34 +0200 Subject: [PATCH] Death of mutant bodyparts AND external organs (#85137) ## About The Pull Request Removes mutant bodyparts and external organs from the game completely Digitgrade behaviour was mutant bodypart for no reason Cat ears now work with the bodyparts overlay system, same as all the other external organs (since all their behaviour is now just on /organ It doesn't remove all the /external types, but moves all behaviour to /organ. I'll follow up with a PR wiping all the /external organ types, but it's just conflict heaven so not this PR I've also streamlined a lot of duplicate/weird species regeneration code Melbert did the same PR as well but due to a lack of time (?) I have absorbed his PR to double nuke mutant bodyparts ## Why It's Good For The Game Frees us from the chain of unmodular code, and kills my greatest nemesis (after the shuttle meteor murder bug) ## Changelog :cl: Time-Green and MrMelbert Refactor: External organ behaviour has been moved to /organ, ears now use the same system as the other organs Refactor: Mutant bodyparts are dead! This likely does not mean much to the average person but it's very dear to me code: Improves digitgrade handling in preference code /:cl: I have absorbed #85126, using Melberts code to improve and add some missing changes. Mainly improving the functioning of preferences and digitgrade legs. I didn't take over the hairstyle improvements. --------- Co-authored-by: Ghom <42542238+Ghommie@users.noreply.github.com> --- code/__DEFINES/mobs.dm | 5 +- code/__HELPERS/duplicating.dm | 2 - .../subsystem/sprite_accessories.dm | 2 - .../mutant_bodypart_overlay.dm | 20 +- code/datums/dna.dm | 4 +- .../quirks/neutral_quirks/transhumanist.dm | 8 +- code/datums/sprite_accessories.dm | 14 -- .../dna_infuser/organ_sets/fly_organs.dm | 1 - .../dna_infuser/organ_sets/fox_organs.dm | 15 +- .../dna_infuser/organ_sets/gondola_organs.dm | 12 +- code/game/objects/items/body_egg.dm | 7 +- code/modules/admin/verbs/manipulate_organs.dm | 5 +- code/modules/admin/verbs/secrets.dm | 2 +- .../antagonists/abductor/equipment/gland.dm | 6 +- .../antagonists/changeling/headslug_eggs.dm | 4 +- .../sacrifice_knowledge.dm | 11 +- .../antagonists/nightmare/nightmare_organs.dm | 3 +- .../antagonists/revenant/revenant_blight.dm | 2 - .../modules/client/preferences/_preference.dm | 7 +- .../preferences/species_features/felinid.dm | 2 +- .../preferences/species_features/lizard.dm | 44 +++- .../species_features/mushperson.dm | 2 +- .../experisci/experiment/experiments.dm | 2 +- .../chaplain/chaplain_vorpal_scythe.dm | 2 +- .../equipment/monster_organs/monster_organ.dm | 9 +- code/modules/mining/lavaland/tendril_loot.dm | 1 - .../modules/mob/living/carbon/alien/organs.dm | 5 +- .../mob/living/carbon/carbon_update_icons.dm | 7 +- .../mob/living/carbon/human/_species.dm | 234 +++--------------- .../living/carbon/human/human_update_icons.dm | 10 +- .../carbon/human/species_types/dullahan.dm | 1 - .../carbon/human/species_types/felinid.dm | 9 +- .../carbon/human/species_types/humans.dm | 1 - .../human/species_types/lizardpeople.dm | 3 +- .../carbon/human/species_types/monkeys.dm | 2 +- .../carbon/human/species_types/mothmen.dm | 8 +- .../carbon/human/species_types/mushpeople.dm | 2 +- .../carbon/human/species_types/podpeople.dm | 2 +- .../carbon/human/species_types/vampire.dm | 1 - code/modules/mod/modules/modules_medical.dm | 3 +- code/modules/surgery/bodyparts/_bodyparts.dm | 4 +- .../surgery/bodyparts/dismemberment.dm | 4 +- code/modules/surgery/bodyparts/helpers.dm | 8 +- code/modules/surgery/organ_manipulation.dm | 30 ++- code/modules/surgery/organs/_organ.dm | 19 +- .../{_external_organ.dm => _visual_organs.dm} | 109 ++------ .../surgery/organs/external/restyling.dm | 15 +- .../modules/surgery/organs/external/spines.dm | 4 +- code/modules/surgery/organs/external/tails.dm | 6 +- .../organs/external/wings/functional_wings.dm | 7 +- .../organs/internal/appendix/_appendix.dm | 2 +- .../organs/internal/cyberimp/augments_eyes.dm | 7 +- .../internal/cyberimp/augments_internal.dm | 2 +- .../surgery/organs/internal/ears/_ears.dm | 45 ++-- .../surgery/organs/internal/eyes/_eyes.dm | 38 ++- .../surgery/organs/internal/heart/_heart.dm | 2 +- .../organs/internal/heart/heart_ethereal.dm | 7 +- .../surgery/organs/internal/liver/_liver.dm | 2 +- .../surgery/organs/internal/lungs/_lungs.dm | 9 +- .../organs/internal/stomach/_stomach.dm | 6 +- .../surgery/organs/internal/tongue/_tongue.dm | 26 +- .../internal/vocal_cords/_vocal_cords.dm | 2 - code/modules/surgery/organs/organ_movement.dm | 27 +- .../unit_tests/organ_bodypart_shuffle.dm | 4 +- code/modules/unit_tests/organ_set_bonus.dm | 2 +- .../wiremod/shell/brain_computer_interface.dm | 1 - icons/mob/human/cat_features.dmi | Bin 1906 -> 1756 bytes tgstation.dme | 2 +- 68 files changed, 331 insertions(+), 539 deletions(-) rename code/modules/surgery/organs/external/{_external_organ.dm => _visual_organs.dm} (75%) diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm index bc71bc13732..e2ed629c46a 100644 --- a/code/__DEFINES/mobs.dm +++ b/code/__DEFINES/mobs.dm @@ -152,7 +152,10 @@ ///The species is forced to have digitigrade legs in generation. #define DIGITIGRADE_FORCED 2 -///Digitigrade's prefs, used in features for legs if you're meant to be a Digitigrade. +// Preferences for leg types +/// Legs that are normal +#define NORMAL_LEGS "Normal Legs" +/// Digitgrade legs that are like bended and uhhh no shoes #define DIGITIGRADE_LEGS "Digitigrade Legs" // Health/damage defines diff --git a/code/__HELPERS/duplicating.dm b/code/__HELPERS/duplicating.dm index 225dca91fb5..f0f3f9a9fce 100644 --- a/code/__HELPERS/duplicating.dm +++ b/code/__HELPERS/duplicating.dm @@ -15,8 +15,6 @@ GLOBAL_LIST_INIT(duplicate_forbidden_vars, list( "contents", "cooldowns", "_datum_components", - "external_organs", - "external_organs_slot", "group", "hand_bodyparts", "held_items", diff --git a/code/controllers/subsystem/sprite_accessories.dm b/code/controllers/subsystem/sprite_accessories.dm index 21bafd5330c..6fd80a8b789 100644 --- a/code/controllers/subsystem/sprite_accessories.dm +++ b/code/controllers/subsystem/sprite_accessories.dm @@ -40,7 +40,6 @@ SUBSYSTEM_DEF(accessories) // just 'accessories' for brevity var/list/horns_list var/list/frills_list var/list/spines_list - var/list/legs_list var/list/tail_spines_list //Mutant Human bits @@ -99,7 +98,6 @@ SUBSYSTEM_DEF(accessories) // just 'accessories' for brevity frills_list = init_sprite_accessory_subtypes(/datum/sprite_accessory/frills, add_blank = TRUE)[DEFAULT_SPRITE_LIST] spines_list = init_sprite_accessory_subtypes(/datum/sprite_accessory/spines, add_blank = TRUE)[DEFAULT_SPRITE_LIST] tail_spines_list = init_sprite_accessory_subtypes(/datum/sprite_accessory/tail_spines, add_blank = TRUE)[DEFAULT_SPRITE_LIST] - legs_list = init_sprite_accessory_subtypes(/datum/sprite_accessory/legs)[DEFAULT_SPRITE_LIST] caps_list = init_sprite_accessory_subtypes(/datum/sprite_accessory/caps)[DEFAULT_SPRITE_LIST] moth_wings_list = init_sprite_accessory_subtypes(/datum/sprite_accessory/moth_wings)[DEFAULT_SPRITE_LIST] moth_antennae_list = init_sprite_accessory_subtypes(/datum/sprite_accessory/moth_antennae)[DEFAULT_SPRITE_LIST] diff --git a/code/datums/bodypart_overlays/mutant_bodypart_overlay.dm b/code/datums/bodypart_overlays/mutant_bodypart_overlay.dm index 3115e3ad62e..d0250cce686 100644 --- a/code/datums/bodypart_overlays/mutant_bodypart_overlay.dm +++ b/code/datums/bodypart_overlays/mutant_bodypart_overlay.dm @@ -13,6 +13,24 @@ ///Take on the dna/preference from whoever we're gonna be inserted in var/imprint_on_next_insertion = TRUE +/datum/bodypart_overlay/mutant/New(obj/item/organ/attached_organ) + . = ..() + + RegisterSignal(attached_organ, COMSIG_ORGAN_IMPLANTED, PROC_REF(on_mob_insert)) + +/datum/bodypart_overlay/mutant/proc/on_mob_insert(obj/item/organ/parent, mob/living/carbon/receiver) + SIGNAL_HANDLER + + if(!should_visual_organ_apply_to(parent.type, receiver)) + stack_trace("adding a [parent.type] to a [receiver.type] when it shouldn't be!") + + if(imprint_on_next_insertion) //We only want this set *once* + var/feature_name = receiver.dna.features[feature_key] + if (isnull(feature_name)) + feature_name = receiver.dna.species.mutant_organs[parent.type] + set_appearance_from_name(feature_name) + imprint_on_next_insertion = FALSE + /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 ..() @@ -67,7 +85,6 @@ return appearance /datum/bodypart_overlay/mutant/color_image(image/overlay, layer, obj/item/bodypart/limb) - overlay.color = sprite_datum.color_src ? draw_color : null /datum/bodypart_overlay/mutant/added_to_limb(obj/item/bodypart/limb) @@ -139,3 +156,4 @@ CRASH("External organ [type] couldn't find sprite accessory [accessory_name]!") else CRASH("External organ [type] had fetch_sprite_datum called with a null accessory name!") + diff --git a/code/datums/dna.dm b/code/datums/dna.dm index df92d57c59b..4fff0104439 100644 --- a/code/datums/dna.dm +++ b/code/datums/dna.dm @@ -682,8 +682,8 @@ GLOBAL_LIST_INIT(total_uf_len_by_block, populate_total_uf_len_by_block()) if(dna.features["pod_hair"]) dna.features["pod_hair"] = SSaccessories.pod_hair_list[deconstruct_block(get_uni_feature_block(features, DNA_POD_HAIR_BLOCK), length(SSaccessories.pod_hair_list))] - for(var/obj/item/organ/external/external_organ in organs) - external_organ.mutate_feature(features, src) + for(var/obj/item/organ/organ in organs) + organ.mutate_feature(features, src) if(icon_update) update_body(is_creating = mutcolor_update) diff --git a/code/datums/quirks/neutral_quirks/transhumanist.dm b/code/datums/quirks/neutral_quirks/transhumanist.dm index ea6494a6b32..aa8ae075df3 100644 --- a/code/datums/quirks/neutral_quirks/transhumanist.dm +++ b/code/datums/quirks/neutral_quirks/transhumanist.dm @@ -127,10 +127,10 @@ else if(isorgan(new_part)) var/obj/item/organ/new_organ = new_part old_part = human_holder.get_organ_slot(new_organ.slot) - if(new_organ.Insert(human_holder, special = TRUE)) - old_part.moveToNullspace() - STOP_PROCESSING(SSobj, old_part) - slot_string = new_organ.name + new_organ.Insert(human_holder, special = TRUE) + old_part.moveToNullspace() + STOP_PROCESSING(SSobj, old_part) + slot_string = new_organ.name /datum/quirk/transhumanist/post_add() if(!slot_string) diff --git a/code/datums/sprite_accessories.dm b/code/datums/sprite_accessories.dm index a37926405f9..a4394b64d52 100644 --- a/code/datums/sprite_accessories.dm +++ b/code/datums/sprite_accessories.dm @@ -34,8 +34,6 @@ * This is the source that this accessory will get its color from. Default is MUTCOLOR, but can also be HAIR, FACEHAIR, EYECOLOR and 0 if none. */ var/color_src = MUTANT_COLOR - /// Decides if this sprite has an "inner" part, such as the fleshy parts on ears. - var/hasinner = FALSE /// Is this part locked from roundstart selection? Used for parts that apply effects. var/locked = FALSE /// Should we center the sprite? @@ -1890,7 +1888,6 @@ /datum/sprite_accessory/ears/cat name = "Cat" icon_state = "cat" - hasinner = TRUE color_src = HAIR_COLOR /datum/sprite_accessory/ears/cat/big @@ -1917,7 +1914,6 @@ icon = 'icons/mob/human/fox_features.dmi' name = "Fox" icon_state = "fox" - hasinner = TRUE color_src = HAIR_COLOR locked = TRUE @@ -2124,16 +2120,6 @@ name = "Aquatic" icon_state = "aqua" -/datum/sprite_accessory/legs //legs are a special case, they aren't actually sprite_accessories but are updated with them. - icon = null //These datums exist for selecting legs on preference, and little else - em_block = TRUE - -/datum/sprite_accessory/legs/none - name = "Normal Legs" - -/datum/sprite_accessory/legs/digitigrade_lizard - name = DIGITIGRADE_LEGS - /datum/sprite_accessory/caps icon = 'icons/mob/human/species/mush_cap.dmi' color_src = HAIR_COLOR 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 e3e7112b0fe..4786bf5753c 100644 --- a/code/game/machinery/dna_infuser/organ_sets/fly_organs.dm +++ b/code/game/machinery/dna_infuser/organ_sets/fly_organs.dm @@ -125,7 +125,6 @@ //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 = FLY_INFUSED_ORGAN_DESC - visual = FALSE /obj/item/organ/internal/fly/Initialize(mapload) . = ..() 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 ab33c1ad57e..3fecac3bb6d 100644 --- a/code/game/machinery/dna_infuser/organ_sets/fox_organs.dm +++ b/code/game/machinery/dna_infuser/organ_sets/fox_organs.dm @@ -6,17 +6,4 @@ visual = TRUE damage_multiplier = 2 -/obj/item/organ/internal/ears/fox/on_mob_insert(mob/living/carbon/human/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/on_mob_remove(mob/living/carbon/human/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() + sprite_accessory_override = /datum/sprite_accessory/ears/fox 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 797c7839b2c..9fcf7e483bb 100644 --- a/code/game/machinery/dna_infuser/organ_sets/gondola_organs.dm +++ b/code/game/machinery/dna_infuser/organ_sets/gondola_organs.dm @@ -34,7 +34,7 @@ Fluoride Stare: After someone says 5 words, blah blah blah... AddElement(/datum/element/noticable_organ, "%PRONOUN_They radiate%PRONOUN_s an aura of serenity.") AddElement(/datum/element/update_icon_blocker) -/obj/item/organ/internal/heart/gondola/Insert(mob/living/carbon/receiver, special, movement_flags) +/obj/item/organ/internal/heart/gondola/mob_insert(mob/living/carbon/receiver, special, movement_flags) . = ..() if(!(FACTION_HOSTILE in receiver.faction)) factions_to_remove += FACTION_HOSTILE @@ -42,7 +42,7 @@ Fluoride Stare: After someone says 5 words, blah blah blah... factions_to_remove += FACTION_MINING receiver.faction |= list(FACTION_HOSTILE, FACTION_MINING) -/obj/item/organ/internal/heart/gondola/Remove(mob/living/carbon/heartless, special, movement_flags) +/obj/item/organ/internal/heart/gondola/mob_remove(mob/living/carbon/heartless, special, movement_flags) . = ..() for(var/faction in factions_to_remove) heartless.faction -= faction @@ -64,11 +64,11 @@ Fluoride Stare: After someone says 5 words, blah blah blah... AddElement(/datum/element/noticable_organ, "%PRONOUN_Their mouth is permanently affixed into a relaxed smile.", BODY_ZONE_PRECISE_MOUTH) AddElement(/datum/element/organ_set_bonus, /datum/status_effect/organ_set_bonus/gondola) -/obj/item/organ/internal/tongue/gondola/Insert(mob/living/carbon/tongue_owner, special, movement_flags) +/obj/item/organ/internal/tongue/gondola/mob_insert(mob/living/carbon/tongue_owner, special, movement_flags) . = ..() tongue_owner.add_mood_event("gondola_zen", /datum/mood_event/gondola_serenity) -/obj/item/organ/internal/tongue/gondola/Remove(mob/living/carbon/tongue_owner, special, movement_flags) +/obj/item/organ/internal/tongue/gondola/mob_remove(mob/living/carbon/tongue_owner, special, movement_flags) tongue_owner.clear_mood_event("gondola_zen") return ..() @@ -87,7 +87,7 @@ Fluoride Stare: After someone says 5 words, blah blah blah... AddElement(/datum/element/noticable_organ, "%PRONOUN_Their left arm has small needles breaching the skin all over it.", BODY_ZONE_L_ARM) AddElement(/datum/element/noticable_organ, "%PRONOUN_Their right arm has small needles breaching the skin all over it.", BODY_ZONE_R_ARM) -/obj/item/organ/internal/liver/gondola/Insert(mob/living/carbon/liver_owner, special, movement_flags) +/obj/item/organ/internal/liver/gondola/mob_insert(mob/living/carbon/liver_owner, special, movement_flags) . = ..() var/has_left = liver_owner.has_left_hand(check_disabled = FALSE) var/has_right = liver_owner.has_right_hand(check_disabled = FALSE) @@ -102,7 +102,7 @@ Fluoride Stare: After someone says 5 words, blah blah blah... RegisterSignal(liver_owner, COMSIG_LIVING_TRY_PULL, PROC_REF(on_owner_try_pull)) RegisterSignal(liver_owner, COMSIG_CARBON_HELPED, PROC_REF(on_hug)) -/obj/item/organ/internal/liver/gondola/Remove(mob/living/carbon/liver_owner, special, movement_flags) +/obj/item/organ/internal/liver/gondola/mob_remove(mob/living/carbon/liver_owner, special, movement_flags) . = ..() UnregisterSignal(liver_owner, list(COMSIG_HUMAN_EQUIPPING_ITEM, COMSIG_LIVING_TRY_PULL, COMSIG_CARBON_HELPED)) diff --git a/code/game/objects/items/body_egg.dm b/code/game/objects/items/body_egg.dm index d244d8c55cc..d8b48e0789b 100644 --- a/code/game/objects/items/body_egg.dm +++ b/code/game/objects/items/body_egg.dm @@ -15,15 +15,14 @@ if(iscarbon(loc)) Insert(loc) -/obj/item/organ/internal/body_egg/Insert(mob/living/carbon/egg_owner, special = FALSE, movement_flags = DELETE_IF_REPLACED) +/obj/item/organ/internal/body_egg/mob_insert(mob/living/carbon/egg_owner, special = FALSE, movement_flags = DELETE_IF_REPLACED) . = ..() - if(!.) - return + egg_owner.add_traits(list(TRAIT_XENO_HOST, TRAIT_XENO_IMMUNE), ORGAN_TRAIT) egg_owner.med_hud_set_status() INVOKE_ASYNC(src, PROC_REF(AddInfectionImages), egg_owner) -/obj/item/organ/internal/body_egg/Remove(mob/living/carbon/egg_owner, special, movement_flags) +/obj/item/organ/internal/body_egg/mob_remove(mob/living/carbon/egg_owner, special, movement_flags) . = ..() egg_owner.remove_traits(list(TRAIT_XENO_HOST, TRAIT_XENO_IMMUNE), ORGAN_TRAIT) egg_owner.med_hud_set_status() diff --git a/code/modules/admin/verbs/manipulate_organs.dm b/code/modules/admin/verbs/manipulate_organs.dm index bfb5050dafa..6c0a86126b6 100644 --- a/code/modules/admin/verbs/manipulate_organs.dm +++ b/code/modules/admin/verbs/manipulate_organs.dm @@ -18,10 +18,7 @@ ADMIN_VERB(manipulate_organs, R_DEBUG, "Manipulate Organs", "Manipulate the orga return organ_to_grant = organs[organ_to_grant] organ_to_grant = new organ_to_grant - if(!organ_to_grant.Insert(carbon_victim)) - to_chat(user, span_notice("[carbon_victim] is unable to carry this organ!")) - qdel(organ_to_grant) - return + organ_to_grant.Insert(carbon_victim) log_admin("[key_name(user)] has added organ [organ_to_grant.type] to [key_name(carbon_victim)]") message_admins("[key_name_admin(user)] has added organ [organ_to_grant.type] to [ADMIN_LOOKUPFLW(carbon_victim)]") diff --git a/code/modules/admin/verbs/secrets.dm b/code/modules/admin/verbs/secrets.dm index c1ce601c514..c8081cadcbd 100644 --- a/code/modules/admin/verbs/secrets.dm +++ b/code/modules/admin/verbs/secrets.dm @@ -526,7 +526,7 @@ ADMIN_VERB(secrets, R_NONE, "Secrets", "Abuse harder than you ever have before w var/forename = names.len > 1 ? names[2] : names[1] var/newname = "[forename]-[pick(honorifics["[H.gender]"])]" H.fully_replace_character_name(H.real_name,newname) - H.update_mutant_bodyparts() + H.update_body_parts() if(animetype == "Yes") var/seifuku = pick(typesof(/obj/item/clothing/under/costume/schoolgirl)) var/obj/item/clothing/under/costume/schoolgirl/I = new seifuku diff --git a/code/modules/antagonists/abductor/equipment/gland.dm b/code/modules/antagonists/abductor/equipment/gland.dm index 29ea5f1e785..f4fe0fb1875 100644 --- a/code/modules/antagonists/abductor/equipment/gland.dm +++ b/code/modules/antagonists/abductor/equipment/gland.dm @@ -84,7 +84,7 @@ active_mind_control = FALSE return TRUE -/obj/item/organ/internal/heart/gland/Remove(mob/living/carbon/gland_owner, special, movement_flags) +/obj/item/organ/internal/heart/gland/mob_remove(mob/living/carbon/gland_owner, special, movement_flags) . = ..() active = FALSE if(initial(uses) == 1) @@ -93,10 +93,8 @@ hud.remove_atom_from_hud(gland_owner) clear_mind_control() -/obj/item/organ/internal/heart/gland/Insert(mob/living/carbon/gland_owner, special = FALSE, movement_flags = DELETE_IF_REPLACED) +/obj/item/organ/internal/heart/gland/mob_insert(mob/living/carbon/gland_owner, special = FALSE, movement_flags = DELETE_IF_REPLACED) . = ..() - if(!.) - return if(special != 2 && uses) // Special 2 means abductor surgery Start() diff --git a/code/modules/antagonists/changeling/headslug_eggs.dm b/code/modules/antagonists/changeling/headslug_eggs.dm index 8f861aec2ec..75c0881c551 100644 --- a/code/modules/antagonists/changeling/headslug_eggs.dm +++ b/code/modules/antagonists/changeling/headslug_eggs.dm @@ -11,11 +11,11 @@ /// When this egg last got removed from a body. If -1, the egg hasn't been removed from a body. var/removal_time = -1 -/obj/item/organ/internal/body_egg/changeling_egg/Insert(mob/living/carbon/egg_owner, special = FALSE, movement_flags = DELETE_IF_REPLACED) +/obj/item/organ/internal/body_egg/changeling_egg/mob_insert(mob/living/carbon/egg_owner, special = FALSE, movement_flags = DELETE_IF_REPLACED) . = ..() hatch_time = world.time + (removal_time == -1 ? EGG_INCUBATION_TIME : (hatch_time - removal_time)) -/obj/item/organ/internal/body_egg/changeling_egg/Remove(mob/living/carbon/egg_owner, special, movement_flags) +/obj/item/organ/internal/body_egg/changeling_egg/mob_remove(mob/living/carbon/egg_owner, special, movement_flags) . = ..() removal_time = world.time diff --git a/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_knowledge.dm b/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_knowledge.dm index 046c518be51..476e3968b2b 100644 --- a/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_knowledge.dm +++ b/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_knowledge.dm @@ -416,20 +416,13 @@ usable_organs -= /obj/item/organ/internal/lungs/corrupt // Their lungs are already more cursed than anything I could give them var/total_implant = rand(2, 4) - var/gave_any = FALSE for (var/i in 1 to total_implant) if (!length(usable_organs)) - break + return var/organ_path = pick_n_take(usable_organs) var/obj/item/organ/internal/to_give = new organ_path - if (!to_give.Insert(sac_target)) - qdel(to_give) - else - gave_any = TRUE - - if (!gave_any) - return + to_give.Insert(sac_target) new /obj/effect/gibspawner/human/bodypartless(get_turf(sac_target)) sac_target.visible_message(span_boldwarning("Several organs force themselves out of [sac_target]!")) diff --git a/code/modules/antagonists/nightmare/nightmare_organs.dm b/code/modules/antagonists/nightmare/nightmare_organs.dm index a77aaa79b23..19328f20378 100644 --- a/code/modules/antagonists/nightmare/nightmare_organs.dm +++ b/code/modules/antagonists/nightmare/nightmare_organs.dm @@ -65,9 +65,10 @@ /obj/item/organ/internal/heart/nightmare name = "heart of darkness" desc = "An alien organ that twists and writhes when exposed to light." + visual = TRUE icon_state = "demon_heart-on" base_icon_state = "demon_heart" - visual = TRUE + color = COLOR_CRAYON_BLACK decay_factor = 0 // No love is to be found in a heart so twisted. diff --git a/code/modules/antagonists/revenant/revenant_blight.dm b/code/modules/antagonists/revenant/revenant_blight.dm index dcbc9bc8181..13a1ff7e1d6 100644 --- a/code/modules/antagonists/revenant/revenant_blight.dm +++ b/code/modules/antagonists/revenant/revenant_blight.dm @@ -19,7 +19,6 @@ if(affected_mob) affected_mob.remove_atom_colour(TEMPORARY_COLOUR_PRIORITY, "#1d2953") if(affected_mob.dna && affected_mob.dna.species) - affected_mob.dna.species.handle_mutant_bodyparts(affected_mob) affected_mob.set_haircolor(null, override = TRUE) to_chat(affected_mob, span_notice("You feel better.")) ..() @@ -66,7 +65,6 @@ affected_mob.adjustStaminaLoss(22.5 * seconds_per_tick, updating_stamina = FALSE) new /obj/effect/temp_visual/revenant(affected_mob.loc) if(affected_mob.dna && affected_mob.dna.species) - affected_mob.dna.species.handle_mutant_bodyparts(affected_mob,"#1d2953") affected_mob.set_haircolor("#1d2953", override = TRUE) affected_mob.visible_message(span_warning("[affected_mob] looks terrifyingly gaunt..."), span_revennotice("You suddenly feel like your skin is wrong...")) affected_mob.add_atom_colour("#1d2953", TEMPORARY_COLOUR_PRIORITY) diff --git a/code/modules/client/preferences/_preference.dm b/code/modules/client/preferences/_preference.dm index 1ba43cf3d8a..2ed2405f376 100644 --- a/code/modules/client/preferences/_preference.dm +++ b/code/modules/client/preferences/_preference.dm @@ -111,10 +111,6 @@ GLOBAL_LIST_INIT(preference_entries_by_key, init_preference_entries_by_key()) /// DOES have random body on, will this already be randomized? var/randomize_by_default = TRUE - /// If the selected species has this in its /datum/species/mutant_bodyparts, - /// will show the feature as selectable. - var/relevant_mutant_bodypart = null - /// If the selected species has this in its /datum/species/body_markings, /// will show the feature as selectable. var/relevant_body_markings = null @@ -336,8 +332,7 @@ GLOBAL_LIST_INIT(preference_entries_by_key, init_preference_entries_by_key()) SHOULD_NOT_SLEEP(TRUE) if ( \ - !isnull(relevant_mutant_bodypart) \ - || !isnull(relevant_inherent_trait) \ + !isnull(relevant_inherent_trait) \ || !isnull(relevant_external_organ) \ || !isnull(relevant_head_flag) \ || !isnull(relevant_body_markings) \ diff --git a/code/modules/client/preferences/species_features/felinid.dm b/code/modules/client/preferences/species_features/felinid.dm index 4c874ea7df7..be90d806323 100644 --- a/code/modules/client/preferences/species_features/felinid.dm +++ b/code/modules/client/preferences/species_features/felinid.dm @@ -20,7 +20,7 @@ savefile_identifier = PREFERENCE_CHARACTER category = PREFERENCE_CATEGORY_SECONDARY_FEATURES can_randomize = FALSE - relevant_mutant_bodypart = "ears" + relevant_external_organ = /obj/item/organ/internal/ears/cat /datum/preference/choiced/ears/init_possible_values() return assoc_to_keys_features(SSaccessories.ears_list) diff --git a/code/modules/client/preferences/species_features/lizard.dm b/code/modules/client/preferences/species_features/lizard.dm index 66c10715330..11fefc17b8b 100644 --- a/code/modules/client/preferences/species_features/lizard.dm +++ b/code/modules/client/preferences/species_features/lizard.dm @@ -93,13 +93,51 @@ savefile_key = "feature_lizard_legs" savefile_identifier = PREFERENCE_CHARACTER category = PREFERENCE_CATEGORY_SECONDARY_FEATURES - relevant_mutant_bodypart = "legs" /datum/preference/choiced/lizard_legs/init_possible_values() - return assoc_to_keys_features(SSaccessories.legs_list) + return list(NORMAL_LEGS, DIGITIGRADE_LEGS) /datum/preference/choiced/lizard_legs/apply_to_human(mob/living/carbon/human/target, value) target.dna.features["legs"] = value + // Hack to update the dummy in the preference menu + // (Because digi legs are ONLY handled on species change) + if(!isdummy(target) || target.dna.species.digitigrade_customization == DIGITIGRADE_NEVER) + return + + var/list/correct_legs = target.dna.species.bodypart_overrides.Copy() & list(BODY_ZONE_R_LEG, BODY_ZONE_L_LEG) + + if(value == DIGITIGRADE_LEGS) + correct_legs[BODY_ZONE_R_LEG] = /obj/item/bodypart/leg/right/digitigrade + correct_legs[BODY_ZONE_L_LEG] = /obj/item/bodypart/leg/left/digitigrade + + for(var/obj/item/bodypart/old_part as anything in target.bodyparts) + if(old_part.change_exempt_flags & BP_BLOCK_CHANGE_SPECIES) + continue + + var/path = correct_legs[old_part.body_zone] + if(!path) + continue + var/obj/item/bodypart/new_part = new path() + new_part.replace_limb(target, TRUE) + new_part.update_limb(is_creating = TRUE) + qdel(old_part) + +/datum/preference/choiced/lizard_legs/is_accessible(datum/preferences/preferences) + if(!..()) + return FALSE + var/datum/species/species_type = preferences.read_preference(/datum/preference/choiced/species) + return initial(species_type.digitigrade_customization) == DIGITIGRADE_OPTIONAL + + +/datum/preference/choiced/lizard_legs/is_accessible(datum/preferences/preferences) + . = ..() + + if(!.) + return + + var/datum/species/species_type = preferences.read_preference(/datum/preference/choiced/species) + + return initial(species_type.digitigrade_customization) & DIGITIGRADE_OPTIONAL /datum/preference/choiced/lizard_snout savefile_key = "feature_lizard_snout" @@ -121,7 +159,7 @@ savefile_key = "feature_lizard_spines" savefile_identifier = PREFERENCE_CHARACTER category = PREFERENCE_CATEGORY_SECONDARY_FEATURES - relevant_mutant_bodypart = "spines" + relevant_external_organ = /obj/item/organ/external/spines /datum/preference/choiced/lizard_spines/init_possible_values() return assoc_to_keys_features(SSaccessories.spines_list) diff --git a/code/modules/client/preferences/species_features/mushperson.dm b/code/modules/client/preferences/species_features/mushperson.dm index 45bd9c4b726..4b624e9c02b 100644 --- a/code/modules/client/preferences/species_features/mushperson.dm +++ b/code/modules/client/preferences/species_features/mushperson.dm @@ -2,7 +2,7 @@ savefile_key = "feature_mushperson_cap" savefile_identifier = PREFERENCE_CHARACTER category = PREFERENCE_CATEGORY_SECONDARY_FEATURES - relevant_mutant_bodypart = "cap" + relevant_external_organ = /obj/item/organ/external/mushroom_cap /datum/preference/choiced/mushroom_cap/init_possible_values() return assoc_to_keys_features(SSaccessories.caps_list) diff --git a/code/modules/experisci/experiment/experiments.dm b/code/modules/experisci/experiment/experiments.dm index 1ec229cd1cd..c9f4f1b3d1b 100644 --- a/code/modules/experisci/experiment/experiments.dm +++ b/code/modules/experisci/experiment/experiments.dm @@ -387,7 +387,7 @@ if (organ.type == target_species.get_mutant_organ_type_for_slot(organ.slot)) continue else - if ((organ.type in target_species.mutant_organs) || (organ.type in target_species.external_organs)) + if ((organ.type in target_species.mutant_organs)) continue return TRUE return FALSE diff --git a/code/modules/jobs/job_types/chaplain/chaplain_vorpal_scythe.dm b/code/modules/jobs/job_types/chaplain/chaplain_vorpal_scythe.dm index ec484ebe6eb..4b07baaa058 100644 --- a/code/modules/jobs/job_types/chaplain/chaplain_vorpal_scythe.dm +++ b/code/modules/jobs/job_types/chaplain/chaplain_vorpal_scythe.dm @@ -10,7 +10,7 @@ If the scythe isn't empowered when you sheath it, you take a heap of damage and desc = "This shard seems to be directly linked to some sinister entity. It might be your god! It also gives you a really horrible rash when you hold onto it for too long." items_to_create = list(/obj/item/vorpalscythe) -/obj/item/organ/internal/cyberimp/arm/shard/scythe/Insert(mob/living/carbon/receiver, special, movement_flags) +/obj/item/organ/internal/cyberimp/arm/shard/scythe/mob_insert(mob/living/carbon/receiver, special, movement_flags) . = ..() if(receiver.mind) ADD_TRAIT(receiver.mind, TRAIT_MORBID, ORGAN_TRAIT) diff --git a/code/modules/mining/equipment/monster_organs/monster_organ.dm b/code/modules/mining/equipment/monster_organs/monster_organ.dm index a854f113740..9b6330f3467 100644 --- a/code/modules/mining/equipment/monster_organs/monster_organ.dm +++ b/code/modules/mining/equipment/monster_organs/monster_organ.dm @@ -36,7 +36,7 @@ icon = 'icons/obj/medical/organs/mining_organs.dmi' icon_state = "hivelord_core" actions_types = list(/datum/action/cooldown/monster_core_action) - visual = FALSE + item_flags = NOBLUDGEON slot = ORGAN_SLOT_MONSTER_CORE organ_flags = ORGAN_ORGANIC @@ -66,10 +66,9 @@ deltimer(decay_timer) return ..() -/obj/item/organ/internal/monster_core/Insert(mob/living/carbon/target_carbon, special = FALSE, movement_flags) +/obj/item/organ/internal/monster_core/mob_insert(mob/living/carbon/target_carbon, special = FALSE, movement_flags) . = ..() - if(!.) - return + if (inert) to_chat(target_carbon, span_notice("[src] breaks down as you try to insert it.")) qdel(src) @@ -80,7 +79,7 @@ target_carbon.visible_message(span_notice("[src] stabilizes as it's inserted.")) return TRUE -/obj/item/organ/internal/monster_core/Remove(mob/living/carbon/target_carbon, special, movement_flags) +/obj/item/organ/internal/monster_core/mob_remove(mob/living/carbon/target_carbon, special, movement_flags) if (!inert && !special) owner.visible_message(span_notice("[src] rapidly decays as it's removed.")) go_inert() diff --git a/code/modules/mining/lavaland/tendril_loot.dm b/code/modules/mining/lavaland/tendril_loot.dm index 20f6b022828..bafb188ca5a 100644 --- a/code/modules/mining/lavaland/tendril_loot.dm +++ b/code/modules/mining/lavaland/tendril_loot.dm @@ -557,7 +557,6 @@ var/obj/item/organ/external/wings/functional/wings = get_wing_choice(exposed_human, chest) wings = new wings() wings.Insert(exposed_human) - exposed_human.dna.species.handle_mutant_bodyparts(exposed_human) playsound(exposed_human.loc, 'sound/items/poster_ripped.ogg', 50, TRUE, -1) exposed_human.apply_damage(20, def_zone = BODY_ZONE_CHEST, forced = TRUE, wound_bonus = CANT_WOUND) exposed_human.emote("scream") diff --git a/code/modules/mob/living/carbon/alien/organs.dm b/code/modules/mob/living/carbon/alien/organs.dm index 9303cd23474..3c063aec7e7 100644 --- a/code/modules/mob/living/carbon/alien/organs.dm +++ b/code/modules/mob/living/carbon/alien/organs.dm @@ -1,6 +1,5 @@ /obj/item/organ/internal/alien icon_state = "acid" - visual = FALSE food_reagents = list(/datum/reagent/consumable/nutriment = 5, /datum/reagent/toxin/acid = 10) /obj/item/organ/internal/alien/plasmavessel @@ -222,11 +221,11 @@ stomach_contents -= source UnregisterSignal(source, list(COMSIG_MOVABLE_MOVED, COMSIG_LIVING_DEATH, COMSIG_QDELETING)) -/obj/item/organ/internal/stomach/alien/Insert(mob/living/carbon/stomach_owner, special, movement_flags) +/obj/item/organ/internal/stomach/alien/mob_insert(mob/living/carbon/stomach_owner, special, movement_flags) RegisterSignal(stomach_owner, COMSIG_ATOM_RELAYMOVE, PROC_REF(something_moved)) return ..() -/obj/item/organ/internal/stomach/alien/Remove(mob/living/carbon/stomach_owner, special, movement_flags) +/obj/item/organ/internal/stomach/alien/mob_remove(mob/living/carbon/stomach_owner, special, movement_flags) UnregisterSignal(stomach_owner, COMSIG_ATOM_RELAYMOVE) return ..() diff --git a/code/modules/mob/living/carbon/carbon_update_icons.dm b/code/modules/mob/living/carbon/carbon_update_icons.dm index 4c98419ef41..2350788e81f 100644 --- a/code/modules/mob/living/carbon/carbon_update_icons.dm +++ b/code/modules/mob/living/carbon/carbon_update_icons.dm @@ -53,13 +53,8 @@ overlays_standing[cache_index] = null SEND_SIGNAL(src, COMSIG_CARBON_REMOVE_OVERLAY, cache_index, I) -//used when putting/removing clothes that hide certain mutant body parts to just update those and not update the whole body. -/mob/living/carbon/human/proc/update_mutant_bodyparts() - dna?.species.handle_mutant_bodyparts(src) - update_body_parts() - /mob/living/carbon/update_body(is_creating = FALSE) - dna?.species.handle_body(src) //This calls `handle_mutant_bodyparts` which calls `update_mutant_bodyparts()`. Don't double call! + dna?.species.handle_body(src) update_body_parts(is_creating) /mob/living/carbon/on_changed_z_level(turf/old_turf, turf/new_turf, same_z_layer, notify_contents) diff --git a/code/modules/mob/living/carbon/human/_species.dm b/code/modules/mob/living/carbon/human/_species.dm index df6b35f16cb..c2ed9b27866 100644 --- a/code/modules/mob/living/carbon/human/_species.dm +++ b/code/modules/mob/living/carbon/human/_species.dm @@ -60,16 +60,6 @@ GLOBAL_LIST_EMPTY(features_by_species) /// Use a [language holder datum][/datum/language_holder] typepath in this var. /// Should never be null. var/datum/language_holder/species_language_holder = /datum/language_holder/human_basic - /** - * Visible CURRENT bodyparts that are unique to a species. - * DO NOT USE THIS AS A LIST OF ALL POSSIBLE BODYPARTS AS IT WILL FUCK - * SHIT UP! Changes to this list for non-species specific bodyparts (ie - * cat ears and tails) should be assigned at organ level if possible. - * Assoc values are defaults for given bodyparts, also modified by aforementioned organs. - * They also allow for faster '[]' list access versus 'in'. Other than that, they are useless right now. - * Layer hiding is handled by [/datum/species/proc/handle_mutant_bodyparts] below. - */ - var/list/mutant_bodyparts = list() ///The bodyparts this species uses. assoc of bodypart string - bodypart type. Make sure all the fucking entries are in or I'll skin you alive. var/list/bodypart_overrides = list( BODY_ZONE_L_ARM = /obj/item/bodypart/arm/left, @@ -79,10 +69,8 @@ GLOBAL_LIST_EMPTY(features_by_species) BODY_ZONE_R_LEG = /obj/item/bodypart/leg/right, BODY_ZONE_CHEST = /obj/item/bodypart/chest, ) - ///Internal organs that are unique to this race, like a tail. list(typepath of organ 1, typepath of organ 2) + ///Internal organs that are unique to this race, like a tail or other cosmetic organs. list(typepath of organ 1, typepath of organ 2 = "Round"). var/list/mutant_organs = list() - ///List of external organs to generate like horns, frills, wings, etc. list(typepath of organ = "Round Beautiful BDSM Snout"). Still WIP - var/list/external_organs = list() ///Replaces default brain with a different organ var/obj/item/organ/internal/brain/mutantbrain = /obj/item/organ/internal/brain ///Replaces default heart with a different organ @@ -283,7 +271,10 @@ GLOBAL_LIST_EMPTY(features_by_species) if(ORGAN_SLOT_STOMACH) return mutantstomach else - CRASH("Invalid organ slot [slot]") + // Non-standard organs we might have + for(var/obj/item/organ/extra_organ as anything in mutant_organs) + if(initial(extra_organ.slot) == slot) + return extra_organ /** * Corrects organs in a carbon, removing ones it doesn't need and adding ones it does. @@ -299,46 +290,33 @@ GLOBAL_LIST_EMPTY(features_by_species) * * visual_only - boolean, only load organs that change how the species looks. Do not use for normal gameplay stuff */ /datum/species/proc/regenerate_organs(mob/living/carbon/organ_holder, datum/species/old_species, replace_current = TRUE, list/excluded_zones, visual_only = FALSE) - //what should be put in if there is no mutantorgan (brains handled separately) - var/list/organ_slots = list( - ORGAN_SLOT_BRAIN, - ORGAN_SLOT_HEART, - ORGAN_SLOT_LUNGS, - ORGAN_SLOT_APPENDIX, - ORGAN_SLOT_EYES, - ORGAN_SLOT_EARS, - ORGAN_SLOT_TONGUE, - ORGAN_SLOT_LIVER, - ORGAN_SLOT_STOMACH, - ) - - for(var/slot in organ_slots) + for(var/slot in get_all_slots()) var/obj/item/organ/existing_organ = organ_holder.get_organ_slot(slot) var/obj/item/organ/new_organ = get_mutant_organ_type_for_slot(slot) + var/old_organ_type = old_species?.get_mutant_organ_type_for_slot(slot) - if(isnull(new_organ)) // if they aren't suppose to have an organ here, remove it - if(existing_organ) - existing_organ.Remove(organ_holder, special = TRUE) + // if we have an extra organ that before changing that the species didnt have, remove it + if(!new_organ) + if(existing_organ && (old_organ_type == existing_organ.type || replace_current)) + existing_organ.Remove(organ_holder) qdel(existing_organ) continue - // we don't want to remove organs that are not the default for this species - if(!isnull(existing_organ)) - if(!isnull(old_species) && existing_organ.type != old_species.get_mutant_organ_type_for_slot(slot)) - continue - else if(!replace_current && existing_organ.type != get_mutant_organ_type_for_slot(slot)) + if(existing_organ) + // we dont want to remove organs that were not from the old species (such as from freak surgery or prosthetics) + if(existing_organ.type != old_organ_type && !replace_current) continue - // at this point we already know new_organ is not null - if(existing_organ?.type == new_organ) - continue // we don't want to remove organs that are the same as the new one + // we don't want to remove organs that are the same as the new one + if(existing_organ.type == new_organ) + continue - if(visual_only && !initial(new_organ.visual)) + if(visual_only && (!initial(new_organ.bodypart_overlay) && !initial(new_organ.visual))) continue var/used_neworgan = FALSE new_organ = SSwardrobe.provide_type(new_organ) - var/should_have = new_organ.get_availability(src, organ_holder) + var/should_have = new_organ.get_availability(src, organ_holder) && should_visual_organ_apply_to(new_organ, organ_holder) // Check for an existing organ, and if there is one check to see if we should remove it var/health_pct = 1 @@ -362,46 +340,6 @@ GLOBAL_LIST_EMPTY(features_by_species) if(!used_neworgan) QDEL_NULL(new_organ) - if(!isnull(old_species)) - for(var/mutant_organ in old_species.mutant_organs) - if(mutant_organ in mutant_organs) - continue // need this mutant organ, but we already have it! - - var/obj/item/organ/current_organ = organ_holder.get_organ_by_type(mutant_organ) - if(current_organ) - current_organ.Remove(organ_holder) - QDEL_NULL(current_organ) - - for(var/obj/item/organ/external/external_organ in organ_holder.organs) - // External organ checking. We need to check the external organs owned by the carbon itself, - // because we want to also remove ones not shared by its species. - // This should be done even if species was not changed. - if(external_organ in external_organs) - continue // Don't remove external organs this species is supposed to have. - - external_organ.Remove(organ_holder) - QDEL_NULL(external_organ) - - var/list/species_organs = mutant_organs + external_organs - for(var/organ_path in species_organs) - var/obj/item/organ/current_organ = organ_holder.get_organ_by_type(organ_path) - if(ispath(organ_path, /obj/item/organ/external) && !should_external_organ_apply_to(organ_path, organ_holder)) - if(!isnull(current_organ) && replace_current) - // if we have an organ here and we're replacing organs, remove it - current_organ.Remove(organ_holder) - QDEL_NULL(current_organ) - continue - - if(!current_organ || replace_current) - var/obj/item/organ/replacement = SSwardrobe.provide_type(organ_path) - // If there's an existing mutant organ, we're technically replacing it. - // Let's abuse the snowflake proc that skillchips added. Basically retains - // feature parity with every other organ too. - if(current_organ) - current_organ.before_organ_replacement(replacement) - // organ.Insert will qdel any current organs in that slot, so we don't need to. - replacement.Insert(organ_holder, special=TRUE, movement_flags = DELETE_IF_REPLACED) - /datum/species/proc/worn_items_fit_body_check(mob/living/carbon/wearer) for(var/obj/item/equipped_item in wearer.get_equipped_items(INCLUDE_POCKETS)) var/equipped_item_slot = wearer.get_slot_by_item(equipped_item) @@ -443,7 +381,7 @@ GLOBAL_LIST_EMPTY(features_by_species) if(old_species.type != type) replace_body(human_who_gained_species, src) - regenerate_organs(human_who_gained_species, old_species, visual_only = human_who_gained_species.visual_only_organs) + regenerate_organs(human_who_gained_species, old_species, replace_current = FALSE, visual_only = human_who_gained_species.visual_only_organs) // Drop the items the new species can't wear INVOKE_ASYNC(src, PROC_REF(worn_items_fit_body_check), human_who_gained_species, TRUE) @@ -459,16 +397,6 @@ GLOBAL_LIST_EMPTY(features_by_species) //Resets blood if it is excessively high so they don't gib normalize_blood(human_who_gained_species) - if(ishuman(human_who_gained_species)) - var/mob/living/carbon/human/human = human_who_gained_species - for(var/obj/item/organ/external/organ_path as anything in external_organs) - if(!should_external_organ_apply_to(organ_path, human)) - continue - - //Load a persons preferences from DNA - var/obj/item/organ/external/new_organ = SSwardrobe.provide_type(organ_path) - new_organ.Insert(human, special=TRUE, movement_flags = DELETE_IF_REPLACED) - add_body_markings(human_who_gained_species) if(length(inherent_traits)) @@ -509,10 +437,6 @@ GLOBAL_LIST_EMPTY(features_by_species) for(var/X in inherent_traits) REMOVE_TRAIT(C, X, SPECIES_TRAIT) - for(var/obj/item/organ/external/organ in C.organs) - organ.Remove(C) - qdel(organ) - //If their inert mutation is not the same, swap it out if((inert_mutation != new_species.inert_mutation) && LAZYLEN(C.dna.mutation_index) && (inert_mutation in C.dna.mutation_index)) C.dna.remove_mutation(inert_mutation) @@ -546,14 +470,13 @@ GLOBAL_LIST_EMPTY(features_by_species) * Handles the body of a human * * Handles lipstick, having no eyes, eye color, undergarnments like underwear, undershirts, and socks, and body layers. - * Calls [handle_mutant_bodyparts][/datum/species/proc/handle_mutant_bodyparts] * Arguments: * * species_human - Human, whoever we're handling the body for */ /datum/species/proc/handle_body(mob/living/carbon/human/species_human) species_human.remove_overlay(BODY_LAYER) if(HAS_TRAIT(species_human, TRAIT_INVISIBLE_MAN)) - return handle_mutant_bodyparts(species_human) + return var/list/standing = list() if(!HAS_TRAIT(species_human, TRAIT_HUSK)) @@ -598,103 +521,7 @@ GLOBAL_LIST_EMPTY(features_by_species) species_human.overlays_standing[BODY_LAYER] = standing species_human.apply_overlay(BODY_LAYER) - handle_mutant_bodyparts(species_human) - -/** - * Handles the mutant bodyparts of a human - * - * Handles the adding and displaying of, layers, colors, and overlays of mutant bodyparts and accessories. - * Handles digitigrade leg displaying and squishing. - * Arguments: - * * H - Human, whoever we're handling the body for - * * forced_colour - The forced color of an accessory. Leave null to use mutant color. - */ -/datum/species/proc/handle_mutant_bodyparts(mob/living/carbon/human/source, forced_colour) - var/list/bodyparts_to_add = mutant_bodyparts.Copy() - var/list/relevent_layers = list(BODY_BEHIND_LAYER, BODY_ADJ_LAYER, BODY_FRONT_LAYER) - var/list/standing = list() - - source.remove_overlay(BODY_BEHIND_LAYER) - source.remove_overlay(BODY_ADJ_LAYER) - source.remove_overlay(BODY_FRONT_LAYER) - - if(!mutant_bodyparts || HAS_TRAIT(source, TRAIT_INVISIBLE_MAN)) - return - - var/obj/item/bodypart/head/noggin = source.get_bodypart(BODY_ZONE_HEAD) - - - if(mutant_bodyparts["ears"]) - if(!source.dna.features["ears"] || source.dna.features["ears"] == "None" || source.head && (source.head.flags_inv & HIDEHAIR) || (source.wear_mask && (source.wear_mask.flags_inv & HIDEHAIR)) || !noggin || IS_ROBOTIC_LIMB(noggin)) - bodyparts_to_add -= "ears" - - if(!bodyparts_to_add) - return - - var/g = (source.physique == FEMALE) ? "f" : "m" - - for(var/layer in relevent_layers) - var/layertext = mutant_bodyparts_layertext(layer) - - for(var/bodypart in bodyparts_to_add) - var/datum/sprite_accessory/accessory - switch(bodypart) - if("ears") - accessory = SSaccessories.ears_list[source.dna.features["ears"]] - if("legs") - accessory = SSaccessories.legs_list[source.dna.features["legs"]] - - if(!accessory || accessory.icon_state == "none") - continue - - var/mutable_appearance/accessory_overlay = mutable_appearance(accessory.icon, layer = -layer) - - if(accessory.gender_specific) - accessory_overlay.icon_state = "[g]_[bodypart]_[accessory.icon_state]_[layertext]" - else - accessory_overlay.icon_state = "m_[bodypart]_[accessory.icon_state]_[layertext]" - - if(accessory.em_block) - accessory_overlay.overlays += emissive_blocker(accessory_overlay.icon, accessory_overlay.icon_state, source, accessory_overlay.alpha) - - if(accessory.center) - accessory_overlay = center_image(accessory_overlay, accessory.dimension_x, accessory.dimension_y) - - if(!(HAS_TRAIT(source, TRAIT_HUSK))) - if(!forced_colour) - switch(accessory.color_src) - if(MUTANT_COLOR) - accessory_overlay.color = fixed_mut_color || source.dna.features["mcolor"] - if(HAIR_COLOR) - accessory_overlay.color = get_fixed_hair_color(source) || source.hair_color - if(FACIAL_HAIR_COLOR) - accessory_overlay.color = get_fixed_hair_color(source) || source.facial_hair_color - if(EYE_COLOR) - accessory_overlay.color = source.eye_color_left - else - accessory_overlay.color = forced_colour - standing += accessory_overlay - - if(accessory.hasinner) - var/mutable_appearance/inner_accessory_overlay = mutable_appearance(accessory.icon, layer = -layer) - if(accessory.gender_specific) - inner_accessory_overlay.icon_state = "[g]_[bodypart]inner_[accessory.icon_state]_[layertext]" - else - inner_accessory_overlay.icon_state = "m_[bodypart]inner_[accessory.icon_state]_[layertext]" - - if(accessory.center) - inner_accessory_overlay = center_image(inner_accessory_overlay, accessory.dimension_x, accessory.dimension_y) - - standing += inner_accessory_overlay - - source.overlays_standing[layer] = standing.Copy() - standing = list() - - source.apply_overlay(BODY_BEHIND_LAYER) - source.apply_overlay(BODY_ADJ_LAYER) - source.apply_overlay(BODY_FRONT_LAYER) - - update_body_markings(source) + update_body_markings(species_human) //This exists so sprite accessories can still be per-layer without having to include that layer's //number in their sprite name, which causes issues when those numbers change. @@ -737,7 +564,9 @@ GLOBAL_LIST_EMPTY(features_by_species) var/list/new_features = list() var/static/list/organs_to_randomize = list() - for(var/obj/item/organ/external/organ_path as anything in external_organs) + for(var/obj/item/organ/organ_path as anything in mutant_organs) + if(!organ_path.bodypart_overlay) + continue var/overlay_path = initial(organ_path.bodypart_overlay) var/datum/bodypart_overlay/mutant/sample_overlay = organs_to_randomize[overlay_path] if(isnull(sample_overlay)) @@ -1579,15 +1408,14 @@ GLOBAL_LIST_EMPTY(features_by_species) var/datum/preference/preference = GLOB.preference_entries[preference_type] if ( \ - (preference.relevant_mutant_bodypart in mutant_bodyparts) \ - || (preference.relevant_inherent_trait in inherent_traits) \ - || (preference.relevant_external_organ in external_organs) \ + (preference.relevant_inherent_trait in inherent_traits) \ + || (preference.relevant_external_organ in mutant_organs) \ || (preference.relevant_head_flag && check_head_flags(preference.relevant_head_flag)) \ || (preference.relevant_body_markings in body_markings) \ ) features += preference.savefile_key - for (var/obj/item/organ/external/organ_type as anything in external_organs) + for (var/obj/item/organ/organ_type as anything in mutant_organs) var/preference = initial(organ_type.preference) if (!isnull(preference)) features += preference @@ -1632,7 +1460,7 @@ GLOBAL_LIST_EMPTY(features_by_species) /datum/species/proc/get_types_to_preload() var/list/to_store = list() to_store += mutant_organs - for(var/obj/item/organ/external/horny as anything in external_organs) + for(var/obj/item/organ/horny as anything in mutant_organs) to_store += horny //Haha get it? //Don't preload brains, cause reuse becomes a horrible headache @@ -2218,6 +2046,10 @@ GLOBAL_LIST_EMPTY(features_by_species) /// Update the overlays if necessary /datum/species/proc/update_body_markings(mob/living/carbon/human/hooman) + if(HAS_TRAIT(hooman, TRAIT_INVISIBLE_MAN)) + remove_body_markings(hooman) + return + var/needs_update = FALSE for(var/datum/bodypart_overlay/simple/body_marking/marking as anything in body_markings) if(initial(marking.dna_feature_key) == body_markings[marking]) // dna is same as our species (sort of mini-cache), so no update needed diff --git a/code/modules/mob/living/carbon/human/human_update_icons.dm b/code/modules/mob/living/carbon/human/human_update_icons.dm index 3fa50fe9c67..77b73cae9bc 100644 --- a/code/modules/mob/living/carbon/human/human_update_icons.dm +++ b/code/modules/mob/living/carbon/human/human_update_icons.dm @@ -133,9 +133,9 @@ There are several things that need to be remembered: var/obj/item/bodypart/chest/my_chest = get_bodypart(BODY_ZONE_CHEST) my_chest?.worn_uniform_offset?.apply_offset(uniform_overlay) overlays_standing[UNIFORM_LAYER] = uniform_overlay - apply_overlay(UNIFORM_LAYER) - update_mutant_bodyparts() + update_body_parts() + apply_overlay(UNIFORM_LAYER) /mob/living/carbon/human/update_worn_id(update_obscured = TRUE) remove_overlay(ID_LAYER) @@ -434,12 +434,10 @@ There are several things that need to be remembered: var/obj/item/bodypart/chest/my_chest = get_bodypart(BODY_ZONE_CHEST) my_chest?.worn_suit_offset?.apply_offset(suit_overlay) overlays_standing[SUIT_LAYER] = suit_overlay + update_body_parts() - update_mutant_bodyparts() - apply_overlay(SUIT_LAYER) - /mob/living/carbon/human/update_pockets() if(client && hud_used) var/atom/movable/screen/inventory/inv @@ -489,7 +487,7 @@ There are several things that need to be remembered: overlays_standing[FACEMASK_LAYER] = mask_overlay apply_overlay(FACEMASK_LAYER) - update_mutant_bodyparts() //e.g. upgate needed because mask now hides lizard snout + update_body_parts() //e.g. upgate needed because mask now hides lizard snout /mob/living/carbon/human/update_worn_back(update_obscured = TRUE) remove_overlay(BACK_LAYER) diff --git a/code/modules/mob/living/carbon/human/species_types/dullahan.dm b/code/modules/mob/living/carbon/human/species_types/dullahan.dm index 1d7c328f882..d4dea2abfcc 100644 --- a/code/modules/mob/living/carbon/human/species_types/dullahan.dm +++ b/code/modules/mob/living/carbon/human/species_types/dullahan.dm @@ -19,7 +19,6 @@ BODY_ZONE_CHEST = /obj/item/bodypart/chest, ) inherent_biotypes = MOB_UNDEAD|MOB_HUMANOID - mutant_bodyparts = list("wings" = "None") mutantbrain = /obj/item/organ/internal/brain/dullahan mutanteyes = /obj/item/organ/internal/eyes/dullahan mutanttongue = /obj/item/organ/internal/tongue/dullahan diff --git a/code/modules/mob/living/carbon/human/species_types/felinid.dm b/code/modules/mob/living/carbon/human/species_types/felinid.dm index b26bd476b4b..8a040425a97 100644 --- a/code/modules/mob/living/carbon/human/species_types/felinid.dm +++ b/code/modules/mob/living/carbon/human/species_types/felinid.dm @@ -3,11 +3,10 @@ name = "Felinid" id = SPECIES_FELINE examine_limb_id = SPECIES_HUMAN - mutant_bodyparts = list("ears" = "Cat", "wings" = "None") mutantbrain = /obj/item/organ/internal/brain/felinid mutanttongue = /obj/item/organ/internal/tongue/cat mutantears = /obj/item/organ/internal/ears/cat - external_organs = list( + mutant_organs = list( /obj/item/organ/external/tail/cat = "Cat", ) inherent_traits = list( @@ -151,7 +150,7 @@ // stored_feature_id is only set once (the first time an organ is inserted), so this should be safe. var/obj/item/organ/internal/ears/cat/kitty_ears = new kitty_ears.Insert(soon_to_be_felinid, special = TRUE, movement_flags = DELETE_IF_REPLACED) - if(should_external_organ_apply_to(/obj/item/organ/external/tail/cat, soon_to_be_felinid)) //only give them a tail if they actually have sprites for it / are a compatible subspecies. + if(should_visual_organ_apply_to(/obj/item/organ/external/tail/cat, soon_to_be_felinid)) //only give them a tail if they actually have sprites for it / are a compatible subspecies. var/obj/item/organ/external/tail/cat/kitty_tail = new kitty_tail.Insert(soon_to_be_felinid, special = TRUE, movement_flags = DELETE_IF_REPLACED) @@ -174,8 +173,8 @@ old_tail.Remove(purrbated_human, special = TRUE) qdel(old_tail) // Locate does not work on assoc lists, so we do it by hand - for(var/external_organ in target_species.external_organs) - if(!should_external_organ_apply_to(external_organ, purrbated_human)) + for(var/external_organ in target_species.mutant_organs) + if(!should_visual_organ_apply_to(external_organ, purrbated_human)) continue if(ispath(external_organ, /obj/item/organ/external/tail)) var/obj/item/organ/external/tail/new_tail = new external_organ() diff --git a/code/modules/mob/living/carbon/human/species_types/humans.dm b/code/modules/mob/living/carbon/human/species_types/humans.dm index ef8140c7d82..be6357f6b4f 100644 --- a/code/modules/mob/living/carbon/human/species_types/humans.dm +++ b/code/modules/mob/living/carbon/human/species_types/humans.dm @@ -4,7 +4,6 @@ inherent_traits = list( TRAIT_USES_SKINTONES, ) - mutant_bodyparts = list("wings" = "None") skinned_type = /obj/item/stack/sheet/animalhide/human changesource_flags = MIRROR_BADMIN | WABBAJACK | MIRROR_MAGIC | MIRROR_PRIDE | ERT_SPAWN | RACE_SWAP | SLIME_EXTRACT payday_modifier = 1.1 diff --git a/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm b/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm index a4e9a85fd6e..65945467f5e 100644 --- a/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm +++ b/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm @@ -7,9 +7,8 @@ TRAIT_MUTANT_COLORS, ) inherent_biotypes = MOB_ORGANIC|MOB_HUMANOID|MOB_REPTILE - mutant_bodyparts = list("legs" = "Normal Legs") body_markings = list(/datum/bodypart_overlay/simple/body_marking/lizard = "None") - external_organs = list( + mutant_organs = list( /obj/item/organ/external/horns = "None", /obj/item/organ/external/frills = "None", /obj/item/organ/external/snout = "Round", diff --git a/code/modules/mob/living/carbon/human/species_types/monkeys.dm b/code/modules/mob/living/carbon/human/species_types/monkeys.dm index 3f2a1752c73..1ca2979d3d6 100644 --- a/code/modules/mob/living/carbon/human/species_types/monkeys.dm +++ b/code/modules/mob/living/carbon/human/species_types/monkeys.dm @@ -3,7 +3,7 @@ /datum/species/monkey name = "\improper Monkey" id = SPECIES_MONKEY - external_organs = list( + mutant_organs = list( /obj/item/organ/external/tail/monkey = "Monkey", ) mutanttongue = /obj/item/organ/internal/tongue/monkey 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 d7134d0a86d..f9a17b310fa 100644 --- a/code/modules/mob/living/carbon/human/species_types/mothmen.dm +++ b/code/modules/mob/living/carbon/human/species_types/mothmen.dm @@ -4,7 +4,7 @@ id = SPECIES_MOTH inherent_biotypes = MOB_ORGANIC|MOB_HUMANOID|MOB_BUG body_markings = list(/datum/bodypart_overlay/simple/body_marking/moth = "None") - external_organs = list(/obj/item/organ/external/wings/moth = "Plain", /obj/item/organ/external/antennae = "Plain") + mutant_organs = list(/obj/item/organ/external/wings/moth = "Plain", /obj/item/organ/external/antennae = "Plain") meat = /obj/item/food/meat/slab/human/mutant/moth mutanttongue = /obj/item/organ/internal/tongue/moth mutanteyes = /obj/item/organ/internal/eyes/moth @@ -24,12 +24,6 @@ BODY_ZONE_R_LEG = /obj/item/bodypart/leg/right/moth, ) -/datum/species/moth/regenerate_organs(mob/living/carbon/C, datum/species/old_species, replace_current= TRUE, list/excluded_zones, visual_only) - . = ..() - if(ishuman(C)) - var/mob/living/carbon/human/H = C - handle_mutant_bodyparts(H) - /datum/species/moth/on_species_gain(mob/living/carbon/human/human_who_gained_species, datum/species/old_species, pref_load) . = ..() RegisterSignal(human_who_gained_species, COMSIG_MOB_APPLY_DAMAGE_MODIFIERS, PROC_REF(damage_weakness)) 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 14d6c1437f0..1e2b73616d9 100644 --- a/code/modules/mob/living/carbon/human/species_types/mushpeople.dm +++ b/code/modules/mob/living/carbon/human/species_types/mushpeople.dm @@ -6,7 +6,7 @@ fixed_mut_color = "#DBBF92" - external_organs = list(/obj/item/organ/external/mushroom_cap = "Round") + mutant_organs = list(/obj/item/organ/external/mushroom_cap = "Round") inherent_traits = list( TRAIT_MUTANT_COLORS, 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 bcc0b6c4b67..e5e735b31e4 100644 --- a/code/modules/mob/living/carbon/human/species_types/podpeople.dm +++ b/code/modules/mob/living/carbon/human/species_types/podpeople.dm @@ -7,7 +7,7 @@ TRAIT_MUTANT_COLORS, TRAIT_PLANT_SAFE, ) - external_organs = list( + mutant_organs = list( /obj/item/organ/external/pod_hair = "None", ) inherent_biotypes = MOB_ORGANIC | MOB_HUMANOID | MOB_PLANT diff --git a/code/modules/mob/living/carbon/human/species_types/vampire.dm b/code/modules/mob/living/carbon/human/species_types/vampire.dm index 111b35cb7f7..d3dfb813520 100644 --- a/code/modules/mob/living/carbon/human/species_types/vampire.dm +++ b/code/modules/mob/living/carbon/human/species_types/vampire.dm @@ -17,7 +17,6 @@ TRAIT_NO_MIRROR_REFLECTION, ) inherent_biotypes = MOB_UNDEAD|MOB_HUMANOID - mutant_bodyparts = list("wings" = "None") changesource_flags = MIRROR_BADMIN | WABBAJACK | ERT_SPAWN exotic_bloodtype = "U" blood_deficiency_drain_rate = BLOOD_DEFICIENCY_MODIFIER // vampires already passively lose blood, so this just makes them lose it slightly more quickly when they have blood deficiency. diff --git a/code/modules/mod/modules/modules_medical.dm b/code/modules/mod/modules/modules_medical.dm index 7a802cf0f6c..8a1d31a92f6 100644 --- a/code/modules/mod/modules/modules_medical.dm +++ b/code/modules/mod/modules/modules_medical.dm @@ -225,8 +225,7 @@ organ_evacced.Remove(target, special = TRUE) organ_evacced.forceMove(get_turf(target)) - if (!organ.Insert(target)) - organ.forceMove(drop_location()) + organ.Insert(target) organ = null ///Patrient Transport - Generates hardlight bags you can put people in. diff --git a/code/modules/surgery/bodyparts/_bodyparts.dm b/code/modules/surgery/bodyparts/_bodyparts.dm index 39eace4d686..296f818e587 100644 --- a/code/modules/surgery/bodyparts/_bodyparts.dm +++ b/code/modules/surgery/bodyparts/_bodyparts.dm @@ -953,7 +953,7 @@ if(should_draw_greyscale) //Should the limb be colored? draw_color ||= species_color || (skin_tone ? skintone2hex(skin_tone) : null) - recolor_external_organs() + recolor_bodypart_overlays() return TRUE //to update the bodypart's icon when not attached to a mob @@ -1274,7 +1274,7 @@ QDEL_NULL(current_gauze) ///Loops through all of the bodypart's external organs and update's their color. -/obj/item/bodypart/proc/recolor_external_organs() +/obj/item/bodypart/proc/recolor_bodypart_overlays() for(var/datum/bodypart_overlay/mutant/overlay in bodypart_overlays) overlay.inherit_color(src, force = TRUE) diff --git a/code/modules/surgery/bodyparts/dismemberment.dm b/code/modules/surgery/bodyparts/dismemberment.dm index 517847fb24f..c656daeb184 100644 --- a/code/modules/surgery/bodyparts/dismemberment.dm +++ b/code/modules/surgery/bodyparts/dismemberment.dm @@ -404,12 +404,12 @@ qdel(phantom_loss) //Copied from /datum/species/proc/on_species_gain() - for(var/obj/item/organ/external/organ_path as anything in dna.species.external_organs) + for(var/obj/item/organ/organ_path as anything in dna.species.mutant_organs) //Load a persons preferences from DNA var/zone = initial(organ_path.zone) if(zone != limb_zone) continue - var/obj/item/organ/external/new_organ = SSwardrobe.provide_type(organ_path) + var/obj/item/organ/new_organ = SSwardrobe.provide_type(organ_path) new_organ.Insert(src) update_body_parts() diff --git a/code/modules/surgery/bodyparts/helpers.dm b/code/modules/surgery/bodyparts/helpers.dm index 8833f87e28e..dec8efb154e 100644 --- a/code/modules/surgery/bodyparts/helpers.dm +++ b/code/modules/surgery/bodyparts/helpers.dm @@ -182,8 +182,8 @@ /mob/living/carbon/proc/synchronize_bodytypes() var/all_limb_flags = NONE for(var/obj/item/bodypart/limb as anything in bodyparts) - for(var/obj/item/organ/external/ext_organ in limb) - all_limb_flags |= ext_organ.external_bodytypes + for(var/obj/item/organ/organ in limb) + all_limb_flags |= organ.external_bodytypes all_limb_flags |= limb.bodytype bodytype = all_limb_flags @@ -192,8 +192,8 @@ /mob/living/carbon/proc/synchronize_bodyshapes() var/all_limb_flags = NONE for(var/obj/item/bodypart/limb as anything in bodyparts) - for(var/obj/item/organ/external/ext_organ in limb) - all_limb_flags |= ext_organ.external_bodyshapes + for(var/obj/item/organ/organ in limb) + all_limb_flags |= organ.external_bodyshapes all_limb_flags |= limb.bodyshape bodyshape = all_limb_flags diff --git a/code/modules/surgery/organ_manipulation.dm b/code/modules/surgery/organ_manipulation.dm index d96d558863b..83474363c6c 100644 --- a/code/modules/surgery/organ_manipulation.dm +++ b/code/modules/surgery/organ_manipulation.dm @@ -239,22 +239,20 @@ tool = tool.contents[1] target_organ = tool user.temporarilyRemoveItemFromInventory(target_organ, TRUE) - if(target_organ.Insert(target)) - if(apparatus) - apparatus.icon_state = initial(apparatus.icon_state) - apparatus.desc = initial(apparatus.desc) - apparatus.cut_overlays() - display_results( - user, - target, - span_notice("You insert [tool] into [target]'s [target.parse_zone_with_bodypart(target_zone)]."), - span_notice("[user] inserts [tool] into [target]'s [target.parse_zone_with_bodypart(target_zone)]!"), - span_notice("[user] inserts something into [target]'s [target.parse_zone_with_bodypart(target_zone)]!"), - ) - display_pain(target, "Your [target.parse_zone_with_bodypart(target_zone)] throbs with pain as your new [tool.name] comes to life!") - target_organ.on_surgical_insertion(user, target, target_zone, tool) - else - target_organ.forceMove(target.loc) + target_organ.Insert(target) + if(apparatus) + apparatus.icon_state = initial(apparatus.icon_state) + apparatus.desc = initial(apparatus.desc) + apparatus.cut_overlays() + display_results( + user, + target, + span_notice("You insert [tool] into [target]'s [target.parse_zone_with_bodypart(target_zone)]."), + span_notice("[user] inserts [tool] into [target]'s [target.parse_zone_with_bodypart(target_zone)]!"), + span_notice("[user] inserts something into [target]'s [target.parse_zone_with_bodypart(target_zone)]!"), + ) + display_pain(target, "Your [target.parse_zone_with_bodypart(target_zone)] throbs with pain as your new [tool.name] comes to life!") + target_organ.on_surgical_insertion(user, target, target_zone, tool) else if(current_type == "extract") if(target_organ && target_organ.owner == target) diff --git a/code/modules/surgery/organs/_organ.dm b/code/modules/surgery/organs/_organ.dm index 98d947793ef..4d28b987abc 100644 --- a/code/modules/surgery/organs/_organ.dm +++ b/code/modules/surgery/organs/_organ.dm @@ -1,4 +1,3 @@ - /obj/item/organ name = "organ" icon = 'icons/obj/medical/organs/organs.dmi' @@ -79,6 +78,9 @@ INITIALIZE_IMMEDIATE(/obj/item/organ) volume = reagent_vol,\ after_eat = CALLBACK(src, PROC_REF(OnEatFrom))) + if(bodypart_overlay) + setup_bodypart_overlay() + /obj/item/organ/Destroy() if(bodypart_owner && !owner && !QDELETED(bodypart_owner)) bodypart_remove(bodypart_owner) @@ -140,7 +142,7 @@ INITIALIZE_IMMEDIATE(/obj/item/organ) return /obj/item/organ/proc/on_life(seconds_per_tick, times_fired) - CRASH("Oh god oh fuck something is calling parent organ life") + return /obj/item/organ/examine(mob/user) . = ..() @@ -342,3 +344,16 @@ INITIALIZE_IMMEDIATE(/obj/item/organ) /// Tries to replace the existing organ on the passed mob with this one, with special handling for replacing a brain without ghosting target /obj/item/organ/proc/replace_into(mob/living/carbon/new_owner) return Insert(new_owner, special = TRUE, movement_flags = DELETE_IF_REPLACED) + + +/// Get all possible organ slots by checking every organ, and then store it and give it whenever needed +/proc/get_all_slots() + var/static/list/all_organ_slots = list() + + if(!all_organ_slots.len) + for(var/obj/item/organ/an_organ as anything in subtypesof(/obj/item/organ)) + if(!initial(an_organ.slot)) + continue + all_organ_slots |= initial(an_organ.slot) + + return all_organ_slots diff --git a/code/modules/surgery/organs/external/_external_organ.dm b/code/modules/surgery/organs/external/_visual_organs.dm similarity index 75% rename from code/modules/surgery/organs/external/_external_organ.dm rename to code/modules/surgery/organs/external/_visual_organs.dm index f41cb2a82bd..3aa54f27418 100644 --- a/code/modules/surgery/organs/external/_external_organ.dm +++ b/code/modules/surgery/organs/external/_visual_organs.dm @@ -1,19 +1,11 @@ -/** -* System for drawing organs with overlays. These overlays are drawn directly on the bodypart, attached to a person or not -* Works in tandem with the /datum/sprite_accessory datum to generate sprites -* Unlike normal organs, we're actually inside a persons limbs at all times +/* +System for drawing organs with overlays. These overlays are drawn directly on the bodypart, attached to a person or not +Works in tandem with the /datum/sprite_accessory datum to generate sprites +Unlike normal organs, we're actually inside a persons limbs at all times */ -/obj/item/organ/external - name = "external organ" - desc = "An external organ that is too external." - - organ_flags = ORGAN_ORGANIC | ORGAN_EDIBLE - visual = TRUE - +/obj/item/organ ///The overlay datum that actually draws stuff on the limb var/datum/bodypart_overlay/mutant/bodypart_overlay - ///If not null, overrides the appearance with this sprite accessory datum - var/sprite_accessory_override /// The savefile_key of the preference this relates to. Used for the preferences UI. var/preference @@ -23,21 +15,24 @@ ///Set to EXTERNAL_BEHIND, EXTERNAL_FRONT or EXTERNAL_ADJACENT if you want to draw one of those layers as the object sprite. FALSE to use your own ///This will not work if it doesn't have a limb to generate its icon with var/use_mob_sprite_as_obj_sprite = FALSE + ///Does this organ have any bodytypes to pass to its bodypart_owner? var/external_bodytypes = NONE ///Does this organ have any bodyshapes to pass to its bodypart_owner? var/external_bodyshapes = NONE + ///Which flags does a 'modification tool' need to have to restyle us, if it all possible (located in code/_DEFINES/mobs) var/restyle_flags = NONE -/**mob_sprite is optional if you havent set sprite_datums for the object, and is used mostly to generate sprite_datums from a persons DNA + ///If not null, overrides the appearance with this sprite accessory datum + var/sprite_accessory_override + +/**accessory_type is optional if you havent set sprite_datums for the object, and is used mostly to generate sprite_datums from a persons DNA * For _mob_sprite we make a distinction between "Round Snout" and "round". Round Snout is the name of the sprite datum, while "round" would be part of the sprite * I'm sorry */ -/obj/item/organ/external/Initialize(mapload, accessory_type) - . = ..() - - bodypart_overlay = new bodypart_overlay() +/obj/item/organ/proc/setup_bodypart_overlay(accessory_type) + bodypart_overlay = new bodypart_overlay(src) accessory_type = accessory_type ? accessory_type : sprite_accessory_override var/update_overlays = TRUE @@ -55,61 +50,13 @@ if(restyle_flags) RegisterSignal(src, COMSIG_ATOM_RESTYLE, PROC_REF(on_attempt_feature_restyle)) -/obj/item/organ/external/Insert(mob/living/carbon/receiver, special, movement_flags) - . = ..() - receiver.update_body_parts() +/// Some sanity checks, but mostly to check if the person has their preference/dna set to load +/proc/should_visual_organ_apply_to(obj/item/organ/organpath, mob/living/carbon/target) + if(!initial(organpath.bodypart_overlay)) + return TRUE -/obj/item/organ/external/Remove(mob/living/carbon/organ_owner, special, movement_flags) - . = ..() - if(!special) - organ_owner.update_body_parts() - -/obj/item/organ/external/mob_insert(mob/living/carbon/receiver, special, movement_flags) - if(!should_external_organ_apply_to(type, receiver)) - stack_trace("adding a [type] to a [receiver.type] when it shouldn't be!") - - . = ..() - - if(!.) - return - - if(bodypart_overlay.imprint_on_next_insertion) //We only want this set *once* - var/feature_name = receiver.dna.features[bodypart_overlay.feature_key] - if (isnull(feature_name)) - feature_name = receiver.dna.species.external_organs[type] - bodypart_overlay.set_appearance_from_name(feature_name) - bodypart_overlay.imprint_on_next_insertion = FALSE - - if(external_bodytypes) - receiver.synchronize_bodytypes() - if(external_bodyshapes) - receiver.synchronize_bodyshapes() - - receiver.update_body_parts() - -/obj/item/organ/external/mob_remove(mob/living/carbon/organ_owner, special, moving) - if(!special) - organ_owner.synchronize_bodytypes() - organ_owner.synchronize_bodyshapes() - organ_owner.update_body_parts() - return ..() - -/obj/item/organ/external/on_bodypart_insert(obj/item/bodypart/bodypart) - bodypart.add_bodypart_overlay(bodypart_overlay) - return ..() - -/obj/item/organ/external/on_bodypart_remove(obj/item/bodypart/bodypart) - bodypart.remove_bodypart_overlay(bodypart_overlay) - - if(use_mob_sprite_as_obj_sprite) - update_appearance(UPDATE_OVERLAYS) - - color = bodypart_overlay.draw_color // so a pink felinid doesn't drop a gray tail - return ..() - -/proc/should_external_organ_apply_to(obj/item/organ/external/organpath, mob/living/carbon/target) if(isnull(organpath) || isnull(target)) - stack_trace("passed a null path or mob to 'should_external_organ_apply_to'") + stack_trace("passed a null path or mob to 'should_visual_organ_apply_to'") return FALSE var/datum/bodypart_overlay/mutant/bodypart_overlay = initial(organpath.bodypart_overlay) @@ -122,7 +69,7 @@ return FALSE ///Update our features after something changed our appearance -/obj/item/organ/external/proc/mutate_feature(features, mob/living/carbon/human/human) +/obj/item/organ/proc/mutate_feature(features, mob/living/carbon/human/human) if(!dna_block) return @@ -131,7 +78,7 @@ bodypart_overlay.set_appearance_from_name(feature_list[deconstruct_block(get_uni_feature_block(features, dna_block), feature_list.len)]) ///If you need to change an external_organ for simple one-offs, use this. Pass the accessory type : /datum/accessory/something -/obj/item/organ/external/proc/simple_change_sprite(accessory_type) +/obj/item/organ/proc/simple_change_sprite(accessory_type) var/datum/sprite_accessory/typed_accessory = accessory_type //we only take types for maintainability bodypart_overlay.set_appearance(typed_accessory) @@ -142,10 +89,7 @@ bodypart_owner.update_icon_dropped() //else if(use_mob_sprite_as_obj_sprite) //are we out in the world, unprotected by flesh? -/obj/item/organ/external/on_life(seconds_per_tick, times_fired) - return - -/obj/item/organ/external/update_overlays() +/obj/item/organ/update_overlays() . = ..() if(!use_mob_sprite_as_obj_sprite) @@ -260,17 +204,16 @@ ///Store our old datum here for if our antennae are healed var/original_sprite_datum -/obj/item/organ/external/antennae/Insert(mob/living/carbon/receiver, special, movement_flags) +/obj/item/organ/external/antennae/mob_insert(mob/living/carbon/receiver, special, movement_flags) . = ..() - if(!.) - return + 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, movement_flags) +/obj/item/organ/external/antennae/mob_remove(mob/living/carbon/organ_owner, special, movement_flags) . = ..() - if(organ_owner) - UnregisterSignal(organ_owner, list(COMSIG_HUMAN_BURNING, COMSIG_LIVING_POST_FULLY_HEAL)) + + UnregisterSignal(organ_owner, list(COMSIG_HUMAN_BURNING, COMSIG_LIVING_POST_FULLY_HEAL)) ///check if our antennae can burn off ;_; /obj/item/organ/external/antennae/proc/try_burn_antennae(mob/living/carbon/human/human) diff --git a/code/modules/surgery/organs/external/restyling.dm b/code/modules/surgery/organs/external/restyling.dm index 7d6be1b6d58..f862d9e9c08 100644 --- a/code/modules/surgery/organs/external/restyling.dm +++ b/code/modules/surgery/organs/external/restyling.dm @@ -1,7 +1,7 @@ -//Contains a bunch of procs for different types, but in the end it just lets you restyle external_organs so thats why its here +//Contains a bunch of procs for different types, but in the end it just lets you restyle the bodypart overlay so thats why its here ///Helper proc to fetch a list of styles a player might want to restyle their features into during the round : returns list("Cabbage" = /datum/sprite_accessory/cabbage) -/obj/item/organ/external/proc/get_valid_restyles() +/obj/item/organ/proc/get_valid_restyles() var/list/valid_restyles valid_restyles = list() @@ -31,18 +31,18 @@ ///Asks the external organs inside the limb if they can restyle /obj/item/bodypart/proc/attempt_feature_restyle(atom/source, mob/living/trimmer, atom/movable/original_target, body_zone, restyle_type, style_speed) var/list/valid_features = list() - for(var/obj/item/organ/external/feature in contents) + for(var/obj/item/organ/feature in contents) if(feature.restyle_flags & restyle_type) valid_features.Add(feature) - var/obj/item/organ/external/target_organ + var/obj/item/organ/target_organ switch(LAZYLEN(valid_features)) if(1) target_organ = valid_features[1] if(2 to INFINITY) var/choose_options = list() var/name_to_organ = list() //literally so I dont have to loop again after someones made their choice - for(var/obj/item/organ/external/organ_choice as anything in valid_features) + for(var/obj/item/organ/organ_choice as anything in valid_features) choose_options[organ_choice.name] = image(organ_choice) name_to_organ[organ_choice.name] = organ_choice var/picked_option = show_radial_menu(trimmer, original_target, choose_options, radius = 38, require_near = TRUE) @@ -57,7 +57,7 @@ target_organ.attempt_feature_restyle(source, trimmer, original_target, body_zone, restyle_type, style_speed) ///Invoke async so we dont break signals -/obj/item/organ/external/proc/on_attempt_feature_restyle(atom/source, mob/living/trimmer, atom/movable/original_target, body_zone, restyle_type, style_speed) +/obj/item/organ/proc/on_attempt_feature_restyle(atom/source, mob/living/trimmer, atom/movable/original_target, body_zone, restyle_type, style_speed) SIGNAL_HANDLER if(restyle_flags & restyle_type) @@ -66,7 +66,7 @@ to_chat(trimmer, span_warning("This tool is incompatible with the [src.name]!")) ///Restyles the external organ from a list of valid options -/obj/item/organ/external/proc/attempt_feature_restyle(atom/source, mob/living/trimmer, atom/movable/original_target, body_zone, restyle_type, style_speed) +/obj/item/organ/proc/attempt_feature_restyle(atom/source, mob/living/trimmer, atom/movable/original_target, body_zone, restyle_type, style_speed) var/list/restyles = get_valid_restyles() var/new_style = tgui_input_list(trimmer, "Select a new style", "Grooming", restyles) @@ -80,5 +80,4 @@ span_notice("You successfully change [original_target == trimmer ? "your" : original_target.name + "'s"] [name].") ) - simple_change_sprite(restyles[new_style]) //turn name to type and pass it on diff --git a/code/modules/surgery/organs/external/spines.dm b/code/modules/surgery/organs/external/spines.dm index 86bff9a7689..214c58df099 100644 --- a/code/modules/surgery/organs/external/spines.dm +++ b/code/modules/surgery/organs/external/spines.dm @@ -14,13 +14,13 @@ bodypart_overlay = /datum/bodypart_overlay/mutant/spines -/obj/item/organ/external/spines/Insert(mob/living/carbon/receiver, special, movement_flags) +/obj/item/organ/external/spines/mob_insert(mob/living/carbon/receiver, special, movement_flags) // If we have a tail, attempt to add a tail spines overlay var/obj/item/organ/external/tail/our_tail = receiver.get_organ_slot(ORGAN_SLOT_EXTERNAL_TAIL) our_tail?.try_insert_tail_spines(our_tail.bodypart_owner) return ..() -/obj/item/organ/external/spines/Remove(mob/living/carbon/organ_owner, special, movement_flags) +/obj/item/organ/external/spines/mob_remove(mob/living/carbon/organ_owner, special, movement_flags) // If we have a tail, remove any tail spines overlay var/obj/item/organ/external/tail/our_tail = organ_owner.get_organ_slot(ORGAN_SLOT_EXTERNAL_TAIL) our_tail?.remove_tail_spines(our_tail.bodypart_owner) diff --git a/code/modules/surgery/organs/external/tails.dm b/code/modules/surgery/organs/external/tails.dm index 1a52bbe56cd..cae83153bdc 100644 --- a/code/modules/surgery/organs/external/tails.dm +++ b/code/modules/surgery/organs/external/tails.dm @@ -20,7 +20,7 @@ ///The overlay for tail spines, if any var/datum/bodypart_overlay/mutant/tail_spines/tail_spines_overlay -/obj/item/organ/external/tail/Insert(mob/living/carbon/receiver, special, movement_flags) +/obj/item/organ/external/tail/mob_insert(mob/living/carbon/receiver, special, movement_flags) . = ..() if(.) receiver.clear_mood_event("tail_lost") @@ -34,7 +34,7 @@ // If it's not your tail AND of different species, we are horrified if(IS_WEAKREF_OF(receiver, original_owner)) receiver.add_mood_event("tail_regained", /datum/mood_event/tail_regained_right) - else if(type in receiver.dna.species.external_organs) + else if(type in receiver.dna.species.mutant_organs) receiver.add_mood_event("tail_regained", /datum/mood_event/tail_regained_species) else receiver.add_mood_event("tail_regained", /datum/mood_event/tail_regained_wrong) @@ -83,7 +83,7 @@ organ_owner.clear_mood_event("tail_regained") - if(type in organ_owner.dna.species.external_organs) + if(type in organ_owner.dna.species.mutant_organs) organ_owner.add_mood_event("tail_lost", /datum/mood_event/tail_lost) organ_owner.add_mood_event("tail_balance_lost", /datum/mood_event/tail_balance_lost) diff --git a/code/modules/surgery/organs/external/wings/functional_wings.dm b/code/modules/surgery/organs/external/wings/functional_wings.dm index f4a5a23bf69..e1f364f5475 100644 --- a/code/modules/surgery/organs/external/wings/functional_wings.dm +++ b/code/modules/surgery/organs/external/wings/functional_wings.dm @@ -35,15 +35,14 @@ QDEL_NULL(fly) return ..() -/obj/item/organ/external/wings/functional/Insert(mob/living/carbon/receiver, special, movement_flags) +/obj/item/organ/external/wings/functional/mob_insert(mob/living/carbon/receiver, special, movement_flags) . = ..() - if(!.) - return + if(QDELETED(fly)) fly = new fly.Grant(receiver) -/obj/item/organ/external/wings/functional/Remove(mob/living/carbon/organ_owner, special, movement_flags) +/obj/item/organ/external/wings/functional/mob_remove(mob/living/carbon/organ_owner, special, movement_flags) . = ..() fly?.Remove(organ_owner) if(wings_open) diff --git a/code/modules/surgery/organs/internal/appendix/_appendix.dm b/code/modules/surgery/organs/internal/appendix/_appendix.dm index 83ed8da84ac..169495bccaa 100644 --- a/code/modules/surgery/organs/internal/appendix/_appendix.dm +++ b/code/modules/surgery/organs/internal/appendix/_appendix.dm @@ -6,7 +6,7 @@ name = "appendix" icon_state = "appendix" base_icon_state = "appendix" - visual = FALSE + zone = BODY_ZONE_PRECISE_GROIN slot = ORGAN_SLOT_APPENDIX food_reagents = list(/datum/reagent/consumable/nutriment = 5, /datum/reagent/toxin/bad_food = 5) diff --git a/code/modules/surgery/organs/internal/cyberimp/augments_eyes.dm b/code/modules/surgery/organs/internal/cyberimp/augments_eyes.dm index d4c4b57d75f..aa67fe0c08d 100644 --- a/code/modules/surgery/organs/internal/cyberimp/augments_eyes.dm +++ b/code/modules/surgery/organs/internal/cyberimp/augments_eyes.dm @@ -27,14 +27,13 @@ eye_owner.remove_traits(HUD_traits, ORGAN_TRAIT) balloon_alert(eye_owner, "hud enabled") -/obj/item/organ/internal/cyberimp/eyes/hud/Insert(mob/living/carbon/eye_owner, special = FALSE, movement_flags) +/obj/item/organ/internal/cyberimp/eyes/hud/mob_insert(mob/living/carbon/eye_owner, special = FALSE, movement_flags) . = ..() - if(!.) - return + eye_owner.add_traits(HUD_traits, ORGAN_TRAIT) toggled_on = TRUE -/obj/item/organ/internal/cyberimp/eyes/hud/Remove(mob/living/carbon/eye_owner, special, movement_flags) +/obj/item/organ/internal/cyberimp/eyes/hud/mob_remove(mob/living/carbon/eye_owner, special, movement_flags) . = ..() eye_owner.remove_traits(HUD_traits, ORGAN_TRAIT) toggled_on = FALSE diff --git a/code/modules/surgery/organs/internal/cyberimp/augments_internal.dm b/code/modules/surgery/organs/internal/cyberimp/augments_internal.dm index f71e29631b3..09955f45b03 100644 --- a/code/modules/surgery/organs/internal/cyberimp/augments_internal.dm +++ b/code/modules/surgery/organs/internal/cyberimp/augments_internal.dm @@ -2,7 +2,7 @@ /obj/item/organ/internal/cyberimp name = "cybernetic implant" desc = "A state-of-the-art implant that improves a baseline's functionality." - visual = FALSE + organ_flags = ORGAN_ROBOTIC failing_desc = "seems to be broken." var/implant_color = COLOR_WHITE diff --git a/code/modules/surgery/organs/internal/ears/_ears.dm b/code/modules/surgery/organs/internal/ears/_ears.dm index eba24086cca..53b95b54d9b 100644 --- a/code/modules/surgery/organs/internal/ears/_ears.dm +++ b/code/modules/surgery/organs/internal/ears/_ears.dm @@ -4,7 +4,6 @@ desc = "There are three parts to the ear. Inner, middle and outer. Only one of these parts should be normally visible." zone = BODY_ZONE_HEAD slot = ORGAN_SLOT_EARS - visual = FALSE gender = PLURAL healing_factor = STANDARD_ORGAN_HEALING @@ -136,28 +135,34 @@ icon_state = "kitty" visual = TRUE damage_multiplier = 2 - // Keeps track of which cat ears sprite is associated with this. - var/variant = "Cat" -/obj/item/organ/internal/ears/cat/Initialize(mapload, variant_pref) - . = ..() - if(variant_pref) - variant = variant_pref + preference = "feature_human_ears" -/obj/item/organ/internal/ears/cat/on_mob_insert(mob/living/carbon/human/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"] = variant - ear_owner.dna.update_uf_block(DNA_EARS_BLOCK) - ear_owner.update_body() + dna_block = DNA_EARS_BLOCK -/obj/item/organ/internal/ears/cat/on_mob_remove(mob/living/carbon/human/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() + bodypart_overlay = /datum/bodypart_overlay/mutant/cat_ears + +/// Bodypart overlay for the horrible cat ears +/datum/bodypart_overlay/mutant/cat_ears + layers = EXTERNAL_FRONT | EXTERNAL_ADJACENT + color_source = ORGAN_COLOR_HAIR + feature_key = "ears" + + /// We dont color the inner part, which is the front layer + var/colorless_layer = EXTERNAL_FRONT + +/datum/bodypart_overlay/mutant/cat_ears/get_global_feature_list() + return SSaccessories.ears_list + +/datum/bodypart_overlay/mutant/cat_ears/can_draw_on_bodypart(mob/living/carbon/human/human) + if((human.head?.flags_inv & HIDEHAIR) || (human.wear_mask?.flags_inv & HIDEHAIR)) + return FALSE + return TRUE + +/datum/bodypart_overlay/mutant/cat_ears/color_image(image/overlay, draw_layer, obj/item/bodypart/limb) + if(draw_layer != bitflag_to_layer(colorless_layer)) + return ..() + return overlay /obj/item/organ/internal/ears/penguin name = "penguin ears" diff --git a/code/modules/surgery/organs/internal/eyes/_eyes.dm b/code/modules/surgery/organs/internal/eyes/_eyes.dm index 9c6b10d26d6..4f94167e948 100644 --- a/code/modules/surgery/organs/internal/eyes/_eyes.dm +++ b/code/modules/surgery/organs/internal/eyes/_eyes.dm @@ -52,21 +52,18 @@ /// Native FOV that will be applied if a config is enabled var/native_fov = FOV_90_DEGREES -/obj/item/organ/internal/eyes/Insert(mob/living/carbon/eye_recipient, special = FALSE, movement_flags = DELETE_IF_REPLACED) +/obj/item/organ/internal/eyes/mob_insert(mob/living/carbon/receiver, special, movement_flags) // If we don't do this before everything else, heterochromia will be reset leading to eye_color_right no longer being accurate - if(ishuman(eye_recipient)) - var/mob/living/carbon/human/human_recipient = eye_recipient + if(ishuman(receiver)) + var/mob/living/carbon/human/human_recipient = receiver old_eye_color_left = human_recipient.eye_color_left old_eye_color_right = human_recipient.eye_color_right . = ..() - if(!.) - return - - eye_recipient.cure_blind(NO_EYES) + receiver.cure_blind(NO_EYES) apply_damaged_eye_effects() - refresh(eye_recipient, call_update = TRUE) + refresh(receiver, call_update = TRUE) /// Refreshes the visuals of the eyes /// If call_update is TRUE, we also will call update_body @@ -94,31 +91,32 @@ if(call_update) affected_human.update_body() -/obj/item/organ/internal/eyes/Remove(mob/living/carbon/eye_owner, special, movement_flags) +/obj/item/organ/internal/eyes/mob_remove(mob/living/carbon/organ_owner, special, movement_flags) . = ..() - if(ishuman(eye_owner)) - var/mob/living/carbon/human/human_owner = eye_owner + + if(ishuman(organ_owner)) + var/mob/living/carbon/human/human_owner = organ_owner if(initial(eye_color_left)) human_owner.eye_color_left = old_eye_color_left if(initial(eye_color_right)) human_owner.eye_color_right = old_eye_color_right if(native_fov) - eye_owner.remove_fov_trait(type) + organ_owner.remove_fov_trait(type) if(!special) human_owner.update_body() // Cure blindness from eye damage - eye_owner.cure_blind(EYE_DAMAGE) - eye_owner.cure_nearsighted(EYE_DAMAGE) + organ_owner.cure_blind(EYE_DAMAGE) + organ_owner.cure_nearsighted(EYE_DAMAGE) // Eye blind and temp blind go to, even if this is a bit of cheesy way to clear blindness - eye_owner.remove_status_effect(/datum/status_effect/eye_blur) - eye_owner.remove_status_effect(/datum/status_effect/temporary_blindness) + organ_owner.remove_status_effect(/datum/status_effect/eye_blur) + organ_owner.remove_status_effect(/datum/status_effect/temporary_blindness) // Then become blind anyways (if not special) if(!special) - eye_owner.become_blind(NO_EYES) + organ_owner.become_blind(NO_EYES) - eye_owner.update_tint() - eye_owner.update_sight() + organ_owner.update_tint() + organ_owner.update_sight() #define OFFSET_X 1 #define OFFSET_Y 2 @@ -428,7 +426,7 @@ deactivate(close_ui = TRUE) /// Set the initial color of the eyes on insert to be the mob's previous eye color. -/obj/item/organ/internal/eyes/robotic/glow/Insert(mob/living/carbon/eye_recipient, special = FALSE, movement_flags = DELETE_IF_REPLACED) +/obj/item/organ/internal/eyes/robotic/glow/mob_insert(mob/living/carbon/eye_recipient, special = FALSE, movement_flags = DELETE_IF_REPLACED) . = ..() left_eye_color_string = old_eye_color_left right_eye_color_string = old_eye_color_right diff --git a/code/modules/surgery/organs/internal/heart/_heart.dm b/code/modules/surgery/organs/internal/heart/_heart.dm index b0fac316ad3..d52e483da38 100644 --- a/code/modules/surgery/organs/internal/heart/_heart.dm +++ b/code/modules/surgery/organs/internal/heart/_heart.dm @@ -3,7 +3,7 @@ desc = "I feel bad for the heartless bastard who lost this." icon_state = "heart-on" base_icon_state = "heart" - visual = FALSE + zone = BODY_ZONE_CHEST slot = ORGAN_SLOT_HEART item_flags = NO_BLOOD_ON_ITEM diff --git a/code/modules/surgery/organs/internal/heart/heart_ethereal.dm b/code/modules/surgery/organs/internal/heart/heart_ethereal.dm index bd30318a722..3e853a965b1 100644 --- a/code/modules/surgery/organs/internal/heart/heart_ethereal.dm +++ b/code/modules/surgery/organs/internal/heart/heart_ethereal.dm @@ -21,15 +21,14 @@ add_atom_colour(ethereal_color, FIXED_COLOUR_PRIORITY) update_appearance() -/obj/item/organ/internal/heart/ethereal/Insert(mob/living/carbon/heart_owner, special = FALSE, movement_flags) +/obj/item/organ/internal/heart/ethereal/mob_insert(mob/living/carbon/heart_owner, special = FALSE, movement_flags) . = ..() - if(!.) - return + RegisterSignal(heart_owner, COMSIG_MOB_STATCHANGE, PROC_REF(on_stat_change)) RegisterSignal(heart_owner, COMSIG_LIVING_POST_FULLY_HEAL, PROC_REF(on_owner_fully_heal)) RegisterSignal(heart_owner, COMSIG_QDELETING, PROC_REF(owner_deleted)) -/obj/item/organ/internal/heart/ethereal/Remove(mob/living/carbon/heart_owner, special, movement_flags) +/obj/item/organ/internal/heart/ethereal/mob_remove(mob/living/carbon/heart_owner, special, movement_flags) UnregisterSignal(heart_owner, list(COMSIG_MOB_STATCHANGE, COMSIG_LIVING_POST_FULLY_HEAL, COMSIG_QDELETING)) REMOVE_TRAIT(heart_owner, TRAIT_CORPSELOCKED, SPECIES_TRAIT) stop_crystalization_process(heart_owner) diff --git a/code/modules/surgery/organs/internal/liver/_liver.dm b/code/modules/surgery/organs/internal/liver/_liver.dm index ef21595faf0..3933a9efa59 100755 --- a/code/modules/surgery/organs/internal/liver/_liver.dm +++ b/code/modules/surgery/organs/internal/liver/_liver.dm @@ -7,7 +7,7 @@ name = "liver" desc = "Pairing suggestion: chianti and fava beans." icon_state = "liver" - visual = FALSE + w_class = WEIGHT_CLASS_SMALL zone = BODY_ZONE_CHEST slot = ORGAN_SLOT_LIVER diff --git a/code/modules/surgery/organs/internal/lungs/_lungs.dm b/code/modules/surgery/organs/internal/lungs/_lungs.dm index 47078bfc803..9b38803dd77 100644 --- a/code/modules/surgery/organs/internal/lungs/_lungs.dm +++ b/code/modules/surgery/organs/internal/lungs/_lungs.dm @@ -1,7 +1,7 @@ /obj/item/organ/internal/lungs name = "lungs" icon_state = "lungs" - visual = FALSE + zone = BODY_ZONE_CHEST slot = ORGAN_SLOT_LUNGS gender = PLURAL @@ -154,17 +154,16 @@ add_gas_reaction(/datum/gas/zauker, while_present = PROC_REF(too_much_zauker)) ///Simply exists so that you don't keep any alerts from your previous lack of lungs. -/obj/item/organ/internal/lungs/Insert(mob/living/carbon/receiver, special = FALSE, movement_flags) +/obj/item/organ/internal/lungs/mob_insert(mob/living/carbon/receiver, special = FALSE, movement_flags) . = ..() - if(!.) - return . + receiver.clear_alert(ALERT_NOT_ENOUGH_OXYGEN) receiver.clear_alert(ALERT_NOT_ENOUGH_CO2) receiver.clear_alert(ALERT_NOT_ENOUGH_NITRO) receiver.clear_alert(ALERT_NOT_ENOUGH_PLASMA) receiver.clear_alert(ALERT_NOT_ENOUGH_N2O) -/obj/item/organ/internal/lungs/Remove(mob/living/carbon/organ_owner, special, movement_flags) +/obj/item/organ/internal/lungs/mob_remove(mob/living/carbon/organ_owner, special, movement_flags) . = ..() // This is very "manual" I realize, but it's useful to ensure cleanup for gases we're removing happens // Avoids stuck alerts and such diff --git a/code/modules/surgery/organs/internal/stomach/_stomach.dm b/code/modules/surgery/organs/internal/stomach/_stomach.dm index f896493f33b..4f1edd1542d 100644 --- a/code/modules/surgery/organs/internal/stomach/_stomach.dm +++ b/code/modules/surgery/organs/internal/stomach/_stomach.dm @@ -5,7 +5,7 @@ name = "stomach" desc = "Onaka ga suite imasu." icon_state = "stomach" - visual = FALSE + w_class = WEIGHT_CLASS_SMALL zone = BODY_ZONE_CHEST slot = ORGAN_SLOT_STOMACH @@ -246,11 +246,11 @@ disgusted.throw_alert(ALERT_DISGUST, /atom/movable/screen/alert/disgusted) disgusted.add_mood_event("disgust", /datum/mood_event/disgusted) -/obj/item/organ/internal/stomach/Insert(mob/living/carbon/receiver, special, movement_flags) +/obj/item/organ/internal/stomach/mob_insert(mob/living/carbon/receiver, special, movement_flags) . = ..() receiver.hud_used?.hunger?.update_appearance() -/obj/item/organ/internal/stomach/Remove(mob/living/carbon/stomach_owner, special, movement_flags) +/obj/item/organ/internal/stomach/mob_remove(mob/living/carbon/stomach_owner, special, movement_flags) if(ishuman(stomach_owner)) var/mob/living/carbon/human/human_owner = owner human_owner.clear_alert(ALERT_DISGUST) diff --git a/code/modules/surgery/organs/internal/tongue/_tongue.dm b/code/modules/surgery/organs/internal/tongue/_tongue.dm index fa034acf22b..7ddcfb6164f 100644 --- a/code/modules/surgery/organs/internal/tongue/_tongue.dm +++ b/code/modules/surgery/organs/internal/tongue/_tongue.dm @@ -2,7 +2,7 @@ name = "tongue" desc = "A fleshy muscle mostly used for lying." icon_state = "tongue" - visual = FALSE + zone = BODY_ZONE_PRECISE_MOUTH slot = ORGAN_SLOT_TONGUE attack_verb_continuous = list("licks", "slobbers", "slaps", "frenches", "tongues") @@ -124,30 +124,30 @@ food_taste_reaction = FOOD_LIKED return food_taste_reaction -/obj/item/organ/internal/tongue/Insert(mob/living/carbon/tongue_owner, special = FALSE, movement_flags) +/obj/item/organ/internal/tongue/mob_insert(mob/living/carbon/receiver, special, movement_flags) . = ..() - if(!.) - return + if(modifies_speech) - RegisterSignal(tongue_owner, COMSIG_MOB_SAY, PROC_REF(handle_speech)) - tongue_owner.voice_filter = voice_filter + RegisterSignal(receiver, COMSIG_MOB_SAY, PROC_REF(handle_speech)) + receiver.voice_filter = voice_filter /* This could be slightly simpler, by making the removal of the * NO_TONGUE_TRAIT conditional on the tongue's `sense_of_taste`, but * then you can distinguish between ageusia from no tongue, and * ageusia from having a non-tasting tongue. */ - REMOVE_TRAIT(tongue_owner, TRAIT_AGEUSIA, NO_TONGUE_TRAIT) + REMOVE_TRAIT(receiver, TRAIT_AGEUSIA, NO_TONGUE_TRAIT) apply_tongue_effects() -/obj/item/organ/internal/tongue/Remove(mob/living/carbon/tongue_owner, special, movement_flags) +/obj/item/organ/internal/tongue/mob_remove(mob/living/carbon/organ_owner, special, movement_flags) . = ..() + temp_say_mod = "" - UnregisterSignal(tongue_owner, COMSIG_MOB_SAY) - REMOVE_TRAIT(tongue_owner, TRAIT_SPEAKS_CLEARLY, SPEAKING_FROM_TONGUE) - REMOVE_TRAIT(tongue_owner, TRAIT_AGEUSIA, ORGAN_TRAIT) + UnregisterSignal(organ_owner, COMSIG_MOB_SAY) + REMOVE_TRAIT(organ_owner, TRAIT_SPEAKS_CLEARLY, SPEAKING_FROM_TONGUE) + REMOVE_TRAIT(organ_owner, TRAIT_AGEUSIA, ORGAN_TRAIT) // Carbons by default start with NO_TONGUE_TRAIT caused TRAIT_AGEUSIA - ADD_TRAIT(tongue_owner, TRAIT_AGEUSIA, NO_TONGUE_TRAIT) - tongue_owner.voice_filter = initial(tongue_owner.voice_filter) + ADD_TRAIT(organ_owner, TRAIT_AGEUSIA, NO_TONGUE_TRAIT) + organ_owner.voice_filter = initial(organ_owner.voice_filter) /obj/item/organ/internal/tongue/apply_organ_damage(damage_amount, maximum = maxHealth, required_organ_flag) . = ..() diff --git a/code/modules/surgery/organs/internal/vocal_cords/_vocal_cords.dm b/code/modules/surgery/organs/internal/vocal_cords/_vocal_cords.dm index 9183c7eb809..f2d9abae416 100644 --- a/code/modules/surgery/organs/internal/vocal_cords/_vocal_cords.dm +++ b/code/modules/surgery/organs/internal/vocal_cords/_vocal_cords.dm @@ -1,7 +1,6 @@ /obj/item/organ/internal/vocal_cords //organs that are activated through speech with the :x/MODE_KEY_VOCALCORDS channel name = "vocal cords" icon_state = "appendix" - visual = FALSE zone = BODY_ZONE_PRECISE_MOUTH slot = ORGAN_SLOT_VOICE gender = PLURAL @@ -87,7 +86,6 @@ next_command = world.time + (cooldown * cooldown_mod) /obj/item/organ/internal/adamantine_resonator - visual = FALSE name = "adamantine resonator" desc = "Fragments of adamantine exist in all golems, stemming from their origins as purely magical constructs. These are used to \"hear\" messages from their leaders." zone = BODY_ZONE_HEAD diff --git a/code/modules/surgery/organs/organ_movement.dm b/code/modules/surgery/organs/organ_movement.dm index ff9f753ce18..010daa3fd24 100644 --- a/code/modules/surgery/organs/organ_movement.dm +++ b/code/modules/surgery/organs/organ_movement.dm @@ -18,7 +18,8 @@ mob_insert(receiver, special, movement_flags) bodypart_insert(limb_owner = receiver, movement_flags = movement_flags) - return TRUE + if(!special) + receiver.update_body_parts() /* * Remove the organ from the select mob. @@ -32,6 +33,9 @@ mob_remove(organ_owner, special, movement_flags) bodypart_remove(limb_owner = organ_owner, movement_flags = movement_flags) + if(!special) + organ_owner.update_body_parts() + /* * Insert the organ into the select mob. * @@ -65,6 +69,11 @@ wash(CLEAN_TYPE_BLOOD) organ_flags &= ~ORGAN_VIRGIN + if(external_bodytypes) + receiver.synchronize_bodytypes() + if(external_bodyshapes) + receiver.synchronize_bodyshapes() + receiver.organs |= src receiver.organs_slot[slot] = src owner = receiver @@ -120,6 +129,9 @@ ADD_TRAIT(src, TRAIT_NODROP, ORGAN_INSIDE_BODY_TRAIT) interaction_flags_item &= ~INTERACT_ITEM_ATTACK_HAND_PICKUP + if(bodypart_overlay) + limb.add_bodypart_overlay(bodypart_overlay) + /* * Remove the organ from the select mob. * @@ -163,6 +175,9 @@ SEND_SIGNAL(organ_owner, COMSIG_CARBON_LOSE_ORGAN, src, special) ADD_TRAIT(src, TRAIT_USED_ORGAN, ORGAN_TRAIT) + organ_owner.synchronize_bodytypes() + organ_owner.synchronize_bodyshapes() + var/list/diseases = organ_owner.get_static_viruses() if(!LAZYLEN(diseases)) return @@ -211,6 +226,16 @@ REMOVE_TRAIT(src, TRAIT_NODROP, ORGAN_INSIDE_BODY_TRAIT) interaction_flags_item |= INTERACT_ITEM_ATTACK_HAND_PICKUP + if(!bodypart_overlay) + return + + limb.remove_bodypart_overlay(bodypart_overlay) + + if(use_mob_sprite_as_obj_sprite) + update_appearance(UPDATE_OVERLAYS) + + color = bodypart_overlay.draw_color // so a pink felinid doesn't drop a gray tail + /// In space station videogame, nothing is sacred. If somehow an organ is removed unexpectedly, handle it properly /obj/item/organ/proc/forced_removal() SIGNAL_HANDLER diff --git a/code/modules/unit_tests/organ_bodypart_shuffle.dm b/code/modules/unit_tests/organ_bodypart_shuffle.dm index 842dd1c6c13..11c0bcd71be 100644 --- a/code/modules/unit_tests/organ_bodypart_shuffle.dm +++ b/code/modules/unit_tests/organ_bodypart_shuffle.dm @@ -2,7 +2,7 @@ /datum/unit_test/organ_bodypart_shuffle /datum/unit_test/organ_bodypart_shuffle/Run() - var/mob/living/carbon/human/hollow_boy = allocate(/mob/living/carbon/human/consistent) + var/mob/living/carbon/human/hollow_boy = allocate(/mob/living/carbon/human/consistent) //freshly filled with wet insides // Test if organs are all properly updating when forcefully removed var/list/removed_organs = list() @@ -30,5 +30,3 @@ continue TEST_ASSERT(organ in hollow_boy.organs, "Organ '[organ.name] was put in an empty bodypart that replaced a humans, but the organ did not come with.") - // Test if bodyparts are all properly updating when forcefully removed - hollow_boy = allocate(/mob/living/carbon/human/consistent) //freshly filled with wet insides diff --git a/code/modules/unit_tests/organ_set_bonus.dm b/code/modules/unit_tests/organ_set_bonus.dm index 967803e223f..1231ddd5c66 100644 --- a/code/modules/unit_tests/organ_set_bonus.dm +++ b/code/modules/unit_tests/organ_set_bonus.dm @@ -30,7 +30,7 @@ // Attempt to insert entire list of mutant organs for the given infusion_entry. for(var/obj/item/organ/organ as anything in output_organs) organ = new organ() - TEST_ASSERT(organ.Insert(lab_rat, special = TRUE, movement_flags = DELETE_IF_REPLACED), "The organ `[organ.type]` for `[infuser_entry.type]` was not inserted in the mob when expected, Insert() returned falsy when TRUE was expected.") + organ.Insert(lab_rat, special = TRUE, movement_flags = DELETE_IF_REPLACED) inserted_organs += organ // Search for added Status Effect. diff --git a/code/modules/wiremod/shell/brain_computer_interface.dm b/code/modules/wiremod/shell/brain_computer_interface.dm index 67a3a41a488..7adefcaa5ed 100644 --- a/code/modules/wiremod/shell/brain_computer_interface.dm +++ b/code/modules/wiremod/shell/brain_computer_interface.dm @@ -3,7 +3,6 @@ desc = "An implant that can be placed in a user's head to control circuits using their brain." icon = 'icons/obj/science/circuits.dmi' icon_state = "bci" - visual = FALSE zone = BODY_ZONE_HEAD w_class = WEIGHT_CLASS_TINY diff --git a/icons/mob/human/cat_features.dmi b/icons/mob/human/cat_features.dmi index 6e7fd024fee1d2b32e9e8feac22fbd88cb568dc4..d2d67fdd9b32e40f5b047be2f169431c1c6f8b53 100644 GIT binary patch literal 1756 zcmYjR2~ZLU7shrm!z|15S0hvLzIDwcL`-)@P4HMEOgzDB#rr}{vocZpHSfEvG}OFJ z|H%7V!=v&@P0YN_G{{|9R6u@rXWIXNGjHC!_r7`eJPO+3Cozy52mkz^%(crtY!q11rf0q}3QT)~lLimR% z_tf4U#oMHp#Ry4fz^b|&(8(0+6JslM20I_!-3hu z)$$cCMn^U=Db)HnLystGZAwp#t2pm1&8V1$XbN0fY=3SUVUGoEtbW!yTR~8^fJ0SK(!Tjd{cG8e*A^2z&MvHtz7E-U z>`(~GP7)!ZptOY6T?Nd${H}4vkZ$Lt?DI82O;e0?PI)@`0q7@S)T7*}J!% ze;6)#B(Jx$wesg^_q#vE>}L66u$zQGSX|MVdoyk9r~9HCBmT#aHu#MyQRZKa8bx^QM*(D~h^jZ-mje)xh&;SD* z>xUH%Xgfh01^vIUcq5JXc0t{aM%8j=xe@G+90moA8a0!?n!pM!GpOe0`WBk72T?)o z6ciAL8Jq9H6z+9hM=diTo8%EA`)hDR&W9?t-VuJMy{#-b$CxV7b}-4=?;)bGsAXT0 zfU?;sM6T1wf@2u$mq7x@A?g`S=3z{x_7ZI-XAc`Rqtg5O&IfobnBG6lD#{rQot&d= zT{iR|V2Klb#*uGy+!e+CSFb*_ebV8{lb?&HeW{7Cd-eFZ`Wwk#*|_(8%)-3GC~;+if;8<( z^ve2L6sqEZv6T)Bejylms|Hs?nRf9JAG5{z=K$8dPf0~N z{o?uLqu~vh?3UniM)l0ZIB4972@6wi{ZW2dk&VL&aa=}BeX^1M-;ahZHy{ZqcT^u{ zJP66(@G*uMDSOsXhg3S=RC;6j^bM~NAfvy@mFvq433EqIxN2&{ zO?XDruOu*MO^z9|jV#UAz_(Ge6q%3lr&|K$9#v)T%D5M`;YV=4pS;U0;H!essY2hT z7V@u?1t-2&I0-6#7dvmXZ=yA5QB>Y2jSjZ~-L&)TRzmYRz_J82dE#Ai-?B{RXZpmpM6^eE zEzpJE0&8~@=KjRXzAGlZ^oAK1t4VmzS3*~8hfS+|t0(DNZ%An9OU0!_0#~45W^Gb` zqd@e?GWs$9X_cTNurqBcaTl+>lJ6^q_f z;WZEJ`3?&ZLXf6s(e$|ohY35!p{39za{)6c!7fU6%Y9D>()(V${A#fzB@;i(uw>6v zMb4)?s*P%8T*=pI`sa211o#0&pPJ3rft~+4S6&G@mBZ4yqULBvz@G(ejua po3bHoeYG?Hqh1vY_*Ykj_XL2z5um=RjrZS8BCQ>)>McEz{|kaUJ7fR= literal 1906 zcmV-&2aWiNP)e9O(v|Ej9Ct*!t6|6N^OtgNiSz`!VgG&ukO00DGTPE!Ct=GbNc008NFR9JLG zWpiV4X>fFDZ*Bkpc$}S-y$*sf6oqH{6b-D#__w+cP#MI;xRYv2xk)Lp6p^==Vxog2 z(x%I~$^CN9JwpQ=#``42(J?^}rfR4p-r~)CfxH%|F8X1;LSDgu7y3QanrPNbzswj> zb#@~<+nrD8#YY|mRU008xgsSqdN~S&@gYg034~eJ1jKY=*ftntIcbDlIFOPltCK!< zE#Vv_pJdxtN|_*?(X-L_(^?7l%C6^um|WZUdV43)$)^M1kj>snRgI)Bh(kIDue=g2 zghM!crz>!Kz#-XRp8&^cH2hI7Tm)xtig>F2000IJNkl!Ef6(6o;iSQGJY( z9ePyK9ePaaF4)mXTcF1PUGD+}_&i{TUA<+%_GeFaGMi5#pG3;G$z8kaVO)K_vd_89TqfnxRcm#z`*{2SviZE+l{}tKI)FR-CY-yCJk|d( zZUCiSiV9#{eO~q*^)o)MJ}=busPm7l&x@-z<_Z{FpO-(J?n~&9=LY})000000N4jH z`ux}|OJ8A2)}aoMN;bE&ET06XNMka%aCzK4WC7UunV0}7Ti*Aja@>iZO^dnE0weMR z;#hb9@}>!IU&;2q0KDy+VrZ~FKR5uTw;jgYesF?ezX2Fuf<=+Xjp#c#!C-_3na_dJ zU0+`SasYf;Qg8cYus$zSp!OYYwfBF6W+~cN?)IK>c|9sh?hdr}qVtFp(a`n)00000 z0001RqD(pY(1&c`0K3E?y*MP+tvA_8dW^QBvxRMg$l zosje`K*`WN+*#6ZQ~NAQYp}W2M{Jee4cE)P21dvstP37)D)91yY`2tYd3-5$8ZWH~ed#K%Cth4v<=l_Sc zZ}jG_F8lWY000000000GL)_vlBV!A)5xS7HEM2vSU0_K(03YMPgZ2Fw=XbT#16X=K z>y;L^IvXsmCh~b>*Tc_U3q+ZL?Zva{?g08b;F57W7snac-ah|H1|anLn1`?JTg089 z&>gS?a3vU&hu=5YgC4Uyj(y+6EpRk!0K`vBOaS(8OjLV&1cwGNg1#>=5Y`t000000 z@bc?+=jP3-T{lzgkIp~!He30R&px&5J}UM{=hJ19TrU)Zo7U@))2qX|6V6%Q;m}z- zfZ{;D006)XndkGC@;H7(W6Is;cD zO`g|!{i=s!uPnAZ4@JAme&6p}{?8pYJJM@ezSqMwd8>uWS<-p|aoeClVSRqQNz&Ve z*1`1$S$=n~dvxdd8+ahP-*0UGlI5?gR|^0D00000001yOjxDe6ogRAqwCSPOPn)&+ zo(Jf^#da6Rda(4AMPPp5xpscO>hHi~ktDYZ)ufRjC`9RMogYwL3d|3LrkJ|*{pR6? zDE+WDPy-0e53II30000000000001}{j-BAhFJCr4X>$UEZarU5farKa-{!1HlG_dw zSuDi)-rgkXdo{!HR(hP~e`wZblOK52J+c{^ALyJwk(fVa`91jo0000000000062A0 zYpKk)NFxB8QA))%_bELw~Ex8F!$@%Oj&{DI$B{aq*q z&3~9})))G!SmZx8YaZ3F;CsDYJoWdWTi@3wKzy#Z^M%qn|J(hM zEUsJE*Hu6Q>F~^6H!r6B3X9WP;%U}$*Q`cx0{{R3000000H#e3y?)yC(Cep7y2%d& zsHeqt=cA=vtJXuA&F_ibDs-w$*tjL-x}_XLQd sS<#UX8AzvI4*&oF0000005C590&#=9ZzMyq!T