Jump to content

How do patches work?


Recommended Posts

I've been code diving for medical shit and still can't figure out how patches work. From what the wiki says, a 30u styptic patch should apply 15u (half efficiency), and those 15u will heal 15 brute damage (heal brute equal to volume). But that's not what happens, and for some reason you can heal around 100 brute with a single mini-patch (15u).

Just want a way to know exactly how much damage a specific volume of patch will heal, since it feels kind of random.

  • eyes 1
Link to comment
Share on other sites

On 11/14/2022 at 5:36 PM, Im99%SureThatIm1%Unsure said:

I've been code diving for medical shit and still can't figure out how patches work. From what the wiki says, a 30u styptic patch should apply 15u (half efficiency), and those 15u will heal 15 brute damage (heal brute equal to volume). But that's not what happens, and for some reason you can heal around 100 brute with a single mini-patch (15u).

Just want a way to know exactly how much damage a specific volume of patch will heal, since it feels kind of random.

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!

Edited by Miraviel
Typo fix
  • Thanks 2
  • eyes 1
  • fastparrot 1
Link to comment
Share on other sites

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue. Terms of Use