diff --git a/code/__DEFINES/do_afters.dm b/code/__DEFINES/do_afters.dm
new file mode 100644
index 000000000000..456cf9404004
--- /dev/null
+++ b/code/__DEFINES/do_afters.dm
@@ -0,0 +1,4 @@
+#define DOAFTER_SOURCE_SURGERY "doafter_surgery"
+#define DOAFTER_SOURCE_MECHADRILL "doafter_mechadrill"
+#define DOAFTER_SOURCE_SURVIVALPEN "doafter_survivalpen"
+#define DOAFTER_SOURCE_GETTING_UP "doafter_gettingup"
diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm
index ad9d5ae5abc8..cee697c7d8c6 100644
--- a/code/__DEFINES/mobs.dm
+++ b/code/__DEFINES/mobs.dm
@@ -395,7 +395,11 @@
#define WABBAJACK (1<<6)
#define SLEEP_CHECK_DEATH(X) sleep(X); if(QDELETED(src) || stat == DEAD) return;
-#define INTERACTING_WITH(X, Y) (Y in X.do_afters)
+
+#define DOING_INTERACTION(user, interaction_key) (LAZYACCESS(user.do_afters, interaction_key))
+#define DOING_INTERACTION_LIMIT(user, interaction_key, max_interaction_count) ((LAZYACCESS(user.do_afters, interaction_key) || 0) >= max_interaction_count)
+#define DOING_INTERACTION_WITH_TARGET(user, target) (LAZYACCESS(user.do_afters, target))
+#define DOING_INTERACTION_WITH_TARGET_LIMIT(user, target, max_interaction_count) ((LAZYACCESS(user.do_afters, target) || 0) >= max_interaction_count)
/// If you examine the same atom twice in this timeframe, we call examine_more() instead of examine()
#define EXAMINE_MORE_TIME 1 SECONDS
diff --git a/code/__DEFINES/timed_action.dm b/code/__DEFINES/timed_action.dm
index 871de95cca56..90572cdc5416 100644
--- a/code/__DEFINES/timed_action.dm
+++ b/code/__DEFINES/timed_action.dm
@@ -1,18 +1,10 @@
// timed_action_flags parameter for 'proc/do_after'
-#define IGNORE_TARGET_IN_DOAFTERS (1<<0)
+
// The user can move freely without canceling the do_after
-#define IGNORE_USER_LOC_CHANGE (1<<1)
+#define IGNORE_USER_LOC_CHANGE (1<<0)
// The target can move freely without canceling the do_after
-#define IGNORE_TARGET_LOC_CHANGE (1<<2)
+#define IGNORE_TARGET_LOC_CHANGE (1<<1)
/// Can do the action even if the item is no longer being held
-#define IGNORE_HELD_ITEM (1<<3)
+#define IGNORE_HELD_ITEM (1<<2)
/// Can do the action even if the mob is incapacitated
-#define IGNORE_INCAPACITATED (1<<4)
-/// Must be adjacent
-#define REQUIRE_ADJACENCY (1<<5)
-/// Can do the action even if the mob is handcuffed
-#define IGNORE_RESTRAINED (1<<6)
-
-// Combined parameters for ease of use
-#define UNINTERRUPTIBLE IGNORE_TARGET_IN_DOAFTERS|IGNORE_USER_LOC_CHANGE|IGNORE_TARGET_LOC_CHANGE|IGNORE_HELD_ITEM|IGNORE_INCAPACITATED
-#define UNINTERRUPTIBLE_CONSCIOUS IGNORE_TARGET_IN_DOAFTERS|IGNORE_USER_LOC_CHANGE|IGNORE_TARGET_LOC_CHANGE|IGNORE_HELD_ITEM
+#define IGNORE_INCAPACITATED (1<<3)
diff --git a/code/__HELPERS/mobs.dm b/code/__HELPERS/mobs.dm
index 6abcf898531c..7e7cdb0f4bc6 100644
--- a/code/__HELPERS/mobs.dm
+++ b/code/__HELPERS/mobs.dm
@@ -270,18 +270,25 @@ GLOBAL_LIST_EMPTY(species_list)
* * progress - if TRUE, a progress bar is displayed.
* * extra_checks - a callback that can be used to add extra checks to the do_after. Returning false in this callback will cancel the do_after.
*/
-/proc/do_after(mob/user, delay = 3 SECONDS, atom/target, timed_action_flags = NONE, progress = TRUE, datum/callback/extra_checks)
+/proc/do_after(mob/user, delay = 3 SECONDS, atom/target, timed_action_flags = NONE, progress = TRUE, datum/callback/extra_checks, interaction_key, max_interact_count = 1)
if(!user)
return FALSE
if(!isnum(delay))
CRASH("do_after was passed a non-number delay: [delay || "null"].")
+
+ /*
if(target && INTERACTING_WITH(user, target))
to_chat(user, "You're already interacting with [target]!")
return
+ */
- if(target)
- LAZYADD(user.do_afters, target)
- LAZYADD(target.targeted_by, user)
+ if(!interaction_key && target)
+ interaction_key = target //Use the direct ref to the target
+ if(interaction_key) //Do we have a interaction_key now?
+ var/current_interaction_count = LAZYACCESS(user.do_afters, interaction_key) || 0
+ if(current_interaction_count >= max_interact_count) //We are at our peak
+ return
+ LAZYSET(user.do_afters, interaction_key, current_interaction_count + 1)
var/atom/user_loc = user.loc
var/atom/target_loc = target?.loc
@@ -296,10 +303,7 @@ GLOBAL_LIST_EMPTY(species_list)
var/datum/progressbar/progbar
if(progress)
- if(target) // the progress bar needs a target, so if we don't have one just pass it the user.
- progbar = new(user, delay, target)
- else
- progbar = new(user, delay, user)
+ progbar = new(user, delay, target || user)
var/endtime = world.time + delay
var/starttime = world.time
@@ -307,10 +311,6 @@ GLOBAL_LIST_EMPTY(species_list)
while (world.time < endtime)
stoplag(1)
- if(QDELETED(user))
- . = FALSE
- break
-
if(!QDELETED(progbar))
progbar.update(world.time - starttime)
@@ -319,35 +319,26 @@ GLOBAL_LIST_EMPTY(species_list)
user_loc = user.loc
// Check flags
- if(!(timed_action_flags & IGNORE_USER_LOC_CHANGE) && !drifting && user.loc != user_loc)
- . = FALSE
-
- if(!(timed_action_flags & IGNORE_HELD_ITEM) && user.get_active_held_item() != holding)
- . = FALSE
-
- if(!(timed_action_flags & IGNORE_INCAPACITATED) && user.incapacitated(ignore_restraints = (timed_action_flags & IGNORE_RESTRAINED)))
- . = FALSE
-
- if(extra_checks && !extra_checks.Invoke())
+ if(QDELETED(user) \
+ || (!(timed_action_flags & IGNORE_USER_LOC_CHANGE) && !drifting && user.loc != user_loc) \
+ || (!(timed_action_flags & IGNORE_HELD_ITEM) && user.get_active_held_item() != holding) \
+ || (!(timed_action_flags & IGNORE_INCAPACITATED) && HAS_TRAIT(user, TRAIT_INCAPACITATED)) \
+ || (extra_checks && !extra_checks.Invoke()))
. = FALSE
+ break
// If we have a target, we check for them moving here. We don't care about it if we're drifting or we ignore target loc change
- if(!(timed_action_flags & IGNORE_TARGET_LOC_CHANGE) && !drifting)
- if(target_loc && user != target && (QDELETED(target) || target_loc != target.loc))
- . = FALSE
-
- if(target && !(timed_action_flags & IGNORE_TARGET_IN_DOAFTERS) && !(target in user.do_afters))
+ if(target && (user != target) && \
+ (QDELETED(target) \
+ || (!(timed_action_flags & IGNORE_TARGET_LOC_CHANGE) && target.loc != target_loc)))
. = FALSE
-
- if(!.)
break
if(!QDELETED(progbar))
progbar.end_progress()
- if(!QDELETED(target))
- LAZYREMOVE(user.do_afters, target)
- LAZYREMOVE(target.targeted_by, user)
+ if(interaction_key)
+ LAZYREMOVE(user.do_afters, interaction_key)
/mob/proc/do_after_coefficent() // This gets added to the delay on a do_after, default 1
. = 1
diff --git a/code/datums/components/butchering.dm b/code/datums/components/butchering.dm
index 9ed1f0d51424..55ba84fb5f7d 100644
--- a/code/datums/components/butchering.dm
+++ b/code/datums/components/butchering.dm
@@ -55,6 +55,10 @@
Butcher(user, M)
/datum/component/butchering/proc/startNeckSlice(obj/item/source, mob/living/carbon/human/H, mob/living/user)
+ if(DOING_INTERACTION_WITH_TARGET(user, H))
+ to_chat(user, "You're already interacting with [H]!")
+ return
+
user.visible_message("[user] is slitting [H]'s throat!", \
"You start slicing [H]'s throat!", \
"You hear a cutting noise!", ignored_mobs = H)
diff --git a/code/game/atoms.dm b/code/game/atoms.dm
index 35878ec3c240..eba17e1df3cb 100644
--- a/code/game/atoms.dm
+++ b/code/game/atoms.dm
@@ -84,9 +84,6 @@
var/list/alternate_appearances
- ///Mobs that are currently do_after'ing this atom, to be cleared from on Destroy()
- var/list/targeted_by
-
/// Last appearance of the atom for demo saving purposes
var/image/demo_last_appearance
@@ -309,11 +306,6 @@
LAZYCLEARLIST(overlays)
LAZYCLEARLIST(managed_overlays)
- for(var/i in targeted_by)
- var/mob/M = i
- LAZYREMOVE(M.do_afters, src)
-
- targeted_by = null
QDEL_NULL(light)
if(smoothing_flags & SMOOTH_QUEUED)
diff --git a/code/game/mecha/equipment/mecha_equipment.dm b/code/game/mecha/equipment/mecha_equipment.dm
index 63d308f69558..3882fe8d5a6f 100644
--- a/code/game/mecha/equipment/mecha_equipment.dm
+++ b/code/game/mecha/equipment/mecha_equipment.dm
@@ -101,13 +101,13 @@
chassis.use_power(energy_drain)
addtimer(CALLBACK(src, PROC_REF(set_ready_state), 1), equip_cooldown)
-/obj/item/mecha_parts/mecha_equipment/proc/do_after_cooldown(atom/target)
+/obj/item/mecha_parts/mecha_equipment/proc/do_after_cooldown(atom/target, mob/user, interaction_key)
if(!chassis)
return
var/C = chassis.loc
set_ready_state(0)
chassis.use_power(energy_drain)
- . = do_after(chassis.occupant, equip_cooldown, target=target)
+ . = do_after(user, equip_cooldown, target=target, interaction_key = interaction_key)
set_ready_state(1)
if(!chassis || chassis.loc != C || src != chassis.selected || !(get_dir(chassis, target)&chassis.dir))
return 0
diff --git a/code/game/objects/items/stacks/sheets/sheet_types.dm b/code/game/objects/items/stacks/sheets/sheet_types.dm
index 46d92aa9b6f8..970ddf5ad0f5 100644
--- a/code/game/objects/items/stacks/sheets/sheet_types.dm
+++ b/code/game/objects/items/stacks/sheets/sheet_types.dm
@@ -331,9 +331,9 @@ GLOBAL_LIST_INIT(bamboo_recipes, list ( \
force = 0
throwforce = 0
merge_type = /obj/item/stack/sheet/cotton
- var/pull_effort = 30
- var/loom_result = /obj/item/stack/sheet/cotton/cloth
grind_results = list(/datum/reagent/cellulose = 20)
+ var/pull_effort = 10
+ var/loom_result = /obj/item/stack/sheet/cotton/cloth
GLOBAL_LIST_INIT(cloth_recipes, list ( \
new/datum/stack_recipe("white jumpskirt", /obj/item/clothing/under/color/jumpskirt/white, 3), /*Ladies first*/ \
@@ -447,7 +447,6 @@ GLOBAL_LIST_INIT(durathread_recipes, list ( \
singular_name = "raw durathread ball"
icon_state = "sheet-durathreadraw"
merge_type = /obj/item/stack/sheet/cotton/durathread
- pull_effort = 70
loom_result = /obj/item/stack/sheet/durathread
grind_results = list()
diff --git a/code/game/objects/structures/loom.dm b/code/game/objects/structures/loom.dm
index 28ff5a8de732..e2c3b8909913 100644
--- a/code/game/objects/structures/loom.dm
+++ b/code/game/objects/structures/loom.dm
@@ -31,11 +31,9 @@
user.show_message("You need at least [FABRIC_PER_SHEET] units of fabric before using this.", MSG_VISUAL)
return FALSE
user.show_message("You start weaving \the [W.name] through the loom..", MSG_VISUAL)
- if(W.use_tool(src, user, W.pull_effort))
- if(W.amount >= FABRIC_PER_SHEET)
- new W.loom_result(drop_location())
- W.use(FABRIC_PER_SHEET)
- user.show_message("You weave \the [W.name] into a workable fabric.", MSG_VISUAL)
+ while(W.use_tool(src, user, W.pull_effort) && W.use(FABRIC_PER_SHEET))
+ new W.loom_result(drop_location())
+ user.show_message("You weave \the [W.name] into a workable fabric.", MSG_VISUAL)
return TRUE
#undef FABRIC_PER_SHEET
diff --git a/code/modules/clothing/shoes/_shoes.dm b/code/modules/clothing/shoes/_shoes.dm
index 02fbb9dad7ac..e5be23a2cda1 100644
--- a/code/modules/clothing/shoes/_shoes.dm
+++ b/code/modules/clothing/shoes/_shoes.dm
@@ -132,6 +132,9 @@
return
if(user == loc && tied != SHOES_TIED) // if they're our own shoes, go tie-wards
+ if(DOING_INTERACTION_WITH_TARGET(user, our_guy))
+ to_chat(user, span_warning("You're already interacting with [src]!"))
+ return
user.visible_message("[user] begins [tied ? "unknotting" : "tying"] the laces of [user.p_their()] [src.name].", "You begin [tied ? "unknotting" : "tying"] the laces of your [src.name]...")
if(do_after(user, lace_time, target = our_guy, extra_checks = CALLBACK(src, PROC_REF(still_shoed), our_guy)))
@@ -149,6 +152,9 @@
if(tied == SHOES_KNOTTED)
to_chat(user, "The laces on [loc]'s [src.name] are already a hopelessly tangled mess!")
return
+ if(DOING_INTERACTION_WITH_TARGET(user, our_guy))
+ to_chat(user, span_warning("You're already interacting with [src]!"))
+ return
var/mod_time = lace_time
to_chat(user, "You quietly set to work [tied ? "untying" : "knotting"] [loc]'s [src.name]...")
@@ -230,6 +236,10 @@
/obj/item/clothing/shoes/attack_self(mob/user)
. = ..()
+ if(DOING_INTERACTION_WITH_TARGET(user, src))
+ to_chat(user, "You're already interacting with [src]!")
+ return
+
to_chat(user, "You begin [tied ? "untying" : "tying"] the laces on [src]...")
if(do_after(user, lace_time, target = src,extra_checks = CALLBACK(src, PROC_REF(still_shoed), user)))
diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm
index b2390fd11320..dd5b29059085 100644
--- a/code/modules/mob/living/carbon/carbon.dm
+++ b/code/modules/mob/living/carbon/carbon.dm
@@ -306,7 +306,7 @@
if(!cuff_break)
visible_message("[src] attempts to remove [I]!")
to_chat(src, "You attempt to remove [I]... (This will take around [DisplayTimeText(breakouttime)] and you need to stand still.)")
- if(do_after(src, breakouttime, target = src, timed_action_flags = IGNORE_HELD_ITEM|IGNORE_RESTRAINED))
+ if(do_after(src, breakouttime, target = src, timed_action_flags = IGNORE_HELD_ITEM))
. = clear_cuffs(I, cuff_break)
else
to_chat(src, "You fail to remove [I]!")
@@ -315,7 +315,7 @@
breakouttime = 50
visible_message("[src] is trying to break [I]!")
to_chat(src, "You attempt to break [I]... (This will take around 5 seconds and you need to stand still.)")
- if(do_after(src, breakouttime, target = src, timed_action_flags = IGNORE_HELD_ITEM|IGNORE_RESTRAINED))
+ if(do_after(src, breakouttime, target = src, timed_action_flags = IGNORE_HELD_ITEM))
. = clear_cuffs(I, cuff_break)
else
to_chat(src, "You fail to break [I]!")
diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm
index 291933deee94..2179a13a7f63 100644
--- a/code/modules/mob/living/carbon/human/human.dm
+++ b/code/modules/mob/living/carbon/human/human.dm
@@ -652,7 +652,7 @@
do
CHECK_DNA_AND_SPECIES(target)
- if (INTERACTING_WITH(src, target))
+ if (DOING_INTERACTION_WITH_TARGET(src,target))
return FALSE
if (target.stat == DEAD || HAS_TRAIT(target, TRAIT_FAKEDEATH))
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index ae5c2660f107..e602547247e6 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -507,7 +507,7 @@
/mob/living/proc/get_up(instant = FALSE)
set waitfor = FALSE
- if(!instant && !do_after(src, 1 SECONDS, src, timed_action_flags = (IGNORE_USER_LOC_CHANGE|IGNORE_TARGET_LOC_CHANGE|IGNORE_HELD_ITEM), extra_checks = CALLBACK(src, TYPE_PROC_REF(/mob/living, rest_checks_callback))))
+ if(!instant && !do_after(src, 1 SECONDS, src, timed_action_flags = (IGNORE_USER_LOC_CHANGE|IGNORE_TARGET_LOC_CHANGE|IGNORE_HELD_ITEM), extra_checks = CALLBACK(src, TYPE_PROC_REF(/mob/living, rest_checks_callback)), interaction_key = DOAFTER_SOURCE_GETTING_UP))
return
if(resting || body_position == STANDING_UP || HAS_TRAIT(src, TRAIT_FLOORED))
return
@@ -977,7 +977,7 @@
"[src] tries to remove your [what.name].", null, null, src)
to_chat(src, "You try to remove [who]'s [what.name]...")
what.add_fingerprint(src)
- if(do_after(src, what.strip_delay, who))
+ if(do_after(src, what.strip_delay, who, interaction_key = what))
if(what && Adjacent(who))
if(islist(where))
var/list/L = where
diff --git a/code/modules/mob/living/simple_animal/bot/medbot.dm b/code/modules/mob/living/simple_animal/bot/medbot.dm
index a4ce830d8372..22d68c8a6190 100644
--- a/code/modules/mob/living/simple_animal/bot/medbot.dm
+++ b/code/modules/mob/living/simple_animal/bot/medbot.dm
@@ -488,6 +488,9 @@
return TRUE
/mob/living/simple_animal/bot/medbot/attack_hand(mob/living/carbon/human/H)
+ if(DOING_INTERACTION_WITH_TARGET(H, src))
+ to_chat(H, "You're already interacting with [src].")
+ return
if(H.a_intent == INTENT_DISARM && mode != BOT_TIPPED)
H.visible_message("[H] begins tipping over [src].", "You begin tipping over [src]...")
diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm
index d475891fc28f..e6be2b728d17 100644
--- a/code/modules/mob/mob.dm
+++ b/code/modules/mob/mob.dm
@@ -503,7 +503,7 @@
to_chat(src, "You don't have a free hand to examine this!")
return FALSE
//can only queue up one examine on something at a time
- if(examined_thing in do_afters)
+ if(DOING_INTERACTION_WITH_TARGET(src, examined_thing))
return FALSE
to_chat(src, "You start feeling around for something...")
diff --git a/code/modules/mob/mob_defines.dm b/code/modules/mob/mob_defines.dm
index 6873ee602dac..def2bde930e0 100644
--- a/code/modules/mob/mob_defines.dm
+++ b/code/modules/mob/mob_defines.dm
@@ -191,7 +191,7 @@
///List of progress bars this mob is currently seeing for actions
var/list/progressbars = null //for stacking do_after bars
- ///For storing what do_after's someone has, in case we want to restrict them to only one of a certain do_after at a time
+ ///For storing what do_after's someone has, key = string, value = amount of interactions of that type happening.
var/list/do_afters
///Allows a datum to intercept all click calls this mob is the source of
diff --git a/code/modules/spells/spell_types/lightning.dm b/code/modules/spells/spell_types/lightning.dm
index 5e9db6156cf2..85f211511135 100644
--- a/code/modules/spells/spell_types/lightning.dm
+++ b/code/modules/spells/spell_types/lightning.dm
@@ -28,7 +28,7 @@
halo = halo || mutable_appearance('icons/effects/effects.dmi', "electricity", EFFECTS_LAYER)
user.add_overlay(halo)
playsound(get_turf(user), Snd, 50, FALSE)
- if(do_after(user, 10 SECONDS, user, timed_action_flags = UNINTERRUPTIBLE))
+ if(do_after(user, 10 SECONDS, user, timed_action_flags = (IGNORE_USER_LOC_CHANGE|IGNORE_TARGET_LOC_CHANGE|IGNORE_HELD_ITEM)))
if(ready && cast_check(skipcharge=1))
choose_targets()
else
diff --git a/shiptest.dme b/shiptest.dme
index b9c9d669050d..c20ad4f84a1b 100644
--- a/shiptest.dme
+++ b/shiptest.dme
@@ -52,6 +52,7 @@
#include "code\__DEFINES\directional.dm"
#include "code\__DEFINES\diseases.dm"
#include "code\__DEFINES\DNA.dm"
+#include "code\__DEFINES\do_afters.dm"
#include "code\__DEFINES\dye_keys.dm"
#include "code\__DEFINES\economy.dm"
#include "code\__DEFINES\events.dm"