mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2025-12-14 03:32:00 +00:00
## 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 /🆑
220 lines
8.2 KiB
Plaintext
220 lines
8.2 KiB
Plaintext
/**
|
|
* Udder component; for farm animals to generate milk.
|
|
*
|
|
* Used for cows, goats, gutlunches. neat!
|
|
*/
|
|
/datum/component/udder
|
|
///abstract item for managing reagents (further down in this file)
|
|
var/obj/item/udder/udder
|
|
///optional proc to callback to when the udder is milked
|
|
var/datum/callback/on_milk_callback
|
|
|
|
//udder_type and reagent_produced_typepath are typepaths, not reference
|
|
/datum/component/udder/Initialize(udder_type = /obj/item/udder, datum/callback/on_milk_callback, datum/callback/on_generate_callback, reagent_produced_override)
|
|
if(!isliving(parent)) //technically is possible to drop this on carbons... but you wouldn't do that to me, would you?
|
|
return COMPONENT_INCOMPATIBLE
|
|
udder = new udder_type(null)
|
|
udder.add_features(parent, on_generate_callback, reagent_produced_override)
|
|
src.on_milk_callback = on_milk_callback
|
|
|
|
/datum/component/udder/RegisterWithParent()
|
|
RegisterSignal(parent, COMSIG_ATOM_EXAMINE, PROC_REF(on_examine))
|
|
RegisterSignal(parent, COMSIG_ATOM_ATTACKBY, PROC_REF(on_attackby))
|
|
|
|
/datum/component/udder/UnregisterFromParent()
|
|
QDEL_NULL(udder)
|
|
on_milk_callback = null
|
|
UnregisterSignal(parent, list(COMSIG_ATOM_EXAMINE, COMSIG_ATOM_ATTACKBY))
|
|
|
|
///signal called on parent being examined
|
|
/datum/component/udder/proc/on_examine(datum/source, mob/user, list/examine_list)
|
|
SIGNAL_HANDLER
|
|
|
|
var/mob/living/milked = parent
|
|
if(milked.stat != CONSCIOUS)
|
|
return //come on now
|
|
|
|
var/udder_filled_percentage = PERCENT(udder.reagents.total_volume / udder.reagents.maximum_volume)
|
|
switch(udder_filled_percentage)
|
|
if(0 to 10)
|
|
examine_list += span_notice("[parent]'s [udder] is dry.")
|
|
if(11 to 99)
|
|
examine_list += span_notice("[parent]'s [udder] can be milked if you have something to contain it.")
|
|
if(100)
|
|
examine_list += span_notice("[parent]'s [udder] is round and full, and can be milked if you have something to contain it.")
|
|
|
|
|
|
///signal called on parent being attacked with an item
|
|
/datum/component/udder/proc/on_attackby(datum/source, obj/item/milking_tool, mob/user)
|
|
SIGNAL_HANDLER
|
|
|
|
var/mob/living/milked = parent
|
|
if(milked.stat == CONSCIOUS && istype(milking_tool, /obj/item/reagent_containers/cup))
|
|
udder.milk(milking_tool, user)
|
|
if(on_milk_callback)
|
|
on_milk_callback.Invoke(udder.reagents.total_volume, udder.reagents.maximum_volume)
|
|
return COMPONENT_NO_AFTERATTACK
|
|
|
|
/**
|
|
* # udder item
|
|
*
|
|
* Abstract item that is held in nullspace and manages reagents. Created by udder component.
|
|
* While perhaps reagents created by udder component COULD be managed in the mob, it would be somewhat finnicky and I actually like the abstract udders.
|
|
*/
|
|
/obj/item/udder
|
|
name = "udder"
|
|
///typepath of reagent produced by the udder
|
|
var/reagent_produced_typepath = /datum/reagent/consumable/milk
|
|
///how much the udder holds
|
|
var/size = 50
|
|
///the probability that the udder will produce the reagent (0 - 100)
|
|
var/production_probability = 5
|
|
///mob that has the udder component
|
|
var/mob/living/udder_mob
|
|
///optional proc to callback to when the udder generates milk
|
|
var/datum/callback/on_generate_callback
|
|
///do we require some food to generate milk?
|
|
var/require_consume_type
|
|
///how long does each food consumption allow us to make milk
|
|
var/require_consume_timer = 2 MINUTES
|
|
///hunger key we set to look for food
|
|
var/hunger_key = BB_CHECK_HUNGRY
|
|
|
|
/obj/item/udder/proc/add_features(parent, callback, reagent_override)
|
|
udder_mob = parent
|
|
on_generate_callback = callback
|
|
create_reagents(size, REAGENT_HOLDER_ALIVE)
|
|
if(reagent_override)
|
|
reagent_produced_typepath = reagent_override
|
|
initial_conditions()
|
|
if(isnull(require_consume_type))
|
|
return
|
|
RegisterSignal(udder_mob, COMSIG_HOSTILE_PRE_ATTACKINGTARGET, PROC_REF(on_mob_consume))
|
|
RegisterSignal(udder_mob, COMSIG_ATOM_ATTACKBY, PROC_REF(on_mob_feed))
|
|
udder_mob.ai_controller?.set_blackboard_key(BB_CHECK_HUNGRY, TRUE)
|
|
|
|
/obj/item/udder/proc/on_mob_consume(datum/source, atom/feed)
|
|
SIGNAL_HANDLER
|
|
|
|
if(!istype(feed, require_consume_type))
|
|
return
|
|
INVOKE_ASYNC(src, PROC_REF(handle_consumption), feed)
|
|
return COMPONENT_HOSTILE_NO_ATTACK
|
|
|
|
/obj/item/udder/proc/on_mob_feed(datum/source, atom/used_item, mob/living/user)
|
|
SIGNAL_HANDLER
|
|
|
|
if(!istype(used_item, require_consume_type))
|
|
return
|
|
INVOKE_ASYNC(src, PROC_REF(handle_consumption), used_item, user)
|
|
return COMPONENT_NO_AFTERATTACK
|
|
|
|
/obj/item/udder/proc/handle_consumption(atom/movable/food, mob/user)
|
|
if(locate(food.type) in src)
|
|
if(user)
|
|
user.balloon_alert(user, "already full!")
|
|
return
|
|
playsound(udder_mob.loc,'sound/items/eatfood.ogg', 50, TRUE)
|
|
udder_mob.visible_message(span_notice("[udder_mob] gobbles up [food]!"), span_notice("You gobble up [food]!"))
|
|
var/atom/movable/final_food = food
|
|
if(isstack(food)) //if stack, only consume 1
|
|
var/obj/item/stack/food_stack = food
|
|
final_food = food_stack.split_stack(1)
|
|
final_food.forceMove(src)
|
|
|
|
/obj/item/udder/Entered(atom/movable/arrived, atom/old_loc, list/atom/old_locs)
|
|
if(!istype(arrived, require_consume_type))
|
|
return ..()
|
|
|
|
udder_mob.ai_controller?.set_blackboard_key(hunger_key, FALSE)
|
|
QDEL_IN(arrived, require_consume_timer)
|
|
return ..()
|
|
|
|
/obj/item/udder/Exited(atom/movable/gone, direction)
|
|
. = ..()
|
|
if(!istype(gone, require_consume_type))
|
|
return
|
|
udder_mob.ai_controller?.set_blackboard_key(hunger_key, TRUE)
|
|
|
|
|
|
/obj/item/udder/Destroy()
|
|
. = ..()
|
|
STOP_PROCESSING(SSobj, src)
|
|
udder_mob = null
|
|
on_generate_callback = null
|
|
|
|
/obj/item/udder/process(seconds_per_tick)
|
|
if(udder_mob.stat != DEAD)
|
|
generate() //callback is on generate() itself as sometimes generate does not add new reagents, or is not called via process
|
|
|
|
/**
|
|
* Proc called on creation separate from the reagent datum creation to allow for signalled milk generation instead of processing milk generation
|
|
* also useful for changing initial amounts in reagent holder (cows start with milk, gutlunches start empty)
|
|
*/
|
|
/obj/item/udder/proc/initial_conditions()
|
|
reagents.add_reagent(reagent_produced_typepath, 20, added_purity = 1)
|
|
START_PROCESSING(SSobj, src)
|
|
|
|
/**
|
|
* Proc called every 2 seconds from SSMobs to add whatever reagent the udder is generating.
|
|
*/
|
|
/obj/item/udder/proc/generate()
|
|
if(!isnull(require_consume_type) && !(locate(require_consume_type) in src))
|
|
return FALSE
|
|
if(!prob(production_probability))
|
|
return FALSE
|
|
reagents.add_reagent(reagent_produced_typepath, rand(5, 10), added_purity = 1)
|
|
if(on_generate_callback)
|
|
on_generate_callback.Invoke(reagents.total_volume, reagents.maximum_volume)
|
|
return TRUE
|
|
|
|
/**
|
|
* Proc called from attacking the component parent with the correct item, moves reagents into the glass basically.
|
|
*
|
|
* Arguments:
|
|
* * obj/item/reagent_containers/cup/milk_holder - what we are trying to transfer the reagents to
|
|
* * mob/user - who is trying to do this
|
|
*/
|
|
/obj/item/udder/proc/milk(obj/item/reagent_containers/cup/milk_holder, mob/user)
|
|
if(milk_holder.reagents.total_volume >= milk_holder.volume)
|
|
to_chat(user, span_warning("[milk_holder] is full."))
|
|
return
|
|
var/transferred = reagents.trans_to(milk_holder, rand(5,10))
|
|
if(transferred)
|
|
user.visible_message(span_notice("[user] milks [udder_mob] using \the [milk_holder]."), span_notice("You milk [udder_mob] using \the [milk_holder]."))
|
|
else
|
|
to_chat(user, span_warning("The udder is dry. Wait a bit longer..."))
|
|
|
|
/**
|
|
* # gutlunch udder subtype
|
|
*/
|
|
|
|
/obj/item/udder/gutlunch
|
|
name = "nutrient sac"
|
|
require_consume_type = /obj/item/stack/ore
|
|
reagent_produced_typepath = /datum/reagent/medicine/mine_salve
|
|
|
|
/obj/item/udder/gutlunch/generate()
|
|
. = ..()
|
|
if(!.)
|
|
return
|
|
if(locate(/obj/item/stack/ore/gold) in src)
|
|
reagents.add_reagent(/datum/reagent/consumable/cream, rand(2, 5), added_purity = 1)
|
|
if(locate(/obj/item/stack/ore/bluespace_crystal) in src)
|
|
reagents.add_reagent(/datum/reagent/medicine/salglu_solution, rand(2,5))
|
|
if(on_generate_callback)
|
|
on_generate_callback.Invoke(reagents.total_volume, reagents.maximum_volume)
|
|
|
|
/obj/item/udder/raptor
|
|
name = "bird udder"
|
|
|
|
/obj/item/udder/raptor/generate()
|
|
if(!prob(production_probability))
|
|
return FALSE
|
|
var/happiness_percentage = udder_mob.ai_controller?.blackboard[BB_BASIC_HAPPINESS]
|
|
if(prob(happiness_percentage))
|
|
reagents.add_reagent(/datum/reagent/consumable/cream, 5, added_purity = 1)
|
|
var/minimum_bound = happiness_percentage > 0.6 ? 10 : 5
|
|
var/upper_bound = minimum_bound + 5
|
|
reagents.add_reagent(reagent_produced_typepath, rand(minimum_bound, upper_bound), added_purity = 1)
|