diff --git a/code/__DEFINES/MC.dm b/code/__DEFINES/MC.dm
index bd980a54b8a..2ea7258816b 100644
--- a/code/__DEFINES/MC.dm
+++ b/code/__DEFINES/MC.dm
@@ -45,6 +45,5 @@
#define SS_POST_FIRE_TIMING 128
//Timing subsystem
-#define GLOBAL_PROC "some_magic_bullshit"
#define TIMER_NORMAL "normal"
-#define TIMER_UNIQUE "unique"
+#define TIMER_UNIQUE "unique"
\ No newline at end of file
diff --git a/code/__DEFINES/callbacks.dm b/code/__DEFINES/callbacks.dm
new file mode 100644
index 00000000000..762e7c38b0f
--- /dev/null
+++ b/code/__DEFINES/callbacks.dm
@@ -0,0 +1,3 @@
+#define GLOBAL_PROC "some_magic_bullshit"
+
+#define CALLBACK new /datum/callback
\ No newline at end of file
diff --git a/code/controllers/subsystem/throwing.dm b/code/controllers/subsystem/throwing.dm
new file mode 100644
index 00000000000..56d887eb8bc
--- /dev/null
+++ b/code/controllers/subsystem/throwing.dm
@@ -0,0 +1,139 @@
+#define MAX_THROWING_DIST 512 // 2 z-levels on default width
+#define MAX_TICKS_TO_MAKE_UP 3 //how many missed ticks will we attempt to make up for this run.
+var/datum/subsystem/throwing/SSthrowing
+
+/datum/subsystem/throwing
+ name = "Throwing"
+ priority = 25
+ wait = 1
+ flags = SS_NO_INIT|SS_KEEP_TIMING|SS_TICKER
+
+ var/list/currentrun
+ var/list/processing
+
+/datum/subsystem/throwing/New()
+ NEW_SS_GLOBAL(SSthrowing)
+ processing = list()
+
+
+/datum/subsystem/throwing/stat_entry()
+ ..("P:[processing.len]")
+
+
+/datum/subsystem/throwing/fire(resumed = 0)
+ if (!resumed)
+ src.currentrun = processing.Copy()
+
+ //cache for sanic speed (lists are references anyways)
+ var/list/currentrun = src.currentrun
+
+ while(length(currentrun))
+ var/atom/movable/AM = currentrun[currentrun.len]
+ var/datum/thrownthing/TT = currentrun[AM]
+ currentrun.len--
+ if (!AM || !TT)
+ processing -= AM
+ if (MC_TICK_CHECK)
+ return
+ continue
+
+ TT.tick()
+
+ if (MC_TICK_CHECK)
+ return
+
+ currentrun = null
+
+/datum/thrownthing
+ var/atom/movable/thrownthing
+ var/atom/target
+ var/turf/target_turf
+ var/init_dir
+ var/maxrange
+ var/speed
+ var/mob/thrower
+ var/diagonals_first
+ var/dist_travelled = 0
+ var/start_time
+ var/dist_x
+ var/dist_y
+ var/dx
+ var/dy
+ var/pure_diagonal
+ var/diagonal_error
+ var/datum/callback/callback
+
+/datum/thrownthing/proc/tick()
+ var/atom/movable/AM = thrownthing
+ if (!isturf(AM.loc) || !AM.throwing)
+ finialize()
+ return
+
+ if (dist_travelled && hitcheck()) //to catch sneaky things moving on our tile while we slept
+ finialize()
+ return
+
+ var/atom/step
+
+ //calculate how many tiles to move, making up for any missed ticks.
+ var/tilestomove = round(min(((((world.time+world.tick_lag) - start_time) * speed) - (dist_travelled ? dist_travelled : -1)), speed*MAX_TICKS_TO_MAKE_UP) * (world.tick_lag * SSthrowing.wait))
+ while (tilestomove-- > 0)
+ if ((dist_travelled >= maxrange || AM.loc == target_turf) && AM.has_gravity(AM.loc))
+ finialize()
+ return
+
+ if (dist_travelled <= max(dist_x, dist_y)) //if we haven't reached the target yet we home in on it, otherwise we use the initial direction
+ step = get_step(AM, get_dir(AM, target_turf))
+ else
+ step = get_step(AM, init_dir)
+
+ if (!pure_diagonal && !diagonals_first) // not a purely diagonal trajectory and we don't want all diagonal moves to be done first
+ if (diagonal_error >= 0 && max(dist_x,dist_y) - dist_travelled != 1) //we do a step forward unless we're right before the target
+ step = get_step(AM, dx)
+ diagonal_error += (diagonal_error < 0) ? dist_x/2 : -dist_y
+
+ if (!step) // going off the edge of the map makes get_step return null, don't let things go off the edge
+ finialize()
+ return
+
+ AM.Move(step, get_dir(AM, step))
+
+ if (!AM.throwing) // we hit something during our move
+ finialize(hit = TRUE)
+ return
+
+ dist_travelled++
+
+ if (dist_travelled > MAX_THROWING_DIST)
+ finialize()
+ return
+
+/datum/thrownthing/proc/finialize(hit = FALSE)
+ set waitfor = 0
+ SSthrowing.processing -= thrownthing
+ //done throwing, either because it hit something or it finished moving
+ thrownthing.throwing = 0
+ if (!hit)
+ for (var/thing in get_turf(thrownthing)) //looking for our target on the turf we land on.
+ var/atom/A = thing
+ if (A == target)
+ hit = 1
+ thrownthing.throw_impact(A)
+ break
+ if (!hit)
+ thrownthing.throw_impact(get_turf(thrownthing)) // we haven't hit something yet and we still must, let's hit the ground.
+ thrownthing.newtonian_move(init_dir)
+ else
+ thrownthing.newtonian_move(init_dir)
+ if (callback)
+ callback.Invoke()
+
+/datum/thrownthing/proc/hitcheck()
+ for (var/thing in get_turf(thrownthing))
+ var/atom/movable/AM = thing
+ if (AM == thrownthing)
+ continue
+ if (AM.density && !(AM.pass_flags & LETPASSTHROW) && !(AM.flags & ON_BORDER))
+ thrownthing.throwing = 0
+ thrownthing.throw_impact(AM)
+ return 1
diff --git a/code/datums/callback.dm b/code/datums/callback.dm
new file mode 100644
index 00000000000..24d6be6106d
--- /dev/null
+++ b/code/datums/callback.dm
@@ -0,0 +1,90 @@
+/*
+ USAGE:
+
+ var/datum/callback/C = new(object|null, /proc/type/path|"procstring", arg1, arg2, ... argn)
+ var/timerid = addtimer(C, time, timertype)
+ OR
+ var/timerid = addtimer(CALLBACK(object|null, /proc/type/path|procstring, arg1, arg2, ... argn), time, timertype)
+
+ Note: proc strings can only be given for datum proc calls, global procs must be proc paths
+ Also proc strings are strongly advised against because they don't compile error if the proc stops existing
+ See the note on proc typepath shortcuts
+
+ INVOKING THE CALLBACK:
+ var/result = C.Invoke(args, to, add) //additional args are added after the ones given when the callback was created
+ OR
+ var/result = C.InvokeAsync(args, to, add) //Sleeps will not block, returns . on the first sleep (then continues on in the "background" after the sleep/block ends), otherwise operates normally.
+
+ PROC TYPEPATH SHORTCUTS (these operate on paths, not types, so to these shortcuts, datum is NOT a parent of atom, etc...)
+
+ global proc while in another global proc:
+ .procname
+ Example:
+ CALLBACK(GLOBAL_PROC, .some_proc_here)
+
+ proc defined on current(src) object (when in a /proc/ and not an override) OR overridden at src or any of it's parents:
+ .procname
+ Example:
+ CALLBACK(src, .some_proc_here)
+
+
+ when the above doesn't apply:
+ .proc/procname
+ Example:
+ CALLBACK(src, .proc/some_proc_here)
+
+ proc defined on a parent of a some type:
+ /some/type/.proc/some_proc_here
+
+
+
+ Other wise you will have to do the full typepath of the proc (/type/of/thing/proc/procname)
+
+*/
+
+/datum/callback
+ var/datum/object = GLOBAL_PROC
+ var/delegate
+ var/list/arguments
+
+/datum/callback/New(thingtocall, proctocall, ...)
+ if (thingtocall)
+ object = thingtocall
+ delegate = proctocall
+ if (length(args) > 2)
+ arguments = args.Copy(3)
+
+
+/datum/callback/proc/Invoke(...)
+ if (!object)
+ CRASH("Cannot call null.[delegate]")
+
+ var/list/calling_arguments = arguments
+
+ if (length(args))
+ if (length(arguments))
+ calling_arguments = calling_arguments + args //not += so that it creates a new list so the arguments list stays clean
+ else
+ calling_arguments = args
+
+ if (object == GLOBAL_PROC)
+ return call(delegate)(arglist(calling_arguments))
+ return call(object, delegate)(arglist(calling_arguments))
+
+//copy and pasted because fuck proc overhead
+/datum/callback/proc/InvokeAsync(...)
+ set waitfor = 0
+ if (!object)
+ CRASH("Cannot call null.[delegate]")
+
+ var/list/calling_arguments = arguments
+
+ if (length(args))
+ if (length(arguments))
+ calling_arguments = calling_arguments + args //not += so that it creates a new list so the arguments list stays clean
+ else
+ calling_arguments = args
+
+ if (object == GLOBAL_PROC)
+ return call(delegate)(arglist(calling_arguments))
+ return call(object, delegate)(arglist(calling_arguments))
diff --git a/code/datums/martial/wrestling.dm b/code/datums/martial/wrestling.dm
index 02a044232e4..9b99a2c75d0 100644
--- a/code/datums/martial/wrestling.dm
+++ b/code/datums/martial/wrestling.dm
@@ -182,9 +182,7 @@
if (T && isturf(T))
if (!D.stat)
D.emote("scream")
- D.throw_at(T, 10, 4)
- D.Weaken(2)
-
+ D.throw_at(T, 10, 4, callback = CALLBACK(D, /mob/living/carbon/human/.Weaken, 2))
add_logs(A, D, "has thrown with wrestling")
return 0
diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm
index d3b41c25845..c8959a49298 100644
--- a/code/game/atoms_movable.dm
+++ b/code/game/atoms_movable.dm
@@ -3,7 +3,7 @@
var/last_move = null
var/anchored = 0
var/throwing = 0
- var/throw_speed = 2
+ var/throw_speed = 2 //How many tiles to move per ds when being thrown. Float values are fully supported
var/throw_range = 7
var/mob/pulledby = null
var/languages_spoken = 0 //For say() and Hear()
@@ -81,11 +81,28 @@
//Called after a successful Move(). By this point, we've already moved
/atom/movable/proc/Moved(atom/OldLoc, Dir)
+ //Objects with opacity will trigger nearby lights of the old location to update at next SSlighting fire
+ if(opacity)
+ if (isturf(OldLoc))
+ OldLoc.UpdateAffectingLights()
+ if (isturf(loc))
+ loc.UpdateAffectingLights()
+ else
+ if(light)
+ light.changed()
+
if (!inertia_moving)
inertia_next_move = world.time + inertia_move_delay
newtonian_move(Dir)
if (length(client_mobs_in_contents))
update_parallax_contents()
+
+ if (orbiters)
+ for (var/thing in orbiters)
+ var/datum/orbit/O = thing
+ O.Check()
+ if (orbiting)
+ orbiting.Check()
return 1
/atom/movable/Destroy()
@@ -200,109 +217,82 @@
step(src, AM.dir)
..()
-/atom/movable/proc/throw_at_fast(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0)
- set waitfor = 0
- throw_at(target, range, speed, thrower, spin, diagonals_first)
+/atom/movable/proc/throw_at(atom/target, range, speed, mob/thrower, spin=TRUE, diagonals_first = FALSE, var/datum/callback/callback)
+ if (!target || (flags & NODROP) || speed <= 0)
+ return
-/atom/movable/proc/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0)
- if(!target || !src || (flags & NODROP))
- return 0
- //use a modified version of Bresenham's algorithm to get from the atom's current position to that of the target
-
- if(pulledby)
+ if (pulledby)
pulledby.stop_pulling()
- throwing = 1
- if(spin) //if we don't want the /atom/movable to spin.
- SpinAnimation(5, 1)
+ //They are moving! Wouldn't it be cool if we calculated their momentum and added it to the throw?
+ if (thrower && thrower.last_move && thrower.client && thrower.client.move_delay >= world.time + world.tick_lag*2)
+ var/user_momentum = thrower.movement_delay()
+ if (!user_momentum) //no movement_delay, this means they move once per byond tick, lets calculate from that instead.
+ user_momentum = world.tick_lag
- var/dist_travelled = 0
- var/next_sleep = 0
+ user_momentum = 1 / user_momentum // convert from ds to the tiles per ds that throw_at uses.
+
+ if (get_dir(thrower, target) & last_move)
+ user_momentum = user_momentum //basically a noop, but needed
+ else if (get_dir(target, thrower) & last_move)
+ user_momentum = -user_momentum //we are moving away from the target, lets slowdown the throw accordingly
+ else
+ user_momentum = 0
+
+
+ if (user_momentum)
+ //first lets add that momentum to range.
+ range *= (user_momentum / speed) + 1
+ //then lets add it to speed
+ speed += user_momentum
+ if (speed <= 0)
+ return //no throw speed, the user was moving too fast.
+
+ var/datum/thrownthing/TT = new()
+ TT.thrownthing = src
+ TT.target = target
+ TT.target_turf = get_turf(target)
+ TT.init_dir = get_dir(src, target)
+ TT.maxrange = range
+ TT.speed = speed
+ TT.thrower = thrower
+ TT.diagonals_first = diagonals_first
+ TT.callback = callback
var/dist_x = abs(target.x - src.x)
var/dist_y = abs(target.y - src.y)
var/dx = (target.x > src.x) ? EAST : WEST
var/dy = (target.y > src.y) ? NORTH : SOUTH
- var/pure_diagonal = 0
- if(dist_x == dist_y)
- pure_diagonal = 1
+ if (dist_x == dist_y)
+ TT.pure_diagonal = 1
- if(dist_x <= dist_y)
+ else if(dist_x <= dist_y)
var/olddist_x = dist_x
var/olddx = dx
dist_x = dist_y
dist_y = olddist_x
dx = dy
dy = olddx
+ TT.dist_x = dist_x
+ TT.dist_y = dist_y
+ TT.dx = dx
+ TT.dy = dy
+ TT.diagonal_error = dist_x/2 - dist_y
+ TT.start_time = world.time
- var/error = dist_x/2 - dist_y //used to decide whether our next move should be forward or diagonal.
- var/atom/finalturf = get_turf(target)
- var/hit = 0
- var/init_dir = get_dir(src, target)
+ if(pulledby)
+ pulledby.stop_pulling()
- while(target && ((dist_travelled < range && loc != finalturf) || !has_gravity(src))) //stop if we reached our destination (or max range) and aren't floating
- var/slept = 0
- if(!isturf(loc))
- hit = 1
- break
+ throwing = 1
+ if(spin)
+ SpinAnimation(5, 1)
- var/atom/step
- if(dist_travelled < max(dist_x, dist_y)) //if we haven't reached the target yet we home in on it, otherwise we use the initial direction
- step = get_step(src, get_dir(src, finalturf))
- else
- step = get_step(src, init_dir)
+ SSthrowing.processing[src] = TT
+ if (SSthrowing.paused && length(SSthrowing.currentrun))
+ SSthrowing.currentrun[src] = TT
+ TT.tick()
- if(!pure_diagonal && !diagonals_first) // not a purely diagonal trajectory and we don't want all diagonal moves to be done first
- if(error >= 0 && max(dist_x,dist_y) - dist_travelled != 1) //we do a step forward unless we're right before the target
- step = get_step(src, dx)
- error += (error < 0) ? dist_x/2 : -dist_y
- if(!step) // going off the edge of the map makes get_step return null, don't let things go off the edge
- break
- Move(step, get_dir(loc, step))
- if(!throwing) // we hit something during our move
- hit = 1
- break
- dist_travelled++
-
- if(dist_travelled > 600) //safety to prevent infinite while loop.
- break
- if(dist_travelled >= next_sleep)
- slept = 1
- next_sleep += speed
- sleep(1)
- if(!slept)
- var/ticks_slept = TICK_CHECK
- if(ticks_slept)
- slept = 1
- next_sleep += speed*(ticks_slept*world.tick_lag) //delay the next normal sleep
-
- if(slept && hitcheck()) //to catch sneaky things moving on our tile while we slept
- hit = 1
- break
-
-
- //done throwing, either because it hit something or it finished moving
- throwing = 0
- if(!hit)
- for(var/atom/A in get_turf(src)) //looking for our target on the turf we land on.
- if(A == target)
- hit = 1
- throw_impact(A)
- return 1
-
- throw_impact(get_turf(src)) // we haven't hit something yet and we still must, let's hit the ground.
- newtonian_move(init_dir)
- return 1
-
-/atom/movable/proc/hitcheck()
- for(var/atom/movable/AM in get_turf(src))
- if(AM == src)
- continue
- if(AM.density && !(AM.pass_flags & LETPASSTHROW) && !(AM.flags & ON_BORDER))
- throwing = 0
- throw_impact(AM)
- return 1
/atom/movable/proc/handle_buckled_mob_movement(newloc,direct)
for(var/m in buckled_mobs)
diff --git a/code/game/gamemodes/changeling/powers/mutations.dm b/code/game/gamemodes/changeling/powers/mutations.dm
index f7aa0e75e74..f87b293e534 100644
--- a/code/game/gamemodes/changeling/powers/mutations.dm
+++ b/code/game/gamemodes/changeling/powers/mutations.dm
@@ -341,15 +341,13 @@
if(INTENT_GRAB)
C.visible_message("[L] is grabbed by [H]'s tentacle!","A tentacle grabs you and pulls you towards [H]!")
- C.throw_at(get_step_towards(H,C), 8, 2)
- addtimer(src, "tentacle_grab", 3, TIMER_NORMAL, H, C)
+ C.throw_at(get_step_towards(H,C), 8, 2, callback=CALLBACK(src, .proc/tentacle_grab, H, C))
return 1
if(INTENT_HARM)
C.visible_message("[L] is thrown towards [H] by a tentacle!","A tentacle grabs you and throws you towards [H]!")
C.Weaken(3)
- C.throw_at(get_step_towards(H,C), 8, 2)
- addtimer(src, "tentacle_stab", 3, TIMER_NORMAL, H, C)
+ C.throw_at(get_step_towards(H,C), 8, 2, callback=CALLBACK(src, .proc/tentacle_stab, H, C))
return 1
else
L.visible_message("[L] is pulled by [H]'s tentacle!","A tentacle grabs you and pulls you towards [H]!")
diff --git a/code/game/machinery/doors/airlock_types.dm b/code/game/machinery/doors/airlock_types.dm
index aa97b109dc0..847de0fc50a 100644
--- a/code/game/machinery/doors/airlock_types.dm
+++ b/code/game/machinery/doors/airlock_types.dm
@@ -377,7 +377,7 @@
M << pick(sound('sound/hallucinations/turn_around1.ogg',0,1,50), sound('sound/hallucinations/turn_around2.ogg',0,1,50))
flash_color(M, flash_color="#960000", flash_time=20)
M.Weaken(2)
- M.throw_at_fast(throwtarget, 5, 1,src)
+ M.throw_at(throwtarget, 5, 1,src)
return 0
/obj/machinery/door/airlock/cult/narsie_act()
diff --git a/code/game/machinery/mass_driver.dm b/code/game/machinery/mass_driver.dm
index bb77e578741..0b5faa30722 100644
--- a/code/game/machinery/mass_driver.dm
+++ b/code/game/machinery/mass_driver.dm
@@ -26,7 +26,7 @@
audible_message("[src] lets out a screech, it doesn't seem to be able to handle the load.")
break
use_power(500)
- O.throw_at_fast(target, drive_range * power, power)
+ O.throw_at(target, drive_range * power, power)
flick("mass_driver1", src)
diff --git a/code/game/mecha/equipment/weapons/weapons.dm b/code/game/mecha/equipment/weapons/weapons.dm
index 4a3e14cd5d6..49ab002574a 100644
--- a/code/game/mecha/equipment/weapons/weapons.dm
+++ b/code/game/mecha/equipment/weapons/weapons.dm
@@ -290,7 +290,7 @@
log_message("Launched a [O.name] from [name], targeting [target].")
projectiles--
proj_init(O)
- O.throw_at_fast(target, missile_range, missile_speed, spin = 0)
+ O.throw_at(target, missile_range, missile_speed, spin = 0)
return 1
//used for projectile initilisation (priming flashbang) and additional logging
diff --git a/code/game/objects/explosion.dm b/code/game/objects/explosion.dm
index d3ba5840d35..a0b4f066016 100644
--- a/code/game/objects/explosion.dm
+++ b/code/game/objects/explosion.dm
@@ -147,7 +147,7 @@
var/throw_range = rand(throw_dist, max_range)
var/turf/throw_at = get_ranged_target_turf(I, throw_dir, throw_range)
I.throw_speed = 4 //Temporarily change their throw_speed for embedding purposes (Reset when it finishes throwing, regardless of hitting anything)
- I.throw_at_fast(throw_at, throw_range, 2)//Throw it at 2 speed, this is purely visual anyway.
+ I.throw_at(throw_at, throw_range, I.throw_speed)
CHECK_TICK
diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm
index a487104dd54..46150235bc4 100644
--- a/code/game/objects/items.dm
+++ b/code/game/objects/items.dm
@@ -519,7 +519,7 @@ var/global/image/fire_overlay = image("icon" = 'icons/effects/fire.dmi', "icon_s
/obj/item/singularity_pull(S, current_size)
if(current_size >= STAGE_FOUR)
- throw_at_fast(S,14,3, spin=0)
+ throw_at(S,14,3, spin=0)
else ..()
/obj/item/throw_impact(atom/A)
@@ -528,12 +528,17 @@ var/global/image/fire_overlay = image("icon" = 'icons/effects/fire.dmi', "icon_s
itempush = 0 //too light to push anything
return A.hitby(src, 0, itempush)
-/obj/item/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0)
+/obj/item/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback)
thrownby = thrower
- . = ..()
- throw_speed = initial(throw_speed) //explosions change this.
+ callback = CALLBACK(src, .proc/after_throw, callback) //replace their callback with our own
+ . = ..(target, range, speed, thrower, spin, diagonals_first, callback)
+/obj/item/proc/after_throw(datum/callback/callback)
+ if (callback) //call the original callback
+ . = callback.Invoke()
+ throw_speed = initial(throw_speed) //explosions change this.
+
/obj/item/proc/remove_item_from_storage(atom/newLoc) //please use this if you're going to snowflake an item out of a obj/item/weapon/storage
if(!newLoc)
return 0
diff --git a/code/game/objects/items/weapons/dice.dm b/code/game/objects/items/weapons/dice.dm
index a9fa9d1b45f..86bd732c55f 100644
--- a/code/game/objects/items/weapons/dice.dm
+++ b/code/game/objects/items/weapons/dice.dm
@@ -147,7 +147,7 @@
/obj/item/weapon/dice/attack_self(mob/user)
diceroll(user)
-/obj/item/weapon/dice/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0)
+/obj/item/weapon/dice/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback)
if(!..())
return
diceroll(thrower)
diff --git a/code/game/objects/items/weapons/handcuffs.dm b/code/game/objects/items/weapons/handcuffs.dm
index c1717acbd80..3f87aedd1b9 100644
--- a/code/game/objects/items/weapons/handcuffs.dm
+++ b/code/game/objects/items/weapons/handcuffs.dm
@@ -323,7 +323,7 @@
origin_tech = "engineering=3;combat=1"
var/weaken = 0
-/obj/item/weapon/restraints/legcuffs/bola/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0)
+/obj/item/weapon/restraints/legcuffs/bola/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback)
if(!..())
return
playsound(src.loc,'sound/weapons/bolathrow.ogg', 75, 1)
diff --git a/code/game/objects/items/weapons/pneumaticCannon.dm b/code/game/objects/items/weapons/pneumaticCannon.dm
index a8701e24cb8..badd8097791 100644
--- a/code/game/objects/items/weapons/pneumaticCannon.dm
+++ b/code/game/objects/items/weapons/pneumaticCannon.dm
@@ -110,7 +110,7 @@
loadedWeightClass -= ITD.w_class
ITD.throw_speed = pressureSetting * 2
ITD.loc = get_turf(src)
- ITD.throw_at_fast(target, pressureSetting * 5, pressureSetting * 2,user)
+ ITD.throw_at(target, pressureSetting * 5, pressureSetting * 2,user)
if(pressureSetting >= 3 && user)
user.visible_message("[user] is thrown down by the force of the cannon!", "[src] slams into your shoulder, knocking you down!")
user.Weaken(3)
diff --git a/code/game/objects/items/weapons/powerfist.dm b/code/game/objects/items/weapons/powerfist.dm
index 3353fdda15b..7f78ac53275 100644
--- a/code/game/objects/items/weapons/powerfist.dm
+++ b/code/game/objects/items/weapons/powerfist.dm
@@ -86,8 +86,8 @@
playsound(loc, 'sound/weapons/genhit2.ogg', 50, 1)
var/atom/throw_target = get_edge_target_turf(target, get_dir(src, get_step_away(target, src)))
- spawn(1)
- target.throw_at(throw_target, 5 * fisto_setting, 0.2)
+
+ target.throw_at(throw_target, 5 * fisto_setting, 0.2)
add_logs(user, target, "power fisted", src)
diff --git a/code/game/objects/items/weapons/singularityhammer.dm b/code/game/objects/items/weapons/singularityhammer.dm
index 09d6727b58c..2449b8629d6 100644
--- a/code/game/objects/items/weapons/singularityhammer.dm
+++ b/code/game/objects/items/weapons/singularityhammer.dm
@@ -89,7 +89,7 @@
"You feel a powerful shock course through your body sending you flying!", \
"You hear a heavy electrical crack!")
var/atom/throw_target = get_edge_target_turf(target, get_dir(src, get_step_away(target, src)))
- target.throw_at_fast(throw_target, 200, 4)
+ target.throw_at(throw_target, 200, 4)
return
/obj/item/weapon/twohanded/mjollnir/attack(mob/living/M, mob/user)
diff --git a/code/game/objects/items/weapons/storage/storage.dm b/code/game/objects/items/weapons/storage/storage.dm
index ea3016f92d0..5da58e012bb 100644
--- a/code/game/objects/items/weapons/storage/storage.dm
+++ b/code/game/objects/items/weapons/storage/storage.dm
@@ -122,7 +122,7 @@
is_seeing |= user
-/obj/item/weapon/storage/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0)
+/obj/item/weapon/storage/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback)
close_all()
return ..()
diff --git a/code/game/objects/items/weapons/twohanded.dm b/code/game/objects/items/weapons/twohanded.dm
index 54b0ebf5d38..e67c9195b19 100644
--- a/code/game/objects/items/weapons/twohanded.dm
+++ b/code/game/objects/items/weapons/twohanded.dm
@@ -670,7 +670,7 @@
/obj/item/weapon/twohanded/skybulge/update_icon()
icon_state = "sky_bulge[wielded]"
-/obj/item/weapon/twohanded/skybulge/throw_at() //Throw cooldown and offhand-proofing.
+/obj/item/weapon/twohanded/skybulge/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback) //Throw cooldown and offhand-proofing.
if(throw_cooldown > world.time)
var/mob/user = thrownby
user.put_in_hands(src)
diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm
index 7ee61aa2700..24b6924edd3 100644
--- a/code/game/objects/objs.dm
+++ b/code/game/objects/objs.dm
@@ -45,7 +45,7 @@
SStgui.close_uis(src)
return ..()
-/obj/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0)
+/obj/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback)
..()
if(is_frozen)
visible_message("[src] shatters into a million pieces!")
diff --git a/code/modules/clothing/clothing.dm b/code/modules/clothing/clothing.dm
index 3e6a01c213c..0a2912f766f 100644
--- a/code/modules/clothing/clothing.dm
+++ b/code/modules/clothing/clothing.dm
@@ -51,7 +51,7 @@
M.put_in_hand(src, H.held_index)
add_fingerprint(usr)
-/obj/item/clothing/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0)
+/obj/item/clothing/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback)
if(pockets)
pockets.close_all()
return ..()
diff --git a/code/modules/clothing/shoes/miscellaneous.dm b/code/modules/clothing/shoes/miscellaneous.dm
index 326d50942cf..693fec80cb3 100644
--- a/code/modules/clothing/shoes/miscellaneous.dm
+++ b/code/modules/clothing/shoes/miscellaneous.dm
@@ -198,7 +198,9 @@
jumping = TRUE
playsound(src.loc, 'sound/effects/stealthoff.ogg', 50, 1, 1)
usr.visible_message("[usr] dashes foward into the air!")
- usr.throw_at(target,jumpdistance,1, spin=0, diagonals_first = 1)
+ usr.throw_at(target, jumpdistance, 1, spin=0, diagonals_first = 1, callback = CALLBACK(src, .proc/hop_end))
+
+/obj/item/clothing/shoes/bhop/proc/hop_end()
jumping = FALSE
recharging_time = world.time + recharging_rate
diff --git a/code/modules/clothing/spacesuits/flightsuit.dm b/code/modules/clothing/spacesuits/flightsuit.dm
index a37bb7c3110..4714eae4e76 100644
--- a/code/modules/clothing/spacesuits/flightsuit.dm
+++ b/code/modules/clothing/spacesuits/flightsuit.dm
@@ -438,7 +438,7 @@
angle -= 360
dir = angle2dir(angle)
var/turf/target = get_edge_target_turf(get_turf(wearer), dir)
- wearer.throw_at_fast(target, (speed+density+anchored), 2, wearer)
+ wearer.throw_at(target, (speed+density+anchored), 2, wearer)
wearer.visible_message("[wearer] is knocked flying by the impact!")
/obj/item/device/flightpack/proc/flight_impact(atom/unmovablevictim, crashdir) //Yes, victim.
@@ -579,7 +579,7 @@
for(var/i in 1 to (knockback-1))
target = get_step(target, throwdir)
wearer.visible_message(knockmessage)
- victim.throw_at_fast(target, knockback, 1)
+ victim.throw_at(target, knockback, 1)
victim.Weaken(stun)
/obj/item/device/flightpack/proc/victimknockback(atom/movable/victim, power, direction)
@@ -607,7 +607,7 @@
for(var/i in 1 to knockback/3)
target = get_step(target, pick(alldirs))
if(knockback)
- victim.throw_at_fast(target, knockback, part_manip.rating)
+ victim.throw_at(target, knockback, part_manip.rating)
if(isobj(victim))
var/obj/O = victim
O.take_damage(damage)
diff --git a/code/modules/food_and_drinks/kitchen_machinery/gibber.dm b/code/modules/food_and_drinks/kitchen_machinery/gibber.dm
index fa975e12ce9..df2183f3dd4 100644
--- a/code/modules/food_and_drinks/kitchen_machinery/gibber.dm
+++ b/code/modules/food_and_drinks/kitchen_machinery/gibber.dm
@@ -223,11 +223,11 @@
var/list/turf/nearby_turfs = RANGE_TURFS(3,T) - T
var/obj/item/skin = allskin
skin.loc = src.loc
- skin.throw_at_fast(pick(nearby_turfs),meat_produced,3)
+ skin.throw_at(pick(nearby_turfs),meat_produced,3)
for (var/i=1 to meat_produced)
var/obj/item/meatslab = allmeat[i]
meatslab.loc = src.loc
- meatslab.throw_at_fast(pick(nearby_turfs),i,3)
+ meatslab.throw_at(pick(nearby_turfs),i,3)
for (var/turfs=1 to meat_produced)
var/turf/gibturf = pick(nearby_turfs)
if (!gibturf.density && src in view(gibturf))
diff --git a/code/modules/lighting/lighting_system.dm b/code/modules/lighting/lighting_system.dm
index bbb2f282840..03f4a78319e 100644
--- a/code/modules/lighting/lighting_system.dm
+++ b/code/modules/lighting/lighting_system.dm
@@ -156,17 +156,6 @@
loc.UpdateAffectingLights()
return ..()
-//Objects with opacity will trigger nearby lights of the old location to update at next SSlighting fire
-/atom/movable/Moved(atom/OldLoc, Dir)
- if(opacity)
- if (isturf(OldLoc))
- OldLoc.UpdateAffectingLights()
- if (isturf(loc))
- loc.UpdateAffectingLights()
- else
- if(light)
- light.changed()
- return ..()
//Sets our luminosity.
//If we have no light it will create one.
diff --git a/code/modules/mob/living/carbon/alien/humanoid/caste/hunter.dm b/code/modules/mob/living/carbon/alien/humanoid/caste/hunter.dm
index 5ffffbd562a..8a52daeb80b 100644
--- a/code/modules/mob/living/carbon/alien/humanoid/caste/hunter.dm
+++ b/code/modules/mob/living/carbon/alien/humanoid/caste/hunter.dm
@@ -54,10 +54,12 @@
leaping = 1
weather_immunities += "lava"
update_icons()
- throw_at(A,MAX_ALIEN_LEAP_DIST,1, spin=0, diagonals_first = 1)
- leaping = 0
- weather_immunities -= "lava"
- update_icons()
+ throw_at(A, MAX_ALIEN_LEAP_DIST, 1, spin=0, diagonals_first = 1, callback = CALLBACK(src, .leap_end))
+
+/mob/living/carbon/alien/humanoid/hunter/proc/leap_end()
+ leaping = 0
+ weather_immunities -= "lava"
+ update_icons()
/mob/living/carbon/alien/humanoid/hunter/throw_impact(atom/A)
diff --git a/code/modules/mob/living/carbon/alien/special/facehugger.dm b/code/modules/mob/living/carbon/alien/special/facehugger.dm
index 2ae266e2a73..9098171b8bf 100644
--- a/code/modules/mob/living/carbon/alien/special/facehugger.dm
+++ b/code/modules/mob/living/carbon/alien/special/facehugger.dm
@@ -101,7 +101,7 @@ var/const/MAX_ACTIVE_TIME = 400
return Attach(AM)
return 0
-/obj/item/clothing/mask/facehugger/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0)
+/obj/item/clothing/mask/facehugger/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback)
if(!..())
return
if(stat == CONSCIOUS)
diff --git a/code/modules/mob/living/carbon/death.dm b/code/modules/mob/living/carbon/death.dm
index 1247c39bd6e..d687dcfd0c7 100644
--- a/code/modules/mob/living/carbon/death.dm
+++ b/code/modules/mob/living/carbon/death.dm
@@ -35,7 +35,7 @@
if(org_zone == "chest")
O.Remove(src)
O.forceMove(get_turf(src))
- O.throw_at_fast(get_edge_target_turf(src,pick(alldirs)),rand(1,3),5)
+ O.throw_at(get_edge_target_turf(src,pick(alldirs)),rand(1,3),5)
else
for(var/X in internal_organs)
var/obj/item/organ/I = X
@@ -44,11 +44,11 @@
continue
I.Remove(src)
I.forceMove(get_turf(src))
- I.throw_at_fast(get_edge_target_turf(src,pick(alldirs)),rand(1,3),5)
+ I.throw_at(get_edge_target_turf(src,pick(alldirs)),rand(1,3),5)
/mob/living/carbon/spread_bodyparts()
for(var/X in bodyparts)
var/obj/item/bodypart/BP = X
BP.drop_limb()
- BP.throw_at_fast(get_edge_target_turf(src,pick(alldirs)),rand(1,3),5)
\ No newline at end of file
+ BP.throw_at(get_edge_target_turf(src,pick(alldirs)),rand(1,3),5)
\ No newline at end of file
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index 75a8ed51bd6..af6bed51839 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -664,7 +664,7 @@
/mob/living/singularity_pull(S, current_size)
if(current_size >= STAGE_SIX)
- throw_at_fast(S,14,3, spin=1)
+ throw_at(S,14,3, spin=1)
else
step_towards(src,S)
@@ -824,7 +824,7 @@
return 1
return 0
-/mob/living/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0)
+/mob/living/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback)
stop_pulling()
. = ..()
diff --git a/code/modules/mob/living/simple_animal/guardian/types/charger.dm b/code/modules/mob/living/simple_animal/guardian/types/charger.dm
index d9966b41a87..31636ed3c80 100644
--- a/code/modules/mob/living/simple_animal/guardian/types/charger.dm
+++ b/code/modules/mob/living/simple_animal/guardian/types/charger.dm
@@ -33,7 +33,9 @@
/mob/living/simple_animal/hostile/guardian/charger/Shoot(atom/targeted_atom)
charging = 1
- throw_at(targeted_atom, range, 1, src, 0)
+ throw_at(targeted_atom, range, 1, src, 0, callback = CALLBACK(src, .proc/charging_end))
+
+/mob/living/simple_animal/hostile/guardian/charger/proc/charging_end()
charging = 0
/mob/living/simple_animal/hostile/guardian/charger/Move()
diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm
index 8942dbe394d..ac04e000e6a 100644
--- a/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm
+++ b/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm
@@ -158,7 +158,8 @@ Difficulty: Hard
var/obj/effect/overlay/temp/decoy/D = PoolOrNew(/obj/effect/overlay/temp/decoy, list(loc,src))
animate(D, alpha = 0, color = "#FF0000", transform = matrix()*2, time = 5)
sleep(5)
- throw_at(T, get_dist(src, T), 1, src, 0)
+ throw_at(T, get_dist(src, T), 1, src, 0, callback = CALLBACK(src, .charge_end))
+/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/charge_end()
charging = 0
try_bloodattack()
if(target)
@@ -184,7 +185,7 @@ Difficulty: Hard
shake_camera(L, 4, 3)
shake_camera(src, 2, 3)
var/throwtarget = get_edge_target_turf(src, get_dir(src, get_step_away(L, src)))
- L.throw_at_fast(throwtarget, 3)
+ L.throw_at(throwtarget, 3)
charging = 0
diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/dragon.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/dragon.dm
index 59a6cb52106..97a1e388f06 100644
--- a/code/modules/mob/living/simple_animal/hostile/megafauna/dragon.dm
+++ b/code/modules/mob/living/simple_animal/hostile/megafauna/dragon.dm
@@ -233,7 +233,7 @@ Difficulty: Medium
if(L.loc == loc)
throw_dir = pick(alldirs)
var/throwtarget = get_edge_target_turf(src, throw_dir)
- L.throw_at_fast(throwtarget, 3)
+ L.throw_at(throwtarget, 3)
visible_message("[L] is thrown clear of [src]!")
for(var/mob/M in range(7, src))
diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/swarmer.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/swarmer.dm
index 8d620287e61..8909052eeb7 100644
--- a/code/modules/mob/living/simple_animal/hostile/megafauna/swarmer.dm
+++ b/code/modules/mob/living/simple_animal/hostile/megafauna/swarmer.dm
@@ -147,7 +147,7 @@ var/global/list/AISwarmerCapsByType = list(/mob/living/simple_animal/hostile/swa
return FALSE
if(istype(newloc, /turf/open/chasm) && !throwing)
- throw_at_fast(get_edge_target_turf(src, get_dir(src, newloc)), 7 , 3, spin = FALSE) //my planet needs me
+ throw_at(get_edge_target_turf(src, get_dir(src, newloc)), 7 , 3, spin = FALSE) //my planet needs me
return FALSE
return ..()
diff --git a/code/modules/orbit/orbit.dm b/code/modules/orbit/orbit.dm
index d4e0436aae7..81b80e48b4c 100644
--- a/code/modules/orbit/orbit.dm
+++ b/code/modules/orbit/orbit.dm
@@ -98,15 +98,6 @@
SpinAnimation(0,0)
qdel(orbiting)
-/atom/movable/Moved(atom/OldLoc, Dir)
- ..()
- if (orbiters)
- for (var/thing in orbiters)
- var/datum/orbit/O = thing
- O.Check()
- if (orbiting)
- orbiting.Check()
-
/atom/Destroy(force = FALSE)
..()
if (orbiters)
diff --git a/code/modules/paperwork/paperplane.dm b/code/modules/paperwork/paperplane.dm
index 519789649f0..b29e0400933 100644
--- a/code/modules/paperwork/paperplane.dm
+++ b/code/modules/paperwork/paperplane.dm
@@ -87,8 +87,8 @@
add_fingerprint(user)
-/obj/item/weapon/paperplane/throw_at(atom/target, range, speed, mob/thrower, spin=FALSE, diagonals_first = FALSE)
- . = ..(target, range, speed, thrower, FALSE, diagonals_first)
+/obj/item/weapon/paperplane/throw_at(atom/target, range, speed, mob/thrower, spin=FALSE, diagonals_first = FALSE, datum/callback/callback)
+ . = ..(target, range, speed, thrower, FALSE, diagonals_first, callback)
/obj/item/weapon/paperplane/throw_impact(atom/hit_atom)
if(..() || !ishuman(hit_atom))//if the plane is caught or it hits a nonhuman
diff --git a/code/modules/projectiles/guns/ballistic/shotgun.dm b/code/modules/projectiles/guns/ballistic/shotgun.dm
index ad4c1a7e51b..f80f2676cc6 100644
--- a/code/modules/projectiles/guns/ballistic/shotgun.dm
+++ b/code/modules/projectiles/guns/ballistic/shotgun.dm
@@ -159,7 +159,7 @@
guns_left = 0
/obj/item/weapon/gun/ballistic/shotgun/boltaction/enchanted/proc/discard_gun(mob/user)
- throw_at_fast(pick(oview(7,get_turf(user))),1,1)
+ throw_at(pick(oview(7,get_turf(user))),1,1)
user.visible_message("[user] tosses aside the spent rifle!")
/obj/item/weapon/gun/ballistic/shotgun/boltaction/enchanted/arcane_barrage/discard_gun(mob/user)
diff --git a/code/modules/projectiles/guns/grenade_launcher.dm b/code/modules/projectiles/guns/grenade_launcher.dm
index 2e0a3daa805..6efa9a24336 100644
--- a/code/modules/projectiles/guns/grenade_launcher.dm
+++ b/code/modules/projectiles/guns/grenade_launcher.dm
@@ -44,7 +44,7 @@
var/obj/item/weapon/grenade/F = grenades[1] //Now with less copypasta!
grenades -= F
F.loc = user.loc
- F.throw_at_fast(target, 30, 2,user)
+ F.throw_at(target, 30, 2, user)
message_admins("[key_name_admin(user)] fired a grenade ([F.name]) from a grenade launcher ([src.name]).")
log_game("[key_name(user)] fired a grenade ([F.name]) from a grenade launcher ([src.name]).")
F.active = 1
diff --git a/code/modules/projectiles/projectile/special.dm b/code/modules/projectiles/projectile/special.dm
index 284056b2d74..d2738021640 100644
--- a/code/modules/projectiles/projectile/special.dm
+++ b/code/modules/projectiles/projectile/special.dm
@@ -245,7 +245,7 @@
if(A == src || (firer && A == src.firer) || A.anchored)
continue
var/throwtarget = get_edge_target_turf(src, get_dir(src, get_step_away(A, src)))
- A.throw_at_fast(throwtarget,power+1,1)
+ A.throw_at(throwtarget,power+1,1)
for(var/turf/F in range(T,power))
var/obj/effect/overlay/gravfield = new /obj/effect/overlay{icon='icons/effects/effects.dmi'; icon_state="shieldsparkles"; mouse_opacity=0; density=0}()
F.overlays += gravfield
@@ -275,7 +275,7 @@
for(var/atom/movable/A in range(T, power))
if(A == src || (firer && A == src.firer) || A.anchored)
continue
- A.throw_at_fast(T, power+1, 1)
+ A.throw_at(T, power+1, 1)
for(var/turf/F in range(T,power))
var/obj/effect/overlay/gravfield = new /obj/effect/overlay{icon='icons/effects/effects.dmi'; icon_state="shieldsparkles"; mouse_opacity=0; density=0}()
F.overlays += gravfield
@@ -305,7 +305,7 @@
for(var/atom/movable/A in range(T, power))
if(A == src|| (firer && A == src.firer) || A.anchored)
continue
- A.throw_at_fast(get_edge_target_turf(A, pick(cardinal)), power+1, 1)
+ A.throw_at(get_edge_target_turf(A, pick(cardinal)), power+1, 1)
for(var/turf/Z in range(T,power))
var/obj/effect/overlay/gravfield = new /obj/effect/overlay{icon='icons/effects/effects.dmi'; icon_state="shieldsparkles"; mouse_opacity=0; density=0}()
Z.overlays += gravfield
diff --git a/code/modules/reagents/chemistry/recipes.dm b/code/modules/reagents/chemistry/recipes.dm
index ebc98cbf5ef..3de44af126c 100644
--- a/code/modules/reagents/chemistry/recipes.dm
+++ b/code/modules/reagents/chemistry/recipes.dm
@@ -74,9 +74,9 @@ var/list/chemical_mob_spawn_nicecritters = list() // and possible friendly mobs
if(moving_power > 2) //if the vortex is powerful and we're close, we get thrown
if(setting_type)
var/atom/throw_target = get_edge_target_turf(X, get_dir(X, get_step_away(X, T)))
- X.throw_at_fast(throw_target, moving_power, 1)
+ X.throw_at(throw_target, moving_power, 1)
else
- X.throw_at_fast(T, moving_power, 1)
+ X.throw_at(T, moving_power, 1)
else
spawn(0) //so everything moves at the same time.
if(setting_type)
diff --git a/code/modules/recycling/disposal-structures.dm b/code/modules/recycling/disposal-structures.dm
index 23a14b77df9..681b9b58610 100644
--- a/code/modules/recycling/disposal-structures.dm
+++ b/code/modules/recycling/disposal-structures.dm
@@ -273,7 +273,7 @@
AM.forceMove(src.loc)
AM.pipe_eject(direction)
if(target)
- AM.throw_at_fast(target, eject_range, 1)
+ AM.throw_at(target, eject_range, 1)
H.vent_gas(T)
qdel(H)
@@ -683,7 +683,7 @@
for(var/atom/movable/AM in H)
AM.forceMove(T)
AM.pipe_eject(dir)
- AM.throw_at_fast(target, eject_range, 1)
+ AM.throw_at(target, eject_range, 1)
H.vent_gas(T)
qdel(H)
diff --git a/code/modules/recycling/disposal-unit.dm b/code/modules/recycling/disposal-unit.dm
index 40b66555f89..2feb130acb1 100644
--- a/code/modules/recycling/disposal-unit.dm
+++ b/code/modules/recycling/disposal-unit.dm
@@ -227,7 +227,7 @@
AM.forceMove(T)
AM.pipe_eject(0)
- AM.throw_at_fast(target, 5, 1)
+ AM.throw_at(target, 5, 1)
H.vent_gas(loc)
qdel(H)
diff --git a/code/modules/research/experimentor.dm b/code/modules/research/experimentor.dm
index c7ee3e805e7..b34bc1ef381 100644
--- a/code/modules/research/experimentor.dm
+++ b/code/modules/research/experimentor.dm
@@ -436,7 +436,7 @@
investigate_log("Experimentor has triggered the 'throw things' reaction.", "experimentor")
for(var/atom/movable/AM in oview(7,src))
if(!AM.anchored)
- AM.throw_at_fast(src,10,1)
+ AM.throw_at(src,10,1)
else if(prob(EFFECT_PROB_LOW-badThingCoeff))
visible_message("[src]'s crusher goes one level too high, crushing right into space-time!")
playsound(src.loc, 'sound/effects/supermatter.ogg', 50, 1, -3)
@@ -447,7 +447,7 @@
throwAt.Add(AM)
for(var/counter = 1, counter < throwAt.len, ++counter)
var/atom/movable/cast = throwAt[counter]
- cast.throw_at_fast(pick(throwAt),10,1)
+ cast.throw_at(pick(throwAt),10,1)
ejectItem(TRUE)
////////////////////////////////////////////////////////////////////////////////////////////////
if(exp == FAIL)
@@ -621,14 +621,13 @@
/obj/item/weapon/relic/proc/throwSmoke(turf/where)
var/datum/effect_system/smoke_spread/smoke = new
- smoke.set_up(0, where)
+ smoke.set_up(0, get_turf(where))
smoke.start()
/obj/item/weapon/relic/proc/corgicannon(mob/user)
playsound(src.loc, "sparks", rand(25,50), 1)
var/mob/living/simple_animal/pet/dog/corgi/C = new/mob/living/simple_animal/pet/dog/corgi(get_turf(user))
- C.throw_at(pick(oview(10,user)),10,rand(3,8))
- throwSmoke(get_turf(C))
+ C.throw_at(pick(oview(10,user)), 10, rand(3,8), callback = CALLBACK(src, .throwSmoke, C))
warn_admins(user, "Corgi Cannon", 0)
/obj/item/weapon/relic/proc/clean(mob/user)
@@ -671,7 +670,7 @@
R.realProc = realProc
R.revealed = TRUE
dupes |= R
- R.throw_at_fast(pick(oview(7,get_turf(src))),10,1)
+ R.throw_at(pick(oview(7,get_turf(src))),10,1)
counter = 0
spawn(rand(10,100))
for(counter = 1; counter <= dupes.len; counter++)
diff --git a/code/modules/shuttle/special.dm b/code/modules/shuttle/special.dm
index 73d850172f6..62dfad2bb7e 100644
--- a/code/modules/shuttle/special.dm
+++ b/code/modules/shuttle/special.dm
@@ -197,7 +197,7 @@
var/mob/living/M = AM
var/throwtarget = get_edge_target_turf(src, boot_dir)
M.Weaken(2)
- M.throw_at_fast(throwtarget, 5, 1,src)
+ M.throw_at(throwtarget, 5, 1,src)
M << "No climbing on the bar please."
else
. = ..()
diff --git a/code/modules/spells/spell_types/wizard.dm b/code/modules/spells/spell_types/wizard.dm
index 7e2f892ad40..c1707ef8f8c 100644
--- a/code/modules/spells/spell_types/wizard.dm
+++ b/code/modules/spells/spell_types/wizard.dm
@@ -342,7 +342,7 @@
var/mob/living/M = AM
M.Weaken(stun_amt)
M << "You're thrown back by [user]!"
- AM.throw_at_fast(throwtarget, ((Clamp((maxthrow - (Clamp(distfromcaster - 2, 0, distfromcaster))), 3, maxthrow))), 1,user)//So stuff gets tossed around at the same time.
+ AM.throw_at(throwtarget, ((Clamp((maxthrow - (Clamp(distfromcaster - 2, 0, distfromcaster))), 3, maxthrow))), 1,user)//So stuff gets tossed around at the same time.
/obj/effect/proc_holder/spell/aoe_turf/repulse/xeno //i fixed conflicts only to find out that this is in the WIZARD file instead of the xeno file?!
name = "Tail Sweep"
@@ -411,7 +411,7 @@
M.electrocute_act(80, src, illusion = 1)
qdel(src)
-/obj/item/spellpacket/lightningbolt/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0)
+/obj/item/spellpacket/lightningbolt/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback)
. = ..()
if(ishuman(thrower))
var/mob/living/carbon/human/H = thrower
diff --git a/code/modules/surgery/bodyparts/dismemberment.dm b/code/modules/surgery/bodyparts/dismemberment.dm
index 58887e9ac39..4ec5dd2890f 100644
--- a/code/modules/surgery/bodyparts/dismemberment.dm
+++ b/code/modules/surgery/bodyparts/dismemberment.dm
@@ -38,7 +38,7 @@
target_turf = new_turf
if(new_turf.density)
break
- throw_at_fast(target_turf, throw_range, throw_speed)
+ throw_at(target_turf, throw_range, throw_speed)
return 1
diff --git a/code/modules/surgery/organs/vocal_cords.dm b/code/modules/surgery/organs/vocal_cords.dm
index a2e6345456e..08017565e59 100644
--- a/code/modules/surgery/organs/vocal_cords.dm
+++ b/code/modules/surgery/organs/vocal_cords.dm
@@ -245,7 +245,7 @@ var/static/regex/multispin_words = regex("like a record baby")
for(var/V in listeners)
var/mob/living/L = V
var/throwtarget = get_edge_target_turf(owner, get_dir(owner, get_step_away(L, owner)))
- L.throw_at_fast(throwtarget, 3 * power_multiplier, 1)
+ L.throw_at(throwtarget, 3 * power_multiplier, 1)
next_command = world.time + cooldown_damage
//WHO ARE YOU?
diff --git a/code/modules/vehicles/scooter.dm b/code/modules/vehicles/scooter.dm
index a70a6a495ec..20f6faee418 100644
--- a/code/modules/vehicles/scooter.dm
+++ b/code/modules/vehicles/scooter.dm
@@ -71,7 +71,7 @@
var/mob/living/carbon/H = buckled_mobs[1]
var/atom/throw_target = get_edge_target_turf(H, pick(cardinal))
unbuckle_mob(H)
- H.throw_at_fast(throw_target, 4, 3)
+ H.throw_at(throw_target, 4, 3)
H.Weaken(5)
H.adjustStaminaLoss(40)
visible_message("[src] crashes into [A], sending [H] flying!")
diff --git a/tgstation.dme b/tgstation.dme
index add4c5d362b..62033852d60 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -24,6 +24,7 @@
#include "code\__DEFINES\admin.dm"
#include "code\__DEFINES\atmospherics.dm"
#include "code\__DEFINES\atom_hud.dm"
+#include "code\__DEFINES\callbacks.dm"
#include "code\__DEFINES\clockcult.dm"
#include "code\__DEFINES\combat.dm"
#include "code\__DEFINES\construction.dm"
@@ -174,6 +175,7 @@
#include "code\controllers\subsystem\stickyban.dm"
#include "code\controllers\subsystem\sun.dm"
#include "code\controllers\subsystem\tgui.dm"
+#include "code\controllers\subsystem\throwing.dm"
#include "code\controllers\subsystem\ticker.dm"
#include "code\controllers\subsystem\timer.dm"
#include "code\controllers\subsystem\voting.dm"
@@ -186,6 +188,7 @@
#include "code\datums\ai_laws.dm"
#include "code\datums\beam.dm"
#include "code\datums\browser.dm"
+#include "code\datums\callback.dm"
#include "code\datums\datacore.dm"
#include "code\datums\datumvars.dm"
#include "code\datums\dna.dm"