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"