mirror of
https://github.com/SPLURT-Station/S.P.L.U.R.T-Station-13.git
synced 2025-12-10 09:54:52 +00:00
366 lines
12 KiB
Plaintext
366 lines
12 KiB
Plaintext
/**
|
|
* Higher overhead "advanced" version of do_after.
|
|
* @params
|
|
* - atom/user is the atom doing the action or the "physical" user
|
|
* - delay is time in deciseconds
|
|
* - atom/target is the atom the action is being done to, defaults to user
|
|
* - do_after_flags see __DEFINES/flags/do_after.dm for details.
|
|
* - datum/callback/extra_checks - Every time this ticks, extra_checks() is invoked with (user, delay, target, time_left, do_after_flags, required_mobility_flags, required_combat_flags, mob_redirect, stage, initially_held_item, tool, passed_in).
|
|
* Stage can be DO_AFTER_STARTING, DO_AFTER_PROGRESSING, DO_AFTER_FINISHING
|
|
* If it returns DO_AFTER_STOP, this breaks.
|
|
* If it returns nothing, all other checks are done.
|
|
* If it returns DO_AFTER_PROCEED, all other checks are ignored.
|
|
* passed_in is a list[PROGRESS_MULTIPLIER], for modification.
|
|
* - required_mobility_flags is checked with CHECK_ALL_MOBILITY. Will immediately fail if the user isn't a mob.
|
|
* - requried_combat_flags is checked with CHECK_MULTIPLE_BITFIELDS. Will immediately fail if the user isn't a mob.
|
|
* - mob/living/mob_redirect - advanced option: If this is specified, movement and mobility/combat flag checks will use this instead of user. Progressbars will also go to this.
|
|
* - obj/item/tool - The tool we're using. See do_after flags for details.
|
|
*/
|
|
#define INVOKE_CALLBACK cb_return = extra_checks?.Invoke(user, delay, target, timeleft, do_after_flags, required_mobility_flags, required_combat_flags, mob_redirect, stage, initially_held_item, tool, passed_in)
|
|
#define CHECK_FLAG_FAILURE ((required_mobility_flags || required_combat_flags) && (!living_user || (required_mobility_flags && !CHECK_ALL_MOBILITY(living_user, required_mobility_flags)) || (required_combat_flags && !CHECK_MULTIPLE_BITFIELDS(living_user.combat_flags, required_combat_flags))))
|
|
#define TIMELEFT (timeleft)
|
|
/proc/do_after_advanced(atom/user, delay, atom/target, do_after_flags, datum/callback/extra_checks, required_mobility_flags, required_combat_flags, mob/living/mob_redirect, obj/item/tool)
|
|
// CHECK AND SET VARIABLES
|
|
if(!user)
|
|
return FALSE
|
|
if(!target)
|
|
target = user
|
|
if((user.loc == null) || (target.loc == null))
|
|
return FALSE
|
|
var/mob/living/living_user = mob_redirect
|
|
if(!living_user && isliving(user))
|
|
living_user = user
|
|
var/stage = DO_AFTER_STARTING
|
|
var/startlocuser = user.loc
|
|
var/startloctarget = target.loc
|
|
var/turf/userturf = get_turf(user)
|
|
var/turf/targetturf = get_turf(target)
|
|
if(!userturf || !targetturf)
|
|
return FALSE
|
|
if((do_after_flags & DO_AFTER_REQUIRES_USER_ON_TURF) && !isturf(user.loc))
|
|
return FALSE
|
|
if(!(do_after_flags & DO_AFTER_NO_COEFFICIENT) && living_user)
|
|
delay *= living_user.cached_multiplicative_actions_slowdown
|
|
var/timeleft = delay
|
|
var/obj/item/initially_held_item = mob_redirect?.get_active_held_item()
|
|
var/atom/movable/AM_user = ismovable(user) && user
|
|
var/drifting = AM_user?.Process_Spacemove(NONE) && AM_user.inertia_dir
|
|
var/initial_dx = targetturf.x - userturf.x
|
|
var/initial_dy = targetturf.y - userturf.y
|
|
var/dx = initial_dx
|
|
var/dy = initial_dy
|
|
// DO OUR STARTING CHECKS
|
|
var/cb_return
|
|
var/list/passed_in = list(1)
|
|
INVOKE_CALLBACK
|
|
if(cb_return == DO_AFTER_STOP)
|
|
return FALSE
|
|
else if(cb_return != DO_AFTER_PROCEED)
|
|
if(CHECK_FLAG_FAILURE)
|
|
return FALSE
|
|
// SETUP LOOP
|
|
var/datum/progressbar/progbar
|
|
if(living_user)
|
|
if(!(do_after_flags & DO_AFTER_NO_PROGRESSBAR))
|
|
progbar = new(living_user, delay, target)
|
|
// MAIN LOOP
|
|
. = TRUE
|
|
if(!delay)
|
|
return
|
|
var/obj/item/held
|
|
var/locchanged
|
|
var/ctu
|
|
var/ctt
|
|
var/tick_time = world.time
|
|
while(timeleft > 0)
|
|
stoplag(1)
|
|
var/timepassed = world.time - tick_time
|
|
tick_time = world.time
|
|
progbar?.update(TIMELEFT)
|
|
if(QDELETED(user) || QDELETED(target) || (user.loc == null) || (target.loc == null))
|
|
. = FALSE
|
|
break
|
|
INVOKE_CALLBACK
|
|
timeleft -= timepassed * passed_in[1]
|
|
if(cb_return == DO_AFTER_STOP)
|
|
. = FALSE
|
|
break
|
|
else if(cb_return == DO_AFTER_PROCEED)
|
|
continue
|
|
// otherwise, go through our normal checks.
|
|
if(((do_after_flags & DO_AFTER_DISALLOW_MOVING_ABSOLUTE_USER) && (user.loc != startlocuser)) || ((do_after_flags & DO_AFTER_DISALLOW_MOVING_ABSOLUTE_TARGET) && (target.loc != startloctarget)))
|
|
. = FALSE
|
|
break
|
|
else if(do_after_flags & DO_AFTER_DISALLOW_MOVING_RELATIVE)
|
|
ctu = get_turf(user)
|
|
ctt = get_turf(target)
|
|
locchanged = (userturf != ctu) || (targetturf != ctt)
|
|
userturf = ctu
|
|
targetturf = ctt
|
|
dx = targetturf.x - userturf.x
|
|
dy = targetturf.y - userturf.y
|
|
if((dx != initial_dx) || (dy != initial_dy))
|
|
. = FALSE
|
|
break
|
|
if(locchanged && !drifting && !(do_after_flags & DO_AFTER_ALLOW_NONSPACEDRIFT_RELATIVITY))
|
|
. = FALSE
|
|
break
|
|
if(!AM_user.inertia_dir)
|
|
drifting = FALSE
|
|
if((do_after_flags & DO_AFTER_REQUIRES_USER_ON_TURF) && !isturf(user.loc))
|
|
return FALSE
|
|
if(CHECK_FLAG_FAILURE)
|
|
. = FALSE
|
|
break
|
|
held = living_user?.get_active_held_item()
|
|
if((do_after_flags & DO_AFTER_DISALLOW_ACTIVE_ITEM_CHANGE) && (held != (tool || initially_held_item)))
|
|
. = FALSE
|
|
break
|
|
if((do_after_flags & DO_AFTER_REQUIRE_FREE_HAND_OR_TOOL) && (!living_user?.is_holding(tool) && !length(living_user?.get_empty_held_indexes())))
|
|
. = FALSE
|
|
break
|
|
|
|
// CLEANUP
|
|
progbar.end_progress()
|
|
// If we failed, just return.
|
|
if(!.)
|
|
return FALSE
|
|
// DO FINISHING CHECKS
|
|
if(QDELETED(user) || QDELETED(target))
|
|
return FALSE
|
|
INVOKE_CALLBACK
|
|
if(cb_return == DO_AFTER_STOP)
|
|
return FALSE
|
|
else if(cb_return == DO_AFTER_PROCEED)
|
|
return TRUE
|
|
if(CHECK_FLAG_FAILURE)
|
|
return FALSE
|
|
if(((do_after_flags & DO_AFTER_DISALLOW_MOVING_ABSOLUTE_USER) && (user.loc != startlocuser)) || ((do_after_flags & DO_AFTER_DISALLOW_MOVING_ABSOLUTE_TARGET) && (target.loc != startloctarget)))
|
|
return FALSE
|
|
else if(do_after_flags & DO_AFTER_DISALLOW_MOVING_RELATIVE)
|
|
ctu = get_turf(user)
|
|
ctt = get_turf(target)
|
|
locchanged = (userturf != ctu) || (targetturf != ctt)
|
|
userturf = ctu
|
|
targetturf = ctt
|
|
dx = targetturf.x - userturf.x
|
|
dy = targetturf.y - userturf.y
|
|
if((dx != initial_dx) || (dy != initial_dy))
|
|
return FALSE
|
|
if(locchanged && !drifting && !(do_after_flags & DO_AFTER_ALLOW_NONSPACEDRIFT_RELATIVITY))
|
|
return FALSE
|
|
if((do_after_flags & DO_AFTER_REQUIRES_USER_ON_TURF) && !isturf(user.loc))
|
|
return FALSE
|
|
held = living_user?.get_active_held_item()
|
|
if((do_after_flags & DO_AFTER_DISALLOW_ACTIVE_ITEM_CHANGE) && (held != (tool || initially_held_item)))
|
|
return FALSE
|
|
if((do_after_flags & DO_AFTER_REQUIRE_FREE_HAND_OR_TOOL) && (!living_user?.is_holding(tool) && !length(living_user?.get_empty_held_indexes())))
|
|
return FALSE
|
|
|
|
#undef INVOKE_CALLBACK
|
|
#undef CHECK_FLAG_FAILURE
|
|
|
|
/proc/do_mob(mob/user , mob/target, time = 30, uninterruptible = 0, progress = 1, datum/callback/extra_checks = null, ignorehelditem = FALSE, resume_time = 0 SECONDS)
|
|
if(!user || !target)
|
|
return 0
|
|
var/user_loc = user.loc
|
|
|
|
var/drifting = 0
|
|
if(!user.Process_Spacemove(0) && user.inertia_dir)
|
|
drifting = 1
|
|
|
|
var/target_loc = target.loc
|
|
|
|
LAZYADD(user.do_afters, target)
|
|
LAZYADD(target.targeted_by, user)
|
|
|
|
var/holding = user.get_active_held_item()
|
|
var/datum/progressbar/progbar
|
|
if (progress)
|
|
progbar = new(user, time, target)
|
|
|
|
var/endtime = world.time+time
|
|
var/starttime = world.time
|
|
. = 1
|
|
while (world.time + resume_time < endtime)
|
|
stoplag(1)
|
|
if (progress)
|
|
progbar.update(world.time - starttime + resume_time)
|
|
if(QDELETED(user) || QDELETED(target))
|
|
. = 0
|
|
break
|
|
if(uninterruptible)
|
|
continue
|
|
if(!(target in user.do_afters))
|
|
. = FALSE
|
|
break
|
|
|
|
if(!(target in user.do_afters))
|
|
. = FALSE
|
|
break
|
|
|
|
if(drifting && !user.inertia_dir)
|
|
drifting = 0
|
|
user_loc = user.loc
|
|
|
|
if((!drifting && user.loc != user_loc) || target.loc != target_loc || (!ignorehelditem && user.get_active_held_item() != holding) || user.incapacitated() || user.lying || (extra_checks && !extra_checks.Invoke()))
|
|
. = 0
|
|
break
|
|
|
|
if(!QDELETED(progbar))
|
|
progbar.end_progress()
|
|
|
|
if(!QDELETED(target))
|
|
LAZYREMOVE(user.do_afters, target)
|
|
LAZYREMOVE(target.targeted_by, user)
|
|
|
|
//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 && !CheckActionCooldown())
|
|
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 ..()
|
|
|
|
/proc/do_after(mob/user, var/delay, needhand = 1, atom/target = null, progress = 1, datum/callback/extra_checks = null, required_mobility_flags = (MOBILITY_USE|MOBILITY_MOVE), resume_time = 0 SECONDS)
|
|
if(!user)
|
|
return 0
|
|
var/atom/Tloc = null
|
|
if(target && !isturf(target))
|
|
Tloc = target.loc
|
|
|
|
if(target)
|
|
LAZYADD(user.do_afters, target)
|
|
LAZYADD(target.targeted_by, user)
|
|
|
|
var/atom/Uloc = user.loc
|
|
|
|
var/drifting = 0
|
|
if(!user.Process_Spacemove(0) && user.inertia_dir)
|
|
drifting = 1
|
|
|
|
var/holding = user.get_active_held_item()
|
|
|
|
var/holdingnull = 1 //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
|
|
|
|
delay *= user.cached_multiplicative_actions_slowdown
|
|
|
|
var/datum/progressbar/progbar
|
|
if (progress)
|
|
progbar = new(user, delay, target)
|
|
|
|
var/endtime = world.time + delay
|
|
var/starttime = world.time
|
|
. = 1
|
|
var/mob/living/L = isliving(user) && user //evals to last thing eval'd
|
|
while (world.time + resume_time < endtime)
|
|
stoplag(1)
|
|
if (progress)
|
|
progbar.update(world.time - starttime + resume_time)
|
|
|
|
if(drifting && !user.inertia_dir)
|
|
drifting = 0
|
|
Uloc = user.loc
|
|
|
|
if(L && !CHECK_ALL_MOBILITY(L, required_mobility_flags))
|
|
. = 0
|
|
break
|
|
|
|
if(QDELETED(user) || user.stat || (!drifting && user.loc != Uloc) || (extra_checks && !extra_checks.Invoke()))
|
|
. = 0
|
|
break
|
|
|
|
if(!QDELETED(Tloc) && (QDELETED(target) || Tloc != target.loc))
|
|
if((Uloc != Tloc || Tloc != user) && !drifting)
|
|
. = 0
|
|
break
|
|
|
|
if(target && !(target in user.do_afters))
|
|
. = FALSE
|
|
break
|
|
|
|
if(needhand)
|
|
//This might seem like an odd check, but you can still need a hand even when it's empty
|
|
//i.e the hand is used to pull some item/tool out of the construction
|
|
if(!holdingnull)
|
|
if(!holding)
|
|
. = 0
|
|
break
|
|
if(user.get_active_held_item() != holding)
|
|
. = 0
|
|
break
|
|
|
|
if(!QDELETED(progbar))
|
|
progbar.end_progress()
|
|
|
|
if(!QDELETED(target))
|
|
LAZYREMOVE(user.do_afters, target)
|
|
|
|
if(!QDELETED(target))
|
|
LAZYREMOVE(user.do_afters, target)
|
|
LAZYREMOVE(target.targeted_by, user)
|
|
|
|
/proc/do_after_mob(mob/user, var/list/targets, time = 30, uninterruptible = 0, progress = 1, datum/callback/extra_checks)
|
|
if(!user || !targets)
|
|
return 0
|
|
if(!islist(targets))
|
|
targets = list(targets)
|
|
var/user_loc = user.loc
|
|
|
|
var/drifting = 0
|
|
if(!user.Process_Spacemove(0) && user.inertia_dir)
|
|
drifting = 1
|
|
|
|
var/list/originalloc = list()
|
|
for(var/atom/target in targets)
|
|
originalloc[target] = target.loc
|
|
LAZYADD(user.do_afters, target)
|
|
LAZYADD(target.targeted_by, user)
|
|
|
|
var/holding = user.get_active_held_item()
|
|
var/datum/progressbar/progbar
|
|
if(progress)
|
|
progbar = new(user, time, targets[1])
|
|
|
|
time *= user.cached_multiplicative_actions_slowdown
|
|
|
|
var/endtime = world.time + time
|
|
var/starttime = world.time
|
|
. = 1
|
|
mainloop:
|
|
while(world.time < endtime)
|
|
stoplag(1)
|
|
if(progress)
|
|
progbar.update(world.time - starttime)
|
|
if(QDELETED(user) || !targets)
|
|
. = 0
|
|
break
|
|
if(uninterruptible)
|
|
continue
|
|
|
|
if(drifting && !user.inertia_dir)
|
|
drifting = 0
|
|
user_loc = user.loc
|
|
|
|
for(var/atom/target in targets)
|
|
if((!drifting && user_loc != user.loc) || QDELETED(target) || originalloc[target] != target.loc || user.get_active_held_item() != holding || user.incapacitated() || user.lying || (extra_checks && !extra_checks.Invoke()))
|
|
. = 0
|
|
break mainloop
|
|
|
|
if(!QDELETED(progbar))
|
|
progbar.end_progress()
|
|
|
|
for(var/thing in targets)
|
|
var/atom/target = thing
|
|
if(!QDELETED(target))
|
|
LAZYREMOVE(user.do_afters, target)
|
|
LAZYREMOVE(target.targeted_by, user)
|