mirror of
https://github.com/CHOMPStation2/CHOMPStation2.git
synced 2025-12-09 16:12:17 +00:00
Merge pull request #2733 from CHOMPStationBot/upstream-merge-11348
[MIRROR] Port/add throwing subsystem
This commit is contained in:
207
code/controllers/subsystems/throwing.dm
Normal file
207
code/controllers/subsystems/throwing.dm
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user