diff --git a/code/__HELPERS/unsorted.dm b/code/__HELPERS/unsorted.dm index 8f9f98e82fe..c1c7931b086 100644 --- a/code/__HELPERS/unsorted.dm +++ b/code/__HELPERS/unsorted.dm @@ -1006,65 +1006,6 @@ B --><-- A */ -//This is just so you can stop an orbit. -//orbit() can run without it (swap orbiting for A) -//but then you can never stop it and that's just silly. -/atom/movable/var/atom/orbiting = null - -//A: atom to orbit -//radius: range to orbit at, radius of the circle formed by orbiting -//clockwise: whether you orbit clockwise or anti clockwise -//rotation_speed: how fast to rotate -//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 - - if(orbiting) - stop_orbit() - - orbiting = A - var/matrix/initial_transform = matrix(transform) - var/lastloc = loc - - //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) - - //we stack the orbits up client side, so we can assign this back to normal server side without it breaking the orbit - transform = initial_transform - while(orbiting && orbiting == A && A.loc) - var/targetloc = get_turf(A) - if(!lockinorbit && loc != lastloc && loc != targetloc) - break - loc = targetloc - lastloc = loc - stoplag() - - if (orbiting == A) //make sure we haven't started orbiting something else. - orbiting = null - SpinAnimation(0,0) - - - -/atom/movable/proc/stop_orbit() - orbiting = null - - //Center's an image. //Requires: //The Image diff --git a/code/controllers/subsystem/orbit.dm b/code/controllers/subsystem/orbit.dm new file mode 100644 index 00000000000..a66a575a6dd --- /dev/null +++ b/code/controllers/subsystem/orbit.dm @@ -0,0 +1,50 @@ +var/datum/subsystem/orbit/SSorbit + +/datum/subsystem/orbit + name = "Orbits" + priority = 35 + wait = 2 + flags = SS_NO_INIT|SS_TICKER + + var/list/currentrun = list() + var/list/processing = list() + +/datum/subsystem/orbit/New() + NEW_SS_GLOBAL(SSorbit) + + +/datum/subsystem/orbit/stat_entry() + ..("P:[processing.len]") + + +/datum/subsystem/orbit/fire(resumed = 0) + if (!resumed) + src.currentrun = processing.Copy() + + //cache for sanic speed (lists are references anyways) + var/list/currentrun = src.currentrun + + while (currentrun.len) + var/datum/orbit/O = currentrun[currentrun.len] + currentrun.len-- + if (!O) + processing -= O + if (MC_TICK_CHECK) + return + continue + if (!O.orbiter) + qdel(O) + if (MC_TICK_CHECK) + return + continue + if (O.lastprocess >= world.time) //we already checked recently + if (MC_TICK_CHECK) + return + continue + var/targetloc = get_turf(O.orbiting) + if (targetloc != O.lastloc || O.orbiter.loc != targetloc) + O.Check(targetloc) + if (MC_TICK_CHECK) + return + + diff --git a/code/modules/mining/lavaland/necropolis_chests.dm b/code/modules/mining/lavaland/necropolis_chests.dm index a26df35f156..2545d47892c 100644 --- a/code/modules/mining/lavaland/necropolis_chests.dm +++ b/code/modules/mining/lavaland/necropolis_chests.dm @@ -95,7 +95,7 @@ " if(wisp.orbiting) - var/atom/A = wisp.orbiting + var/atom/A = wisp.orbiting.orbiting if(istype(A, /mob/living)) var/mob/living/M = A M.sight &= ~SEE_MOBS @@ -534,8 +534,15 @@ var/turf/T = get_turf(src) var/list/contents = T.GetAllContents() var/mob/dead/observer/current_spirits = list() - for(var/mob/dead/observer/G in dead_mob_list) - if(G.orbiting in contents) + var/list/orbiters = list() + for(var/thing in contents) + var/atom/A = thing + if (A.orbiters) + orbiters += A.orbiters + + for(var/thing in orbiters) + if (isobserver(thing)) + var/mob/dead/observer/G = thing ghost_counter++ G.invisibility = 0 current_spirits |= G diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm index 8642e27ca31..3f36349ff7f 100644 --- a/code/modules/mob/dead/observer/observer.dm +++ b/code/modules/mob/dead/observer/observer.dm @@ -383,7 +383,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp var/orbitsize = (I.Width()+I.Height())*0.5 orbitsize -= (orbitsize/world.icon_size)*(world.icon_size*0.25) - if(orbiting != target) + if(orbiting && orbiting.orbiting != target) src << "Now orbiting [target]." var/rot_seg @@ -403,13 +403,14 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp orbit(target,orbitsize, FALSE, 20, rot_seg) /mob/dead/observer/orbit() - setDir(2 )//reset dir so the right directional sprites show up + setDir(2)//reset dir so the right directional sprites show up + ..() + +/mob/dead/observer/stop_orbit() ..() //restart our floating animation after orbit is done. - sleep 2 //orbit sets up a 2ds animation when it finishes, so we wait for that to end - if (!orbiting) //make sure another orbit hasn't started - pixel_y = 0 - animate(src, pixel_y = 2, time = 10, loop = -1) + pixel_y = 0 + animate(src, pixel_y = 2, time = 10, loop = -1) /mob/dead/observer/verb/jumptomob() //Moves the ghost instead of just changing the ghosts's eye -Nodrak set category = "Ghost" diff --git a/code/modules/orbit/orbit.dm b/code/modules/orbit/orbit.dm new file mode 100644 index 00000000000..76062b47494 --- /dev/null +++ b/code/modules/orbit/orbit.dm @@ -0,0 +1,121 @@ +/datum/orbit + var/atom/movable/orbiter + var/atom/orbiting + var/lock = TRUE + var/turf/lastloc + var/lastprocess + +/datum/orbit/New(_orbiter, _orbiting, _lock) + orbiter = _orbiter + orbiting = _orbiting + 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 = 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 + ..() + +/datum/orbit/proc/Check(turf/targetloc) + 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() + 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 + lastloc = orbiter.loc + + +/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 + var/matrix/initial_transform = matrix(transform) + + //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) + + //we stack the orbits up client side, so we can assign this back to normal server side without it breaking the orbit + transform = initial_transform + +/atom/movable/proc/stop_orbit() + 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) + for (var/thing in orbiters) + var/datum/orbit/O = thing + if (O.orbiter) + O.orbiter.stop_orbit() + +/atom/movable/Destroy(force = FALSE) + ..() + if (orbiting) + stop_orbit() \ No newline at end of file diff --git a/code/modules/power/tesla/energy_ball.dm b/code/modules/power/tesla/energy_ball.dm index 4b7399cad3e..46d5f269e33 100644 --- a/code/modules/power/tesla/energy_ball.dm +++ b/code/modules/power/tesla/energy_ball.dm @@ -43,10 +43,9 @@ var/list/blacklisted_tesla_types = typecacheof(list(/obj/machinery/atmospherics, return /obj/singularity/energy_ball/Destroy() - if(orbiting && istype(orbiting, /obj/singularity/energy_ball)) - var/obj/singularity/energy_ball/EB = orbiting + if(orbiting && istype(orbiting.orbiting, /obj/singularity/energy_ball)) + var/obj/singularity/energy_ball/EB = orbiting.orbiting EB.orbiting_balls -= src - orbiting = null for(var/ball in orbiting_balls) var/obj/singularity/energy_ball/EB = ball @@ -140,13 +139,16 @@ var/list/blacklisted_tesla_types = typecacheof(list(/obj/machinery/atmospherics, target.dissipate_strength = target.orbiting_balls.len . = ..() - - if (istype(target)) - target.orbiting_balls -= src - target.dissipate_strength = target.orbiting_balls.len +/obj/singularity/energy_ball/stop_orbit() + if (orbiting && istype(orbiting.orbiting, /obj/singularity/energy_ball)) + var/obj/singularity/energy_ball/orbitingball = orbiting.orbiting + orbitingball.orbiting_balls -= src + orbitingball.dissipate_strength = orbitingball.orbiting_balls.len + ..() if (!loc) qdel(src) + /obj/singularity/energy_ball/proc/dust_mobs(atom/A) if(istype(A, /mob/living/carbon)) var/mob/living/carbon/C = A diff --git a/tgstation.dme b/tgstation.dme index a29bd5bc4da..f1bd42061ad 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -150,6 +150,7 @@ #include "code\controllers\subsystem\minimap.dm" #include "code\controllers\subsystem\mobs.dm" #include "code\controllers\subsystem\npcpool.dm" +#include "code\controllers\subsystem\orbit.dm" #include "code\controllers\subsystem\pai.dm" #include "code\controllers\subsystem\persistence.dm" #include "code\controllers\subsystem\pool.dm" @@ -1602,6 +1603,7 @@ #include "code\modules\ninja\suit\n_suit_verbs\ninja_stealth.dm" #include "code\modules\ninja\suit\n_suit_verbs\ninja_sword_recall.dm" #include "code\modules\ninja\suit\n_suit_verbs\ninja_teleporting.dm" +#include "code\modules\orbit\orbit.dm" #include "code\modules\paperwork\clipboard.dm" #include "code\modules\paperwork\contract.dm" #include "code\modules\paperwork\filingcabinet.dm"