[MIRROR] Refactor can_inject, and introduce try_inject (#3300)

* Refactor can_inject, and introduce try_inject (#56816)

Splits can_inject into can_inject and try_inject. The latter can perform side effects, while the former cannot. Moved the show_error bool parameter to a flag that try_inject checks.

Changes the signature to can_inject(mob/user, target_zone, injection_flags). This was previously redefined in several places, making it impossible to use named parameters. This refactor now allows named parameters to be used, and thus avoiding the ugliness of TRUE, FALSE, FALSE in parameters.

* Refactor can_inject, and introduce try_inject

Co-authored-by: Mothblocks <35135081+Jared-Fogle@users.noreply.github.com>
This commit is contained in:
SkyratBot
2021-02-11 20:49:47 +01:00
committed by GitHub
parent 9f41912d72
commit b902f89062
21 changed files with 67 additions and 31 deletions

View File

@@ -0,0 +1,8 @@
/// This injection can penetrate through thick clothing.
#define INJECT_CHECK_PENETRATE_THICK (1 << 0)
/// This injection bypasses species specific restrictions.
#define INJECT_CHECK_IGNORE_SPECIES (1 << 1)
/// When trying this injection, show an error message if it fails.
#define INJECT_TRY_SHOW_ERROR_MESSAGE (1 << 2)

View File

@@ -247,7 +247,7 @@
if(ishuman(victim)) // check to see if the limb is actually exposed
var/mob/living/carbon/human/victim_human = victim
if(!victim_human.can_inject(user, TRUE, limb.body_zone, ignore_species = TRUE))
if(!victim_human.try_inject(user, limb.body_zone, INJECT_CHECK_IGNORE_SPECIES | INJECT_TRY_SHOW_ERROR_MESSAGE))
return TRUE
INVOKE_ASYNC(src, .proc/tweezePluck, possible_tweezers, user)

View File

@@ -296,7 +296,7 @@
// & such may need to use bone gel but may be wearing a space suit for..... whatever reason a skeleton would wear a space suit for
if(ishuman(victim))
var/mob/living/carbon/human/victim_human = victim
if(!victim_human.can_inject(user, TRUE, ignore_species = TRUE))
if(!victim_human.try_inject(user, injection_flags = INJECT_CHECK_IGNORE_SPECIES | INJECT_TRY_SHOW_ERROR_MESSAGE))
return TRUE
// lastly, treat them

View File

@@ -151,7 +151,7 @@
suture(I, user)
/datum/wound/slash/try_handling(mob/living/carbon/human/user)
if(user.pulling != victim || user.zone_selected != limb.body_zone || !isfelinid(user) || !victim.can_inject(user, TRUE))
if(user.pulling != victim || user.zone_selected != limb.body_zone || !isfelinid(user) || !victim.try_inject(user, injection_flags = INJECT_TRY_SHOW_ERROR_MESSAGE))
return FALSE
if(DOING_INTERACTION_WITH_TARGET(user, victim))
to_chat(user, "<span class='warning'>You're already interacting with [victim]!</span>")

View File

@@ -56,7 +56,7 @@
return
if(ishuman(target))
var/mob/living/carbon/human/humantarget = target
if (!humantarget.can_inject(user, 1))
if (!humantarget.try_inject(user, injection_flags = INJECT_TRY_SHOW_ERROR_MESSAGE))
return
log_combat(user, target, "attempted to inject", src)

View File

@@ -38,17 +38,17 @@
/// In which we print the message that we're starting to heal someone, then we try healing them. Does the do_after whether or not it can actually succeed on a targeted mob
/obj/item/stack/medical/proc/try_heal(mob/living/patient, mob/user, silent = FALSE)
if(!patient.can_inject(user, TRUE))
if(!patient.try_inject(user, injection_flags = INJECT_TRY_SHOW_ERROR_MESSAGE))
return
if(patient == user)
if(!silent)
user.visible_message("<span class='notice'>[user] starts to apply [src] on [user.p_them()]self...</span>", "<span class='notice'>You begin applying [src] on yourself...</span>")
if(!do_mob(user, patient, self_delay, extra_checks=CALLBACK(patient, /mob/living/proc/can_inject, user, TRUE)))
if(!do_mob(user, patient, self_delay, extra_checks=CALLBACK(patient, /mob/living/proc/try_inject, user, null, INJECT_TRY_SHOW_ERROR_MESSAGE)))
return
else if(other_delay)
if(!silent)
user.visible_message("<span class='notice'>[user] starts to apply [src] on [patient].</span>", "<span class='notice'>You begin applying [src] on [patient]...</span>")
if(!do_mob(user, patient, other_delay, extra_checks=CALLBACK(patient, /mob/living/proc/can_inject, user, TRUE)))
if(!do_mob(user, patient, other_delay, extra_checks=CALLBACK(patient, /mob/living/proc/try_inject, user, null, INJECT_TRY_SHOW_ERROR_MESSAGE)))
return
if(heal(patient, user))

View File

@@ -516,7 +516,7 @@
/datum/plant_gene/trait/stinging/on_throw_impact(obj/item/food/grown/G, atom/target)
if(isliving(target) && G.reagents && G.reagents.total_volume)
var/mob/living/L = target
if(L.reagents && L.can_inject(null, 0))
if(L.reagents && L.can_inject())
var/injecting_amount = max(1, G.seed.potency*0.2) // Minimum of 1, max of 20
G.reagents.trans_to(L, injecting_amount, methods = INJECT)
to_chat(target, "<span class='danger'>You are pricked by [G]!</span>")

View File

@@ -17,7 +17,7 @@
// as a wise man once wrote: "pull over that ass too fat"
REMOVE_TRAIT(src, TRAIT_VENTCRAWLER_ALWAYS, INNATE_TRAIT)
/mob/living/carbon/alien/humanoid/royal/can_inject()
/mob/living/carbon/alien/humanoid/royal/can_inject(mob/user, target_zone, injection_flags)
return FALSE
/mob/living/carbon/alien/humanoid/royal/queen

View File

@@ -184,7 +184,7 @@
/mob/living/carbon/attack_paw(mob/living/carbon/human/M, modifiers)
if(can_inject(M, TRUE))
if(try_inject(M, injection_flags = INJECT_TRY_SHOW_ERROR_MESSAGE))
for(var/thing in diseases)
var/datum/disease/D = thing
if((D.spread_flags & DISEASE_SPREAD_CONTACT_SKIN) && prob(85))

View File

@@ -527,25 +527,29 @@
/mob/living/carbon/human/proc/canUseHUD()
return (mobility_flags & MOBILITY_USE)
/mob/living/carbon/human/can_inject(mob/user, error_msg, target_zone, penetrate_thick = FALSE, ignore_species = FALSE)
/mob/living/carbon/human/can_inject(mob/user, target_zone, injection_flags)
. = TRUE // Default to returning true.
if(user && !target_zone)
target_zone = user.zone_selected
// we may choose to ignore species trait pierce immunity in case we still want to check skellies for thick clothing without insta failing them (wounds)
if(ignore_species)
if(injection_flags & INJECT_CHECK_IGNORE_SPECIES)
if(HAS_TRAIT_NOT_FROM(src, TRAIT_PIERCEIMMUNE, SPECIES_TRAIT))
. = FALSE
else if(HAS_TRAIT(src, TRAIT_PIERCEIMMUNE))
. = FALSE
var/obj/item/bodypart/the_part = get_bodypart(target_zone) || get_bodypart(BODY_ZONE_CHEST)
// Loop through the clothing covering this bodypart and see if there's any thiccmaterials
if(!penetrate_thick)
if(!(injection_flags & INJECT_CHECK_PENETRATE_THICK))
for(var/obj/item/clothing/iter_clothing in clothingonpart(the_part))
if(iter_clothing.clothing_flags & THICKMATERIAL)
. = FALSE
break
if(!. && error_msg && user)
// Might need re-wording.
/mob/living/carbon/human/try_inject(mob/user, target_zone, injection_flags)
. = ..()
if(!. && (injection_flags & INJECT_TRY_SHOW_ERROR_MESSAGE) && user)
var/obj/item/bodypart/the_part = get_bodypart(target_zone) || get_bodypart(BODY_ZONE_CHEST)
to_chat(user, "<span class='alert'>There is no exposed flesh or thin material on [p_their()] [the_part.name].</span>")
/mob/living/carbon/human/assess_threat(judgement_criteria, lasercolor = "", datum/callback/weaponcheck=null)

View File

@@ -249,7 +249,7 @@
if(M.limb_destroyer)
dismembering_strike(M, affecting.body_zone)
if(can_inject(M, 1, affecting))//Thick suits can stop monkey bites.
if(try_inject(M, affecting, injection_flags = INJECT_TRY_SHOW_ERROR_MESSAGE))//Thick suits can stop monkey bites.
if(..()) //successful monkey bite, this handles disease contraction.
var/damage = rand(M.dna.species.punchdamagelow, M.dna.species.punchdamagehigh)
if(!damage)

View File

@@ -620,10 +620,30 @@
ret |= F.contents
return ret
// Living mobs use can_inject() to make sure that the mob is not syringe-proof in general.
/mob/living/proc/can_inject()
/**
* Returns whether or not the mob can be injected. Should not perform any side effects.
*
* Arguments:
* * user - The user trying to inject the mob.
* * target_zone - The zone being targeted.
* * injection_flags - A bitflag for extra properties to check.
* Check __DEFINES/injection.dm for more details, specifically the ones prefixed INJECT_CHECK_*.
*/
/mob/living/proc/can_inject(mob/user, target_zone, injection_flags)
return TRUE
/**
* Like can_inject, but it can perform side effects.
*
* Arguments:
* * user - The user trying to inject the mob.
* * target_zone - The zone being targeted.
* * injection_flags - A bitflag for extra properties to check. Check __DEFINES/injection.dm for more details.
* Check __DEFINES/injection.dm for more details. Unlike can_inject, the INJECT_TRY_* defines will behave differently.
*/
/mob/living/proc/try_inject(mob/user, target_zone, injection_flags)
return can_inject(user, target_zone, injection_flags)
/mob/living/is_injectable(mob/user, allowmobs = TRUE)
return (allowmobs && reagents && can_inject(user))

View File

@@ -160,11 +160,14 @@
for(var/key in alarm_types_clear)
alarm_types_clear[key] = 0
/mob/living/silicon/can_inject(mob/user, error_msg)
if(error_msg)
to_chat(user, "<span class='alert'>[p_their(TRUE)] outer shell is too tough.</span>")
/mob/living/silicon/can_inject(mob/user, target_zone, injection_flags)
return FALSE
/mob/living/silicon/try_inject(mob/user, target_zone, injection_flags)
. = ..()
if(!. && (injection_flags & INJECT_TRY_SHOW_ERROR_MESSAGE))
to_chat(user, "<span class='alert'>[p_their(TRUE)] outer shell is too tough.</span>")
/proc/islinked(mob/living/silicon/robot/bot, mob/living/silicon/ai/ai)
if(!istype(bot) || !istype(ai))
return FALSE

View File

@@ -133,7 +133,7 @@
return
if(!force)
if(M.can_inject(user, 1))
if(M.try_inject(user, injection_flags = INJECT_TRY_SHOW_ERROR_MESSAGE))
to_chat(user, "<span class='warning'>You stab [M] with the pen.</span>")
if(!stealth)
to_chat(M, "<span class='danger'>You feel a tiny prick!</span>")

View File

@@ -14,7 +14,7 @@
if(iscarbon(target))
var/mob/living/carbon/M = target
if(blocked != 100) // not completely blocked
if(M.can_inject(null, FALSE, def_zone, piercing)) // Pass the hit zone to see if it can inject by whether it hit the head or the body.
if(M.can_inject(target_zone = def_zone, injection_flags = piercing ? INJECT_CHECK_PENETRATE_THICK : null)) // Pass the hit zone to see if it can inject by whether it hit the head or the body.
..()
reagents.trans_to(M, reagents.total_volume, methods = INJECT)
return BULLET_ACT_HIT

View File

@@ -11,7 +11,7 @@
if(iscarbon(target))
var/mob/living/carbon/M = target
if(blocked != 100)
if(M.can_inject(null, FALSE, def_zone, FALSE))
if(M.can_inject(target_zone = def_zone))
if(injector.inject(M, firer))
QDEL_NULL(injector)
return BULLET_ACT_HIT

View File

@@ -109,7 +109,7 @@ Borg Hypospray
return
if(!istype(M))
return
if(R.total_volume && M.can_inject(user, 1, user.zone_selected,bypass_protection))
if(R.total_volume && M.try_inject(user, user.zone_selected, injection_flags = INJECT_TRY_SHOW_ERROR_MESSAGE | (bypass_protection ? INJECT_CHECK_PENETRATE_THICK : 0)))
to_chat(M, "<span class='warning'>You feel a tiny prick!</span>")
to_chat(user, "<span class='notice'>You inject [M] with the injector.</span>")
if(M.reagents)

View File

@@ -38,7 +38,7 @@
var/contained = english_list(injected)
log_combat(user, M, "attempted to inject", src, "([contained])")
if(reagents.total_volume && (ignore_flags || M.can_inject(user, 1))) // Ignore flag should be checked first or there will be an error message.
if(reagents.total_volume && (ignore_flags || M.try_inject(user, injection_flags = INJECT_TRY_SHOW_ERROR_MESSAGE))) // Ignore flag should be checked first or there will be an error message.
to_chat(M, "<span class='warning'>You feel a tiny prick!</span>")
to_chat(user, "<span class='notice'>You inject [M] with [src].</span>")
var/fraction = min(amount_per_transfer_from_this/reagents.total_volume, 1)

View File

@@ -63,7 +63,7 @@
var/mob/living/L
if(isliving(target))
L = target
if(!L.can_inject(user, 1))
if(!L.try_inject(user, injection_flags = INJECT_TRY_SHOW_ERROR_MESSAGE))
return
// chance of monkey retaliation
@@ -82,7 +82,7 @@
target.visible_message("<span class='danger'>[user] is trying to take a blood sample from [target]!</span>", \
"<span class='userdanger'>[user] is trying to take a blood sample from you!</span>")
busy = TRUE
if(!do_mob(user, target, extra_checks=CALLBACK(L, /mob/living/proc/can_inject, user, TRUE)))
if(!do_mob(user, target, extra_checks=CALLBACK(L, /mob/living/proc/try_inject, user, null, INJECT_TRY_SHOW_ERROR_MESSAGE)))
busy = FALSE
return
if(reagents.total_volume >= reagents.maximum_volume)
@@ -127,12 +127,12 @@
return
if(L) //living mob
if(!L.can_inject(user, TRUE))
if(!L.try_inject(user, injection_flags = INJECT_TRY_SHOW_ERROR_MESSAGE))
return
if(L != user)
L.visible_message("<span class='danger'>[user] is trying to inject [L]!</span>", \
"<span class='userdanger'>[user] is trying to inject you!</span>")
if(!do_mob(user, L, extra_checks=CALLBACK(L, /mob/living/proc/can_inject, user, TRUE)))
if(!do_mob(user, L, extra_checks=CALLBACK(L, /mob/living/proc/try_inject, user, null, INJECT_TRY_SHOW_ERROR_MESSAGE)))
return
if(!reagents.total_volume)
return

View File

@@ -151,7 +151,7 @@ To add a crossbreed:
if(self_use_only && M != user)
to_chat(user, "<span class='warning'>This can only be used on yourself.</span>")
return
if(reagents.total_volume && (ignore_flags || M.can_inject(user, 1)))
if(reagents.total_volume && (ignore_flags || M.try_inject(user, injection_flags = INJECT_TRY_SHOW_ERROR_MESSAGE)))
reagents.trans_to(M, reagents.total_volume, transfered_by = user)
if(user != M)
to_chat(M, "<span class='warning'>[user] presses [src] against you!</span>")

View File

@@ -62,6 +62,7 @@
#include "code\__DEFINES\forensics.dm"
#include "code\__DEFINES\hud.dm"
#include "code\__DEFINES\icon_smoothing.dm"
#include "code\__DEFINES\injection.dm"
#include "code\__DEFINES\instruments.dm"
#include "code\__DEFINES\interaction_flags.dm"
#include "code\__DEFINES\inventory.dm"