From b00592255367802a90e745bedac78c9e9cbef45a Mon Sep 17 00:00:00 2001 From: Filatelele Date: Thu, 14 Dec 2023 00:38:02 +0300 Subject: [PATCH] tweak(following): ghosts can orbit now --- baystation12.dme | 2 + code/__defines/ces/signals_global.dm | 4 + code/__defines/subsystem-priority.dm | 1 + code/_helpers/matrices.dm | 32 ++++-- code/controllers/subsystems/orbit.dm | 50 +++++++++ code/modules/mob/mob_movement.dm | 8 ++ code/modules/mob/observer/ghost/ghost.dm | 26 +++-- code/modules/orbit/orbit.dm | 127 +++++++++++++++++++++++ 8 files changed, 233 insertions(+), 17 deletions(-) create mode 100644 code/controllers/subsystems/orbit.dm create mode 100644 code/modules/orbit/orbit.dm diff --git a/baystation12.dme b/baystation12.dme index 326f3a8844f..93a4de538a2 100644 --- a/baystation12.dme +++ b/baystation12.dme @@ -241,6 +241,7 @@ #include "code\controllers\subsystems\mapping.dm" #include "code\controllers\subsystems\misc_late.dm" #include "code\controllers\subsystems\open_space.dm" +#include "code\controllers\subsystems\orbit.dm" #include "code\controllers\subsystems\overlays.dm" #include "code\controllers\subsystems\plants.dm" #include "code\controllers\subsystems\radiation.dm" @@ -2490,6 +2491,7 @@ #include "code\modules\nano\modules\human_appearance.dm" #include "code\modules\nano\modules\law_manager.dm" #include "code\modules\nano\modules\nano_module.dm" +#include "code\modules\orbit\orbit.dm" #include "code\modules\paperwork\adminpaper.dm" #include "code\modules\paperwork\carbonpaper.dm" #include "code\modules\paperwork\clipboard.dm" diff --git a/code/__defines/ces/signals_global.dm b/code/__defines/ces/signals_global.dm index 7e35eecec3c..4a2b92b3d4e 100644 --- a/code/__defines/ces/signals_global.dm +++ b/code/__defines/ces/signals_global.dm @@ -18,3 +18,7 @@ #define SIGNAL_MOB_RESIST "!mob_resist" #define SIGNAL_MOB_GRAB_SET_STATE "!mob_grab_set_state" + +#define SIGNAL_ORBIT_BEGIN "orbit_begin" + +#define SIGNAL_ORBIT_STOP "orbit_stop" diff --git a/code/__defines/subsystem-priority.dm b/code/__defines/subsystem-priority.dm index 33e5ce3a5bc..23b739855da 100644 --- a/code/__defines/subsystem-priority.dm +++ b/code/__defines/subsystem-priority.dm @@ -17,6 +17,7 @@ #define SS_PRIORITY_MACHINERY 95 // Machinery + powernet ticks. #define SS_PRIORITY_PHYSICS 94 #define SS_PRIORITY_AIR 80 // ZAS processing. +#define SS_PRIORITY_ORBIT 35 #define SS_PRIORITY_ALARM 20 // Alarm processing. #define SS_PRIORITY_EVENT 20 // Event processing and queue handling. #define SS_PRIORITY_STORYTELLER 20 diff --git a/code/_helpers/matrices.dm b/code/_helpers/matrices.dm index ba775afcace..929840773d0 100644 --- a/code/_helpers/matrices.dm +++ b/code/_helpers/matrices.dm @@ -29,16 +29,28 @@ Multiply(others) return src -/atom/proc/SpinAnimation(speed = 10, loops = -1) - var/matrix/m120 = matrix(transform).Update(rotation = 120) - var/matrix/m240 = matrix(transform).Update(rotation = 240) - var/matrix/m360 = matrix(transform).Update(rotation = 360) - speed /= 3 //Gives us 3 equal time segments for our three turns. - //Why not one turn? Because byond will see that the start and finish are the same place and do nothing - //Why not two turns? Because byond will do a flip instead of a turn - animate(src, transform = m120, time = speed, loops) - animate(transform = m240, time = speed) - animate(transform = m360, time = speed) +/atom/proc/SpinAnimation(speed = 10, loops = -1, clockwise = TRUE, segments = 3) + if(!segments) + return + + var/segment = 360/segments + if(!clockwise) + segment = -segment + + var/list/matrices = list() + for(var/i in 1 to segments-1) + var/matrix/M = matrix(transform) + M.Turn(segment*i) + matrices += M + + var/matrix/last = matrix(transform) + matrices += last + + speed /= segments + + animate(src, transform = matrices[1], time = speed, loops) + for(var/i in 2 to segments) + animate(transform = matrices[i], time = speed) /atom/proc/shake_animation(intensity = 8, stime = 6) var/init_px = pixel_x diff --git a/code/controllers/subsystems/orbit.dm b/code/controllers/subsystems/orbit.dm new file mode 100644 index 00000000000..bdc32d7616b --- /dev/null +++ b/code/controllers/subsystems/orbit.dm @@ -0,0 +1,50 @@ +SUBSYSTEM_DEF(orbit) + name = "Orbit" + + priority = SS_PRIORITY_ORBIT + wait = 2 + + flags = SS_NO_INIT | SS_TICKER + + var/list/currentrun = list() + var/list/orbits = list() + +/datum/controller/subsystem/orbit/stat_entry() + ..("P:[orbits.len]") + +/datum/controller/subsystem/orbit/fire(resumed = 0) + if(!resumed) + src.currentrun = orbits.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) + orbits -= 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/mob/mob_movement.dm b/code/modules/mob/mob_movement.dm index 76566b99264..ed946795283 100644 --- a/code/modules/mob/mob_movement.dm +++ b/code/modules/mob/mob_movement.dm @@ -180,6 +180,14 @@ if ((A != src.loc && A && A.z == src.z)) src.last_move = get_dir(A, src.loc) + if(orbiters) + for(var/thing in orbiters) + var/datum/orbit/O = thing + O.Check() + + if(orbiting) + orbiting.Check() + SEND_SIGNAL(src, SIGNAL_MOVED, src, old_loc, loc) /proc/step_glide(atom/movable/am, dir, glide_size_override) diff --git a/code/modules/mob/observer/ghost/ghost.dm b/code/modules/mob/observer/ghost/ghost.dm index 2bdc1bbc1d9..6cb7277a197 100644 --- a/code/modules/mob/observer/ghost/ghost.dm +++ b/code/modules/mob/observer/ghost/ghost.dm @@ -451,20 +451,32 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp register_signal(following, SIGNAL_DIR_SET, /atom/proc/recursive_dir_set) register_signal(following, SIGNAL_QDELETING, /mob/observer/ghost/proc/stop_following) - to_chat(src, SPAN_NOTICE("Now following \the [following].")) - move_to_turf(following, loc, following.loc) - glide_before_follow = src.glide_size - src.glide_size = target.glide_size + var/orbitsize + if(target.icon) + var/icon/I = icon(target.icon, target.icon_state, target.dir) + orbitsize = (I.Width() + I.Height()) * 0.5 + else + orbitsize = world.icon_size + orbitsize -= (orbitsize / world.icon_size) * (world.icon_size * 0.25) + + if(orbiting && orbiting.orbiting != target) + to_chat(src, "Now following \the [target].") + + forceMove(target) + orbit(target, orbitsize, FALSE, 20, 36) + +/mob/dead/observer/orbit() + set_dir(WEST) // Reset dir so the right directional sprites show up + ..() /mob/observer/ghost/proc/stop_following() - if(following) + if(orbiting) + stop_orbit() to_chat(src, SPAN_NOTICE("No longer following \the [following].")) unregister_signal(following, SIGNAL_MOVED) unregister_signal(following, SIGNAL_DIR_SET) unregister_signal(following, SIGNAL_QDELETING) following = null - glide_size = glide_before_follow - glide_before_follow = 0 /mob/observer/ghost/move_to_turf(atom/movable/am, old_loc, new_loc) var/turf/T = get_turf(new_loc) diff --git a/code/modules/orbit/orbit.dm b/code/modules/orbit/orbit.dm new file mode 100644 index 00000000000..33728668e6f --- /dev/null +++ b/code/modules/orbit/orbit.dm @@ -0,0 +1,127 @@ +/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.orbits += src + if(!orbiting.orbiters) + orbiting.orbiters = list() + orbiting.orbiters += src + + if(orbiter.orbiting) + orbiter.stop_orbit() + orbiter.orbiting = src + + if(!Check()) + return + + lock = _lock + + SEND_SIGNAL(orbiter, SIGNAL_ORBIT_BEGIN, orbiting) + +//do not qdel directly, use stop_orbit on the orbiter. (This way the orbiter can bind to the orbit stopping) +/datum/orbit/Destroy() + SEND_SIGNAL(orbiter, SIGNAL_ORBIT_STOP, orbiting) + SSorbit.orbits -= 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 + + return ..() + +/datum/orbit/proc/Check(turf/targetloc) + if(!orbiter) + qdel_self() + return FALSE + + if(!orbiting) + orbiter.stop_orbit() + return FALSE + + 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 FALSE + + orbiter.loc = targetloc + lastloc = orbiter.loc + return TRUE + + +/atom/movable/var/datum/orbit/orbiting = null +/atom/var/list/orbiters = null + +/atom/movable/var/cached_transform = 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) + cached_transform = initial_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) + +/atom/movable/proc/stop_orbit() + SpinAnimation(0, 0) + qdel(orbiting) + transform = cached_transform + +/atom/Destroy() + . = ..() + if(orbiters) + for(var/thing in orbiters) + var/datum/orbit/O = thing + if(O.orbiter) + O.orbiter.stop_orbit() + +/atom/movable/Destroy() + . = ..() + if(orbiting) + stop_orbit()