diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm index 94ace816ac..d562755083 100644 --- a/code/__DEFINES/traits.dm +++ b/code/__DEFINES/traits.dm @@ -235,6 +235,8 @@ #define TRAIT_BEING_CARRIED "being_carried" #define TRAIT_GLASS_BONES "glass_bones" #define TRAIT_PAPER_SKIN "paper_skin" +//used because it's more reliable than checking for the component +#define TRAIT_DULLAHAN "dullahan" // mobility flag traits // IN THE FUTURE, IT WOULD BE NICE TO DO SOMETHING SIMILAR TO https://github.com/tgstation/tgstation/pull/48923/files (ofcourse not nearly the same because I have my.. thoughts on it) diff --git a/code/datums/accents.dm b/code/datums/accents.dm index d9e073b729..cfd6e873fa 100644 --- a/code/datums/accents.dm +++ b/code/datums/accents.dm @@ -94,10 +94,9 @@ /datum/accent/dullahan/modify_speech(list/speech_args, datum/source, mob/living/carbon/owner) if(owner) - if(isdullahan(owner)) - var/datum/species/dullahan/D = owner.dna.species - if(isobj(D.myhead.loc)) - var/obj/O = D.myhead.loc - O.say(speech_args[SPEECH_MESSAGE]) + var/datum/component/dullahan/dullahan = owner.GetComponent(/datum/component/dullahan) + if(dullahan) + if(dullahan.dullahan_head) + dullahan.dullahan_head.say(speech_args[SPEECH_MESSAGE]) speech_args[SPEECH_MESSAGE] = "" return speech_args diff --git a/code/datums/components/dullahan.dm b/code/datums/components/dullahan.dm index ee6d5beef0..a3e1335e6b 100644 --- a/code/datums/components/dullahan.dm +++ b/code/datums/components/dullahan.dm @@ -1,30 +1,69 @@ /datum/component/dullahan var/obj/item/dullahan_head/dullahan_head - /datum/component/dullahan/Initialize() + . = ..() + var/mob/living/carbon/human/H = parent if(!H) - return + return . + + ADD_TRAIT(H, TRAIT_DULLAHAN, "dullahan_component") dullahan_head = new(get_turf(H)) dullahan_head.name = "[H.name]'s head" dullahan_head.desc = "the decapitated head of [H.name]" dullahan_head.owner = H + RegisterSignal(H, COMSIG_LIVING_REGENERATE_LIMBS, .proc/unlist_head) // make sure the brain can't decay or fall out var/obj/item/organ/brain/B = H.getorganslot(ORGAN_SLOT_BRAIN) B.zone = "abstract" // it exists in the ethereal plain B.organ_flags = ORGAN_NO_SPOIL | ORGAN_NO_DISMEMBERMENT | ORGAN_VITAL + dullahan_head.B = B + + // the eyes get similar treatment + var/obj/item/organ/eyes/dullahan/new_eyes = new() + var/obj/item/organ/eyes/E = H.getorganslot(ORGAN_SLOT_EYES) + new_eyes.left_eye_color = E.left_eye_color + new_eyes.right_eye_color = E.right_eye_color + E.Remove() + qdel(E) + new_eyes.Insert(H) + + // make sure you handle the tongue correctly, too! + var/obj/item/organ/tongue/T = H.getorganslot(ORGAN_SLOT_TONGUE) + T.Remove() + qdel(T) + + var/obj/item/organ/tongue/dullahan/new_tongue = new() + new_tongue.Insert(H) // moving the brain's zone means we don't need the head to survive var/obj/item/bodypart/head/head = H.get_bodypart(BODY_ZONE_HEAD) head.drop_limb() qdel(head) + H.flags_1 &= ~(HEAR_1) + dullahan_head.update_appearance() +/datum/component/dullahan/proc/unlist_head(datum/source, noheal = FALSE, list/excluded_limbs) + excluded_limbs |= BODY_ZONE_HEAD // So we don't gib when regenerating limbs. + +/obj/item/organ/tongue/dullahan + zone = "abstract" + initial_accents = list(/datum/accent/dullahan) + organ_flags = ORGAN_NO_SPOIL | ORGAN_NO_DISMEMBERMENT + +/obj/item/organ/eyes/dullahan + name = "head vision" + desc = "An abstraction." + actions_types = list(/datum/action/item_action/organ_action/dullahan) + zone = "abstract" + tint = INFINITY // used to switch the vision perspective to the head on species_gain(). + /obj/item/dullahan_head name = "coders lament" desc = "you shouldn't be reading this" @@ -32,6 +71,12 @@ var/mob/living/carbon/human/owner // this is for keeping track of the overlays because you can't read the actual overlays list as it's a special byond var var/list/overlays_standing + var/obj/item/organ/brain/B + +/obj/item/dullahan_head/Destroy() + B.Remove() + B.forceMove(get_turf(src)) + . = ..() // allow the 'fake' head to relay speech back to the mob /obj/item/dullahan_head/Hear(message, atom/movable/speaker, datum/language/message_language, raw_message, radio_freq, list/spans, message_mode) @@ -43,7 +88,7 @@ if (owner.client?.prefs.chat_on_map && (owner.client.prefs.see_chat_non_mob || ismob(speaker))) owner.create_chat_message(speaker, message_language, raw_message, spans, message_mode) - owner.show_message(rendered, MSG_AUDIBLE) + owner.show_message(rendered, "") // update head sprite /obj/item/dullahan_head/proc/remove_head_overlays() @@ -59,13 +104,12 @@ // to do this without duplicating large amounts of code // it's best to regenerate the head, then remove it once we have the overlays we want owner.regenerate_limb(BODY_ZONE_HEAD, TRUE) // don't heal them - owner.regenerate_icons() // yes i know it's expensive but do you want me to rewrite our entire overlay system + owner.regenerate_icons(TRUE) // yes i know it's expensive but do you want me to rewrite our entire overlay system, also block recursive calls here by passing in TRUE (it wont go back to call update_appearance this way) var/obj/item/bodypart/head/head = owner.get_bodypart(BODY_ZONE_HEAD) add_overlay(head.get_limb_icon(FALSE, TRUE, TRUE)) for(var/overlay in owner.overlays_standing) if(istype(overlay, /mutable_appearance)) var/mutable_appearance/mutable = overlay - message_admins("category is [mutable.category] and icon is [mutable.icon_state]") if(mutable.category == "HEAD") add_head_overlay(mutable) else @@ -74,8 +118,40 @@ for(var/overlay2 in list_appearances) if(istype(overlay2, /mutable_appearance)) var/mutable_appearance/mutable = overlay2 - message_admins("category is [mutable.category] and icon is [mutable.icon_state]") if(mutable.category == "HEAD") add_head_overlay(mutable) - //head.drop_limb() - //qdel(head) \ No newline at end of file + head.drop_limb() + qdel(head) + +/obj/item/dullahan_head/proc/unlist_head(datum/source, noheal = FALSE, list/excluded_limbs) + excluded_limbs |= BODY_ZONE_HEAD // So we don't gib when regenerating limbs. + +/datum/action/item_action/organ_action/dullahan + name = "Toggle Perspective" + desc = "Switch between seeing normally from your head, or blindly from your body." + +/datum/action/item_action/organ_action/dullahan/Trigger() + . = ..() + var/mob/living/carbon/human/H = owner + var/obj/item/organ/eyes/E = owner.getorganslot(ORGAN_SLOT_EYES) + if(E) + if(E.tint) + E.tint = 0 + else + E.tint = INFINITY + + var/datum/component/dullahan/D = H.GetComponent(/datum/component/dullahan) + if(D) + D.update_vision_perspective() + +/datum/component/dullahan/proc/update_vision_perspective() + var/mob/living/carbon/human/H = parent + if(!H) + return . + var/obj/item/organ/eyes/eyes = H.getorganslot(ORGAN_SLOT_EYES) + if(eyes) + H.update_tint() + if(eyes.tint) + H.reset_perspective(H) + else + H.reset_perspective(dullahan_head) diff --git a/code/game/objects/structures/mirror.dm b/code/game/objects/structures/mirror.dm index a70d9d4678..9ff41c4404 100644 --- a/code/game/objects/structures/mirror.dm +++ b/code/game/objects/structures/mirror.dm @@ -41,7 +41,7 @@ if(new_style) H.hair_style = new_style - H.update_hair() + H.update_mutant_bodyparts() /obj/structure/mirror/examine_status(mob/user) if(broken) diff --git a/code/modules/mob/living/carbon/human/species.dm b/code/modules/mob/living/carbon/human/species.dm index c8cc58c916..b73f1da93a 100644 --- a/code/modules/mob/living/carbon/human/species.dm +++ b/code/modules/mob/living/carbon/human/species.dm @@ -345,7 +345,7 @@ GLOBAL_LIST_EMPTY(roundstart_race_names) var/obj/item/organ/stomach/stomach = C.getorganslot(ORGAN_SLOT_STOMACH) var/obj/item/organ/tail/tail = C.getorganslot(ORGAN_SLOT_TAIL) - var/should_have_brain = TRUE + var/should_have_brain = !(HAS_TRAIT(C, TRAIT_DULLAHAN)) var/should_have_heart = !(NOBLOOD in species_traits) var/should_have_lungs = ((TRAIT_AUXILIARY_LUNGS in inherent_traits) || !(TRAIT_NOBREATH in inherent_traits)) var/should_have_appendix = !(TRAIT_NOHUNGER in inherent_traits) @@ -803,7 +803,7 @@ GLOBAL_LIST_EMPTY(roundstart_race_names) H.apply_overlay(HAIR_LAYER) -/datum/species/proc/handle_body(mob/living/carbon/human/H) +/datum/species/proc/handle_body(mob/living/carbon/human/H, block_recursive_calls = FALSE) H.remove_overlay(BODY_LAYER) var/list/standing = list() @@ -825,10 +825,9 @@ GLOBAL_LIST_EMPTY(roundstart_race_names) // eyes if(!(NOEYES in species_traits)) - var/has_eyes = H.getorganslot(ORGAN_SLOT_EYES) - if(!has_eyes && !H.GetComponent(/datum/component/dullahan)) + var/has_eyes = H.getorganslot(ORGAN_SLOT_EYES) || HAS_TRAIT(H, TRAIT_DULLAHAN) // if they are a dullahan just assume eyes exist + if(!has_eyes) standing += mutable_appearance('icons/mob/eyes.dmi', "eyes_missing", -BODY_LAYER) - message_admins("EYES MISSING APPLIED 2") else var/left_state = DEFAULT_LEFT_EYE_STATE var/right_state = DEFAULT_RIGHT_EYE_STATE @@ -899,9 +898,9 @@ GLOBAL_LIST_EMPTY(roundstart_race_names) H.overlays_standing[BODY_LAYER] = standing H.apply_overlay(BODY_LAYER) - handle_mutant_bodyparts(H) + handle_mutant_bodyparts(H, null, block_recursive_calls) -/datum/species/proc/handle_mutant_bodyparts(mob/living/carbon/human/H, forced_colour) +/datum/species/proc/handle_mutant_bodyparts(mob/living/carbon/human/H, forced_colour, block_recursive_calls = FALSE) var/list/bodyparts_to_add = mutant_bodyparts.Copy() H.remove_overlay(BODY_BEHIND_LAYER) @@ -1216,6 +1215,11 @@ GLOBAL_LIST_EMPTY(roundstart_race_names) H.apply_overlay(BODY_FRONT_LAYER) H.apply_overlay(HORNS_LAYER) + if(!block_recursive_calls) + var/datum/component/dullahan/D = H.GetComponent(/datum/component/dullahan) + if(D && D.dullahan_head) + D.dullahan_head.update_appearance() + /* * Equip the outfit required for life. Replaces items currently worn. */ diff --git a/code/modules/mob/living/carbon/human/species_types/dullahan.dm b/code/modules/mob/living/carbon/human/species_types/dullahan.dm index facc3dfd8d..ed06d2be5a 100644 --- a/code/modules/mob/living/carbon/human/species_types/dullahan.dm +++ b/code/modules/mob/living/carbon/human/species_types/dullahan.dm @@ -82,38 +82,9 @@ decoy_override = TRUE organ_flags = ORGAN_NO_SPOIL//Do not decay -/obj/item/organ/tongue/dullahan - zone = "abstract" - accents = list(/datum/accent/dullahan) - /obj/item/organ/ears/dullahan zone = "abstract" -/obj/item/organ/eyes/dullahan - name = "head vision" - desc = "An abstraction." - actions_types = list(/datum/action/item_action/organ_action/dullahan) - zone = "abstract" - tint = INFINITY // used to switch the vision perspective to the head on species_gain(). - -/datum/action/item_action/organ_action/dullahan - name = "Toggle Perspective" - desc = "Switch between seeing normally from your head, or blindly from your body." - -/datum/action/item_action/organ_action/dullahan/Trigger() - . = ..() - var/obj/item/organ/eyes/dullahan/DE = target - if(DE.tint) - DE.tint = 0 - else - DE.tint = INFINITY - - if(ishuman(owner)) - var/mob/living/carbon/human/H = owner - if(isdullahan(H)) - var/datum/species/dullahan/D = H.dna.species - D.update_vision_perspective(H) - /obj/item/dullahan_relay name = "dullahan relay" var/mob/living/owner diff --git a/code/modules/mob/living/carbon/human/update_icons.dm b/code/modules/mob/living/carbon/human/update_icons.dm index f225afdd79..db0b4ddfac 100644 --- a/code/modules/mob/living/carbon/human/update_icons.dm +++ b/code/modules/mob/living/carbon/human/update_icons.dm @@ -58,14 +58,14 @@ There are several things that need to be remembered: dna.species.handle_hair(src) //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() +/mob/living/carbon/human/proc/update_mutant_bodyparts(block_recursive_calls = FALSE) if(!HAS_TRAIT(src, TRAIT_HUMAN_NO_RENDER)) - dna.species.handle_mutant_bodyparts(src) + dna.species.handle_mutant_bodyparts(src, null, block_recursive_calls) -/mob/living/carbon/human/update_body(update_genitals = FALSE) +/mob/living/carbon/human/update_body(update_genitals = FALSE, block_recursive_calls = FALSE) if(!HAS_TRAIT(src, TRAIT_HUMAN_NO_RENDER)) remove_overlay(BODY_LAYER) - dna.species.handle_body(src) + dna.species.handle_body(src, block_recursive_calls) ..() if(update_genitals) update_genitals() @@ -75,24 +75,24 @@ There are several things that need to be remembered: /* --------------------------------------- */ //For legacy support. -/mob/living/carbon/human/regenerate_icons() +/mob/living/carbon/human/regenerate_icons(block_recursive_calls = FALSE) if(!HAS_TRAIT(src, TRAIT_HUMAN_NO_RENDER)) if(!..()) icon_render_key = null //invalidate bodyparts cache - update_body(TRUE) + update_body(TRUE, block_recursive_calls) update_hair() - update_inv_w_uniform() + update_inv_w_uniform(block_recursive_calls) update_inv_wear_id() update_inv_gloves() update_inv_glasses() update_inv_ears() update_inv_shoes() update_inv_s_store() - update_inv_wear_mask() - update_inv_head() + update_inv_wear_mask(block_recursive_calls) + update_inv_head(block_recursive_calls) update_inv_belt() update_inv_back() - update_inv_wear_suit() + update_inv_wear_suit(block_recursive_calls) update_inv_pockets() update_inv_neck() update_transform() @@ -123,7 +123,7 @@ There are several things that need to be remembered: apply_overlay(ANTAG_LAYER) -/mob/living/carbon/human/update_inv_w_uniform() +/mob/living/carbon/human/update_inv_w_uniform(block_recursive_calls = FALSE) if(!HAS_TRAIT(src, TRAIT_HUMAN_NO_RENDER)) remove_overlay(UNIFORM_LAYER) @@ -173,7 +173,7 @@ There are several things that need to be remembered: overlays_standing[UNIFORM_LAYER] = uniform_overlay apply_overlay(UNIFORM_LAYER) - update_mutant_bodyparts() + update_mutant_bodyparts(block_recursive_calls) /mob/living/carbon/human/update_inv_wear_id() if(!HAS_TRAIT(src, TRAIT_HUMAN_NO_RENDER)) @@ -349,7 +349,7 @@ There are several things that need to be remembered: overlays_standing[SUIT_STORE_LAYER] = s_store_overlay apply_overlay(SUIT_STORE_LAYER) -/mob/living/carbon/human/update_inv_head() +/mob/living/carbon/human/update_inv_head(block_recursive_calls = FALSE) if(!HAS_TRAIT(src, TRAIT_HUMAN_NO_RENDER)) remove_overlay(HEAD_LAYER) @@ -387,7 +387,7 @@ There are several things that need to be remembered: head_overlay.pixel_y += dna.species.offset_features[OFFSET_HEAD][2] overlays_standing[HEAD_LAYER] = head_overlay apply_overlay(HEAD_LAYER) - update_mutant_bodyparts() + update_mutant_bodyparts(block_recursive_calls) /mob/living/carbon/human/update_inv_belt() if(!HAS_TRAIT(src, TRAIT_HUMAN_NO_RENDER)) @@ -411,7 +411,7 @@ There are several things that need to be remembered: overlays_standing[BELT_LAYER] = belt_overlay apply_overlay(BELT_LAYER) -/mob/living/carbon/human/update_inv_wear_suit() +/mob/living/carbon/human/update_inv_wear_suit(block_recursive_calls = FALSE) if(!HAS_TRAIT(src, TRAIT_HUMAN_NO_RENDER)) remove_overlay(SUIT_LAYER) @@ -471,7 +471,7 @@ There are several things that need to be remembered: suit_overlay = center_image(suit_overlay, dimension_x, dimension_y) overlays_standing[SUIT_LAYER] = suit_overlay update_hair() - update_mutant_bodyparts() + update_mutant_bodyparts(block_recursive_calls) apply_overlay(SUIT_LAYER) @@ -498,7 +498,7 @@ There are several things that need to be remembered: update_observer_view(r_store) -/mob/living/carbon/human/update_inv_wear_mask() +/mob/living/carbon/human/update_inv_wear_mask(block_recursive_calls = FALSE) if(!HAS_TRAIT(src, TRAIT_HUMAN_NO_RENDER)) remove_overlay(FACEMASK_LAYER) @@ -537,7 +537,7 @@ There are several things that need to be remembered: mask_overlay.pixel_y += dna.species.offset_features[OFFSET_FACEMASK][2] overlays_standing[FACEMASK_LAYER] = mask_overlay apply_overlay(FACEMASK_LAYER) - update_mutant_bodyparts() //e.g. upgate needed because mask now hides lizard snout + update_mutant_bodyparts(block_recursive_calls) //e.g. upgate needed because mask now hides lizard snout /mob/living/carbon/human/update_inv_back() if(!HAS_TRAIT(src, TRAIT_HUMAN_NO_RENDER)) @@ -791,9 +791,8 @@ use_mob_overlay_icon: if FALSE, it will always use the default_icon_file even if // eyes if(!(NOEYES in dna.species.species_traits)) var/has_eyes = getorganslot(ORGAN_SLOT_EYES) - if(!has_eyes && !GetComponent(/datum/component/dullahan)) + if(!has_eyes && !HAS_TRAIT(src, TRAIT_DULLAHAN)) add_overlay(mutable_appearance('icons/mob/eyes.dmi', "eyes_missing", -BODY_LAYER)) - message_admins("EYES MISSING APPLIED") else var/left_state = DEFAULT_LEFT_EYE_STATE var/right_state = DEFAULT_RIGHT_EYE_STATE diff --git a/code/modules/surgery/bodyparts/dismemberment.dm b/code/modules/surgery/bodyparts/dismemberment.dm index 6b5b4a14aa..96b9290239 100644 --- a/code/modules/surgery/bodyparts/dismemberment.dm +++ b/code/modules/surgery/bodyparts/dismemberment.dm @@ -142,7 +142,7 @@ update_icon_dropped() C.update_health_hud() //update the healthdoll - C.update_body() + C.update_body(FALSE, TRUE) // block recursive calls because we dont want to crash, i.e. don't tell a dullahan to update when dropping its limb C.update_hair() C.update_mobility() @@ -370,7 +370,7 @@ update_disabled() C.updatehealth() - C.update_body() + C.update_body(FALSE, TRUE) // again block recursive calls because dullahans will try update their icons by regenerating their head C.update_hair() C.update_damage_overlays() C.update_mobility() diff --git a/code/modules/surgery/bodyparts/head.dm b/code/modules/surgery/bodyparts/head.dm index 7278dc6888..ea43740093 100644 --- a/code/modules/surgery/bodyparts/head.dm +++ b/code/modules/surgery/bodyparts/head.dm @@ -134,7 +134,7 @@ I.pixel_y = px_y add_overlay(standing) -/obj/item/bodypart/head/get_limb_icon(dropped, ignore_brain = FALSE, ignore_eyes = FALSE) +/obj/item/bodypart/head/get_limb_icon(dropped, ignore_brain = FALSE) if(custom_head) return cut_overlays() @@ -181,7 +181,7 @@ . += lips_overlay // eyes - if(eyes || ignore_eyes) + if(eyes || ignore_brain) var/left_state = DEFAULT_LEFT_EYE_STATE var/right_state = DEFAULT_RIGHT_EYE_STATE if(owner && owner.dna.species) @@ -203,7 +203,6 @@ . += right_eye else var/eyes_overlay = image('icons/mob/hair.dmi', "eyes_missing", -BODY_LAYER, SOUTH) - message_admins("EYES MISSING ALSO IGNORE EYES IS [ignore_eyes]") . += eyes_overlay /obj/item/bodypart/head/monkey