diff --git a/code/__defines/misc.dm b/code/__defines/misc.dm index 3056e6a8a6..82b1eabc60 100644 --- a/code/__defines/misc.dm +++ b/code/__defines/misc.dm @@ -155,6 +155,8 @@ #define MAT_METALHYDROGEN "mhydrogen" #define MAT_OSMIUM "osmium" #define MAT_GRAPHITE "graphite" +#define MAT_LEATHER "leather" +#define MAT_CHITIN "chitin" #define SHARD_SHARD "shard" #define SHARD_SHRAPNEL "shrapnel" diff --git a/code/game/machinery/washing_machine.dm b/code/game/machinery/washing_machine.dm index 2cd581c9f1..c48f98fc33 100644 --- a/code/game/machinery/washing_machine.dm +++ b/code/game/machinery/washing_machine.dm @@ -32,7 +32,7 @@ /obj/machinery/washing_machine/Initialize() . = ..() default_apply_parts() - + /obj/machinery/washing_machine/AltClick() start() @@ -67,9 +67,14 @@ //Tanning! for(var/obj/item/stack/hairlesshide/HH in washing) - var/obj/item/stack/wetleather/WL = new(src) - WL.amount = HH.amount - qdel(HH) + var/obj/item/stack/WL = new HH.wet_type(src) + if(istype(WL)) + WL.amount = HH.amount + washing -= HH + HH.forceMove(get_turf(src)) + HH.use(HH.amount) + + washing += WL if(locate(/mob,washing)) state = 7 @@ -130,7 +135,7 @@ to_chat(user, "You can't fit \the [W] inside.") return - else if(istype(W, /obj/item/clothing) || istype(W, /obj/item/weapon/bedsheet)) + else if(istype(W, /obj/item/clothing) || istype(W, /obj/item/weapon/bedsheet) || istype(W, /obj/item/stack/hairlesshide)) if(washing.len < 5) if(state in list(1, 3)) user.drop_item() diff --git a/code/game/objects/items/stacks/sheets/leather.dm b/code/game/objects/items/stacks/sheets/leather.dm index 8bf366268a..54bc1e2fce 100644 --- a/code/game/objects/items/stacks/sheets/leather.dm +++ b/code/game/objects/items/stacks/sheets/leather.dm @@ -1,70 +1,75 @@ +/obj/item/stack/animalhide + name = "hide" + desc = "The hide of some creature." + icon_state = "sheet-hide" + drop_sound = 'sound/items/drop/cloth.ogg' + pickup_sound = 'sound/items/pickup/cloth.ogg' + amount = 1 + stacktype = "hide" + no_variants = TRUE + + var/process_type = /obj/item/stack/hairlesshide + /obj/item/stack/animalhide/human - name = "human skin" - desc = "The by-product of human farming." - singular_name = "human skin piece" + name = "skin" + desc = "The by-product of sapient farming." + singular_name = "skin piece" icon_state = "sheet-hide" no_variants = FALSE drop_sound = 'sound/items/drop/leather.ogg' pickup_sound = 'sound/items/pickup/leather.ogg' - -/obj/item/stack/animalhide/human - amount = 50 + amount = 1 + stacktype = "hide-human" /obj/item/stack/animalhide/corgi name = "corgi hide" desc = "The by-product of corgi farming." singular_name = "corgi hide piece" icon_state = "sheet-corgi" - -/obj/item/stack/animalhide/corgi - amount = 50 + amount = 1 + stacktype = "hide-corgi" /obj/item/stack/animalhide/cat name = "cat hide" desc = "The by-product of cat farming." singular_name = "cat hide piece" icon_state = "sheet-cat" - -/obj/item/stack/animalhide/cat - amount = 50 + amount = 1 + stacktype = "hide-cat" /obj/item/stack/animalhide/monkey name = "monkey hide" desc = "The by-product of monkey farming." singular_name = "monkey hide piece" icon_state = "sheet-monkey" - -/obj/item/stack/animalhide/monkey - amount = 50 + amount = 1 + stacktype = "hide-monkey" /obj/item/stack/animalhide/lizard name = "lizard skin" desc = "Sssssss..." singular_name = "lizard skin piece" icon_state = "sheet-lizard" - -/obj/item/stack/animalhide/lizard - amount = 50 + amount = 1 + stacktype = "hide-lizard" /obj/item/stack/animalhide/xeno name = "alien hide" desc = "The skin of a terrible creature." singular_name = "alien hide piece" icon_state = "sheet-xeno" - -/obj/item/stack/animalhide/xeno - amount = 50 + amount = 1 + stacktype = "hide-xeno" //don't see anywhere else to put these, maybe together they could be used to make the xenos suit? /obj/item/stack/xenochitin name = "alien chitin" desc = "A piece of the hide of a terrible creature." - singular_name = "alien hide piece" + singular_name = "alien chitin piece" icon = 'icons/mob/alien.dmi' icon_state = "chitin" - -/obj/item/stack/xenochitin - amount = 50 + amount = 1 + stacktype = "hide-chitin" /obj/item/xenos_claw name = "alien claw" @@ -84,9 +89,22 @@ singular_name = "hairless hide piece" icon_state = "sheet-hairlesshide" no_variants = FALSE + amount = 1 + stacktype = "hairlesshide" + var/cleaning = FALSE // Can we be water_acted, or are we busy? To prevent accidental hide duplication and the collapse of causality. -/obj/item/stack/hairlesshide - amount = 50 + var/wet_type = /obj/item/stack/wetleather + +/obj/item/stack/hairlesshide/water_act(var/wateramount) + ..() + cleaning = TRUE + while(amount > 0 && wateramount > 0) + use(1) + wateramount-- + new wet_type(get_turf(src)) + cleaning = FALSE + + return /obj/item/stack/wetleather name = "wet leather" @@ -96,29 +114,28 @@ var/wetness = 30 //Reduced when exposed to high temperautres var/drying_threshold_temperature = 500 //Kelvin to start drying no_variants = FALSE + amount = 1 + stacktype = "wetleather" -/obj/item/stack/wetleather - amount = 50 + var/dry_type = /obj/item/stack/material/leather //Step one - dehairing. /obj/item/stack/animalhide/attackby(obj/item/weapon/W as obj, mob/user as mob) - if( istype(W, /obj/item/weapon/material/knife) || \ - istype(W, /obj/item/weapon/material/twohanded/fireaxe) || \ - istype(W, /obj/item/weapon/material/knife/machete/hatchet) ) - + if(has_edge(W) || is_sharp(W)) //visible message on mobs is defined as visible_message(var/message, var/self_message, var/blind_message) usr.visible_message("\The [usr] starts cutting hair off \the [src]", "You start cutting the hair off \the [src]", "You hear the sound of a knife rubbing against flesh") if(do_after(user,50)) to_chat(usr, "You cut the hair from this [src.singular_name]") //Try locating an exisitng stack on the tile and add to there if possible for(var/obj/item/stack/hairlesshide/HS in usr.loc) - if(HS.amount < 50) + if(HS.amount < 50 && istype(HS, process_type)) HS.amount++ src.use(1) - break + return //If it gets to here it means it did not find a suitable stack on the tile. - var/obj/item/stack/hairlesshide/HS = new(usr.loc) - HS.amount = 1 + var/obj/item/stack/HS = new process_type(usr.loc) + if(istype(HS)) + HS.amount = 1 src.use(1) else ..() @@ -132,15 +149,20 @@ if(exposed_temperature >= drying_threshold_temperature) wetness-- if(wetness == 0) - //Try locating an exisitng stack on the tile and add to there if possible - for(var/obj/item/stack/material/leather/HS in src.loc) - if(HS.amount < 50) - HS.amount++ - src.use(1) - wetness = initial(wetness) - break - //If it gets to here it means it did not find a suitable stack on the tile. - var/obj/item/stack/material/leather/HS = new(src.loc) - HS.amount = 1 + dry() + +/obj/item/stack/wetleather/proc/dry() + //Try locating an exisitng stack on the tile and add to there if possible + for(var/obj/item/stack/material/leather/HS in src.loc) + if(HS.amount < 50) + HS.amount++ wetness = initial(wetness) src.use(1) + return + //If it gets to here it means it did not find a suitable stack on the tile. + var/obj/item/stack/HS = new dry_type(src.loc) + + if(istype(HS)) + HS.amount = 1 + wetness = initial(wetness) + src.use(1) diff --git a/code/game/objects/items/stacks/stack.dm b/code/game/objects/items/stacks/stack.dm index 6e35e74974..5fa718564f 100644 --- a/code/game/objects/items/stacks/stack.dm +++ b/code/game/objects/items/stacks/stack.dm @@ -59,7 +59,7 @@ /obj/item/stack/examine(mob/user) . = ..() - + if(Adjacent(user)) if(!uses_charge) . += "There are [src.amount] [src.singular_name]\s in the stack." @@ -380,17 +380,17 @@ var/use_material var/pass_color - New(title, result_type, req_amount = 1, res_amount = 1, max_res_amount = 1, time = 0, one_per_turf = 0, on_floor = 0, supplied_material = null, pass_stack_color) - src.title = title - src.result_type = result_type - src.req_amount = req_amount - src.res_amount = res_amount - src.max_res_amount = max_res_amount - src.time = time - src.one_per_turf = one_per_turf - src.on_floor = on_floor - src.use_material = supplied_material - src.pass_color = pass_stack_color +/datum/stack_recipe/New(title, result_type, req_amount = 1, res_amount = 1, max_res_amount = 1, time = 0, one_per_turf = 0, on_floor = 0, supplied_material = null, pass_stack_color) + src.title = title + src.result_type = result_type + src.req_amount = req_amount + src.res_amount = res_amount + src.max_res_amount = max_res_amount + src.time = time + src.one_per_turf = one_per_turf + src.on_floor = on_floor + src.use_material = supplied_material + src.pass_color = pass_stack_color /* * Recipe list datum diff --git a/code/game/objects/structures/bonfire.dm b/code/game/objects/structures/bonfire.dm index 005686eeca..18630e2f8e 100644 --- a/code/game/objects/structures/bonfire.dm +++ b/code/game/objects/structures/bonfire.dm @@ -250,6 +250,16 @@ removed.add_thermal_energy(heat_transfer) + for(var/mob/living/L in view(3, src)) + L.add_modifier(/datum/modifier/endothermic, 10 SECONDS, null, TRUE) + + for(var/obj/item/stack/wetleather/WL in view(2, src)) + if(WL.wetness >= 0) + WL.dry() + continue + + WL.wetness = max(0, WL.wetness - rand(1, 4)) + env.merge(removed) /obj/structure/bonfire/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume) diff --git a/code/modules/food/kitchen/gibber.dm b/code/modules/food/kitchen/gibber.dm index 1fca845091..6402a432ae 100644 --- a/code/modules/food/kitchen/gibber.dm +++ b/code/modules/food/kitchen/gibber.dm @@ -188,18 +188,13 @@ update_icon() var/slab_name = occupant.name - var/slab_count = 3 - var/slab_type = /obj/item/weapon/reagent_containers/food/snacks/meat + var/slab_count = 2 + occupant.meat_amount + var/slab_type = occupant.meat_type ? occupant.meat_type : /obj/item/weapon/reagent_containers/food/snacks/meat var/slab_nutrition = src.occupant.nutrition / 15 - // Some mobs have specific meat item types. - if(istype(src.occupant,/mob/living/simple_mob)) - var/mob/living/simple_mob/critter = src.occupant - if(critter.meat_amount) - slab_count = critter.meat_amount - if(critter.meat_type) - slab_type = critter.meat_type - else if(istype(src.occupant,/mob/living/carbon/human)) + var/list/byproducts = occupant?.butchery_loot?.Copy() + + if(istype(src.occupant,/mob/living/carbon/human)) var/mob/living/carbon/human/H = occupant slab_name = src.occupant.real_name slab_type = H.isSynthetic() ? /obj/item/stack/material/steel : H.species.meat_type @@ -209,7 +204,8 @@ slab_nutrition *= 0.5 slab_nutrition /= slab_count - for(var/i=1 to slab_count) + while(slab_count) + slab_count-- var/obj/item/weapon/reagent_containers/food/snacks/meat/new_meat = new slab_type(src, rand(3,8)) if(istype(new_meat)) new_meat.name = "[slab_name] [new_meat.name]" @@ -222,17 +218,26 @@ src.occupant.ghostize() spawn(gib_time) - - operating = 0 occupant.gib() occupant = null - playsound(src, 'sound/effects/splat.ogg', 50, 1) operating = 0 + if(LAZYLEN(byproducts)) + for(var/path in byproducts) + while(byproducts[path]) + if(prob(min(90,30 * byproducts[path]))) + new path(src) + + byproducts[path] -= 1 + for (var/obj/thing in contents) - // There's a chance that the gibber will fail to destroy some evidence. + // There's a chance that the gibber will fail to destroy or butcher some evidence. if(istype(thing,/obj/item/organ) && prob(80)) - qdel(thing) + var/obj/item/organ/OR = thing + if(OR.can_butcher(src)) + OR.butcher(src, null, src) // Butcher it, and add it to our list of things to launch. + else + qdel(thing) continue thing.forceMove(get_turf(thing)) // Drop it onto the turf for throwing. thing.throw_at(get_edge_target_turf(src,gib_throw_dir),rand(0,3),emagged ? 100 : 50) // Being pelted with bits of meat and bone would hurt. diff --git a/code/modules/food/kitchen/smartfridge.dm b/code/modules/food/kitchen/smartfridge.dm index 7184b2f68c..d776e45296 100644 --- a/code/modules/food/kitchen/smartfridge.dm +++ b/code/modules/food/kitchen/smartfridge.dm @@ -137,6 +137,10 @@ var/obj/item/weapon/reagent_containers/food/snacks/S = O if (S.dried_type) return 1 + + if(istype(O, /obj/item/stack/wetleather)) + return 1 + return 0 /obj/machinery/smartfridge/drying_rack/process() @@ -180,6 +184,17 @@ new D(get_turf(src)) qdel(S) return + + for(var/obj/item/stack/wetleather/WL in I.instances) + if(!WL.wetness) + if(WL.amount == 1) + WL.forceMove(get_turf(src)) + I.instances -= WL + WL.dry() + break + + WL.wetness = max(0, WL.wetness - rand(1, 3)) + return /obj/machinery/smartfridge/process() diff --git a/code/modules/materials/material_recipes.dm b/code/modules/materials/material_recipes.dm index ff567c55f4..57bf6b9b7d 100644 --- a/code/modules/materials/material_recipes.dm +++ b/code/modules/materials/material_recipes.dm @@ -248,3 +248,24 @@ recipes += new/datum/stack_recipe("[display_name] net", /obj/item/weapon/material/fishing_net, 10, time = 5 SECONDS, supplied_material = "[name]", pass_stack_color = TRUE) recipes += new/datum/stack_recipe("[display_name] membrane", /obj/effect/alien/resin/membrane, 1, time = 2 SECONDS, pass_stack_color = TRUE) recipes += new/datum/stack_recipe("[display_name] node", /obj/effect/alien/weeds/node, 1, time = 4 SECONDS) + +/datum/material/leather/generate_recipes() + recipes = list() + recipes += new/datum/stack_recipe("bedsheet", /obj/item/weapon/bedsheet, 10, time = 30 SECONDS, pass_stack_color = TRUE) + recipes += new/datum/stack_recipe("uniform", /obj/item/clothing/under/color/white, 8, time = 15 SECONDS, pass_stack_color = TRUE) + recipes += new/datum/stack_recipe("foot wraps", /obj/item/clothing/shoes/footwraps, 2, time = 5 SECONDS, pass_stack_color = TRUE) + recipes += new/datum/stack_recipe("gloves", /obj/item/clothing/gloves/white, 2, time = 5 SECONDS, pass_stack_color = TRUE) + recipes += new/datum/stack_recipe("wig", /obj/item/clothing/head/powdered_wig, 4, time = 10 SECONDS, pass_stack_color = TRUE) + recipes += new/datum/stack_recipe("philosopher's wig", /obj/item/clothing/head/philosopher_wig, 50, time = 2 MINUTES, pass_stack_color = TRUE) + recipes += new/datum/stack_recipe("taqiyah", /obj/item/clothing/head/taqiyah, 3, time = 6 SECONDS, pass_stack_color = TRUE) + recipes += new/datum/stack_recipe("turban", /obj/item/clothing/head/turban, 3, time = 6 SECONDS, pass_stack_color = TRUE) + recipes += new/datum/stack_recipe("hijab", /obj/item/clothing/head/hijab, 3, time = 6 SECONDS, pass_stack_color = TRUE) + recipes += new/datum/stack_recipe("kippa", /obj/item/clothing/head/kippa, 3, time = 6 SECONDS, pass_stack_color = TRUE) + recipes += new/datum/stack_recipe("scarf", /obj/item/clothing/accessory/scarf/white, 4, time = 5 SECONDS, pass_stack_color = TRUE) + recipes += new/datum/stack_recipe("baggy pants", /obj/item/clothing/under/pants/baggy/white, 8, time = 10 SECONDS, pass_stack_color = TRUE) + recipes += new/datum/stack_recipe("belt pouch", /obj/item/weapon/storage/belt/fannypack/white, 25, time = 1 MINUTE, pass_stack_color = TRUE) + recipes += new/datum/stack_recipe("crude [display_name] bandage", /obj/item/stack/medical/crude_pack, 1, time = 2 SECONDS, pass_stack_color = TRUE) + recipes += new/datum/stack_recipe("[display_name] net", /obj/item/weapon/material/fishing_net, 10, time = 5 SECONDS, supplied_material = "[name]", pass_stack_color = TRUE) + recipes += new/datum/stack_recipe("[display_name] ring", /obj/item/clothing/gloves/ring/material, 1, on_floor = 1, supplied_material = "[name]", pass_stack_color = TRUE) + recipes += new/datum/stack_recipe("[display_name] bracelet", /obj/item/clothing/accessory/bracelet/material, 1, on_floor = 1, supplied_material = "[name]", pass_stack_color = TRUE) + recipes += new/datum/stack_recipe("[display_name] armor plate", /obj/item/weapon/material/armor_plating, 1, time = 20, on_floor = 1, supplied_material = "[name]", pass_stack_color = TRUE) diff --git a/code/modules/materials/material_sheets.dm b/code/modules/materials/material_sheets.dm index 6eb7b990e8..2a17e05d36 100644 --- a/code/modules/materials/material_sheets.dm +++ b/code/modules/materials/material_sheets.dm @@ -444,7 +444,18 @@ name = "leather" desc = "The by-product of mob grinding." icon_state = "sheet-leather" - default_type = "leather" + default_type = MAT_LEATHER + no_variants = FALSE + pass_color = TRUE + strict_color_stacking = TRUE + drop_sound = 'sound/items/drop/leather.ogg' + pickup_sound = 'sound/items/pickup/leather.ogg' + +/obj/item/stack/material/chitin + name = "chitin" + desc = "The by-product of mob grinding." + icon_state = "chitin" + default_type = MAT_CHITIN no_variants = FALSE pass_color = TRUE strict_color_stacking = TRUE diff --git a/code/modules/materials/materials.dm b/code/modules/materials/materials.dm index 211ebcfe80..5de882b549 100644 --- a/code/modules/materials/materials.dm +++ b/code/modules/materials/materials.dm @@ -922,7 +922,12 @@ var/list/name_to_material sheet_singular_name = null sheet_plural_name = "pile" pass_stack_colors = TRUE +<<<<<<< HEAD supply_conversion_value = 3 //YW Adds: logs worth points +||||||| parent of 0841aacdac... Merge pull request #9287 from VOREStation/upstream-merge-7629 +======= + supply_conversion_value = 1 +>>>>>>> 0841aacdac... Merge pull request #9287 from VOREStation/upstream-merge-7629 /datum/material/wood/log/sif name = MAT_SIFLOG @@ -1008,6 +1013,7 @@ var/list/name_to_material flags = MATERIAL_PADDING conductive = 0 pass_stack_colors = TRUE + supply_conversion_value = 2 /datum/material/cult name = "cult" @@ -1033,16 +1039,32 @@ var/list/name_to_material /datum/material/cult/reinf/place_dismantled_product(var/turf/target) new /obj/effect/decal/remains/human(target) +/datum/material/chitin + name = MAT_CHITIN + icon_colour = "#8d6653" + stack_type = /obj/item/stack/material/chitin + stack_origin_tech = list(TECH_MATERIAL = 3, TECH_BIO = 4) + icon_base = "solid" + icon_reinf = "reinf_mesh" + integrity = 60 + ignition_point = T0C+400 + melting_point = T0C+500 + protectiveness = 25 + conductive = 0 + supply_conversion_value = 4 + //TODO PLACEHOLDERS: /datum/material/leather - name = "leather" + name = MAT_LEATHER icon_colour = "#5C4831" - stack_origin_tech = list(TECH_MATERIAL = 2) + stack_type = /obj/item/stack/material/leather + stack_origin_tech = list(TECH_MATERIAL = 2, TECH_BIO = 2) flags = MATERIAL_PADDING ignition_point = T0C+300 melting_point = T0C+300 protectiveness = 3 // 13% conductive = 0 + supply_conversion_value = 3 //CHOMPstation Removal Start: Moved to materials_ch and changed to allow for material var /* diff --git a/code/modules/mob/living/butchering.dm b/code/modules/mob/living/butchering.dm new file mode 100644 index 0000000000..514475e2a2 --- /dev/null +++ b/code/modules/mob/living/butchering.dm @@ -0,0 +1,66 @@ + + +/mob/living + var/meat_amount = 0 // How much meat to drop from this mob when butchered + var/obj/meat_type // The meat object to drop + + var/gib_on_butchery = FALSE + + var/list/butchery_loot // Associated list, path = number. + +// Harvest an animal's delicious byproducts +/mob/living/proc/harvest(var/mob/user, var/obj/item/I) + if(meat_type && meat_amount>0 && (stat == DEAD)) + while(meat_amount > 0 && do_after(user, 0.5 SECONDS * (mob_size / 10), src)) + var/obj/item/meat = new meat_type(get_turf(src)) + meat.name = "[src.name] [meat.name]" + new /obj/effect/decal/cleanable/blood/splatter(get_turf(src)) + meat_amount-- + + if(!meat_amount) + handle_butcher(user, I) + +/mob/living/proc/can_butcher(var/mob/user, var/obj/item/I) // Override for special butchering checks. + if(((meat_type && meat_amount) || LAZYLEN(butchery_loot)) && stat == DEAD) + return TRUE + + return FALSE + +/mob/living/proc/handle_butcher(var/mob/user, var/obj/item/I) + if(!user || do_after(user, 2 SECONDS * mob_size / 10, src)) + if(LAZYLEN(butchery_loot)) + if(LAZYLEN(butchery_loot)) + for(var/path in butchery_loot) + while(butchery_loot[path]) + butchery_loot[path] -= 1 + var/obj/item/loot = new path(get_turf(src)) + loot.pixel_x = rand(-12, 12) + loot.pixel_y = rand(-12, 12) + + butchery_loot.Cut() + butchery_loot = null + + if(LAZYLEN(organs)) + organs_by_name.Cut() + + for(var/obj/item/organ/OR in organs) + OR.removed() + organs -= OR + + if(LAZYLEN(internal_organs)) + internal_organs_by_name.Cut() + + for(var/obj/item/organ/OR in internal_organs) + OR.removed() + internal_organs -= OR + + if(!ckey) + if(issmall(src)) + user?.visible_message("[user] chops up \the [src]!") + new /obj/effect/decal/cleanable/blood/splatter(get_turf(src)) + if(gib_on_butchery) + qdel(src) + else + user?.visible_message("[user] butchers \the [src] messily!") + if(gib_on_butchery) + gib() diff --git a/code/modules/mob/living/carbon/carbon_defines.dm b/code/modules/mob/living/carbon/carbon_defines.dm index 1112b4e49c..07cd72fce9 100644 --- a/code/modules/mob/living/carbon/carbon_defines.dm +++ b/code/modules/mob/living/carbon/carbon_defines.dm @@ -1,4 +1,4 @@ -/mob/living/carbon/ +/mob/living/carbon gender = MALE var/datum/species/species //Contains icon generation and language information, set during New(). var/list/stomach_contents = list() diff --git a/code/modules/mob/living/carbon/human/human_damage.dm b/code/modules/mob/living/carbon/human/human_damage.dm index b8343ad694..50b3c9b053 100644 --- a/code/modules/mob/living/carbon/human/human_damage.dm +++ b/code/modules/mob/living/carbon/human/human_damage.dm @@ -288,7 +288,7 @@ oxyloss = 0 else ..() - + /mob/living/carbon/human/adjustHalLoss(var/amount) if(species.flags & NO_PAIN) halloss = 0 @@ -439,13 +439,14 @@ This function restores all organs. return 0 return - +/* /mob/living/carbon/human/proc/get_organ(var/zone) if(!zone) zone = BP_TORSO else if (zone in list( O_EYES, O_MOUTH )) zone = BP_HEAD return organs_by_name[zone] +*/ /mob/living/carbon/human/apply_damage(var/damage = 0, var/damagetype = BRUTE, var/def_zone = null, var/blocked = 0, var/soaked = 0, var/sharp = 0, var/edge = 0, var/obj/used_weapon = null) if(Debug2) diff --git a/code/modules/mob/living/carbon/human/human_defines.dm b/code/modules/mob/living/carbon/human/human_defines.dm index dcde2ac30b..208e1e1ffc 100644 --- a/code/modules/mob/living/carbon/human/human_defines.dm +++ b/code/modules/mob/living/carbon/human/human_defines.dm @@ -44,7 +44,7 @@ var/age = 30 //Player's age (pure fluff) var/b_type = "A+" //Player's bloodtype - var/datum/robolimb/synthetic //If they are a synthetic (aka synthetic torso) + var/datum/robolimb/synthetic //If they are a synthetic (aka synthetic torso). Also holds the datum for the type of robolimb. var/list/all_underwear = list() var/list/all_underwear_metadata = list() @@ -85,7 +85,6 @@ var/special_voice = "" // For changing our voice. Used by a symptom. var/last_dam = -1 //Used for determining if we need to process all organs or just some or even none. - var/list/bad_external_organs = list()// organs we check until they are good. var/xylophone = 0 //For the spoooooooky xylophone cooldown @@ -118,3 +117,4 @@ var/obj/machinery/machine_visual //machine that is currently applying visual effects to this mob. Only used for camera monitors currently. inventory_panel_type = /datum/inventory_panel/human + butchery_loot = list(/obj/item/stack/animalhide/human = 1) diff --git a/code/modules/mob/living/carbon/human/human_organs.dm b/code/modules/mob/living/carbon/human/human_organs.dm index 96005d7407..59d3576485 100644 --- a/code/modules/mob/living/carbon/human/human_organs.dm +++ b/code/modules/mob/living/carbon/human/human_organs.dm @@ -5,6 +5,7 @@ update_icons_body() //Body handles eyes update_eyes() //For floating eyes only +/* /mob/living/carbon/var/list/internal_organs = list() /mob/living/carbon/human/var/list/organs = list() /mob/living/carbon/human/var/list/organs_by_name = list() // map organ names to organs @@ -13,6 +14,7 @@ /mob/living/carbon/human/proc/get_bodypart_name(var/zone) var/obj/item/organ/external/E = get_organ(zone) if(E) . = E.name +*/ /mob/living/carbon/human/proc/recheck_bad_external_organs() var/damage_this_tick = getToxLoss() diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index 05990e8de9..d6f6fbda73 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -31,9 +31,25 @@ nest = null if(buckled) buckled.unbuckle_mob(src, TRUE) + qdel(selected_image) QDEL_NULL(vorePanel) //VOREStation Add QDEL_LIST_NULL(vore_organs) //VOREStation Add + + if(LAZYLEN(organs)) + organs_by_name.Cut() + while(organs.len) + var/obj/item/OR = organs[1] + organs -= OR + qdel(OR) + + if(LAZYLEN(internal_organs)) + internal_organs_by_name.Cut() + while(internal_organs.len) + var/obj/item/OR = internal_organs[1] + internal_organs -= OR + qdel(OR) + return ..() //mob verbs are faster than object verbs. See mob/verb/examine. @@ -1462,4 +1478,4 @@ default behaviour is: // Tries to turn off things that let you see through walls, like mesons. // Each mob does vision a bit differently so this is just for inheritence and also so overrided procs can make the vision apply instantly if they call `..()`. /mob/living/proc/disable_spoiler_vision() - handle_vision() \ No newline at end of file + handle_vision() diff --git a/code/modules/mob/living/living_defines.dm b/code/modules/mob/living/living_defines.dm index 319dff87f8..4238a56080 100644 --- a/code/modules/mob/living/living_defines.dm +++ b/code/modules/mob/living/living_defines.dm @@ -74,4 +74,4 @@ var/image/selected_image = null // Used for buildmode AI control stuff. var/inventory_panel_type = /datum/inventory_panel - var/datum/inventory_panel/inventory_panel \ No newline at end of file + var/datum/inventory_panel/inventory_panel diff --git a/code/modules/mob/living/organs.dm b/code/modules/mob/living/organs.dm new file mode 100644 index 0000000000..d0489bcf6f --- /dev/null +++ b/code/modules/mob/living/organs.dm @@ -0,0 +1,28 @@ +/mob/living + var/list/internal_organs = list() + var/list/organs = list() + var/list/organs_by_name = list() // map organ names to organs + var/list/internal_organs_by_name = list() // so internal organs have less ickiness too + var/list/bad_external_organs = list()// organs we check until they are good. + +/mob/living/proc/get_bodypart_name(var/zone) + var/obj/item/organ/external/E = get_organ(zone) + if(E) . = E.name + +/mob/living/proc/get_organ(var/zone) + if(!zone) + zone = BP_TORSO + else if (zone in list( O_EYES, O_MOUTH )) + zone = BP_HEAD + return organs_by_name[zone] + +/mob/living/gib() + for(var/obj/item/organ/I in internal_organs) + I.removed() + if(isturf(I?.loc)) // Some organs qdel themselves or other things when removed + I.throw_at(get_edge_target_turf(src,pick(alldirs)),rand(1,3),30) + + for(var/obj/item/organ/external/E in src.organs) + E.droplimb(0,DROPLIMB_EDGE,1) + + ..() diff --git a/code/modules/mob/living/simple_mob/appearance.dm b/code/modules/mob/living/simple_mob/appearance.dm index 14f09e9057..106c565a31 100644 --- a/code/modules/mob/living/simple_mob/appearance.dm +++ b/code/modules/mob/living/simple_mob/appearance.dm @@ -71,6 +71,5 @@ /mob/living/simple_mob/proc/remove_eyes() cut_overlay(eye_layer) - /mob/living/simple_mob/gib() - ..(icon_gib,1,icon) // we need to specify where the gib animation is stored \ No newline at end of file + ..(icon_gib,1,icon) // we need to specify where the gib animation is stored diff --git a/code/modules/mob/living/simple_mob/butchering.dm b/code/modules/mob/living/simple_mob/butchering.dm new file mode 100644 index 0000000000..c03c6b8015 --- /dev/null +++ b/code/modules/mob/living/simple_mob/butchering.dm @@ -0,0 +1,8 @@ +/mob/living/simple_mob + gib_on_butchery = TRUE + +/mob/living/simple_mob/can_butcher(var/mob/user, var/obj/item/I) // Override for special butchering checks. + . = ..() + + if(. && (!is_sharp(I) || !has_edge(I))) + return FALSE diff --git a/code/modules/mob/living/simple_mob/defense.dm b/code/modules/mob/living/simple_mob/defense.dm index 1a301b8bfd..b17274bd2f 100644 --- a/code/modules/mob/living/simple_mob/defense.dm +++ b/code/modules/mob/living/simple_mob/defense.dm @@ -68,9 +68,9 @@ else var/datum/gender/T = gender_datums[src.get_visible_gender()] to_chat(user, "\The [src] is dead, medical items won't bring [T.him] back to life.") // the gender lookup is somewhat overkill, but it functions identically to the obsolete gender macros and future-proofs this code - if(meat_type && (stat == DEAD)) //if the animal has a meat, and if it is dead. - if(istype(O, /obj/item/weapon/material/knife)) - harvest(user) + if(can_butcher(user, O)) //if the animal can be butchered, do so and return. It's likely to be gibbed. + harvest(user, O) + return if(user.a_intent == I_HELP && harvest_tool && istype(O, harvest_tool) && stat != DEAD) if(world.time > (harvest_recent + harvest_cooldown)) diff --git a/code/modules/mob/living/simple_mob/harvesting.dm b/code/modules/mob/living/simple_mob/harvesting.dm index 95de747fee..e9439fab99 100644 --- a/code/modules/mob/living/simple_mob/harvesting.dm +++ b/code/modules/mob/living/simple_mob/harvesting.dm @@ -17,7 +17,7 @@ /mob/living/simple_mob/examine(mob/user) . = ..() - if(user && harvest_tool && (get_dist(user, src) <= 3)) + if(stat != DEAD && user && harvest_tool && (get_dist(user, src) <= 3)) . += "\The [src] can be [harvest_verb] with a [initial(harvest_tool.name)] every [round(harvest_cooldown, 0.1)] minutes." var/time_to_harvest = (harvest_recent + harvest_cooldown) - world.time if(time_to_harvest > 0) diff --git a/code/modules/mob/living/simple_mob/life.dm b/code/modules/mob/living/simple_mob/life.dm index 781589dad1..493a68e43b 100644 --- a/code/modules/mob/living/simple_mob/life.dm +++ b/code/modules/mob/living/simple_mob/life.dm @@ -14,6 +14,8 @@ handle_special() + handle_guts() + return TRUE @@ -94,7 +96,7 @@ throw_alert("oxy", /obj/screen/alert/too_much_oxy) else clear_alert("oxy") - + if(min_tox && Environment.gas["phoron"] < min_tox) atmos_unsuitable = 2 throw_alert("tox_in_air", /obj/screen/alert/not_enough_tox) @@ -137,6 +139,12 @@ else adjustOxyLoss(-unsuitable_atoms_damage) +/mob/living/simple_mob/proc/handle_guts() + for(var/obj/item/organ/OR in internal_organs) + OR.process() + + for(var/obj/item/organ/OR in organs) + OR.process() /mob/living/simple_mob/proc/handle_supernatural() if(purge) diff --git a/code/modules/mob/living/simple_mob/simple_mob.dm b/code/modules/mob/living/simple_mob/simple_mob.dm index f3bf4543db..4d178b4ece 100644 --- a/code/modules/mob/living/simple_mob/simple_mob.dm +++ b/code/modules/mob/living/simple_mob/simple_mob.dm @@ -57,8 +57,6 @@ var/response_harm = "tries to hurt" // If clicked on harm intent var/list/friends = list() // Mobs on this list wont get attacked regardless of faction status. var/harm_intent_damage = 3 // How much an unarmed harm click does to this mob. - var/meat_amount = 0 // How much meat to drop from this mob when butchered - var/obj/meat_type // The meat object to drop var/list/loot_list = list() // The list of lootable objects to drop, with "/path = prob%" structure var/obj/item/weapon/card/id/myid// An ID card if they have one to give them access to stuff. @@ -158,6 +156,10 @@ // don't process me if there's nobody around to see it low_priority = TRUE + // Used for if the mob can drop limbs. Overrides species dmi. + var/limb_icon + // Used for if the mob can drop limbs. Overrides the icon cache key, so it doesn't keep remaking the icon needlessly. + var/limb_icon_key /mob/living/simple_mob/Initialize() verbs -= /mob/verb/observe @@ -170,8 +172,31 @@ if(has_eye_glow) add_eyes() - return ..() + if(LAZYLEN(organs)) + for(var/path in organs) + if(ispath(path)) + var/obj/item/organ/external/neworg = new path(src) + neworg.name = "[name] [neworg.name]" + neworg.meat_type = meat_type + + if(limb_icon) + neworg.force_icon = limb_icon + neworg.force_icon_key = limb_icon_key + + organs |= neworg + organs -= path + + if(LAZYLEN(internal_organs)) + for(var/path in internal_organs) + if(ispath(path)) + var/obj/item/organ/neworg = new path(src) + neworg.name = "[name] [neworg.name]" + neworg.meat_type = meat_type + internal_organs |= neworg + internal_organs -= path + + return ..() /mob/living/simple_mob/Destroy() default_language = null @@ -190,7 +215,6 @@ update_icon() ..() - //Client attached /mob/living/simple_mob/Login() . = ..() @@ -269,27 +293,6 @@ /mob/living/simple_mob/get_speech_ending(verb, var/ending) return verb - -// Harvest an animal's delicious byproducts -/mob/living/simple_mob/proc/harvest(var/mob/user, var/invisible) - var/actual_meat_amount = max(1,(meat_amount/2)) - var/attacker_name = user.name - if(invisible) - attacker_name = "someone" - - if(meat_type && actual_meat_amount>0 && (stat == DEAD)) - for(var/i=0;i[attacker_name] chops up \the [src]!") - new/obj/effect/decal/cleanable/blood/splatter(get_turf(src)) - qdel(src) - else - user.visible_message("[attacker_name] butchers \the [src] messily!") - gib() - - /mob/living/simple_mob/is_sentient() return mob_class & MOB_CLASS_HUMANOID|MOB_CLASS_ANIMAL|MOB_CLASS_SLIME // Update this if needed. diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/animal.dm b/code/modules/mob/living/simple_mob/subtypes/animal/animal.dm index e41d5ae66b..56194f13bb 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/animal.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/animal.dm @@ -6,4 +6,17 @@ response_disarm = "shoos" response_harm = "hits" - ai_holder_type = /datum/ai_holder/simple_mob/melee \ No newline at end of file + ai_holder_type = /datum/ai_holder/simple_mob/melee + + internal_organs = list(\ + /obj/item/organ/internal/brain,\ + /obj/item/organ/internal/heart,\ + /obj/item/organ/internal/liver,\ + /obj/item/organ/internal/stomach,\ + /obj/item/organ/internal/intestine,\ + /obj/item/organ/internal/lungs\ + ) + + butchery_loot = list(\ + /obj/item/stack/animalhide = 3\ + ) diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/_giant_spider.dm b/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/_giant_spider.dm index f0c47be7f8..c0c44250a8 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/_giant_spider.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/_giant_spider.dm @@ -110,6 +110,10 @@ var/poison_chance = 10 // Chance for injection to occur. var/poison_per_bite = 5 // Amount added per injection. + butchery_loot = list(\ + /obj/item/stack/material/chitin = 1\ + ) + /mob/living/simple_mob/animal/giant_spider/apply_melee_effects(var/atom/A) if(isliving(A)) var/mob/living/L = A diff --git a/code/modules/organs/internal/augment.dm b/code/modules/organs/internal/augment.dm index 569b5dda44..da8ee8d577 100644 --- a/code/modules/organs/internal/augment.dm +++ b/code/modules/organs/internal/augment.dm @@ -14,6 +14,8 @@ target_parent_classes = list() // Is the parent supposed to be organic, robotic, assisted? forgiving_class = TRUE // Will the organ give its verbs when it isn't a perfect match? I.E., assisted in organic, synthetic in organic. + butcherable = FALSE + var/obj/item/integrated_object // Objects held by the organ, used for re-usable, deployable things. var/integrated_object_type // Object type the organ will spawn. var/target_slot = null diff --git a/code/modules/organs/internal/brain.dm b/code/modules/organs/internal/brain.dm index 0412ab1470..30c539bbde 100644 --- a/code/modules/organs/internal/brain.dm +++ b/code/modules/organs/internal/brain.dm @@ -83,8 +83,11 @@ GLOBAL_LIST_BOILERPLATE(all_brain_organs, /obj/item/organ/internal/brain) health = config.default_brain_health defib_timer = (config.defib_timer MINUTES) / 2 spawn(5) - if(brainmob && brainmob.client) - brainmob.client.screen.len = null //clear the hud + if(brainmob) + butcherable = FALSE + + if(brainmob.client) + brainmob.client.screen.len = null //clear the hud /obj/item/organ/internal/brain/Destroy() QDEL_NULL(brainmob) @@ -96,9 +99,11 @@ GLOBAL_LIST_BOILERPLATE(all_brain_organs, /obj/item/organ/internal/brain) brainmob = new(src) brainmob.name = H.real_name brainmob.real_name = H.real_name - brainmob.dna = H.dna.Clone() - brainmob.timeofhostdeath = H.timeofdeath - brainmob.ooc_notes = H.ooc_notes //VOREStation Edit + + if(istype(H)) + brainmob.dna = H.dna.Clone() + brainmob.timeofhostdeath = H.timeofdeath + brainmob.ooc_notes = H.ooc_notes //VOREStation Edit // Copy modifiers. for(var/datum/modifier/M in H.modifiers) @@ -125,13 +130,13 @@ GLOBAL_LIST_BOILERPLATE(all_brain_organs, /obj/item/organ/internal/brain) if(name == initial(name)) name = "\the [owner.real_name]'s [initial(name)]" - var/mob/living/simple_mob/animal/borer/borer = owner.has_brain_worms() + var/mob/living/simple_mob/animal/borer/borer = owner?.has_brain_worms() if(borer) borer.detatch() //Should remove borer if the brain is removed - RR var/obj/item/organ/internal/brain/B = src - if(istype(B) && istype(owner)) + if(istype(B) && owner) B.transfer_identity(owner) ..() diff --git a/code/modules/organs/internal/liver.dm b/code/modules/organs/internal/liver.dm index 1fcfac93bb..c23467466a 100644 --- a/code/modules/organs/internal/liver.dm +++ b/code/modules/organs/internal/liver.dm @@ -8,7 +8,7 @@ /obj/item/organ/internal/liver/process() ..() - if(!owner) return + if(!iscarbon(owner)) return if(owner.life_tick % PROCESS_ACCURACY == 0) diff --git a/code/modules/organs/internal/robotic/robotic.dm b/code/modules/organs/internal/robotic/robotic.dm index 7a40ab8101..71414af1ae 100644 --- a/code/modules/organs/internal/robotic/robotic.dm +++ b/code/modules/organs/internal/robotic/robotic.dm @@ -9,3 +9,4 @@ decays = FALSE // Ditto. Rust takes a while. robotic = ORGAN_ROBOT + butcherable = FALSE diff --git a/code/modules/organs/organ.dm b/code/modules/organs/organ.dm index 2d3f68a40c..c60420d359 100644 --- a/code/modules/organs/organ.dm +++ b/code/modules/organs/organ.dm @@ -44,6 +44,9 @@ var/list/organ_cache = list() var/list/target_parent_classes = list() // Is the parent supposed to be organic, robotic, assisted? var/forgiving_class = TRUE // Will the organ give its verbs when it isn't a perfect match? I.E., assisted in organic, synthetic in organic. + var/butcherable = TRUE + var/meat_type // What does butchering, if possible, make? + /obj/item/organ/Destroy() handle_organ_mod_special(TRUE) @@ -59,21 +62,42 @@ var/list/organ_cache = list() /obj/item/organ/proc/update_health() return -/obj/item/organ/New(var/mob/living/carbon/holder, var/internal) +/obj/item/organ/New(var/mob/living/holder, var/internal) ..(holder) create_reagents(5) - if(!max_damage) - max_damage = min_broken_damage * 2 - if(istype(holder)) + + if(isliving(holder)) src.owner = holder src.w_class = max(src.w_class + mob_size_difference(holder.mob_size, MOB_MEDIUM), 1) //smaller mobs have smaller organs. + if(internal) + if(!LAZYLEN(holder.internal_organs)) + holder.internal_organs = list() + if(!LAZYLEN(holder.internal_organs_by_name)) + holder.internal_organs_by_name = list() + + holder.internal_organs |= src + holder.internal_organs_by_name[organ_tag] = src + + else + if(!LAZYLEN(holder.organs)) + holder.organs = list() + if(!LAZYLEN(holder.organs_by_name)) + holder.organs_by_name = list() + + holder.internal_organs |= src + holder.internal_organs_by_name[organ_tag] = src + + if(!max_damage) + max_damage = min_broken_damage * 2 + if(iscarbon(holder)) + var/mob/living/carbon/C = holder species = GLOB.all_species[SPECIES_HUMAN] if(holder.dna) - dna = holder.dna.Clone() - species = holder.species //VOREStation Edit - For custom species + dna = C.dna.Clone() + species = C.species //VOREStation Edit - For custom species else log_debug("[src] at [loc] spawned without a proper DNA.") - var/mob/living/carbon/human/H = holder + var/mob/living/carbon/human/H = C if(istype(H)) if(internal) var/obj/item/organ/external/E = H.get_organ(parent_organ) @@ -81,18 +105,32 @@ var/list/organ_cache = list() if(E.internal_organs == null) E.internal_organs = list() E.internal_organs |= src - H.internal_organs_by_name[organ_tag] = src if(dna) if(!blood_DNA) blood_DNA = list() blood_DNA[dna.unique_enzymes] = dna.b_type - if(internal) - holder.internal_organs |= src else species = GLOB.all_species["Human"] handle_organ_mod_special() +/obj/item/organ/Initialize() + ..() + + if(owner) + if(!meat_type) + if(owner.isSynthetic()) + meat_type = /obj/item/stack/material/steel + else if(ishuman(owner)) + var/mob/living/carbon/human/H = owner + meat_type = H?.species?.meat_type + + if(!meat_type) + if(owner.meat_type) + meat_type = owner.meat_type + else + meat_type = /obj/item/weapon/reagent_containers/food/snacks/meat + /obj/item/organ/proc/set_dna(var/datum/dna/new_dna) if(new_dna) dna = new_dna.Clone() @@ -134,7 +172,7 @@ var/list/organ_cache = list() handle_organ_proc_special() //Process infections - if(robotic >= ORGAN_ROBOT || (owner && owner.species && (owner.species.flags & IS_PLANT || (owner.species.flags & NO_INFECT)))) + if(robotic >= ORGAN_ROBOT || (istype(owner) && (owner.species && (owner.species.flags & (IS_PLANT | NO_INFECT))))) germ_level = 0 return @@ -152,7 +190,7 @@ var/list/organ_cache = list() if(germ_level >= INFECTION_LEVEL_THREE) die() - else if(owner && owner.bodytemperature >= 170) //cryo stops germs from moving and doing their bad stuffs + else if(owner && owner?.bodytemperature >= 170) //cryo stops germs from moving and doing their bad stuffs //** Handle antibiotics and curing infections handle_antibiotics() handle_rejection() @@ -170,7 +208,7 @@ var/list/organ_cache = list() germ_level = 0 return 0 - var/antibiotics = owner.chem_effects[CE_ANTIBIOTIC] || 0 + var/antibiotics = iscarbon(owner) ? owner.chem_effects[CE_ANTIBIOTIC] || 0 : 0 var/infection_damage = 0 @@ -203,10 +241,12 @@ var/list/organ_cache = list() //Level 1 qualifies for specific organ processing effects if(germ_level >= INFECTION_LEVEL_ONE) - . = 1 - var/fever_temperature = owner.species.heat_discomfort_level * 1.10 //Heat discomfort level plus 10% - if(owner.bodytemperature < fever_temperature) - owner.bodytemperature += min(0.2,(fever_temperature - owner.bodytemperature) / 10) //Will usually climb by 0.2, else 10% of the difference if less + . = 1 //Organ qualifies for effect-specific processing + //var/fever_temperature = (owner.species.heat_level_1 - owner.species.body_temperature - 5)* min(germ_level/INFECTION_LEVEL_TWO, 1) + owner.species.body_temperature + //owner.bodytemperature += between(0, (fever_temperature - T20C)/BODYTEMP_COLD_DIVISOR + 1, fever_temperature - owner.bodytemperature) + var/fever_temperature = owner?.species.heat_discomfort_level * 1.10 //Heat discomfort level plus 10% + if(owner?.bodytemperature < fever_temperature) + owner?.bodytemperature += min(0.2,(fever_temperature - owner?.bodytemperature) / 10) //Will usually climb by 0.2, else 10% of the difference if less //Level two qualifies for further processing effects if (germ_level >= INFECTION_LEVEL_TWO) @@ -269,19 +309,20 @@ var/list/organ_cache = list() //Germs /obj/item/organ/proc/handle_antibiotics() - var/antibiotics = owner.chem_effects[CE_ANTIBIOTIC] || 0 + if(istype(owner)) + var/antibiotics = owner.chem_effects[CE_ANTIBIOTIC] || 0 - if (!germ_level || antibiotics < ANTIBIO_NORM) - return + if (!germ_level || antibiotics < ANTIBIO_NORM) + return - if (germ_level < INFECTION_LEVEL_ONE) - germ_level = 0 //cure instantly - else if (germ_level < INFECTION_LEVEL_TWO) - adjust_germ_level(-antibiotics*4) //at germ_level < 500, this should cure the infection in a minute - else if (germ_level < INFECTION_LEVEL_THREE) - adjust_germ_level(-antibiotics*2) //at germ_level < 1000, this will cure the infection in 5 minutes - else - adjust_germ_level(-antibiotics) // You waited this long to get treated, you don't really deserve this organ + if (germ_level < INFECTION_LEVEL_ONE) + germ_level = 0 //cure instantly + else if (germ_level < INFECTION_LEVEL_TWO) + adjust_germ_level(-antibiotics*4) //at germ_level < 500, this should cure the infection in a minute + else if (germ_level < INFECTION_LEVEL_THREE) + adjust_germ_level(-antibiotics*2) //at germ_level < 1000, this will cure the infection in 5 minutes + else + adjust_germ_level(-antibiotics) // You waited this long to get treated, you don't really deserve this organ //Adds autopsy data for used_weapon. /obj/item/organ/proc/add_autopsy_data(var/used_weapon, var/damage) @@ -304,7 +345,7 @@ var/list/organ_cache = list() //only show this if the organ is not robotic if(owner && parent_organ && amount > 0) - var/obj/item/organ/external/parent = owner.get_organ(parent_organ) + var/obj/item/organ/external/parent = owner?.get_organ(parent_organ) if(parent && !silent) owner.custom_pain("Something inside your [parent.name] hurts a lot.", amount) @@ -322,6 +363,7 @@ var/list/organ_cache = list() robotic = ORGAN_ASSISTED min_bruised_damage = 15 min_broken_damage = 35 + butcherable = FALSE /obj/item/organ/proc/digitize() //Used to make the circuit-brain. On this level in the event more circuit-organs are added/tweaks are wanted. robotize() @@ -341,31 +383,30 @@ var/list/organ_cache = list() take_damage(rand(1,3)) /obj/item/organ/proc/removed(var/mob/living/user) + if(owner) + owner.internal_organs_by_name[organ_tag] = null + owner.internal_organs_by_name -= organ_tag + owner.internal_organs_by_name -= null + owner.internal_organs -= src - if(!istype(owner)) - return + var/obj/item/organ/external/affected = owner.get_organ(parent_organ) + if(affected) affected.internal_organs -= src - owner.internal_organs_by_name[organ_tag] = null - owner.internal_organs_by_name -= organ_tag - owner.internal_organs_by_name -= null - owner.internal_organs -= src + forceMove(owner.drop_location()) + START_PROCESSING(SSobj, src) + rejecting = null - var/obj/item/organ/external/affected = owner.get_organ(parent_organ) - if(affected) affected.internal_organs -= src + if(istype(owner)) + var/datum/reagent/blood/organ_blood = locate(/datum/reagent/blood) in reagents.reagent_list + if(!organ_blood || !organ_blood.data["blood_DNA"]) + owner.vessel.trans_to(src, 5, 1, 1) - forceMove(owner.drop_location()) - START_PROCESSING(SSobj, src) - rejecting = null - var/datum/reagent/blood/organ_blood = locate(/datum/reagent/blood) in reagents.reagent_list - if(!organ_blood || !organ_blood.data["blood_DNA"]) - owner.vessel.trans_to(src, 5, 1, 1) - - if(owner && vital) - if(user) - add_attack_logs(user, owner, "Removed vital organ [src.name]") - if(owner.stat != DEAD) - owner.can_defib = 0 - owner.death() + if(owner && vital) + if(user) + add_attack_logs(user, owner, "Removed vital organ [src.name]") + if(owner.stat != DEAD) + owner.can_defib = 0 + owner.death() handle_organ_mod_special(TRUE) @@ -379,13 +420,13 @@ var/list/organ_cache = list() var/datum/reagent/blood/transplant_blood = locate(/datum/reagent/blood) in reagents.reagent_list transplant_data = list() if(!transplant_blood) - transplant_data["species"] = target.species.name - transplant_data["blood_type"] = target.dna.b_type - transplant_data["blood_DNA"] = target.dna.unique_enzymes + transplant_data["species"] = target?.species.name + transplant_data["blood_type"] = target?.dna.b_type + transplant_data["blood_DNA"] = target?.dna.unique_enzymes else - transplant_data["species"] = transplant_blood.data["species"] - transplant_data["blood_type"] = transplant_blood.data["blood_type"] - transplant_data["blood_DNA"] = transplant_blood.data["blood_DNA"] + transplant_data["species"] = transplant_blood?.data["species"] + transplant_data["blood_type"] = transplant_blood?.data["blood_type"] + transplant_data["blood_DNA"] = transplant_blood?.data["blood_DNA"] owner = target loc = owner @@ -428,6 +469,46 @@ var/list/organ_cache = list() bitten(user) return +/obj/item/organ/attackby(obj/item/weapon/W as obj, mob/user as mob) + if(can_butcher(W, user)) + butcher(W, user) + return + + return ..() + +/obj/item/organ/proc/can_butcher(var/obj/item/O, var/mob/living/user) + if(butcherable && meat_type) + + if(istype(O, /obj/machinery/gibber)) // The great equalizer. + return TRUE + + if(robotic >= ORGAN_ROBOT) + if(O.is_screwdriver()) + return TRUE + + else + if(is_sharp(O) && has_edge(O)) + return TRUE + + return FALSE + +/obj/item/organ/proc/butcher(var/obj/item/O, var/mob/living/user, var/atom/newtarget) + if(robotic >= ORGAN_ROBOT) + user?.visible_message("[user] disassembles \the [src].") + + else + user?.visible_message("[user] butchers \the [src].") + + if(!newtarget) + newtarget = get_turf(src) + + var/obj/item/newmeat = new meat_type(newtarget) + + if(istype(newmeat, /obj/item/weapon/reagent_containers/food/snacks/meat)) + newmeat.name = "[src.name] [newmeat.name]" // "liver meat" "heart meat", etc. + + qdel(src) + /obj/item/organ/proc/organ_can_feel_pain() if(species.flags & NO_PAIN) return 0 diff --git a/code/modules/organs/organ_external.dm b/code/modules/organs/organ_external.dm index 3dd0cc0309..05f7e4c201 100644 --- a/code/modules/organs/organ_external.dm +++ b/code/modules/organs/organ_external.dm @@ -34,7 +34,8 @@ var/body_part = null // Part flag var/icon_position = 0 // Used in mob overlay layering calculations. var/model // Used when caching robolimb icons. - var/force_icon // Used to force override of species-specific limb icons (for prosthetics). + var/force_icon // Used to force override of species-specific limb icons (for prosthetics). Also used for any limbs chopped from a simple mob, and then attached to humans. + var/force_icon_key // Used to force the override of the icon-key generated using the species. Must be used in tandem with the above. var/icon/mob_icon // Cached icon for use in mob overlays. var/gendered_icon = 0 // Whether or not the icon state appends a gender. var/s_tone // Skin tone. @@ -97,7 +98,7 @@ qdel(splinted) splinted = null - if(owner) + if(istype(owner)) owner.organs -= src owner.organs_by_name[organ_tag] = null owner.organs_by_name -= organ_tag @@ -199,7 +200,7 @@ return dislocated = 1 - if(owner) + if(istype(owner)) owner.verbs |= /mob/living/carbon/human/proc/relocate /obj/item/organ/external/proc/relocate() @@ -207,7 +208,7 @@ return dislocated = 0 - if(owner) + if(istype(owner)) owner.shock_stage += 20 //check to see if we still need the verb @@ -221,7 +222,7 @@ /obj/item/organ/external/New(var/mob/living/carbon/holder) ..(holder, 0) - if(owner) + if(istype(owner)) replaced(owner) sync_colour_to_human(owner) spawn(1) @@ -887,11 +888,11 @@ Note that amputating the affected organ does in fact remove the infection from t var/mob/living/carbon/human/victim = owner //Keep a reference for post-removed(). var/obj/item/organ/external/parent_organ = parent - var/use_flesh_colour = species.get_flesh_colour(owner) - var/use_blood_colour = species.get_blood_colour(owner) + var/use_flesh_colour = species?.get_flesh_colour(owner) ? species.get_flesh_colour(owner) : "#C80000" + var/use_blood_colour = species?.get_blood_colour(owner) ? species.get_blood_colour(owner) : "#C80000" removed(null, ignore_children) - victim.traumatic_shock += 60 + victim?.traumatic_shock += 60 if(parent_organ) var/datum/wound/lost_limb/W = new (src, disintegrate, clean) @@ -907,9 +908,12 @@ Note that amputating the affected organ does in fact remove the infection from t stump.update_damages() spawn(1) - victim.updatehealth() - victim.UpdateDamageIcon() - victim.update_icons_body() + if(istype(victim)) + victim.updatehealth() + victim.UpdateDamageIcon() + victim.update_icons_body() + else + victim.update_icons() dir = 2 var/atom/droploc = victim.drop_location() @@ -1236,7 +1240,7 @@ Note that amputating the affected organ does in fact remove the infection from t organ.loc = src // Remove parent references - parent.children -= src + parent?.children -= src parent = null release_restraints(victim) diff --git a/code/modules/organs/organ_icon.dm b/code/modules/organs/organ_icon.dm index a5be7341fc..1fda536c93 100644 --- a/code/modules/organs/organ_icon.dm +++ b/code/modules/organs/organ_icon.dm @@ -58,7 +58,7 @@ var/global/list/limb_icon_cache = list() cut_overlays() //Every 'addon' below requires information from species - if(!owner || !owner.species) + if(!iscarbon(owner) || !owner.species) return //Eye color/icon @@ -135,7 +135,10 @@ var/global/list/limb_icon_cache = list() if(owner && owner.gender == FEMALE) gender = "f" - icon_cache_key = "[icon_name]_[species ? species.get_bodytype() : SPECIES_HUMAN]" //VOREStation Edit + if(!force_icon_key) + icon_cache_key = "[icon_name]_[species ? species.get_bodytype() : SPECIES_HUMAN]" //VOREStation Edit + else + icon_cache_key = "[icon_name]_[force_icon_key]" if(force_icon) mob_icon = new /icon(force_icon, "[icon_name][gendered_icon ? "_[gender]" : ""]") diff --git a/code/modules/organs/subtypes/machine.dm b/code/modules/organs/subtypes/machine.dm index 8624a71453..01b0abdc61 100644 --- a/code/modules/organs/subtypes/machine.dm +++ b/code/modules/organs/subtypes/machine.dm @@ -38,6 +38,7 @@ var/brain_type = /obj/item/device/mmi var/obj/item/device/mmi/stored_mmi robotic = ORGAN_ASSISTED + butcherable = FALSE /obj/item/organ/internal/mmi_holder/Destroy() if(stored_mmi && (stored_mmi.loc == src)) diff --git a/code/modules/organs/subtypes/standard.dm b/code/modules/organs/subtypes/standard.dm index bb4a9c7a13..1ebd92106c 100644 --- a/code/modules/organs/subtypes/standard.dm +++ b/code/modules/organs/subtypes/standard.dm @@ -282,14 +282,15 @@ /obj/item/organ/external/head/removed() if(owner) - name = "[owner.real_name]'s head" - owner.drop_from_inventory(owner.glasses) - owner.drop_from_inventory(owner.head) - owner.drop_from_inventory(owner.l_ear) - owner.drop_from_inventory(owner.r_ear) - owner.drop_from_inventory(owner.wear_mask) - spawn(1) - owner.update_hair() + if(iscarbon(owner)) + name = "[owner.real_name]'s head" + owner.drop_from_inventory(owner.glasses) + owner.drop_from_inventory(owner.head) + owner.drop_from_inventory(owner.l_ear) + owner.drop_from_inventory(owner.r_ear) + owner.drop_from_inventory(owner.wear_mask) + spawn(1) + owner.update_hair() get_icon() ..() diff --git a/html/changelogs/mechoid - butchery.yml b/html/changelogs/mechoid - butchery.yml new file mode 100644 index 0000000000..433bf787e7 --- /dev/null +++ b/html/changelogs/mechoid - butchery.yml @@ -0,0 +1,37 @@ +################################ +# Example Changelog File +# +# Note: This file, and files beginning with ".", and files that don't end in ".yml" will not be read. If you change this file, you will look really dumb. +# +# Your changelog will be merged with a master changelog. (New stuff added only, and only on the date entry for the day it was merged.) +# When it is, any changes listed below will disappear. +# +# Valid Prefixes: +# bugfix +# wip (For works in progress) +# tweak +# soundadd +# sounddel +# rscadd (general adding of nice things) +# rscdel (general deleting of nice things) +# imageadd +# imagedel +# maptweak +# spellcheck (typo fixes) +# experiment +################################# + +# Your name. +author: Mechoid + +# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again. +delete-after: True + +# Any changes you've made. See valid prefix list above. +# INDENT WITH TWO SPACES. NOT TABS. SPACES. +# SCREW THIS UP AND IT WON'T WORK. +# Also, all entries are changed into a single [] after a master changelog generation. Just remove the brackets when you add new entries. +# Please surround your changes in double quotes ("), as certain characters otherwise screws up compiling. The quotes will not show up in the changelog. +changes: + - rscadd: "Animals can be butchered for organs and hide. Requires scraping (sharp), washing (water or washing machine), and then drying (bonfire or drying rack)." + - rscadd: "Organs can be butchered for meat, named "[organ] meat". Heart meat, liver meat, etc. Brains from player mobs cannot be butchered." diff --git a/icons/obj/butchery.dmi b/icons/obj/butchery.dmi new file mode 100644 index 0000000000..eef08d1aa7 Binary files /dev/null and b/icons/obj/butchery.dmi differ diff --git a/vorestation.dme b/vorestation.dme index 31d0ea3395..b20d26992d 100644 --- a/vorestation.dme +++ b/vorestation.dme @@ -2610,6 +2610,7 @@ #include "code\modules\mob\language\station_YW.dm" #include "code\modules\mob\language\synthetic.dm" #include "code\modules\mob\living\autohiss.dm" +#include "code\modules\mob\living\butchering.dm" #include "code\modules\mob\living\damage_procs.dm" #include "code\modules\mob\living\death.dm" #include "code\modules\mob\living\default_language.dm" @@ -2624,6 +2625,7 @@ #include "code\modules\mob\living\living_vr.dm" #include "code\modules\mob\living\login.dm" #include "code\modules\mob\living\logout.dm" +#include "code\modules\mob\living\organs.dm" #include "code\modules\mob\living\say.dm" #include "code\modules\mob\living\status_indicators.dm" #include "code\modules\mob\living\bot\bot.dm" @@ -2857,6 +2859,7 @@ #include "code\modules\mob\living\silicon\robot\subtypes\syndicate.dm" #include "code\modules\mob\living\simple_animal\aliens\synx.dm" #include "code\modules\mob\living\simple_mob\appearance.dm" +#include "code\modules\mob\living\simple_mob\butchering.dm" #include "code\modules\mob\living\simple_mob\combat.dm" #include "code\modules\mob\living\simple_mob\defense.dm" #include "code\modules\mob\living\simple_mob\hands.dm"