From e4173fd9d660a1cf17563bd4ceca62596eb6213b Mon Sep 17 00:00:00 2001 From: Y0SH1M4S73R Date: Fri, 4 Jul 2025 15:49:49 -0400 Subject: [PATCH] Knit Flesh can insert organs into carbons (#91817) Co-authored-by: Lucy --- .../heretic/magic/flesh_surgery.dm | 185 ++++++++++++++++-- 1 file changed, 173 insertions(+), 12 deletions(-) diff --git a/code/modules/antagonists/heretic/magic/flesh_surgery.dm b/code/modules/antagonists/heretic/magic/flesh_surgery.dm index 806a51ccb4f..26e21f54ae4 100644 --- a/code/modules/antagonists/heretic/magic/flesh_surgery.dm +++ b/code/modules/antagonists/heretic/magic/flesh_surgery.dm @@ -2,6 +2,7 @@ name = "Knit Flesh" desc = "A touch spell that allows you to either harvest or restore flesh of target. \ Left-clicking will extract the organs of a victim without needing to complete surgery or disembowel. \ + You can also pick up a loose organ and insert it into your vitcim. \ Right-clicking, if done on summons or minions, will restore health. Can also be used to heal damaged organs." background_icon_state = "bg_heretic" overlay_icon_state = "bg_heretic_border" @@ -24,22 +25,37 @@ var/monster_brute_healing = 10 /// If used on a heretic mob, how much burn do we heal var/monster_burn_healing = 5 + /// The organ being held in the touch attack - which will be implanted into mobs. + var/obj/item/organ/held_organ + /// Can we implant cybernetic organs? + var/allow_cyber_organs = FALSE /datum/action/cooldown/spell/touch/flesh_surgery/is_valid_target(atom/cast_on) return isliving(cast_on) || isorgan(cast_on) -/datum/action/cooldown/spell/touch/flesh_surgery/cast_on_hand_hit(obj/item/melee/touch_attack/hand, atom/victim, mob/living/carbon/caster) +/datum/action/cooldown/spell/touch/flesh_surgery/remove_hand(mob/living/hand_owner, reset_cooldown_after) + var/obj/item/organ/the_organ = held_organ + if(the_organ) + unregister_held_organ(the_organ) + the_organ.forceMove(hand_owner.drop_location()) + + return ..() + +/datum/action/cooldown/spell/touch/flesh_surgery/cast_on_hand_hit(obj/item/melee/touch_attack/flesh_surgery/hand, atom/victim, mob/living/carbon/caster) if(isorgan(victim)) - return heal_organ(hand, victim, caster) + grab_organ(hand, victim, caster) + return FALSE if(isliving(victim)) + if(held_organ) + return insert_organ_into_mob(held_organ, hand, victim, caster) return steal_organ_from_mob(hand, victim, caster) return FALSE -/datum/action/cooldown/spell/touch/flesh_surgery/cast_on_secondary_hand_hit(obj/item/melee/touch_attack/hand, atom/victim, mob/living/carbon/caster) +/datum/action/cooldown/spell/touch/flesh_surgery/cast_on_secondary_hand_hit(obj/item/melee/touch_attack/flesh_surgery/hand, atom/victim, mob/living/carbon/caster) if(isorgan(victim)) - return SECONDARY_ATTACK_CALL_NORMAL + return heal_organ(hand, victim, caster) if(isliving(victim)) var/mob/living/mob_victim = victim @@ -54,11 +70,17 @@ /datum/action/cooldown/spell/touch/flesh_surgery/register_hand_signals() . = ..() RegisterSignal(attached_hand, COMSIG_ITEM_REQUESTING_CONTEXT_FOR_TARGET, PROC_REF(add_item_context)) + RegisterSignal(attached_hand, COMSIG_ATOM_ITEM_INTERACTION, PROC_REF(hand_interacted_with)) attached_hand.item_flags |= ITEM_HAS_CONTEXTUAL_SCREENTIPS /datum/action/cooldown/spell/touch/flesh_surgery/unregister_hand_signals() . = ..() - UnregisterSignal(attached_hand, COMSIG_ITEM_REQUESTING_CONTEXT_FOR_TARGET) + UnregisterSignal(attached_hand, list(COMSIG_ITEM_REQUESTING_CONTEXT_FOR_TARGET, COMSIG_ATOM_ITEM_INTERACTION)) + +/datum/action/cooldown/spell/touch/flesh_surgery/proc/hand_interacted_with(obj/item/melee/touch_attack/source, mob/living/user, obj/item/tool) + SIGNAL_HANDLER + if(isorgan(tool)) + return grab_organ(source, tool, user) /// Signal proc for [COMSIG_ITEM_REQUESTING_CONTEXT_FOR_TARGET] to add some context to the hand. /datum/action/cooldown/spell/touch/flesh_surgery/proc/add_item_context(obj/item/melee/touch_attack/source, list/context, atom/victim, mob/living/user) @@ -70,7 +92,10 @@ var/mob/living/mob_victim = victim if(iscarbon(mob_victim)) - context[SCREENTIP_CONTEXT_LMB] = "Extract organ" + if(held_organ) + context[SCREENTIP_CONTEXT_LMB] = "Insert organ" + else + context[SCREENTIP_CONTEXT_LMB] = "Extract organ" . = CONTEXTUAL_SCREENTIP_SET if(HAS_TRAIT(mob_victim, TRAIT_HERETIC_SUMMON)) @@ -78,13 +103,56 @@ . = CONTEXTUAL_SCREENTIP_SET else if(isorgan(victim)) - context[SCREENTIP_CONTEXT_LMB] = "Heal organ" + context[SCREENTIP_CONTEXT_LMB] = "Pick up organ" + context[SCREENTIP_CONTEXT_RMB] = "Heal organ" . = CONTEXTUAL_SCREENTIP_SET return . -/// If cast on an organ, we'll restore its health and even un-fail it. +/// If cast on an organ with left-click, we'll try to grab it. +/datum/action/cooldown/spell/touch/flesh_surgery/proc/grab_organ(obj/item/melee/touch_attack/hand, obj/item/organ/to_grab, mob/living/carbon/caster) + if(held_organ) + hand.balloon_alert(caster, "already holding organ!") + return ITEM_INTERACT_FAILURE + if(to_grab.organ_flags & ORGAN_ROBOTIC && !allow_cyber_organs) + hand.balloon_alert(caster, "cybernetic organs not allowed!") + return ITEM_INTERACT_FAILURE + if(!caster.transferItemToLoc(to_grab, hand)) + hand.balloon_alert(caster, "couldn't grab organ!") + return ITEM_INTERACT_FAILURE + register_held_organ(to_grab, hand) + return ITEM_INTERACT_SUCCESS + +/datum/action/cooldown/spell/touch/flesh_surgery/proc/register_held_organ(obj/item/organ/new_held_organ, obj/item/melee/touch_attack/hand) + hand.vis_contents += new_held_organ + held_organ = new_held_organ + new_held_organ.flags_1 |= IS_ONTOP_1 + new_held_organ.vis_flags |= VIS_INHERIT_PLANE + RegisterSignal(new_held_organ, COMSIG_MOVABLE_MOVED, PROC_REF(unregister_held_organ)) + RegisterSignal(new_held_organ, COMSIG_QDELETING, PROC_REF(unregister_held_organ)) + // We gotta offset ourselves via pixel_w/z, so we don't end up z fighting with the touch attack + new_held_organ.pixel_w = new_held_organ.pixel_x + new_held_organ.pixel_z = new_held_organ.pixel_y + new_held_organ.pixel_x = 0 + new_held_organ.pixel_y = 0 + +/datum/action/cooldown/spell/touch/flesh_surgery/proc/unregister_held_organ(obj/item/organ/removed_organ) + LAZYREMOVE(attached_hand.vis_contents, removed_organ) + held_organ = null + removed_organ.flags_1 &= ~IS_ONTOP_1 + removed_organ.vis_flags &= ~VIS_INHERIT_PLANE + UnregisterSignal(removed_organ, list(COMSIG_MOVABLE_MOVED, COMSIG_QDELETING)) + // Reset item offsets + removed_organ.pixel_x = removed_organ.pixel_w + removed_organ.pixel_y = removed_organ.pixel_z + removed_organ.pixel_w = 0 + removed_organ.pixel_z = 0 + +/// If cast on an organ with right-click, we'll restore its health and even un-fail it. /datum/action/cooldown/spell/touch/flesh_surgery/proc/heal_organ(obj/item/melee/touch_attack/hand, obj/item/organ/to_heal, mob/living/carbon/caster) + if(held_organ) + hand.balloon_alert(caster, "drop held organ first!") + return FALSE if(to_heal.damage == 0) to_heal.balloon_alert(caster, "already in good condition!") return FALSE @@ -214,16 +282,109 @@ addtimer(CALLBACK(caster, TYPE_PROC_REF(/mob, put_in_hands), picked_organ), 0.1 SECONDS) return TRUE +/datum/action/cooldown/spell/touch/flesh_surgery/proc/insert_organ_into_mob(obj/item/organ/inserted_organ, obj/item/melee/touch_attack/flesh_surgery/hand, mob/living/carbon/victim, mob/living/carbon/caster) + if(!istype(victim)) + hand.balloon_alert(caster, "no organs!") + return FALSE + + var/zone_organ_goes_in = inserted_organ.zone + if(!victim.get_bodypart(deprecise_zone(zone_organ_goes_in))) + hand.balloon_alert(caster, "nowhere for organ to go!") + return FALSE + + var/slot_organ_goes_in = inserted_organ.slot + var/obj/item/organ/organ_victim_already_has = victim.get_organ_slot(slot_organ_goes_in) + if(organ_victim_already_has?.organ_flags & ORGAN_VITAL|ORGAN_UNREMOVABLE) + hand.balloon_alert(caster, "can't replace organ!") + return FALSE + + var/time_it_takes + var/using_on_self = victim == caster + var/replacing_with_failing = !(organ_victim_already_has?.organ_flags & ORGAN_FAILING) && inserted_organ.organ_flags & ORGAN_FAILING + if(using_on_self) + time_it_takes = inserted_organ.w_class * 2 SECONDS + else if(victim.stat == DEAD) + time_it_takes = inserted_organ.w_class * 1 SECONDS + else if(replacing_with_failing) + time_it_takes = 15 SECONDS + else + time_it_takes = inserted_organ.w_class * 3 SECONDS + + if(using_on_self && replacing_with_failing) + var/are_you_sure = tgui_alert(caster, + "Are you sure you want to replace your [organ_victim_already_has.name] with a non-functional [inserted_organ.name]?", + "Are you sure?", + list("Yes", "No")) + if(!are_you_sure) + return FALSE + + if(!insertion_checks(inserted_organ, hand, victim, caster)) + return FALSE + + if(using_on_self) + caster.visible_message( + span_danger("[caster]'s hand glows a brilliant red as [caster.p_they()] begin[caster.p_es()] forcing [inserted_organ] into [caster.p_their()] [zone_organ_goes_in]!!"), + span_userdanger("You begin forcing [inserted_organ] into your [zone_organ_goes_in]!") + ) + else + caster.visible_message( + span_danger("[caster]'s hand glows a brilliant red as [caster.p_they()] begin[caster.p_es()] forcing [inserted_organ] into [victim]'s [zone_organ_goes_in]!!"), + span_notice("You begin forcing [inserted_organ] into [victim]'s [zone_organ_goes_in].") + ) + + victim.balloon_alert(caster, "inserting [inserted_organ]...") + playsound(victim, 'sound/items/weapons/slice.ogg', 50, TRUE) + victim.add_atom_colour(COLOR_DARK_RED, TEMPORARY_COLOUR_PRIORITY) + if(!do_after(caster, time_it_takes, victim, extra_checks = CALLBACK(src, PROC_REF(insertion_checks), inserted_organ, hand, victim, caster))) + victim.balloon_alert(caster, "interrupted!") + victim.remove_atom_colour(TEMPORARY_COLOUR_PRIORITY, COLOR_DARK_RED) + return FALSE + + organ_victim_already_has = victim.get_organ_slot(slot_organ_goes_in) // This COULD have changed over the course of the do_after + + if(using_on_self) + caster.visible_message( + span_danger("[caster] crams [inserted_organ] into [caster.p_their()] own [zone_organ_goes_in][organ_victim_already_has ? ", forcing out [caster.p_their()] [organ_victim_already_has.name]": ""]!"), + span_userdanger("You finish inserting [inserted_organ] into your [zone_organ_goes_in][organ_victim_already_has ? ", forcing out your [organ_victim_already_has]" : ""]!") + ) + else + caster.visible_message( + span_danger("[caster] crams [inserted_organ] into [victim]'s [zone_organ_goes_in][organ_victim_already_has ? ", forcing out [victim.p_their()] [organ_victim_already_has.name]": ""]!"), + span_notice("You finish inserting [inserted_organ] into [victim]'s [zone_organ_goes_in][organ_victim_already_has ? ", forcing out [victim.p_their()] [organ_victim_already_has.name]": ""].") + ) + + unregister_held_organ(inserted_organ) + inserted_organ.Insert(victim) + victim.balloon_alert(caster, "[inserted_organ] inserted") + victim.remove_atom_colour(TEMPORARY_COLOUR_PRIORITY, COLOR_DARK_RED) + playsound(victim, 'sound/effects/dismember.ogg', 50, TRUE) + if(victim.stat == CONSCIOUS) + victim.emote("scream") + if(!using_on_self) + victim.adjust_timed_status_effect(15 SECONDS, /datum/status_effect/speech/slurring/heretic) + + return TRUE + /// Extra checks ran while we're extracting an organ to make sure we can continue to do. -/datum/action/cooldown/spell/touch/flesh_surgery/proc/extraction_checks(obj/item/organ/picked_organ, obj/item/melee/touch_attack/hand, mob/living/carbon/victim, mob/living/carbon/caster) - if(QDELETED(src) || QDELETED(hand) || QDELETED(picked_organ) || QDELETED(victim) || !IsAvailable()) +/datum/action/cooldown/spell/touch/flesh_surgery/proc/extraction_checks(obj/item/organ/picked_organ, obj/item/melee/touch_attack/flesh_surgery/hand, mob/living/carbon/victim, mob/living/carbon/caster) + if(QDELETED(src) || QDELETED(hand) || QDELETED(picked_organ) || QDELETED(victim) || held_organ || !IsAvailable()) return FALSE return TRUE /// Extra checks ran while we're healing something (organ, mob). -/datum/action/cooldown/spell/touch/flesh_surgery/proc/heal_checks(obj/item/melee/touch_attack/hand, atom/healing, mob/living/carbon/caster) - if(QDELETED(src) || QDELETED(hand) || QDELETED(healing) || !IsAvailable()) +/datum/action/cooldown/spell/touch/flesh_surgery/proc/heal_checks(obj/item/melee/touch_attack/flesh_surgery/hand, atom/healing, mob/living/carbon/caster) + if(QDELETED(src) || QDELETED(hand) || QDELETED(healing) || held_organ || !IsAvailable()) + return FALSE + + return TRUE + +/// Extra checks ran while we're inserting an organ. +/datum/action/cooldown/spell/touch/flesh_surgery/proc/insertion_checks(obj/item/organ/inserted_organ, obj/item/melee/touch_attack/flesh_surgery/hand, mob/living/carbon/victim, mob/living/carbon/caster) + if(QDELETED(src) || QDELETED(hand) || QDELETED(victim) || QDELETED(inserted_organ) || (held_organ != inserted_organ) || !IsAvailable()) + return FALSE + var/obj/item/organ/organ_victim_already_has = victim.get_organ_slot(inserted_organ.slot) + if(organ_victim_already_has?.organ_flags & ORGAN_VITAL|ORGAN_UNREMOVABLE) return FALSE return TRUE