Files
CHOMPStation2/code/controllers/subsystems/throwing.dm
CHOMPStation2 4d9879937f [MIRROR] Port tg statpanel (#9242)
Co-authored-by: ShadowLarkens <shadowlarkens@gmail.com>
Co-authored-by: Kashargul <KashL@t-online.de>
2024-10-17 23:29:59 +02:00

215 lines
5.5 KiB
Plaintext

#define MAX_THROWING_DIST 1280 // 5 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.
SUBSYSTEM_DEF(throwing)
name = "Throwing"
wait = 1
flags = SS_NO_INIT|SS_KEEP_TIMING
var/list/currentrun
var/list/processing = list()
/datum/controller/subsystem/throwing/stat_entry(msg)
msg = "P:[processing.len]"
return ..()
/datum/controller/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 (QDELETED(AM) || QDELETED(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/target_zone
var/init_dir
var/maxrange
var/speed
var/mob/thrower
var/start_time
var/dist_travelled = 0
var/dist_x
var/dist_y
var/dx
var/dy
var/diagonal_error
var/pure_diagonal
var/datum/callback/callback
var/paused = FALSE
var/delayed_time = 0
var/last_move = 0
/datum/thrownthing/New(var/atom/movable/thrownthing, var/atom/target, var/range, var/speed, var/mob/thrower, var/datum/callback/callback)
src.thrownthing = thrownthing
src.target = target
src.target_turf = get_turf(target)
src.init_dir = get_dir(thrownthing, target)
src.maxrange = range
src.speed = speed
src.thrower = thrower
src.callback = callback
if(!QDELETED(thrower) && ismob(thrower))
src.target_zone = thrower.zone_sel ? thrower.zone_sel.selecting : null
dist_x = abs(target.x - thrownthing.x)
dist_y = abs(target.y - thrownthing.y)
dx = (target.x > thrownthing.x) ? EAST : WEST
dy = (target.y > thrownthing.y) ? NORTH : SOUTH//same up to here
if (dist_x == dist_y)
pure_diagonal = TRUE
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
diagonal_error = dist_x/2 - dist_y
start_time = world.time
/datum/thrownthing/Destroy()
SSthrowing.processing -= thrownthing
thrownthing.throwing = null
thrownthing = null
target = null
thrower = null
callback = null
return ..()
/datum/thrownthing/proc/tick()
var/atom/movable/AM = thrownthing
if (!isturf(AM.loc) || !AM.throwing)
finalize()
return
if(paused)
delayed_time += world.time - last_move
return
if (dist_travelled && hitcheck(get_turf(thrownthing))) //to catch sneaky things moving on our tile while we slept
finalize()
return
var/area/A = get_area(AM.loc)
var/atom/step
last_move = world.time
//calculate how many tiles to move, making up for any missed ticks.
var/tilestomove = CEILING(min(((((world.time+world.tick_lag) - start_time + delayed_time) * speed) - (dist_travelled ? dist_travelled : -1)), speed*MAX_TICKS_TO_MAKE_UP) * (world.tick_lag * SSthrowing.wait), 1)
while (tilestomove-- > 0)
if ((dist_travelled >= maxrange || AM.loc == target_turf) && (A && A.get_gravity()))
finalize()
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) // 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
finalize()
return
if (hitcheck(step))
finalize()
return
AM.Move(step, get_dir(AM, step))
if (!AM) // Us moving somehow destroyed us?
return
if (!AM.throwing) // we hit something during our move
finalize(hit = TRUE)
return
dist_travelled++
if (dist_travelled > MAX_THROWING_DIST)
finalize()
return
A = get_area(AM.loc)
/datum/thrownthing/proc/finalize(hit = FALSE, t_target=null)
set waitfor = FALSE
//done throwing, either because it hit something or it finished moving
if(QDELETED(thrownthing))
return
thrownthing.throwing = null
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 = TRUE
thrownthing.throw_impact(A, speed)
break
if (!hit)
thrownthing.throw_impact(get_turf(thrownthing), speed) // we haven't hit something yet and we still must, let's hit the ground.
if(ismob(thrownthing))
var/mob/M = thrownthing
M.inertia_dir = init_dir
if(t_target && !QDELETED(thrownthing))
thrownthing.throw_impact(t_target, speed)
if (callback)
callback.Invoke()
if (!QDELETED(thrownthing))
thrownthing.fall()
qdel(src)
/datum/thrownthing/proc/hit_atom(atom/A)
finalize(hit=TRUE, t_target=A)
/datum/thrownthing/proc/hitcheck(var/turf/T)
var/atom/movable/hit_thing
for (var/thing in T)
var/atom/movable/AM = thing
if (AM == thrownthing || (AM == thrower && !ismob(thrownthing)))
continue
if (!AM.density || AM.throwpass)//check if ATOM_FLAG_CHECKS_BORDER as an atom_flag is needed
continue
if (!hit_thing || AM.layer > hit_thing.layer)
hit_thing = AM
if(hit_thing)
finalize(hit=TRUE, t_target=hit_thing)
return TRUE
#undef MAX_THROWING_DIST
#undef MAX_TICKS_TO_MAKE_UP