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"