Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions cev_eris.dme
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,7 @@
#include "code\datums\autolathe\tools.dm"
#include "code\datums\changelog\changelog.dm"
#include "code\datums\components\_component.dm"
#include "code\datums\components\butchering.dm"
#include "code\datums\components\clothing_sanity_protection.dm"
#include "code\datums\components\fabric.dm"
#include "code\datums\components\jamming.dm"
Expand Down
16 changes: 16 additions & 0 deletions code/__DEFINES/mobs.dm
Original file line number Diff line number Diff line change
Expand Up @@ -255,3 +255,19 @@ GLOBAL_REAL_VAR(list/voice_type2sound = list(

///Managed global that is a reference to the real global
GLOBAL_LIST_INIT(voice_type2sound_ref, voice_type2sound)

//butchering defines
//defines for base success chance
#define BUTCHER_IMPOSSIBLE 5
#define BUTCHER_VERY_HARD 10
#define BUTCHER_HARD 25
#define BUTCHER_CHALLENGING 35
#define BUTCHER_DIFFICULT 45
#define BUTCHER_NORMAL 55
#define BUTCHER_EASY 65
#define BUTCHER_VERY_EASY 80
#define BUTCHER_EFFORTLESS 100//still technically possible to fail with terrible tool + 0 bio, so not 'zero'

//divisor for BIO effect on butchering
//values of bio above this will improve butchering chances, values below will dmg it
#define BUTCHER_BIO_DIVISOR 28
133 changes: 133 additions & 0 deletions code/datums/components/butchering.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
/datum/component/butchering
/// Whether or not this component can be used to butcher currently. Used to temporarily disable an existing butcher component for later
var/butchering_enabled = TRUE

/// Whether or not this component is compatible with blunt tools.
var/can_be_blunt = FALSE

/datum/component/butchering/Initialize(
disabled = FALSE,
can_be_blunt = FALSE,
)
if(disabled)
src.butchering_enabled = FALSE
src.can_be_blunt = can_be_blunt
if(isitem(parent))
RegisterSignal(parent, COMSIG_IATTACK, PROC_REF(onItemAttack))
RegisterSignal(parent, COMSIG_APPVAL, PROC_REF(onStatusChange))

///checks if we can butcher, and intercepts the attack chain if we successfully do so
/datum/component/butchering/proc/onItemAttack(atom/target, mob/living/user, params)
SIGNAL_HANDLER
var/obj/item/ourparent
if(isitem(parent))
ourparent = parent

if(!(user.a_intent == I_HURT))//are we on harm intent?
return

if(isliving(target))
var/mob/living/ourmob = target
if(ourmob.stat == DEAD && (ourmob.butcher_results)) //can we butcher it?
if(butchering_enabled && (can_be_blunt || ourparent.sharp))
INVOKE_ASYNC(src, PROC_REF(startButcher), ourparent, ourmob, user)
return TRUE //ends attack chain, so all we do is butcher
return

///handles the use tool containing our butchering action. needed since signal_handler procs hate do afters
/datum/component/butchering/proc/startButcher(obj/item/source, mob/living/meat, mob/living/user)
to_chat(user, span_notice("You begin to butcher \the [meat]..."))
var/needed_quality = QUALITY_CUTTING
if(!source.has_quality(QUALITY_CUTTING))
needed_quality = null
if(source.use_tool(user, meat, WORKTIME_NORMAL, needed_quality, FAILCHANCE_NORMAL, required_stat = STAT_BIO))
on_butchering(user, meat, source)
/**
* Handles a user butchering a target
*
* Arguments:
* - [butcher][/mob/living]: The mob doing the butchering
* - [meat][/mob/living]: The mob being butchered
* - [source][/obj/item]: The item holding our component, expressly typed
*/
/datum/component/butchering/proc/on_butchering(mob/living/butcher, mob/living/meat, obj/item/source)
//our final items to spawn
var/list/butchered = list()
//did we fail to extract something?
var/mulched
var/turf/dropturf = get_turf(meat)
var/bio = butcher.stats.getStat(STAT_BIO)
//mult to success chance based on tool quality
var/toolpowr = 0.5

if(source.has_quality(QUALITY_CUTTING))//otherwise, we're working with something stupid like a glass shard or a beartrap(truly, advanced cutting tool)
toolpowr = round((source.get_tool_quality(QUALITY_CUTTING) / 10))

if(!meat.butcher_results)
log_runtime(" [meat.type] was butchered without any possible butcher results.")
meat.gib()

//for getting the dialogue hinting at relative power
var/msgpowr = toolpowr * (max((bio / BUTCHER_BIO_DIVISOR), 0.25))
switch(msgpowr)
if(0 to 0.8)//go get an actual knife you gross-ass roundstart vagabond
butcher.visible_message(span_bolddanger("[butcher] can't get anywhere with this tool! Instead, they rip \the [meat] to shreds like an animal!"))
if(0.9 to 3.5)
butcher.visible_message(span_danger("[butcher] messily chops up \the [meat]!"))
if(3.6 to 6)
butcher.visible_message(span_danger("[butcher] carefully butchers \the [meat]."))
if(6.1 to 99)
butcher.visible_message(span_notice("[butcher] precisely dissects \the [meat]."))

// Chance of triggering an additional hazard effect when butchering an animal.
var/hazard_chance = 5
for(var/thing in meat.butcher_results)
var/obj/result = thing
//get the base percentage of awarding this result
var/difficulty = meat.butcher_results[result]
//takes difficulty and multiplies it by tool and bio stat to get final chance
var/trueprob = clamp((difficulty * toolpowr) * max((bio / BUTCHER_BIO_DIVISOR), 0.25), 1, 100)
if(prob(trueprob))
butchered += result
else
mulched++
hazard_chance = hazard_chance + 10//chance of complications increases for each item you fail to harvest

if(mulched)
to_chat(butcher, span_warning("You [LAZYLEN(meat.butcher_results) <= mulched ? "completely destroyed all of" : "lost some of"] the meat from \the [meat]."))

if(LAZYLEN(butchered))
for(var/reward in butchered)
var/obj/item/ourmeat = new reward(dropturf)
ourmeat.name = "[meat.name] [ourmeat.name]"

//try to invoke a hazard effect on the butcher
if(meat.butchery_hazard && prob(hazard_chance))
butcher.visible_message(span_danger("While cutting up \the [src], [butcher]'s hand slips..."), span_danger("While cutting up \the [src], your hand slips..."))
meat.butchery_fail(butcher)

//let's finish up.
meat.drop_embedded()
meat.gib()
if(meat.client)//if a player just got hardgibbed, tattle
message_admins("[meat] ([key_name(meat)]) was butchered for meat by [butcher] ([key_name(butcher)]) [ADMIN_JMP(butcher)].")

///Enables the butchering mechanic.
/datum/component/butchering/proc/enable_butchering(datum/source)
SIGNAL_HANDLER
butchering_enabled = TRUE

///Disables the butchering mechanic.
/datum/component/butchering/proc/disable_butchering(datum/source)
SIGNAL_HANDLER
butchering_enabled = FALSE

///signal check to see if our holder is still sharp, for variable butchering items. If not, shut down the component till sharpness comes back.
/datum/component/butchering/proc/onStatusChange(atom/holder)
SIGNAL_HANDLER
if(isitem(holder))
var/obj/item/itemholder = holder
if(!itemholder.sharp && !can_be_blunt)
disable_butchering()
else if(itemholder.sharp && butchering_enabled == FALSE)
enable_butchering()
2 changes: 2 additions & 0 deletions code/game/jobs/job/civilian.dm
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
wage = WAGE_NONE // Makes his own money
department_account_access = TRUE
stat_modifiers = list(
STAT_BIO = 25,
STAT_ROB = 15,
STAT_TGH = 15,
STAT_VIG = 15,
Expand Down Expand Up @@ -59,6 +60,7 @@
wage = WAGE_NONE //They should get paid by the club owner, otherwise you know what to do.
department_account_access = TRUE
stat_modifiers = list(
STAT_BIO = 18,
STAT_ROB = 10,
STAT_TGH = 10,
STAT_VIG = 5,
Expand Down
12 changes: 6 additions & 6 deletions code/game/machinery/kitchen/gibber.dm
Original file line number Diff line number Diff line change
Expand Up @@ -186,17 +186,17 @@

var/meat_amount = occupant.mob_size / 2
var/meat_type = /obj/item/reagent_containers/food/snacks/meat
if(issuperioranimal(occupant))
var/mob/living/carbon/superior_animal/S = occupant
meat_type = S.meat_type
if(issuperioranimal(occupant) || isanimal(occupant))
if(occupant.butcher_results)//nab a meat from the occupant's butcher results. Otherwise use default meat
for(var/possiblemeat in occupant.butcher_results)
if(istype(possiblemeat, /obj/item/reagent_containers/food/snacks/meat))
meat_type = possiblemeat
break
else if(iscarbon(occupant))
var/mob/living/carbon/C = occupant
meat_type = C.species.meat_type
if(occupant.stats.getPerk(PERK_SURVIVOR))
meat_type = /obj/item/reagent_containers/food/snacks/meat/pork
else if(isanimal(occupant))
var/mob/living/simple_animal/A = occupant
meat_type = A.meat_type

for(var/i in 1 to meat_amount)
var/obj/item/reagent_containers/food/snacks/meat/new_meat = new meat_type(src)
Expand Down
2 changes: 2 additions & 0 deletions code/game/objects/items.dm
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,8 @@
if(chameleon_type)
verbs.Add(/obj/item/proc/set_chameleon_appearance)
tact_visual = new /obj/effect/effect/melee/alert
if(sharp && !isProjectile(src))//any item with sharpness on spawn gets butchering
AddComponent(/datum/component/butchering, FALSE, FALSE)
. = ..()

/obj/item/Destroy(force)
Expand Down
2 changes: 2 additions & 0 deletions code/game/objects/items/weapons/tools/mods/_upgrades.dm
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,8 @@
T.max_upgrades += tool_upgrades[UPGRADE_MAXUPGRADES]
if(tool_upgrades[UPGRADE_SHARP])
T.sharp = tool_upgrades[UPGRADE_SHARP]
if(!T.GetComponent(/datum/component/butchering))
T.AddComponent(/datum/component/butchering, FALSE, FALSE)
if(tool_upgrades[UPGRADE_COLOR])
T.color = tool_upgrades[UPGRADE_COLOR]
if(tool_upgrades[UPGRADE_ITEMFLAGPLUS])
Expand Down
3 changes: 1 addition & 2 deletions code/modules/holodeck/HolodeckObjects.dm
Original file line number Diff line number Diff line change
Expand Up @@ -487,8 +487,7 @@
icon_dead = "holo4"
alpha = 127
icon_gib = null
meat_amount = 0
meat_type = null
butcher_results = null

/mob/living/simple_animal/hostile/carp/holodeck/New()
..()
Expand Down
25 changes: 2 additions & 23 deletions code/modules/mob/living/carbon/superior_animal/defense.dm
Original file line number Diff line number Diff line change
@@ -1,20 +1,3 @@
/mob/living/carbon/superior_animal/proc/harvest(mob/user)
var/actual_meat_amount = max(1,(meat_amount/2))
if(meat_type && actual_meat_amount>0 && (stat == DEAD))
drop_embedded()
for(var/i=0;i<actual_meat_amount;i++)
var/obj/item/meat = new meat_type(get_turf(src))
meat.name = "[src.name] [meat.name]"
if(issmall(src))
user.visible_message(span_danger("[user] chops up \the [src]!"))
var/obj/effect/decal/cleanable/blood/blood_effect = new/obj/effect/decal/cleanable/blood/splatter(get_turf(src))
blood_effect.basecolor = bloodcolor
blood_effect.update_icon()
qdel(src)
else
user.visible_message(span_danger("[user] butchers \the [src] messily!"))
gib()

/mob/living/carbon/superior_animal/update_lying_buckled_and_verb_status()
..()

Expand All @@ -25,12 +8,8 @@
updatehealth()

/mob/living/carbon/superior_animal/attackby(obj/item/I, mob/living/user, params)
if (meat_type && (stat == DEAD) && (QUALITY_CUTTING in I.tool_qualities))
if (I.use_tool(user, src, WORKTIME_NORMAL, QUALITY_CUTTING, FAILCHANCE_NORMAL, required_stat = STAT_BIO))
harvest(user)
else
. = ..()
updatehealth()
. = ..()
updatehealth()

/mob/living/carbon/superior_animal/resolve_item_attack(obj/item/I, mob/living/user, hit_zone)
//mob.attackby -> item.attack -> mob.resolve_item_attack -> item.apply_hit_effect
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,9 @@
move_to_delay = 5
turns_per_move = 5
see_in_dark = 10
meat_type = /obj/item/reagent_containers/food/snacks/meat/spider
meat_amount = 3
butcher_results = list(/obj/item/reagent_containers/food/snacks/meat/spider = BUTCHER_NORMAL,\
/obj/item/reagent_containers/food/snacks/meat/spider = BUTCHER_DIFFICULT,\
/obj/item/reagent_containers/food/snacks/meat/spider = BUTCHER_DIFFICULT)
stop_automated_movement_when_pulled = 0

melee_damage_lower = 12
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
melee_damage_upper = 20
poison_per_bite = 8
move_to_delay = 3
meat_type = /obj/item/reagent_containers/food/snacks/meat/spider/hunter
meat_amount = 4
butcher_results = list(/obj/item/reagent_containers/food/snacks/meat/spider/hunter = BUTCHER_NORMAL,\
/obj/item/reagent_containers/food/snacks/meat/spider/hunter = BUTCHER_DIFFICULT,\
/obj/item/reagent_containers/food/snacks/meat/spider/hunter = BUTCHER_DIFFICULT,\
/obj/item/reagent_containers/food/snacks/meat/spider/hunter = BUTCHER_DIFFICULT)
rarity_value = 75
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,10 @@
poison_per_bite = 3
var/atom/cocoon_target
poison_type = "aranecolmin"
meat_type = /obj/item/reagent_containers/food/snacks/meat/spider/nurse
butcher_results = list(/obj/item/reagent_containers/food/snacks/meat/spider/nurse = BUTCHER_NORMAL,\
/obj/item/reagent_containers/food/snacks/meat/spider/nurse = BUTCHER_DIFFICULT,\
/obj/item/reagent_containers/food/snacks/meat/spider/nurse = BUTCHER_DIFFICULT)
move_to_delay = 4
meat_amount = 3
rarity_value = 75
var/fed = 0
var/egg_inject_chance = 4
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,7 @@
speak_chance = 5

see_in_dark = 10
meat_type = null
meat_amount = 0
butcher_results = null
stop_automated_movement_when_pulled = 0
wander = FALSE
viewRange = 8
Expand Down
4 changes: 2 additions & 2 deletions code/modules/mob/living/carbon/superior_animal/roach/life.dm
Original file line number Diff line number Diff line change
Expand Up @@ -91,12 +91,12 @@
// End message
src.visible_message(span_warning("\The [src] finishes eating \the [eat_target], leaving only bones."))
// Get fed
fed += rand(1,tasty.meat_amount)
fed += rand(1,LAZYLEN(tasty.butcher_results))
if (isroach(tasty))
var/mob/living/carbon/superior_animal/roach/cannibalism = tasty
fed += cannibalism.fed
if(istype(src, /mob/living/carbon/superior_animal/roach/roachling))
if(tasty.meat_amount >= 6)// ate a fuhrer or kaiser
if(istype(tasty, /mob/living/carbon/superior_animal/roach/kaiser) || istype(tasty, /mob/living/carbon/superior_animal/roach/fuhrer))// ate a fuhrer or kaiser
var/mob/living/carbon/superior_animal/roach/roachling/bigboss = src
bigboss.big_boss = TRUE
clearEatTarget()
Expand Down
4 changes: 2 additions & 2 deletions code/modules/mob/living/carbon/superior_animal/roach/roach.dm
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
turns_per_move = 4
turns_since_move = 0

meat_type = /obj/item/reagent_containers/food/snacks/meat/roachmeat/kampfer
meat_amount = 2
butcher_results = list(/obj/item/reagent_containers/food/snacks/meat/roachmeat/kampfer = BUTCHER_NORMAL,\
/obj/item/reagent_containers/food/snacks/meat/roachmeat/kampfer = BUTCHER_NORMAL)

maxHealth = 10
health = 10
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@
maxHealth = 25
health = 25
melee_damage_upper = 3
meat_type = /obj/item/reagent_containers/food/snacks/meat/roachmeat/benzin
meat_amount = 3
butcher_results = list(/obj/item/reagent_containers/food/snacks/meat/roachmeat/benzin = 35,\
/obj/item/reagent_containers/food/snacks/meat/roachmeat/benzin = 35,\
/obj/item/reagent_containers/food/snacks/meat/roachmeat/benzin = 35)
rarity_value = 15


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@
icon_state = "bluespaceroach"
maxHealth = 25
health = 25
meat_type = /obj/item/bluespace_crystal
butcher_results = list(/obj/item/bluespace_crystal = BUTCHER_CHALLENGING,\
/obj/item/bluespace_crystal = BUTCHER_HARD,\
/obj/item/bluespace_crystal = BUTCHER_HARD,\
/obj/item/bluespace_crystal = BUTCHER_HARD)
melee_damage_lower = 4
melee_damage_upper = 11
armor_divisor = ARMOR_PEN_MAX // Hits through armor
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,12 @@
extra_burrow_chance = 100
blattedin_revives_left = 0 //He only lives once, cuz he's huge

meat_type = /obj/item/reagent_containers/food/snacks/meat/roachmeat/fuhrer
meat_amount = 6
butcher_results = list(/obj/item/reagent_containers/food/snacks/meat/roachmeat/fuhrer = BUTCHER_EASY,\
/obj/item/reagent_containers/food/snacks/meat/roachmeat/fuhrer = BUTCHER_NORMAL,\
/obj/item/reagent_containers/food/snacks/meat/roachmeat/fuhrer = BUTCHER_NORMAL,\
/obj/item/reagent_containers/food/snacks/meat/roachmeat/fuhrer = BUTCHER_DIFFICULT,\
/obj/item/reagent_containers/food/snacks/meat/roachmeat/fuhrer = BUTCHER_DIFFICULT,\
/obj/item/reagent_containers/food/snacks/meat/roachmeat/fuhrer = BUTCHER_DIFFICULT)

sanity_damage = 1
rarity_value = 90
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,11 @@

attacktext = list("slashed", "rended", "diced")

meat_type = /obj/item/reagent_containers/food/snacks/meat/roachmeat/jager
meat_amount = 3

butcher_results = list(/obj/item/reagent_containers/food/snacks/meat/roachmeat/jager = BUTCHER_NORMAL,\
/obj/item/reagent_containers/food/snacks/meat/roachmeat/jager = BUTCHER_NORMAL,\
/obj/item/reagent_containers/food/snacks/meat/roachmeat/jager = BUTCHER_DIFFICULT,\
/obj/item/reagent_containers/food/snacks/meat/roachmeat/jager = BUTCHER_DIFFICULT)
rarity_value = 11.25

// Armor related variables - jager jacket
Expand Down
Loading
Loading