Files
CHOMPStation2/code/datums/orbit.dm
CHOMPStation2StaffMirrorBot 6fcd225bfa [MIRROR] fixes stack memleaks (#11598)
Co-authored-by: Kashargul <144968721+Kashargul@users.noreply.github.com>
Co-authored-by: Cameron Lennox <killer65311@gmail.com>
2025-09-09 22:10:42 -04:00

131 lines
3.8 KiB
Plaintext

/datum/orbit
var/atom/movable/orbiter
var/atom/orbiting
var/lock = TRUE
var/turf/lastloc
var/lastprocess
var/matrix/init_transform
/datum/orbit/New(var/atom/movable/_orbiter, var/atom/_orbiting, _lock)
orbiter = _orbiter
orbiting = _orbiting
init_transform = _orbiter.transform
SSorbit.processing += src
if (!orbiting.orbiters)
orbiting.orbiters = list()
orbiting.orbiters += src
if (orbiter.orbiting)
orbiter.stop_orbit()
orbiter.orbiting = src
Check()
lock = _lock
//do not qdel directly, use stop_orbit on the orbiter. (This way the orbiter can bind to the orbit stopping)
/datum/orbit/Destroy(force = FALSE)
SSorbit.processing -= src
if (orbiter)
orbiter.orbiting = null
orbiter.transform = init_transform
orbiter = null
if (orbiting)
if (orbiting.orbiters)
orbiting.orbiters -= src
if (!orbiting.orbiters.len)//we are the last orbit, delete the list
orbiting.orbiters = null
orbiting = null
return ..()
/datum/orbit/proc/Check(turf/targetloc, list/checked_already = list())
//Avoid infinite loops for people who end up orbiting themself through another orbiter
checked_already[src] = TRUE
if (!orbiter)
qdel(src)
return
if (!orbiting)
orbiter.stop_orbit()
return
if (!orbiter.orbiting) //admin wants to stop the orbit.
orbiter.orbiting = src //set it back to us first
orbiter.stop_orbit()
var/atom/movable/AM = orbiting
if(istype(AM) && AM.orbiting && AM.orbiting.orbiting == orbiter)
orbiter.stop_orbit()
return
lastprocess = world.time
if (!targetloc)
targetloc = get_turf(orbiting)
if (!targetloc || (!lock && orbiter.loc != lastloc && orbiter.loc != targetloc))
orbiter.stop_orbit()
return
orbiter.loc = targetloc
//TODO-LESH-DEL orbiter.update_parallax_contents()
orbiter.update_light()
lastloc = orbiter.loc
for(var/other_orbit in orbiter.orbiters)
var/datum/orbit/OO = other_orbit
//Skip if checked already
if(checked_already[OO])
continue
OO.Check(targetloc, checked_already)
/atom/movable/var/datum/orbit/orbiting = null
/atom/var/list/orbiters = null
//A: atom to orbit
//radius: range to orbit at, radius of the circle formed by orbiting (in pixels)
//clockwise: whether you orbit clockwise or anti clockwise
//rotation_speed: how fast to rotate (how many ds should it take for a rotation to complete)
//rotation_segments: the resolution of the orbit circle, less = a more block circle, this can be used to produce hexagons (6 segments) triangles (3 segments), and so on, 36 is the best default.
//pre_rotation: Chooses to rotate src 90 degress towards the orbit dir (clockwise/anticlockwise), useful for things to go "head first" like ghosts
//lockinorbit: Forces src to always be on A's turf, otherwise the orbit cancels when src gets too far away (eg: ghosts)
/atom/movable/proc/orbit(atom/A, radius = 10, clockwise = FALSE, rotation_speed = 20, rotation_segments = 36, pre_rotation = TRUE, lockinorbit = FALSE)
if (!istype(A))
return
new/datum/orbit(src, A, lockinorbit)
if (!orbiting) //something failed, and our orbit datum deleted itself
return
//Head first!
if (pre_rotation)
var/matrix/M = matrix(transform)
var/pre_rot = 90
if(!clockwise)
pre_rot = -90
M.Turn(pre_rot)
transform = M
var/matrix/shift = matrix(transform)
shift.Translate(0,radius)
transform = shift
SpinAnimation(rotation_speed, -1, clockwise, rotation_segments)
/atom/movable/proc/stop_orbit()
SpinAnimation(0,0)
QDEL_NULL(orbiting)
/atom/Destroy(force = FALSE)
. = ..()
if (orbiters)
for(var/datum/orbit/O as anything in orbiters)
if (O.orbiter)
O.orbiter.stop_orbit()
orbiters = null
/atom/movable/Destroy(force = FALSE)
. = ..()
if (orbiting)
stop_orbit()
/*
/atom/movable/proc/transfer_observers_to(atom/movable/target)
if(orbiters)
for(var/datum/orbit/O as anything in orbiters)
if(O.orbiter && isobserver(O.orbiter))
var/mob/dead/observer/D = O.orbiter
D.ManualFollow(target)
*/