Skip to content

Commit

Permalink
Refactor do_after() into a datum. (#1149)
Browse files Browse the repository at this point in the history
* refactor do_after into a datum

* remove shitcode

* Wake up github

* subsystemize

* i may be tarded
  • Loading branch information
Kapu1178 authored Dec 9, 2024
1 parent 8205d94 commit d26ce21
Show file tree
Hide file tree
Showing 9 changed files with 262 additions and 196 deletions.
4 changes: 3 additions & 1 deletion code/__DEFINES/flags.dm
Original file line number Diff line number Diff line change
Expand Up @@ -291,8 +291,10 @@ GLOBAL_LIST_INIT(bitflags, list(1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 204
#define IGNORE_INCAPACITATED (1<<3)
/// Used to prevent important slowdowns from being abused by drugs like kronkaine
#define IGNORE_SLOWDOWNS (1<<4)
/// If the user has their next_move value changed (usually by clicking), fail.
#define DO_RESTRICT_CLICKING (1<<5)
/// Shown to all mobs not just the user
#define DO_PUBLIC (1<<5)
#define DO_PUBLIC (1<<6)



Expand Down
1 change: 1 addition & 0 deletions code/__DEFINES/subsystems.dm
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@
#define FIRE_PRIORITY_CHAT 400
#define FIRE_PRIORITY_SPEECH_CONTROLLER 300
#define FIRE_PRIORITY_RUNECHAT 250
#define FIRE_PRIORITY_DO_AFTERS 100
#define FIRE_PRIORITY_THROWING 20
/* DEFAULT WOULD BE HERE */
#define FIRE_PRIORITY_SPACEDRIFT 15
Expand Down
191 changes: 0 additions & 191 deletions code/__HELPERS/mobs.dm
Original file line number Diff line number Diff line change
Expand Up @@ -245,197 +245,6 @@ GLOBAL_LIST_EMPTY(species_list)
else
return "unknown"

//some additional checks as a callback for for do_afters that want to break on losing health or on the mob taking action
/mob/proc/break_do_after_checks(list/checked_health, check_clicks)
if(check_clicks && next_move > world.time)
return FALSE
return TRUE

//pass a list in the format list("health" = mob's health var) to check health during this
/mob/living/break_do_after_checks(list/checked_health, check_clicks)
if(islist(checked_health))
if(health < checked_health["health"])
return FALSE
checked_health["health"] = health
return ..()


/**
* Timed action involving one mob user. Target is optional, defaulting to user.
*
* Checks that `user` does not move, change hands, get stunned, etc. for the
* given `time`. Returns `TRUE` on success or `FALSE` on failure.
* Interaction_key is the assoc key under which the do_after is capped, with max_interact_count being the cap. Interaction key will default to target if not set.
*/
/proc/do_after(atom/movable/user, atom/target, time, timed_action_flags = NONE, progress = TRUE, datum/callback/extra_checks, interaction_key, max_interact_count = 1, image/display)
if(!user)
return FALSE

if(!target)
target = user
if(isnum(target))
CRASH("a do_after created by [user] had a target set as [target]- probably intended to be the time instead.")
if(isatom(time))
CRASH("a do_after created by [user] had a timer of [time]- probably intended to be the target instead.")

if(!interaction_key)
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

var/drifting = FALSE
if(SSmove_manager.processing_on(user, SSspacedrift))
drifting = TRUE

var/holding
if(ismob(user))
var/mob/mobuser = user
holding = mobuser.get_active_held_item()
if(!(timed_action_flags & IGNORE_SLOWDOWNS))
time *= mobuser.cached_multiplicative_actions_slowdown
else
timed_action_flags |= IGNORE_HELD_ITEM|IGNORE_INCAPACITATED|IGNORE_SLOWDOWNS|DO_PUBLIC

var/datum/progressbar/progbar
if(progress)
if(timed_action_flags & DO_PUBLIC)
progbar = new /datum/world_progressbar(user, time, display)
else
progbar = new(user, time, target || user)

var/endtime = world.time + time
var/starttime = world.time
. = TRUE
while (world.time < endtime)
stoplag(1)

if(!QDELETED(progbar))
progbar.update(world.time - starttime)

if(drifting && !SSmove_manager.processing_on(user, SSspacedrift))
drifting = FALSE
user_loc = user.loc

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(user != target && \
(QDELETED(target) || (!(timed_action_flags & IGNORE_TARGET_LOC_CHANGE) && target.loc != target_loc)))
. = FALSE
break

if(!QDELETED(progbar))
progbar.end_progress()

if(interaction_key)
var/reduced_interaction_count = (LAZYACCESS(user.do_afters, interaction_key)) - 1
if(reduced_interaction_count > 0) // Not done yet!
LAZYSET(user.do_afters, interaction_key, reduced_interaction_count)
return
LAZYREMOVE(user.do_afters, interaction_key)


///Timed action involving at least one mob user and a list of targets. interaction_key is the assoc key under which the do_after is capped under, and the max interaction count is how many of this interaction you can do at once.
/proc/do_after_mob(mob/user, list/targets, time = 3 SECONDS, timed_action_flags = NONE, progress = TRUE, datum/callback/extra_checks, interaction_key, max_interact_count = 1)
if(!user)
return FALSE
if(!islist(targets))
targets = list(targets)
if(!length(targets))
return FALSE
var/user_loc = user.loc

if(!(timed_action_flags & IGNORE_SLOWDOWNS))
time *= user.cached_multiplicative_actions_slowdown

var/drifting = FALSE
if(SSmove_manager.processing_on(user, SSspacedrift))
drifting = TRUE

var/list/originalloc = list()

for(var/atom/target in targets)
originalloc[target] = target.loc

if(interaction_key)
var/current_interaction_count = LAZYACCESS(user.do_afters, interaction_key) || 0
if(current_interaction_count >= max_interact_count) //We are at our peak
to_chat(user, span_warning("You can't do this at the moment!"))
return
LAZYSET(user.do_afters, interaction_key, current_interaction_count + 1)


var/holding = user.get_active_held_item()
var/datum/progressbar/progbar
if(progress)
progbar = new(user, time, targets[1])

var/endtime = world.time + time
var/starttime = world.time
. = TRUE
while(world.time < endtime)
stoplag(1)

if(!QDELETED(progbar))
progbar.update(world.time - starttime)
if(QDELETED(user) || !length(targets))
. = FALSE
break

if(drifting && !SSmove_manager.processing_on(user, SSspacedrift))
drifting = FALSE
user_loc = user.loc

if(
(!(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

for(var/atom/target as anything in targets)
if(
(QDELETED(target)) \
|| (!(timed_action_flags & IGNORE_TARGET_LOC_CHANGE) && originalloc[target] != target.loc) \
)
. = FALSE
break

if(!.) // In case the for-loop found a reason to break out of the while.
break

if(!QDELETED(progbar))
progbar.end_progress()

if(interaction_key)
var/reduced_interaction_count = (LAZYACCESS(user.do_afters, interaction_key)) - 1
if(reduced_interaction_count > 0) // Not done yet!
LAZYSET(user.do_afters, interaction_key, reduced_interaction_count)
return
LAZYREMOVE(user.do_afters, interaction_key)

/// Returns the total amount of do_afters this mob is taking part in
/mob/proc/do_after_count()
var/count = 0
for(var/key in do_afters)
count += do_afters[key]
return count

/proc/is_species(A, species_datum)
. = FALSE
if(ishuman(A))
Expand Down
5 changes: 5 additions & 0 deletions code/controllers/subsystem/processing/do_afters.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
PROCESSING_SUBSYSTEM_DEF(timed_action)
name = "Timed Actions"
priority = FIRE_PRIORITY_DO_AFTERS
flags = SS_TICKER|SS_NO_INIT|SS_HIBERNATE
wait = 1
2 changes: 1 addition & 1 deletion code/game/objects/structures/crates_lockers/closets.dm
Original file line number Diff line number Diff line change
Expand Up @@ -512,7 +512,7 @@ DEFINE_INTERACTABLE(/obj/structure/closet)
span_warning("You [actuallyismob ? "try to ":""]stuff [O] into [src]."), \
span_hear("You hear clanging."))
if(actuallyismob)
if(do_after_mob(user, targets, 40))
if(do_after(user, targets, 40))
user.visible_message(span_notice("[user] stuffs [O] into [src]."), \
span_notice("You stuff [O] into [src]."), \
span_hear("You hear a loud metal bang."))
Expand Down
20 changes: 17 additions & 3 deletions code/modules/antagonists/cult/rune_spawn_action.dm
Original file line number Diff line number Diff line change
Expand Up @@ -58,15 +58,22 @@
cooldown = base_cooldown + world.time
owner?.update_mob_action_buttons()
addtimer(CALLBACK(owner, TYPE_PROC_REF(/mob, update_mob_action_buttons)), base_cooldown)
var/list/health

var/mob_health = null
if(damage_interrupt && isliving(owner))
var/mob/living/L = owner
health = list("health" = L.health)
mob_health = L.health

var/scribe_mod = scribe_time
if(istype(T, /turf/open/floor/engine/cult))
scribe_mod *= 0.5

playsound(T, 'sound/magic/enter_blood.ogg', 100, FALSE)
if(do_after(owner, owner, scribe_mod, extra_checks = CALLBACK(owner, TYPE_PROC_REF(/mob, break_do_after_checks), health, action_interrupt)))

var/flags = action_interrupt ? DO_RESTRICT_CLICKING : NONE
var/datum/callback/cb = mob_health ? CALLBACK(src, PROC_REF(check_health_changed), owner, list(mob_health)) : null

if(do_after(owner, owner, scribe_mod, flags, extra_checks = cb))
new rune_type(owner.loc, chosen_keyword)
else
qdel(R1)
Expand All @@ -79,6 +86,13 @@
cooldown = 0
owner?.update_mob_action_buttons()

/datum/action/innate/cult/create_rune/proc/check_health_changed(mob/living/user, list/old_health)
if(user.health < old_health[1])
return FALSE

old_health[1] = user.health
return TRUE

//teleport rune
/datum/action/innate/cult/create_rune/tele
name = "Summon Teleport Rune"
Expand Down
64 changes: 64 additions & 0 deletions code/modules/do_after/do_after.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/**
* Timed action involving one mob user. Target is optional, defaulting to user.
* Returns TRUE if the action succeeded and was not interrupted.
*
* Args:
* * user: The one performing the action.
* * target: An atom or list of atoms to perform the action on. If null, defaults to user.
* * timed_action_flags: A bitfield defining the behavior of the action.
* * progress: Boolean value, if TRUE, show a progress bar over the target's head.
* * extra_checks: An optional callback to check in addition to the default checks.
* * interaction_key: An optional non-numeric value to disamibiguate the action, to be used with DOING_INTERACTION() macros. Defaults to target.
* * max_interact_count: The action will automatically fail if they are already performing this many or more actions with the given interaction_key.
* * display: An atom or image to display over the user's head. Only works with DO_PUBLIC flag.
*/
/proc/do_after(atom/movable/user, atom/target, time, timed_action_flags = NONE, progress = TRUE, datum/callback/extra_checks, interaction_key, max_interact_count = 1, image/display)
if(!user)
return FALSE

if(time == 0)
return TRUE

if(!target)
target = user

if(isnum(target))
CRASH("a do_after created by [user] had a target set as [target]- probably intended to be the time instead.")
if(isatom(time))
CRASH("a do_after created by [user] had a timer of [time]- probably intended to be the target instead.")

if(!interaction_key)
if(!islist(target))
interaction_key = target
else
var/list/temp = list()
for(var/atom/atom as anything in target)
temp += ref(atom)

sortTim(temp, GLOBAL_PROC_REF(cmp_text_asc))
interaction_key = jointext(temp, "-")

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/datum/timed_action/action = new(user, target, time, progress, timed_action_flags, extra_checks, display)

. = action.wait()

if(interaction_key)
var/reduced_interaction_count = (LAZYACCESS(user.do_afters, interaction_key)) - 1
if(reduced_interaction_count > 0) // Not done yet!
LAZYSET(user.do_afters, interaction_key, reduced_interaction_count)
return
LAZYREMOVE(user.do_afters, interaction_key)


/// Returns the total amount of do_afters this mob is taking part in
/mob/proc/do_after_count()
var/count = 0
for(var/key in do_afters)
count += do_afters[key]
return count
Loading

0 comments on commit d26ce21

Please sign in to comment.