Files
Bubberstation/code/modules/surgery/organs/organ_movement.dm

340 lines
13 KiB
Plaintext

// There are two kinds of organ movement: mob movement and limb movement
// If you pull someones brain out, you remove it from the mob and the limb
// If you take someones head off, you remove it from the mob but not the limb
// If you remove the brain from an already decapitated head, you remove it from the limb but not the mob
// Keep the seperation of limb removal and mob removal absolute
/*
* Insert the organ into the select mob.
*
* receiver - the mob who will get our organ
* special - "quick swapping" an organ out - when TRUE, the mob will be unaffected by not having that organ for the moment
* movement_flags - Flags for how we behave in movement. See DEFINES/organ_movement for flags
*/
/obj/item/organ/proc/Insert(mob/living/carbon/receiver, special = FALSE, movement_flags)
SHOULD_CALL_PARENT(TRUE)
if(!mob_insert(receiver, special, movement_flags))
return FALSE
if(bodypart_owner && loc == bodypart_owner && receiver == bodypart_owner.owner)
// ok this is a bit confusing but essentially, thanks to some EXTREME shenanigans
// (tl;dr mob_insert -> set_species -> replace_limb -> bodypart_insert)
// mob_insert can result in bodypart_insert being handled already
// to avoid double insertion, and potential bugs, we'll stop here
return TRUE
bodypart_insert(limb_owner = receiver, movement_flags = movement_flags)
if(!special && !(receiver.living_flags & STOP_OVERLAY_UPDATE_BODY_PARTS))
receiver.update_body_parts()
return TRUE
/*
* Remove the organ from the select mob.
*
* * organ_owner - the mob who owns our organ, that we're removing the organ from. Can be null
* * special - "quick swapping" an organ out - when TRUE, the mob will be unaffected by not having that organ for the moment
*/
/obj/item/organ/proc/Remove(mob/living/carbon/organ_owner, special = FALSE, movement_flags)
SHOULD_CALL_PARENT(TRUE)
mob_remove(organ_owner, special, movement_flags)
bodypart_remove(limb_owner = organ_owner, movement_flags = movement_flags)
if(!special && !(organ_owner.living_flags & STOP_OVERLAY_UPDATE_BODY_PARTS))
organ_owner.update_body_parts()
/*
* Insert the organ into the select mob.
*
* receiver - the mob who will get our organ
* special - "quick swapping" an organ out - when TRUE, the mob will be unaffected by not having that organ for the moment
* movement_flags - Flags for how we behave in movement. See DEFINES/organ_movement for flags
*/
/obj/item/organ/proc/mob_insert(mob/living/carbon/receiver, special, movement_flags)
SHOULD_NOT_OVERRIDE(TRUE)
if(!iscarbon(receiver))
//We try to insert the organ in a corgi when running the test, expecting it to return FALSE.
if(!PERFORM_ALL_TESTS(organ_sanity))
stack_trace("Tried to insert organ into non-carbon: [receiver.type]")
return FALSE
if(owner == receiver)
stack_trace("Organ receiver is already organ owner")
return FALSE
var/obj/item/organ/replaced = receiver.get_organ_slot(slot)
if(replaced)
replaced.Remove(receiver, special = TRUE)
if(movement_flags & DELETE_IF_REPLACED)
qdel(replaced)
else
replaced.forceMove(get_turf(receiver))
if(!IS_ROBOTIC_ORGAN(src) && (organ_flags & ORGAN_VIRGIN))
blood_dna_info = receiver.get_blood_dna_list()
// need to remove the synethic blood DNA that is initialized
// wash also adds the blood dna again
wash(CLEAN_TYPE_BLOOD)
organ_flags &= ~ORGAN_VIRGIN
receiver.organs |= src
receiver.organs_slot[slot] = src
owner = receiver
on_mob_insert(receiver, special, movement_flags)
return TRUE
/// Called after the organ is inserted into a mob.
/// Adds Traits, Actions, and Status Effects on the mob in which the organ is impanted.
/// Override this proc to create unique side-effects for inserting your organ. Must be called by overrides.
/obj/item/organ/proc/on_mob_insert(mob/living/carbon/organ_owner, special = FALSE, movement_flags)
SHOULD_CALL_PARENT(TRUE)
for(var/trait in organ_traits)
ADD_TRAIT(organ_owner, trait, REF(src))
for(var/datum/action/action as anything in actions)
action.Grant(organ_owner)
for(var/datum/status_effect/effect as anything in organ_effects)
organ_owner.apply_status_effect(effect, type)
if(!special)
organ_owner.hud_used?.update_locked_slots()
SEND_SIGNAL(src, COMSIG_ORGAN_IMPLANTED, organ_owner)
SEND_SIGNAL(organ_owner, COMSIG_CARBON_GAIN_ORGAN, src, special)
// organs_slot must ALWAYS be ordered in the same way as organ_process_order
// Otherwise life processing breaks down
sortTim(owner.organs_slot, GLOBAL_PROC_REF(cmp_organ_slot_asc))
STOP_PROCESSING(SSobj, src)
/// Insert an organ into a limb, assume the limb as always detached and include no owner operations here (except the get_bodypart helper here I guess)
/// Give EITHER a limb OR a limb owner
/obj/item/organ/proc/bodypart_insert(obj/item/bodypart/bodypart, mob/living/carbon/limb_owner, movement_flags)
SHOULD_CALL_PARENT(TRUE)
if(limb_owner)
bodypart = limb_owner.get_bodypart(deprecise_zone(zone))
if(bodypart_owner == bodypart)
stack_trace("Organ bodypart_insert called when organ is already owned by that bodypart")
else if(!isnull(bodypart_owner))
stack_trace("Organ bodypart_insert called when organ is already owned by a different bodypart")
// In the event that we're already in the bodypart, DO NOT MOVE IT! otherwise it triggers forced_removal
if(loc != bodypart)
forceMove(bodypart) // The true movement
// Don't re-register if we are already owned
if(bodypart_owner != bodypart)
bodypart_owner = bodypart
RegisterSignal(src, COMSIG_MOVABLE_MOVED, PROC_REF(forced_removal))
// Apply unique side-effects. Return value does not matter.
on_bodypart_insert(bodypart)
return TRUE
/// Add any limb specific effects you might want here
/obj/item/organ/proc/on_bodypart_insert(obj/item/bodypart/limb)
SHOULD_CALL_PARENT(TRUE)
item_flags |= ABSTRACT
ADD_TRAIT(src, TRAIT_NODROP, ORGAN_INSIDE_BODY_TRAIT)
interaction_flags_item &= ~INTERACT_ITEM_ATTACK_HAND_PICKUP
if(external_bodytypes)
limb.owner?.synchronize_bodytypes()
if(external_bodyshapes)
limb.owner?.synchronize_bodyshapes()
if(bodypart_overlay)
limb.add_bodypart_overlay(bodypart_overlay)
SEND_SIGNAL(src, COMSIG_ORGAN_BODYPART_INSERTED, limb)
/*
* Remove the organ from the select mob.
*
* * organ_owner - the mob who owns our organ, that we're removing the organ from. Can be null
* * special - "quick swapping" an organ out - when TRUE, the mob will be unaffected by not having that organ for the moment
*/
/obj/item/organ/proc/mob_remove(mob/living/carbon/organ_owner, special = FALSE, movement_flags)
SHOULD_NOT_OVERRIDE(TRUE)
if(organ_owner)
if(organ_owner.organs_slot[slot] == src)
organ_owner.organs_slot.Remove(slot)
organ_owner.organs -= src
owner = null
on_mob_remove(organ_owner, special, movement_flags)
return TRUE
/// Called after the organ is removed from a mob.
/// Removes Traits, Actions, and Status Effects on the mob in which the organ was impanted.
/// Override this proc to create unique side-effects for removing your organ. Must be called by overrides.
/obj/item/organ/proc/on_mob_remove(mob/living/carbon/organ_owner, special = FALSE, movement_flags)
SHOULD_CALL_PARENT(TRUE)
if(!iscarbon(organ_owner))
stack_trace("Organ removal should not be happening on non carbon mobs: [organ_owner]")
for(var/trait in organ_traits)
REMOVE_TRAIT(organ_owner, trait, REF(src))
for(var/datum/action/action as anything in actions)
action.Remove(organ_owner)
for(var/datum/status_effect/effect as anything in organ_effects)
organ_owner.remove_status_effect(effect, type)
SEND_SIGNAL(src, COMSIG_ORGAN_REMOVED, organ_owner)
SEND_SIGNAL(organ_owner, COMSIG_CARBON_LOSE_ORGAN, src, special)
if(organ_owner.mind)
ADD_TRAIT(src, TRAIT_ORGAN_USED_BY_PLAYER, ORGAN_TRAIT)
if(!special)
organ_owner.hud_used?.update_locked_slots()
if((organ_flags & ORGAN_VITAL) && !special && !HAS_TRAIT(organ_owner, TRAIT_GODMODE))
if(organ_owner.stat != DEAD)
organ_owner.investigate_log("has been killed by losing a vital organ ([src]).", INVESTIGATE_DEATHS)
organ_owner.death()
START_PROCESSING(SSobj, src)
var/list/diseases = organ_owner.get_static_viruses()
if(!LAZYLEN(diseases))
return
var/list/datum/disease/diseases_to_add = list()
for(var/datum/disease/disease as anything in diseases)
// robotic organs are immune to disease unless 'inorganic biology' symptom is present
if(IS_ROBOTIC_ORGAN(src) && !(disease.infectable_biotypes & MOB_ROBOTIC))
continue
// admin or special viruses that should not be reproduced
if(disease.spread_flags & (DISEASE_SPREAD_SPECIAL | DISEASE_SPREAD_NON_CONTAGIOUS))
continue
diseases_to_add += disease
if(LAZYLEN(diseases_to_add))
AddComponent(/datum/component/infective, diseases_to_add)
/// Called to remove an organ from a limb. Do not put any mob operations here (except the bodypart_getter at the start)
/// Give EITHER a limb OR a limb_owner
/obj/item/organ/proc/bodypart_remove(obj/item/bodypart/limb, mob/living/carbon/limb_owner, movement_flags)
SHOULD_CALL_PARENT(TRUE)
if(!isnull(limb_owner))
limb = limb_owner.get_bodypart(deprecise_zone(zone))
UnregisterSignal(src, COMSIG_MOVABLE_MOVED) //DONT MOVE THIS!!!! we moves the organ right after, so we unregister before we move them physically
// The true movement is here
moveToNullspace()
bodypart_owner = null
if(!isnull(limb))
on_bodypart_remove(limb)
return TRUE
/// Called on limb removal to remove limb specific limb effects or statuses
/obj/item/organ/proc/on_bodypart_remove(obj/item/bodypart/limb, movement_flags)
SHOULD_CALL_PARENT(TRUE)
if(!IS_ROBOTIC_ORGAN(src) && !(item_flags & NO_BLOOD_ON_ITEM) && !QDELING(src))
var/blood_color = get_color_from_blood_list(blood_dna_info)
if (blood_color)
AddElement(/datum/element/decal/blood, _color = blood_color)
item_flags &= ~ABSTRACT
REMOVE_TRAIT(src, TRAIT_NODROP, ORGAN_INSIDE_BODY_TRAIT)
interaction_flags_item |= INTERACT_ITEM_ATTACK_HAND_PICKUP
SEND_SIGNAL(src, COMSIG_ORGAN_BODYPART_REMOVED, limb, movement_flags) // BUBBER CHANGE, added COMSIG_ORGAN_BODYPART_REMOVED to on_bodypart_remove
limb.owner?.synchronize_bodytypes()
limb.owner?.synchronize_bodyshapes()
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
if(greyscale_config)
get_greyscale_color_from_draw_color()
else
color = bodypart_overlay.draw_color // so a pink felinid doesn't drop a gray tail
///Here we define how draw_color from the bodypart overlay sets the greyscale colors of organs that use GAGS
/obj/item/organ/proc/get_greyscale_color_from_draw_color()
color = bodypart_overlay.draw_color //Defaults to the legacy behaviour of applying the color to the item.
/// In space station videogame, nothing is sacred. If somehow an organ is removed unexpectedly, handle it properly
/obj/item/organ/proc/forced_removal(datum/source, atom/old_loc, ...)
SIGNAL_HANDLER
if(owner)
if(loc?.loc == owner) // loc = some bodypart, loc.loc = some bodypart's owner
stack_trace("Forced removal triggered on [src] ([type]) moving into the same mob [owner] ([owner.type])!")
else
Remove(owner)
else if(bodypart_owner)
if(loc == bodypart_owner)
stack_trace("Forced removal triggered on [src] ([type]) moving into the same bodypart [bodypart_owner] ([bodypart_owner.type])!")
else
bodypart_remove(bodypart_owner)
else
stack_trace("Force removed an already removed organ!")
/**
* Proc that gets called when the organ is surgic d for special effects
*/
/obj/item/organ/proc/on_surgical_removal(mob/living/user, obj/item/bodypart/limb, obj/item/tool)
SHOULD_CALL_PARENT(TRUE)
SEND_SIGNAL(src, COMSIG_ORGAN_SURGICALLY_REMOVED, user, limb.owner, limb.body_zone, tool)
RemoveElement(/datum/element/decal/blood, _color = get_organ_blood_color(limb) || BLOOD_COLOR_RED)
/**
* Proc that gets called when the organ is surgically inserted by someone. Seem familiar?
*/
/obj/item/organ/proc/on_surgical_insertion(mob/living/user, obj/item/bodypart/limb)
SHOULD_CALL_PARENT(TRUE)
SEND_SIGNAL(src, COMSIG_ORGAN_SURGICALLY_INSERTED, user, limb.owner, limb.body_zone)
/// Proc that gets called when someone starts surgically inserting the organ
/obj/item/organ/proc/pre_surgical_insertion(mob/living/user, mob/living/carbon/new_owner, target_zone)
if (valid_zones)
swap_zone(target_zone)
/// Readjusts the organ to fit into a different body zone/slot
/obj/item/organ/proc/swap_zone(target_zone)
if (!valid_zones[target_zone])
CRASH("[src]'s ([type]) swap_zone was called with invalid zone [target_zone]")
zone = target_zone
slot = valid_zones[zone]
/obj/item/organ/proc/get_organ_blood_color(obj/item/bodypart/limb)
var/blood_color = owner?.get_bloodtype()?.get_color()
if (!limb)
return blood_color
if (!blood_color)
blood_color = limb.owner?.get_bloodtype()?.get_color()
if (!blood_color && length(limb.blood_dna_info))
blood_color = get_color_from_blood_list(limb.blood_dna_info)
return blood_color