Files
Bubberstation/code/datums/elements/basic_eating.dm
Roxy ba988f2230 Refactor split_stack to two separate procs, fixes a hard delete in the process (#91306)
## About The Pull Request

I was trying to fix a hard delete and it became a whole thing
- Reworks `/obj/item/stack/proc/split_stack` to no longer also try to
put it in the user's hands, or take a user at all. The proc now purely
splits the stack and returns the new one
- Creates `/obj/item/stack/proc/split_n_take` which uses `split_stack`
and does the other behaviors like fingerprint adding and putting in the
user's hands
- Update usages of `split_stack` to either properly use it and remove
the code added to get around the put in hands behavior, or change them
to use `split_n_take` instead
- Fix a random bug in pipe bomb building I noticed while testing

## Why It's Good For The Game

Ultimately this fixes a hard delete with goldgrubs eating a piece of ore
while sitting on top of another piece of ore of the same type (the put
in hands behavior of split stack would cause the ore being consumed to
drop to the ground briefly, merge with the ore there, and then qdel
while still being moved to the goldgrub contents), but also added bonus
that it makes `split_stack` just split the stack and not some other
stuff too. Also a pipe bomb bug fix

## Changelog

🆑
fix: fixed being able to add seemingly infinite refined bluespace
crystals to pipe bombs
refactor: /obj/item/stack/split_stack no longer tries to move the stack
into the user's hands, use /obj/item/stack/split_n_take for that
/🆑
2025-05-24 22:07:11 -04:00

114 lines
3.8 KiB
Plaintext

/**
* ## basic eating element!
*
* Small behavior for non-carbons to eat certain stuff they interact with
*/
/datum/element/basic_eating
element_flags = ELEMENT_BESPOKE
argument_hash_start_idx = 2
/// Amount to heal
var/heal_amt
/// Amount to hurt
var/damage_amount
/// Type of hurt to apply
var/damage_type
/// Whether to flavor it as drinking rather than eating.
var/drinking
/// If true, we put food in our tummy instead of deleting it
var/add_to_contents
/// Types the animal can eat.
var/list/food_types
/datum/element/basic_eating/Attach(datum/target, heal_amt = 0, damage_amount = 0, damage_type = null, drinking = FALSE, add_to_contents = FALSE, food_types = list())
. = ..()
if(!isliving(target))
return ELEMENT_INCOMPATIBLE
ADD_TRAIT(target, TRAIT_MOB_EATER, REF(src))
src.heal_amt = heal_amt
src.damage_amount = damage_amount
src.damage_type = damage_type
src.drinking = drinking
src.add_to_contents = add_to_contents
src.food_types = food_types
RegisterSignal(target, COMSIG_ATOM_ITEM_INTERACTION, PROC_REF(try_feed))
RegisterSignal(target, COMSIG_LIVING_UNARMED_ATTACK, PROC_REF(on_unarm_attack))
/datum/element/basic_eating/Detach(datum/target)
REMOVE_TRAIT(target, TRAIT_MOB_EATER, REF(src))
UnregisterSignal(target, list(
COMSIG_LIVING_UNARMED_ATTACK,
COMSIG_ATOM_ITEM_INTERACTION,
))
return ..()
/datum/element/basic_eating/proc/try_feed(atom/source, mob/living/user, atom/possible_food)
SIGNAL_HANDLER
if(user.combat_mode || !is_type_in_list(possible_food, food_types))
return NONE
var/mob/living/living_source = source
if(living_source.stat != CONSCIOUS)
return NONE
return try_eating(source, possible_food, user) ? ITEM_INTERACT_SUCCESS : NONE
/datum/element/basic_eating/proc/on_unarm_attack(mob/living/eater, atom/target, proximity, modifiers)
SIGNAL_HANDLER
if(!proximity)
return NONE
if(try_eating(eater, target))
return COMPONENT_CANCEL_ATTACK_CHAIN
return NONE
/datum/element/basic_eating/proc/try_eating(mob/living/eater, atom/target, mob/living/feeder)
if(!is_type_in_list(target, food_types))
return FALSE
if(SEND_SIGNAL(eater, COMSIG_MOB_PRE_EAT, target, feeder) & COMSIG_MOB_CANCEL_EAT)
return FALSE
if(add_to_contents && !ismovable(target))
return FALSE
var/eat_verb
if(drinking)
eat_verb = pick("slurp","sip","guzzle","drink","quaff","suck")
else
eat_verb = pick("bite","chew","nibble","gnaw","gobble","chomp")
if (heal_amt > 0)
var/healed = heal_amt && eater.health < eater.maxHealth
eater.heal_overall_damage(heal_amt)
eater.visible_message(span_notice("[eater] [eat_verb]s [target]."), span_notice("You [eat_verb] [target][healed ? ", restoring some health" : ""]."))
else if (damage_amount > 0 && damage_type)
eater.apply_damage(damage_amount, damage_type)
eater.visible_message(span_notice("[eater] [eat_verb]s [target], and seems to hurt itself."), span_notice("You [eat_verb] [target], hurting yourself in the process."))
else
eater.visible_message(span_notice("[eater] [eat_verb]s [target]."), span_notice("You [eat_verb] [target]."))
finish_eating(eater, target, feeder)
return TRUE
/datum/element/basic_eating/proc/finish_eating(mob/living/eater, atom/target, mob/living/feeder)
set waitfor = FALSE
if(drinking)
playsound(eater.loc,'sound/items/drink.ogg', rand(10,50), TRUE)
else
playsound(eater.loc,'sound/items/eatfood.ogg', rand(10,50), TRUE)
var/atom/final_target = target
if(SEND_SIGNAL(eater, COMSIG_MOB_ATE, final_target, feeder) & COMSIG_MOB_TERMINATE_EAT)
return
if(isstack(target)) //if stack, only consume 1
var/obj/item/stack/food_stack = target
final_target = food_stack.split_stack(1)
eater.log_message("has eaten [target], [add_to_contents ? "swallowing it" : "destroying it"]!", LOG_ATTACK)
if (add_to_contents)
var/atom/movable/movable_target = final_target
movable_target.forceMove(eater)
else
qdel(final_target)