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
parent a92a5f8613
commit 095f7e3b70
68 changed files with 331 additions and 539 deletions

View File

@@ -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

View File

@@ -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",

View File

@@ -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]

View File

@@ -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!")

View File

@@ -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)

View File

@@ -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)

View File

@@ -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

View File

@@ -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)
. = ..()

View File

@@ -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

View File

@@ -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))

View File

@@ -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()

View File

@@ -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)]")

View File

@@ -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

View File

@@ -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()

View File

@@ -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

View File

@@ -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]!"))

View File

@@ -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.

View File

@@ -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 <i>wrong</i>..."))
affected_mob.add_atom_colour("#1d2953", TEMPORARY_COLOUR_PRIORITY)

View File

@@ -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) \

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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

View File

@@ -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)

View File

@@ -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()

View File

@@ -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")

View File

@@ -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 ..()

View File

@@ -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)

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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()

View File

@@ -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

View File

@@ -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",

View File

@@ -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

View File

@@ -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))

View File

@@ -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,

View File

@@ -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

View File

@@ -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.

View File

@@ -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.

View File

@@ -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)

View File

@@ -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()

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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)

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

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)

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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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"

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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)
. = ..()

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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.

View File

@@ -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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@@ -6028,7 +6028,7 @@
#include "code\modules\surgery\organs\autosurgeon.dm"
#include "code\modules\surgery\organs\helpers.dm"
#include "code\modules\surgery\organs\organ_movement.dm"
#include "code\modules\surgery\organs\external\_external_organ.dm"
#include "code\modules\surgery\organs\external\_visual_organs.dm"
#include "code\modules\surgery\organs\external\restyling.dm"
#include "code\modules\surgery\organs\external\spines.dm"
#include "code\modules\surgery\organs\external\tails.dm"