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
🆑 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
/🆑

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>
This commit is contained in:
Time-Green
2024-08-20 16:33:34 +02:00
committed by GitHub
co-authored by Ghom
parent a92a5f8613
commit 095f7e3b70
68 changed files with 331 additions and 539 deletions
@@ -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)
+7 -8
View File
@@ -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
+2 -2
View File
@@ -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)
+3 -3
View File
@@ -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)
@@ -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)