Merge pull request #2733 from CHOMPStationBot/upstream-merge-11348

[MIRROR] Port/add throwing subsystem
This commit is contained in:
Razgriz
2021-08-06 23:11:49 -07:00
committed by GitHub
3 changed files with 222 additions and 82 deletions

View File

@@ -0,0 +1,207 @@
#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()
..("P:[processing.len]")
/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))
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.has_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.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, src)
break
if (!hit)
thrownthing.throw_impact(get_turf(thrownthing), src) // 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, src)
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

View File

@@ -418,92 +418,24 @@
continue
src.throw_impact(A,speed)
/atom/movable/proc/throw_at(atom/target, range, speed, thrower)
if(!target || !src)
return 0
if(target.z != src.z)
return 0
//use a modified version of Bresenham's algorithm to get from the atom's current position to that of the target
src.throwing = 1
src.thrower = thrower
src.throw_source = get_turf(src) //store the origin turf
src.pixel_z = 0
if(usr)
if(HULK in usr.mutations)
src.throwing = 2 // really strong throw!
/atom/movable/proc/throw_at(atom/target, range, speed, mob/thrower, spin = TRUE, datum/callback/callback) //If this returns FALSE then callback will not be called.
. = TRUE
if (!target || speed <= 0 || QDELETED(src) || (target.z != src.z))
return FALSE
var/dist_travelled = 0
var/dist_since_sleep = 0
var/area/a = get_area(src.loc)
if (pulledby)
pulledby.stop_pulling()
var/dist_x = abs(target.x - src.x)
var/dist_y = abs(target.y - src.y)
var/datum/thrownthing/TT = new(src, target, range, speed, thrower, callback)
throwing = TT
var/dx
if (target.x > src.x)
dx = EAST
else
dx = WEST
var/dy
if (target.y > src.y)
dy = NORTH
else
dy = SOUTH
var/error
var/major_dir
var/major_dist
var/minor_dir
var/minor_dist
if(dist_x > dist_y)
error = dist_x/2 - dist_y
major_dir = dx
major_dist = dist_x
minor_dir = dy
minor_dist = dist_y
else
error = dist_y/2 - dist_x
major_dir = dy
major_dist = dist_y
minor_dir = dx
minor_dist = dist_x
range = min(dist_x + dist_y, range)
while(src && target && src.throwing && istype(src.loc, /turf) \
&& ((abs(target.x - src.x)+abs(target.y - src.y) > 0 && dist_travelled < range) \
|| (a && a.has_gravity == 0) \
|| istype(src.loc, /turf/space)))
// only stop when we've gone the whole distance (or max throw range) and are on a non-space tile, or hit something, or hit the end of the map, or someone picks it up
var/atom/step
if(error >= 0)
step = get_step(src, major_dir)
error -= minor_dist
else
step = get_step(src, minor_dir)
error += major_dist
if(!step) // going off the edge of the map makes get_step return null, don't let things go off the edge
break
src.Move(step)
hit_check(speed)
dist_travelled++
dist_since_sleep++
if(dist_since_sleep >= speed)
dist_since_sleep = 0
sleep(1)
a = get_area(src.loc)
// and yet it moves
if(src.does_spin)
src.SpinAnimation(speed = 4, loops = 1)
//done throwing, either because it hit something or it finished moving
if(isobj(src)) src.throw_impact(get_turf(src),speed)
src.throwing = 0
src.thrower = null
src.throw_source = null
fall()
pixel_z = 0
if(spin && does_spin)
SpinAnimation(4,1)
SSthrowing.processing[src] = TT
if (SSthrowing.state == SS_PAUSED && length(SSthrowing.currentrun))
SSthrowing.currentrun[src] = TT
//Overlays
/atom/movable/overlay

View File

@@ -302,6 +302,7 @@
#include "code\controllers\subsystems\sun.dm"
#include "code\controllers\subsystems\supply.dm"
#include "code\controllers\subsystems\tgui.dm"
#include "code\controllers\subsystems\throwing.dm"
#include "code\controllers\subsystems\ticker.dm"
#include "code\controllers\subsystems\time_track.dm"
#include "code\controllers\subsystems\timer.dm"