mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-27 23:27:34 +01:00
## About The Pull Request Merry Christmas! This PR expands on the more gruesome areas of chef content, such as cannibalism. Human limbs can now be individually butchered for their meat and skin with a knife or another butchering implement once the skin has been cut and retracted (forks now can be used as bootleg retractors). If the limb contains organs, those will need to be removed first, which also can be quickly performed with a butchering tool at the cost of damaging the inhards (head and chest will require the bone to be cut first, which can be done with a butcher's cleaver if you don't have a saw) You can also butcher humans whole by right clicking them with a butchering implement on combat mode, which will attempt to gut/cut apart the selected limb. Obviously this requires the limb to be prepped first, so meat hooks/kitchenspikes now allow mobs strung on them to be operated on without needing drapes. Its a pretty bad spot to be performing surgeries at though, so beware! Torsos can only be butchered after all other limbs have been. The gutting/butchering delay is based off the owner's stat, halving if the limb is detached and doubling if the owner is still alive and kicking. <img width="392" height="265" alt="dreamseeker_D7QWbWKStd" src="https://github.com/user-attachments/assets/050d7c25-3572-4b02-ae93-d8efefe54885" /> (The video is too large for github) https://streamable.com/459a0y After being butchered, most limbs will leave behind a nonfunctional skeletal limb (with exceptions like jelly limbs with no skeleton inside) which is always disabled. Skeleton chests husk their owner, so you'll need to replace it first. Gibbers now source their meat from the victim's limbs (and not their species) at an RNG-based percentage based on the matter bin's tier. As to allow individual limbs to be butchered, you can now perform (some) surgeries on individual limbs. There are some obvious restrictions like zombification or bioware surgeries, but most of the ones that make sense can be performed on someone's chopped off head as long as you've got the tools. Wounds also contribute to surgeries now, major slash wounds count as cut/open skin (and sealing the vessels would reduce their bleed rate), and critical blunt wounds count as sawed bones. I've refactored gibbers, butchering and meat spike code as well as fixed some backend bugs while I was at it. ## Why It's Good For The Game While rather gruesome, I feel like the freedom which these mechanics provide fits us pretty well. We already have plenty of nasty stuff and this doesn't really go that much further, and a chef serving their own leg in their dish is pretty funny. (Also you can scrape plasma off plasmamen bones)
195 lines
8.4 KiB
Plaintext
195 lines
8.4 KiB
Plaintext
/datum/surgery_operation/limb/prepare_cavity
|
|
name = "widen chest cavity"
|
|
desc = "Widen a patient's chest cavity to allow for implanting of larger items."
|
|
implements = list(
|
|
TOOL_RETRACTOR = 1,
|
|
TOOL_CROWBAR = 1.5,
|
|
)
|
|
time = 4.8 SECONDS
|
|
preop_sound = 'sound/items/handling/surgery/retractor1.ogg'
|
|
success_sound = 'sound/items/handling/surgery/retractor2.ogg'
|
|
all_surgery_states_required = SURGERY_SKIN_OPEN|SURGERY_ORGANS_CUT
|
|
any_surgery_states_blocked = SURGERY_CAVITY_WIDENED
|
|
|
|
/datum/surgery_operation/limb/prepare_cavity/get_default_radial_image()
|
|
return image(/obj/item/retractor)
|
|
|
|
/datum/surgery_operation/limb/prepare_cavity/all_required_strings()
|
|
return list("operate on chest (target chest)") + ..()
|
|
|
|
/datum/surgery_operation/limb/prepare_cavity/state_check(obj/item/bodypart/chest/limb)
|
|
return limb.body_zone == BODY_ZONE_CHEST
|
|
|
|
/datum/surgery_operation/limb/prepare_cavity/on_preop(obj/item/bodypart/chest/limb, mob/living/surgeon, tool, list/operation_args)
|
|
display_results(
|
|
surgeon,
|
|
limb.owner,
|
|
span_notice("You begin to open [FORMAT_LIMB_OWNER(limb)] cavity wide..."),
|
|
span_notice("[surgeon] begins to open [FORMAT_LIMB_OWNER(limb)] cavity wide."),
|
|
span_notice("[surgeon] begins to open [FORMAT_LIMB_OWNER(limb)] cavity wide."),
|
|
)
|
|
display_pain(limb.owner, "You can feel pressure as your [limb.plaintext_zone] is being opened wide!")
|
|
|
|
/datum/surgery_operation/limb/prepare_cavity/on_success(obj/item/bodypart/chest/limb, mob/living/surgeon, tool, list/operation_args)
|
|
. = ..()
|
|
limb.add_surgical_state(SURGERY_CAVITY_WIDENED)
|
|
|
|
/datum/surgery_operation/limb/cavity_implant
|
|
name = "cavity implant"
|
|
desc = "Implant an item into a patient's body cavity."
|
|
operation_flags = OPERATION_NOTABLE
|
|
implements = list(
|
|
/obj/item = 1,
|
|
)
|
|
time = 3.2 SECONDS
|
|
preop_sound = 'sound/items/handling/surgery/organ1.ogg'
|
|
success_sound = 'sound/items/handling/surgery/organ2.ogg'
|
|
all_surgery_states_required = SURGERY_SKIN_OPEN|SURGERY_ORGANS_CUT|SURGERY_CAVITY_WIDENED
|
|
/// Items that bypass normal size restrictions for cavity implantation
|
|
var/list/heavy_cavity_implants
|
|
|
|
/datum/surgery_operation/limb/cavity_implant/New()
|
|
. = ..()
|
|
heavy_cavity_implants = typecacheof(list(
|
|
/obj/item/transfer_valve,
|
|
))
|
|
|
|
/datum/surgery_operation/limb/cavity_implant/all_required_strings()
|
|
return list("operate on chest (target chest)") + ..()
|
|
|
|
/datum/surgery_operation/limb/cavity_implant/get_default_radial_image()
|
|
return image('icons/hud/screen_gen.dmi', "arrow_large_still")
|
|
|
|
/datum/surgery_operation/limb/cavity_implant/state_check(obj/item/bodypart/chest/limb)
|
|
if(limb.body_zone != BODY_ZONE_CHEST)
|
|
return FALSE
|
|
if(!isnull(limb.cavity_item))
|
|
return FALSE
|
|
return TRUE
|
|
|
|
/datum/surgery_operation/limb/cavity_implant/snowflake_check_availability(obj/item/bodypart/chest/limb, mob/living/surgeon, obj/item/tool, operated_zone)
|
|
if(!surgeon.canUnEquip(tool))
|
|
return FALSE
|
|
// Stops accidentally putting a tool you meant to operate with
|
|
// Besides who really wants to put a scalpel or a wrench inside someone that's lame
|
|
if(IS_ROBOTIC_LIMB(limb) && (tool.tool_behaviour in GLOB.all_mechanical_tools))
|
|
return FALSE
|
|
if(IS_ORGANIC_LIMB(limb) && (tool.tool_behaviour in GLOB.all_surgical_tools))
|
|
return FALSE
|
|
return TRUE
|
|
|
|
/datum/surgery_operation/limb/cavity_implant/tool_check(obj/item/tool)
|
|
if(tool.w_class > WEIGHT_CLASS_NORMAL && !is_type_in_typecache(tool, heavy_cavity_implants))
|
|
return FALSE
|
|
if(tool.item_flags & (ABSTRACT|DROPDEL|HAND_ITEM))
|
|
return FALSE
|
|
if(isorgan(tool))
|
|
return FALSE // use organ manipulation
|
|
return TRUE
|
|
|
|
/datum/surgery_operation/limb/cavity_implant/on_preop(obj/item/bodypart/chest/limb, mob/living/surgeon, obj/item/tool, list/operation_args)
|
|
display_results(
|
|
surgeon,
|
|
limb.owner,
|
|
span_notice("You begin to insert [tool] into [FORMAT_LIMB_OWNER(limb)]..."),
|
|
span_notice("[surgeon] begins to insert [tool] into [FORMAT_LIMB_OWNER(limb)]."),
|
|
span_notice("[surgeon] begins to insert [tool.w_class > WEIGHT_CLASS_SMALL ? tool : "something"] into [FORMAT_LIMB_OWNER(limb)]."),
|
|
)
|
|
display_pain(limb.owner, "You can feel something being inserted into your [limb.plaintext_zone], it hurts like hell!")
|
|
|
|
/datum/surgery_operation/limb/cavity_implant/on_success(obj/item/bodypart/chest/limb, mob/living/surgeon, obj/item/tool, list/operation_args)
|
|
if (!surgeon.transferItemToLoc(tool, limb.owner, force = TRUE)) // shouldn't fail but just in case
|
|
display_results(
|
|
surgeon,
|
|
limb.owner,
|
|
span_warning("You can't seem to fit [tool] in [limb.owner]'s [limb.plaintext_zone]!"),
|
|
span_warning("[surgeon] can't seem to fit [tool] in [limb.owner]'s [limb.plaintext_zone]!"),
|
|
span_warning("[surgeon] can't seem to fit [tool.w_class > WEIGHT_CLASS_SMALL ? tool : "something"] in [limb.owner]'s [limb.plaintext_zone]!"),
|
|
)
|
|
return
|
|
|
|
limb.cavity_item = tool
|
|
limb.remove_surgical_state(SURGERY_CAVITY_WIDENED)
|
|
display_results(
|
|
surgeon,
|
|
limb.owner,
|
|
span_notice("You stuff [tool] into [FORMAT_LIMB_OWNER(limb)]."),
|
|
span_notice("[surgeon] stuffs [tool] into [FORMAT_LIMB_OWNER(limb)]!"),
|
|
span_notice("[surgeon] stuffs [tool.w_class > WEIGHT_CLASS_SMALL ? tool : "something"] into [FORMAT_LIMB_OWNER(limb)]."),
|
|
)
|
|
|
|
|
|
/datum/surgery_operation/limb/undo_cavity_implant
|
|
name = "remove cavity implant"
|
|
desc = "Remove an item from a body cavity."
|
|
implements = list(
|
|
IMPLEMENT_HAND = 1,
|
|
TOOL_HEMOSTAT = 2,
|
|
TOOL_CROWBAR = 2.5,
|
|
/obj/item/kitchen/fork = 5,
|
|
)
|
|
|
|
time = 3.2 SECONDS
|
|
preop_sound = 'sound/items/handling/surgery/organ1.ogg'
|
|
success_sound = 'sound/items/handling/surgery/organ2.ogg'
|
|
all_surgery_states_required = SURGERY_SKIN_OPEN|SURGERY_ORGANS_CUT|SURGERY_CAVITY_WIDENED
|
|
|
|
/datum/surgery_operation/limb/undo_cavity_implant/all_required_strings()
|
|
return list("operate on chest (target chest)") + ..()
|
|
|
|
/datum/surgery_operation/limb/undo_cavity_implant/get_default_radial_image()
|
|
return image('icons/hud/screen_gen.dmi', "arrow_large_still")
|
|
|
|
/datum/surgery_operation/limb/undo_cavity_implant/get_radial_options(obj/item/bodypart/chest/limb, obj/item/tool, operating_zone)
|
|
// Not bothering to cache this as the chance of hitting the same cavity item in the same round is rather low
|
|
var/datum/radial_menu_choice/option = new()
|
|
option.name = "remove [limb.cavity_item]"
|
|
option.info = "Replace the [limb.cavity_item] embededd in the patient's chest cavity."
|
|
option.image = get_generic_limb_radial_image(BODY_ZONE_CHEST)
|
|
option.image.overlays += add_radial_overlays(limb.cavity_item)
|
|
return option
|
|
|
|
/datum/surgery_operation/limb/undo_cavity_implant/state_check(obj/item/bodypart/chest/limb)
|
|
if(limb.body_zone != BODY_ZONE_CHEST)
|
|
return FALSE
|
|
// unlike implant removal, don't show the surgery as an option unless something is actually implanted
|
|
// it would stand to reason standard implants would be hidden from view (requires a search)
|
|
// while cavity implants would be blatantly visible (no search necessary)
|
|
if(isnull(limb.cavity_item))
|
|
return FALSE
|
|
return TRUE
|
|
|
|
/datum/surgery_operation/limb/undo_cavity_implant/on_preop(obj/item/bodypart/chest/limb, mob/living/surgeon, obj/item/tool, list/operation_args)
|
|
display_results(
|
|
surgeon,
|
|
limb.owner,
|
|
span_notice("You begin to extract [limb.cavity_item] from [FORMAT_LIMB_OWNER(limb)]..."),
|
|
span_notice("[surgeon] begins to extract [limb.cavity_item] from [FORMAT_LIMB_OWNER(limb)]."),
|
|
span_notice("[surgeon] begins to extract something from [FORMAT_LIMB_OWNER(limb)]."),
|
|
)
|
|
display_pain(limb.owner, "You feel a serious pain in your [limb.plaintext_zone]!")
|
|
|
|
/datum/surgery_operation/limb/undo_cavity_implant/on_success(obj/item/bodypart/chest/limb, mob/living/surgeon, obj/item/tool, list/operation_args)
|
|
if(isnull(limb.cavity_item)) // something else could have removed it mid surgery?
|
|
display_results(
|
|
surgeon,
|
|
limb.owner,
|
|
span_warning("You find nothing to remove from [FORMAT_LIMB_OWNER(limb)]."),
|
|
span_warning("[surgeon] finds nothing to remove from [FORMAT_LIMB_OWNER(limb)]."),
|
|
span_warning("[surgeon] finds nothing to remove from [FORMAT_LIMB_OWNER(limb)]."),
|
|
)
|
|
return
|
|
|
|
var/obj/item/implant = limb.cavity_item
|
|
limb.cavity_item = null
|
|
limb.remove_surgical_state(SURGERY_CAVITY_WIDENED)
|
|
display_results(
|
|
surgeon,
|
|
limb.owner,
|
|
span_notice("You pull [implant] out of [FORMAT_LIMB_OWNER(limb)]."),
|
|
span_notice("[surgeon] pulls [implant] out of [FORMAT_LIMB_OWNER(limb)]!"),
|
|
span_notice("[surgeon] pulls something out of [FORMAT_LIMB_OWNER(limb)]!"),
|
|
)
|
|
display_pain(limb.owner, "You can feel [implant.name] being pulled out of you!")
|
|
surgeon.put_in_hands(implant)
|