Files
Bubberstation/code/datums/components/udder.dm
T
LemonInTheDark ae5a4f955d Pulls apart the vestiges of components still hanging onto signals (#75914)
## About The Pull Request

Signals were initially only usable with component listeners, which while
no longer the case has lead to outdated documentation, names, and a
similar location in code.

This pr pulls the two apart. Partially because mso thinks we should, but
also because they really aren't directly linked anymore, and having them
in this midstate just confuses people.

[Renames comp_lookup to listen_lookup, since that's what it
does](https://github.com/tgstation/tgstation/commit/102b79694fa8eb57ecf7b36032616a9e368ccced)

[Moves signal procs over to their own
file](https://github.com/tgstation/tgstation/commit/33d07d01fd336726b4f6f6f1b61bb0b3f11a00dc)

[Renames the PREQDELETING and QDELETING comsigs to drop the parent bit
since they can hook to more then just comps
now](https://github.com/tgstation/tgstation/commit/335ea4ad081ec63c42cfa05856e582cca833af6e)

[Does something similar to the attackby comsigs (PARENT ->
ATOM)](https://github.com/tgstation/tgstation/commit/210e57051df63f88dac3dd83321236da825aae5e)

[And finally passes over the examine
signals](https://github.com/tgstation/tgstation/commit/65917658fb8a1e7d28ae23c9437a583d646f0302)

## Why It's Good For The Game

Code makes more sense, things are better teased apart, s just good imo

## Changelog
🆑
refactor: Pulled apart the last vestiges of names/docs directly linking
signals to components
/🆑
2023-06-09 06:14:31 +00:00

171 lines
6.7 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_typepath = /datum/reagent/consumable/milk)
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, parent, on_generate_callback, reagent_produced_typepath)
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)
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
///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
/obj/item/udder/Initialize(mapload, udder_mob, on_generate_callback, reagent_produced_typepath = /datum/reagent/consumable/milk)
src.udder_mob = udder_mob
src.on_generate_callback = on_generate_callback
create_reagents(size, REAGENT_HOLDER_ALIVE)
src.reagent_produced_typepath = reagent_produced_typepath
initial_conditions()
. = ..()
/obj/item/udder/Destroy()
. = ..()
STOP_PROCESSING(SSobj, src)
udder_mob = 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)
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(prob(5))
reagents.add_reagent(reagent_produced_typepath, rand(5, 10))
if(on_generate_callback)
on_generate_callback.Invoke(reagents.total_volume, reagents.maximum_volume)
/**
* 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/transfered = reagents.trans_to(milk_holder, rand(5,10))
if(transfered)
user.visible_message(span_notice("[user] milks [src] using \the [milk_holder]."), span_notice("You milk [src] using \the [milk_holder]."))
else
to_chat(user, span_warning("The udder is dry. Wait a bit longer..."))
/**
* # gutlunch udder subtype
*
* Used by gutlunches, and generates healing reagents instead of milk on eating gibs instead of a process. Starts empty!
* Female gutlunches (ahem, guthens if you will) make babies when their udder is full under processing, instead of milk generation
*/
/obj/item/udder/gutlunch
name = "nutrient sac"
/obj/item/udder/gutlunch/initial_conditions()
if(!udder_mob)
return
if(udder_mob.gender == FEMALE)
START_PROCESSING(SSobj, src)
RegisterSignal(udder_mob, COMSIG_HOSTILE_PRE_ATTACKINGTARGET, PROC_REF(on_mob_attacking))
/obj/item/udder/gutlunch/process(seconds_per_tick)
var/mob/living/simple_animal/hostile/asteroid/gutlunch/gutlunch = udder_mob
if(reagents.total_volume != reagents.maximum_volume)
return
if(gutlunch.make_babies())
reagents.clear_reagents()
//usually this would be a callback but this is a specifically gutlunch feature so fuck it, gutlunch specific proccall
gutlunch.regenerate_icons(reagents.total_volume, reagents.maximum_volume)
/**
* signal called on parent attacking an atom
*/
/obj/item/udder/gutlunch/proc/on_mob_attacking(mob/living/simple_animal/hostile/gutlunch, atom/target)
SIGNAL_HANDLER
if(is_type_in_typecache(target, gutlunch.wanted_objects)) //we eats
generate()
gutlunch.visible_message(span_notice("[udder_mob] slurps up [target]."))
qdel(target)
return COMPONENT_HOSTILE_NO_ATTACK //there is no longer a target to attack
/obj/item/udder/gutlunch/generate()
var/made_something = FALSE
if(prob(60))
reagents.add_reagent(/datum/reagent/consumable/cream, rand(2, 5))
made_something = TRUE
if(prob(45))
reagents.add_reagent(/datum/reagent/medicine/salglu_solution, rand(2,5))
made_something = TRUE
if(made_something && on_generate_callback)
on_generate_callback.Invoke(reagents.total_volume, reagents.maximum_volume)