Exactly, 50% of the original reagents are transferred, you can see it here.
/obj/item/reagent_containers/food/pill/patch
(...)
apply_type = REAGENT_TOUCH
transfer_efficiency = 0.5 //patches aren't as effective at getting chemicals into the bloodstream.
What you are interested is the very next proc in this (patch.dm) file:
/obj/item/reagent_containers/food/pill/patch/attack(mob/living/carbon/M, mob/user, def_zone)
if(!istype(M))
return FALSE
bitesize = 0
if(M.eat(src, user))
if(user.get_active_hand() == src)
user.drop_item() // Only drop if they're holding the patch directly
forceMove(M)
LAZYADD(M.processing_patches, src)
return TRUE
return FALSE
Here, you basically force-feed people something but since it has zero bitesize, they don't crunch on it. The transfer_efficiency ensures you only gave them 50% of the reagents in it. What you want to look out here for is this:
LAZYADD(M.processing_patches, src)
Lists like this are usually used in other procs, so let's follow it. Searching for it brings up life.dm, which handles your mob every tick (among other stuff):
/mob/living/carbon/Life(seconds, times_fired)
(...)
if(LAZYLEN(processing_patches))
handle_patches()
Looks like our patches are more than "forcefeed and transfer units into the person", it has its own proc. Let's see it:
/mob/living/carbon/proc/handle_patches()
var/multiple_patch_multiplier = processing_patches.len > 1 ? (processing_patches.len * 1.5) : 1
var/applied_amount = 0.35 * multiple_patch_multiplier
for(var/patch in processing_patches)
var/obj/item/reagent_containers/food/pill/patch/P = patch
if(P.reagents && P.reagents.total_volume)
var/fractional_applied_amount = applied_amount / P.reagents.total_volume
P.reagents.reaction(src, REAGENT_TOUCH, fractional_applied_amount, P.needs_to_apply_reagents)
P.needs_to_apply_reagents = FALSE
P.reagents.trans_to(src, applied_amount * 0.5)
P.reagents.remove_any(applied_amount * 0.5)
else
if(!P.reagents || P.reagents.total_volume <= 0)
LAZYREMOVE(processing_patches, P)
qdel(P)
That is a lot of maths and I am bad at maths, so let's substitute some of it with numbers. Let's say we applied a 15 units styptic patch and the mob has no other patches applied.
/mob/living/carbon/proc/handle_patches()
var/multiple_patch_multiplier = 1
var/applied_amount = 0.35
for(var/patch in processing_patches)
var/obj/item/reagent_containers/food/pill/patch/P = patch
if(P.reagents && P.reagents.total_volume)
var/fractional_applied_amount = 0.0233333333333333
P.reagents.reaction(src, REAGENT_TOUCH, 0.0233333333333333, P.needs_to_apply_reagents)
P.needs_to_apply_reagents = FALSE
P.reagents.trans_to(src, 0.175)
P.reagents.remove_any(0.175)
else
if(!P.reagents || P.reagents.total_volume <= 0)
LAZYREMOVE(processing_patches, P)
qdel(P)
(Disclaimer: using a number such 0.0233333333333333 will inevitably lead to inaccuracy, but to keep things simple, I'll keep it here).
First, it reacts with the mob with the reaction() proc, let's see that. The first half of the proc checks if the applied reagent is too cold or hot, so we can skip that. The second half is what we are interested in:
for(var/AB in reagent_list)
var/datum/reagent/R = AB
switch(react_type)
if("LIVING")
var/check = reaction_check(A, R)
if(!check)
continue
R.reaction_mob(A, method, R.volume * volume_modifier, show_message)
The last line is the only thing that matters in our case. Volume here refers to the original volume of the patch, which is 15, so it becomes this:
R.reaction_mob(A, method, 15 * 0.0233333333333333, show_message)
This equals to about 0.35, we will use a rounded number here for sanity's sake.
This is a deep rabbit hole, let's keep going!!
So what do we have in the end? Let's go back to handle_patches():
/mob/living/carbon/proc/handle_patches()
var/multiple_patch_multiplier = processing_patches.len > 1 ? (processing_patches.len * 1.5) : 1
var/applied_amount = 0.35 * multiple_patch_multiplier
for(var/patch in processing_patches)
var/obj/item/reagent_containers/food/pill/patch/P = patch
if(P.reagents && P.reagents.total_volume)
var/fractional_applied_amount = applied_amount / P.reagents.total_volume
P.reagents.reaction(src, REAGENT_TOUCH, fractional_applied_amount, P.needs_to_apply_reagents)
P.needs_to_apply_reagents = FALSE
P.reagents.trans_to(src, applied_amount * 0.5)
P.reagents.remove_any(applied_amount * 0.5)
else
if(!P.reagents || P.reagents.total_volume <= 0)
LAZYREMOVE(processing_patches, P)
qdel(P)
Now we know that reaction() applies 0.35 per proc, while the P.reagents.trans_to(src, applied_amount * 0.5) transfers 0.175 units. At any given time, the mob will have 0.175 units in their system (you can use a health analyzer to prove this), but an extra 0.35 will be applied every tick. Application and transfer are different, you'll see soon why!
We know patches transfer half of their volume, so a 15 units styptic patch will only transfer 7.5 units in the end. 7.5 units are transferred in 0.175 increments, so a 15 units patch transfers its contents 42.85 times.
Check what styptic powder does:
/datum/reagent/medicine/styptic_powder/on_mob_life(mob/living/M)
var/update_flags = STATUS_UPDATE_NONE
update_flags |= M.adjustBruteLoss(-2*REAGENTS_EFFECT_MULTIPLIER, FALSE)
return ..() | update_flags
/datum/reagent/medicine/styptic_powder/reaction_mob(mob/living/M, method=REAGENT_TOUCH, volume, show_message = 1)
if(iscarbon(M))
if(method == REAGENT_TOUCH)
M.adjustBruteLoss(-volume)
if(show_message)
to_chat(M, "<span class='notice'>The styptic powder stings like hell as it closes some of your wounds!</span>")
M.emote("scream")
if(method == REAGENT_INGEST)
M.adjustToxLoss(0.5*volume)
if(show_message)
to_chat(M, "<span class='warning'>You feel gross!</span>")
..()
When it ticks inside the body, it heals 2 brute. We know it will tick 42.85 times, so we know the transfer will heal a whoppin' 85.7 brute! But what happened to the applied part? Let's see the reaction_mob() proc. If it is applied via touch, it heals its amount. Its amount is 0.35 (from the reaction() proc from before, remember?) which means we will heal an extra ~14.9975.
Or, to make it a bit more simple: from the transfer, it heals 2 brute, from the application, it heals 0.35, so we heal about 2.35 per tick 42.85 times. We lost some precision due to me not being able to use numbers properly, but we should heal about 100.6975 (+ an extra 0.35 when we actually apply the patch, so about 101.0475).
Let's spawn a human and deal 150 brute to it, then apply a 15u styptic patch. He went down to 48.95, which means he healed 101.05 brute. That is plausibly close to our expected number (101.0475), so you'll have to take my word for it!
What's fascinating is that due to how the apply part is calculated, a twice as big patch does not heal twice as much. The apply part will always heal 0.35 per tick, and the patch will always transfer 0.175 (as long as only one patch is present). So by increasing the volume of the patches, you make it tick for longer, not heal more at each given tick.
To prove it, spawn a 30 units styptic patch and deal 100 brute to a mob. Apply to the patch. Every time the mob falls below 20 brute damage, apply another 100 brute damage to it (to avoid getting him into cardiac arrest). In the end, the mob will heal for 202 damage.
From our calculations, a 30 u styptic patch ticks for 30 / 2 (remember, half of the volume is transferred) / 0.175 = 85.71 times (roughly), healing 2.35 every tick + an initial 0.35. That's 85.71 * 2.35 + 0.35 = 201.7685. Our maths add up!
Frankly, I suck at maths, but I hope it answered your question somewhat!