diff --git a/code/__HELPERS/mobs.dm b/code/__HELPERS/mobs.dm
index 6be2b411376..ff127fd3a48 100644
--- a/code/__HELPERS/mobs.dm
+++ b/code/__HELPERS/mobs.dm
@@ -355,24 +355,35 @@ This is always put in the attack log.
if(progress)
qdel(progbar)
-/proc/do_after(mob/user, delay, needhand = 1, atom/target = null, progress = 1, datum/callback/extra_checks = null)
+/* Use this proc when you want to have code under it execute after a delay, and ensure certain conditions are met during that delay...
+ * Such as the user not being interrupted via getting stunned or by moving off the tile they're currently on.
+ *
+ * Example usage:
+ *
+ * if(do_after(user, 50, target = sometarget, extra_checks = list(callback_check1, callback_check2)))
+ * do_stuff()
+ *
+ * This will create progress bar that lasts for 5 seconds. If the user doesn't move or otherwise do something that would cause the checks to fail in those 5 seconds, do_stuff() would execute.
+ * The Proc returns TRUE upon success (the progress bar reached the end), or FALSE upon failure (the user moved or some other check failed)
+ */
+/proc/do_after(mob/user, delay, needhand = 1, atom/target = null, progress = 1, list/extra_checks = list(), use_default_checks = TRUE)
if(!user)
- return 0
+ return FALSE
var/atom/Tloc = null
if(target)
Tloc = target.loc
var/atom/Uloc = user.loc
- var/drifting = 0
+ var/drifting = FALSE
if(!user.Process_Spacemove(0) && user.inertia_dir)
- drifting = 1
+ drifting = TRUE
var/holding = user.get_active_hand()
- var/holdingnull = 1 //User's hand started out empty, check for an empty hand
+ var/holdingnull = TRUE //User's hand started out empty, check for an empty hand
if(holding)
- holdingnull = 0 //Users hand started holding something, check to see if it's still holding that
+ holdingnull = FALSE //Users hand started holding something, check to see if it's still holding that
var/datum/progressbar/progbar
if(progress)
@@ -380,22 +391,29 @@ This is always put in the attack log.
var/endtime = world.time + delay
var/starttime = world.time
- . = 1
+ . = TRUE
+
+ // By default, checks for weakness and stunned get added to the extra_checks list.
+ // Setting `use_default_checks` to FALSE means that you don't want the do_after to check for these statuses, or that you will be supplying your own checks.
+ if(use_default_checks)
+ extra_checks += CALLBACK(user, /mob.proc/IsWeakened)
+ extra_checks += CALLBACK(user, /mob.proc/IsStunned)
+
while(world.time < endtime)
sleep(1)
if(progress)
progbar.update(world.time - starttime)
if(drifting && !user.inertia_dir)
- drifting = 0
+ drifting = FALSE
Uloc = user.loc
- if(!user || user.stat || user.IsWeakened() || user.stunned || (!drifting && user.loc != Uloc)|| (extra_checks && !extra_checks.Invoke()))
- . = 0
+ if(!user || user.stat || (!drifting && user.loc != Uloc) || check_for_true_callbacks(extra_checks))
+ . = FALSE
break
if(Tloc && (!target || Tloc != target.loc))
- . = 0
+ . = FALSE
break
if(needhand)
@@ -403,14 +421,21 @@ This is always put in the attack log.
//i.e the hand is used to pull some item/tool out of the construction
if(!holdingnull)
if(!holding)
- . = 0
+ . = FALSE
break
if(user.get_active_hand() != holding)
- . = 0
+ . = FALSE
break
if(progress)
qdel(progbar)
+// Upon any of the callbacks in the list returning TRUE, the proc will return TRUE.
+/proc/check_for_true_callbacks(list/extra_checks)
+ for(var/datum/callback/CB in extra_checks)
+ if(CB.Invoke())
+ return TRUE
+ return FALSE
+
#define DOAFTERONCE_MAGIC "Magic~~"
GLOBAL_LIST_INIT(do_after_once_tracker, list())
/proc/do_after_once(mob/user, delay, needhand = 1, atom/target = null, progress = 1, attempt_cancel_message = "Attempt cancelled.")
@@ -423,7 +448,7 @@ GLOBAL_LIST_INIT(do_after_once_tracker, list())
to_chat(user, "[attempt_cancel_message]")
return FALSE
GLOB.do_after_once_tracker[cache_key] = TRUE
- . = do_after(user, delay, needhand, target, progress, extra_checks = CALLBACK(GLOBAL_PROC, .proc/do_after_once_checks, cache_key))
+ . = do_after(user, delay, needhand, target, progress, extra_checks = list(CALLBACK(GLOBAL_PROC, .proc/do_after_once_checks, cache_key)))
GLOB.do_after_once_tracker[cache_key] = FALSE
/proc/do_after_once_checks(cache_key)
diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm
index bf83e6c5d49..692024899a2 100644
--- a/code/game/machinery/doors/airlock.dm
+++ b/code/game/machinery/doors/airlock.dm
@@ -1003,7 +1003,7 @@ About the new airlock wires panel:
"You begin [welded ? "unwelding":"welding"] the airlock...", \
"You hear welding.")
- if(I.use_tool(src, user, 40, volume = I.tool_volume, extra_checks = CALLBACK(src, .proc/weld_checks, I, user)))
+ if(I.use_tool(src, user, 40, volume = I.tool_volume, extra_checks = list(CALLBACK(src, .proc/weld_checks, I, user))))
if(!density && !welded)
return
welded = !welded
@@ -1014,7 +1014,7 @@ About the new airlock wires panel:
user.visible_message("[user] is welding the airlock.", \
"You begin repairing the airlock...", \
"You hear welding.")
- if(I.use_tool(src, user, 40, volume = I.tool_volume, extra_checks = CALLBACK(src, .proc/weld_checks, I, user)))
+ if(I.use_tool(src, user, 40, volume = I.tool_volume, extra_checks = list(CALLBACK(src, .proc/weld_checks, I, user))))
obj_integrity = max_integrity
stat &= ~BROKEN
user.visible_message("[user.name] has repaired [src].", \
diff --git a/code/game/objects/items/tools/tool_behaviour.dm b/code/game/objects/items/tools/tool_behaviour.dm
index c937f80c30d..80b00dcd1ed 100644
--- a/code/game/objects/items/tools/tool_behaviour.dm
+++ b/code/game/objects/items/tools/tool_behaviour.dm
@@ -16,11 +16,11 @@
var/datum/callback/tool_check = CALLBACK(src, .proc/tool_check_callback, user, target, amount, extra_checks)
if(ismob(target))
- if(!do_mob(user, target, delay, extra_checks=tool_check))
+ if(!do_mob(user, target, delay, extra_checks = list(tool_check)))
return
else
- if(!do_after(user, delay, target=target, extra_checks=tool_check))
+ if(!do_after(user, delay, target=target, extra_checks = list(tool_check)))
return
else
// Invoke the extra checks once, just in case.
diff --git a/code/game/objects/structures/window.dm b/code/game/objects/structures/window.dm
index 9faab45cf97..8fe46ca052d 100644
--- a/code/game/objects/structures/window.dm
+++ b/code/game/objects/structures/window.dm
@@ -254,7 +254,7 @@ GLOBAL_LIST_INIT(wcCommon, pick(list("#379963", "#0d8395", "#58b5c3", "#49e46e",
if(!can_be_reached(user))
return
to_chat(user, "You begin to lever the window [state == WINDOW_OUT_OF_FRAME ? "into":"out of"] the frame...")
- if(!I.use_tool(src, user, decon_speed, volume = I.tool_volume, extra_checks = CALLBACK(src, .proc/check_state_and_anchored, state, anchored)))
+ if(!I.use_tool(src, user, decon_speed, volume = I.tool_volume, extra_checks = list(CALLBACK(src, .proc/check_state_and_anchored, state, anchored))))
return
state = (state == WINDOW_OUT_OF_FRAME ? WINDOW_IN_FRAME : WINDOW_OUT_OF_FRAME)
to_chat(user, "You pry the window [state == WINDOW_IN_FRAME ? "into":"out of"] the frame.")
@@ -268,20 +268,20 @@ GLOBAL_LIST_INIT(wcCommon, pick(list("#379963", "#0d8395", "#58b5c3", "#49e46e",
if(reinf)
if(state == WINDOW_SCREWED_TO_FRAME || state == WINDOW_IN_FRAME)
to_chat(user, "You begin to [state == WINDOW_SCREWED_TO_FRAME ? "unscrew the window from":"screw the window to"] the frame...")
- if(!I.use_tool(src, user, decon_speed, volume = I.tool_volume, extra_checks = CALLBACK(src, .proc/check_state_and_anchored, state, anchored)))
+ if(!I.use_tool(src, user, decon_speed, volume = I.tool_volume, extra_checks = list(CALLBACK(src, .proc/check_state_and_anchored, state, anchored))))
return
state = (state == WINDOW_IN_FRAME ? WINDOW_SCREWED_TO_FRAME : WINDOW_IN_FRAME)
to_chat(user, "You [state == WINDOW_IN_FRAME ? "unfasten the window from":"fasten the window to"] the frame.")
else if(state == WINDOW_OUT_OF_FRAME)
to_chat(user, "You begin to [anchored ? "unscrew the frame from":"screw the frame to"] the floor...")
- if(!I.use_tool(src, user, decon_speed, volume = I.tool_volume, extra_checks = CALLBACK(src, .proc/check_state_and_anchored, state, anchored)))
+ if(!I.use_tool(src, user, decon_speed, volume = I.tool_volume, extra_checks = list(CALLBACK(src, .proc/check_state_and_anchored, state, anchored))))
return
anchored = !anchored
update_nearby_icons()
to_chat(user, "You [anchored ? "fasten the frame to":"unfasten the frame from"] the floor.")
else //if we're not reinforced, we don't need to check or update state
to_chat(user, "You begin to [anchored ? "unscrew the window from":"screw the window to"] the floor...")
- if(!I.use_tool(src, user, decon_speed, volume = I.tool_volume, extra_checks = CALLBACK(src, .proc/check_anchored, anchored)))
+ if(!I.use_tool(src, user, decon_speed, volume = I.tool_volume, extra_checks = list(CALLBACK(src, .proc/check_anchored, anchored))))
return
anchored = !anchored
air_update_turf(TRUE)
@@ -297,7 +297,7 @@ GLOBAL_LIST_INIT(wcCommon, pick(list("#379963", "#0d8395", "#58b5c3", "#49e46e",
if(!can_be_reached(user))
return
TOOL_ATTEMPT_DISMANTLE_MESSAGE
- if(!I.use_tool(src, user, decon_speed, volume = I.tool_volume, extra_checks = CALLBACK(src, .proc/check_state_and_anchored, state, anchored)))
+ if(!I.use_tool(src, user, decon_speed, volume = I.tool_volume, extra_checks = list(CALLBACK(src, .proc/check_state_and_anchored, state, anchored))))
return
var/obj/item/stack/sheet/G = new glass_type(user.loc, glass_amount)
G.add_fingerprint(user)
diff --git a/code/modules/mob/living/carbon/human/species/slime.dm b/code/modules/mob/living/carbon/human/species/slime.dm
index d9fa1ccbf3c..622e0919b45 100644
--- a/code/modules/mob/living/carbon/human/species/slime.dm
+++ b/code/modules/mob/living/carbon/human/species/slime.dm
@@ -146,11 +146,13 @@
return
var/limb_select = input(H, "Choose a limb to regrow", "Limb Regrowth") as null|anything in missing_limbs
+ if(!limb_select) // If the user hit cancel on the popup, return
+ return
var/chosen_limb = missing_limbs[limb_select]
H.visible_message("[H] begins to hold still and concentrate on [H.p_their()] missing [limb_select]...", "You begin to focus on regrowing your missing [limb_select]... (This will take [round(SLIMEPERSON_REGROWTHDELAY/10)] seconds, and you must hold still.)")
- if(do_after(H, SLIMEPERSON_REGROWTHDELAY, needhand = 0, target = H))
- if(H.incapacitated())
+ if(do_after(H, SLIMEPERSON_REGROWTHDELAY, FALSE, H, extra_checks = list(CALLBACK(H, /mob.proc/IsStunned)), use_default_checks = FALSE)) // Override the check for weakness, only check for stunned
+ if(H.incapacitated(ignore_lying = TRUE, extra_checks = list(CALLBACK(H, /mob.proc/IsStunned)), use_default_checks = FALSE)) // Override the check for weakness, only check for stunned
to_chat(H, "You cannot regenerate missing limbs in your current state.")
return
@@ -196,4 +198,4 @@
#undef SLIMEPERSON_HUNGERCOST
#undef SLIMEPERSON_MINHUNGER
-#undef SLIMEPERSON_REGROWTHDELAY
+#undef SLIMEPERSON_REGROWTHDELAY
\ No newline at end of file
diff --git a/code/modules/mob/living/update_status.dm b/code/modules/mob/living/update_status.dm
index 20cc969119f..d24301b0dd0 100644
--- a/code/modules/mob/living/update_status.dm
+++ b/code/modules/mob/living/update_status.dm
@@ -65,8 +65,14 @@
return !(IsWeakened() || paralysis || stat || (status_flags & FAKEDEATH))
// Whether the mob is capable of actions or not
-/mob/living/incapacitated(ignore_restraints = FALSE, ignore_grab = FALSE, ignore_lying = FALSE)
- if(stat || paralysis || stunned || IsWeakened() || (!ignore_restraints && restrained()) || (!ignore_lying && lying))
+/mob/living/incapacitated(ignore_restraints = FALSE, ignore_grab = FALSE, ignore_lying = FALSE, list/extra_checks = list(), use_default_checks = TRUE)
+ // By default, checks for weakness and stunned get added to the extra_checks list.
+ // Setting `use_default_checks` to FALSE means that you don't want it checking for these statuses or you are supplying your own checks.
+ if(use_default_checks)
+ extra_checks += CALLBACK(src, /mob.proc/IsWeakened)
+ extra_checks += CALLBACK(src, /mob.proc/IsStunned)
+
+ if(stat || paralysis || (!ignore_restraints && restrained()) || (!ignore_lying && lying) || check_for_true_callbacks(extra_checks))
return TRUE
// wonderful proc names, I know - used to check whether the blur overlay
diff --git a/code/modules/mob/status_procs.dm b/code/modules/mob/status_procs.dm
index 0aeb9beef84..b61ce171194 100644
--- a/code/modules/mob/status_procs.dm
+++ b/code/modules/mob/status_procs.dm
@@ -173,6 +173,9 @@
/mob/proc/Stun()
return
+/mob/proc/IsStunned()
+ return stunned
+
/mob/proc/SetStunned()
return