Skip to content

Commit

Permalink
tweak(following): ghosts can orbit now
Browse files Browse the repository at this point in the history
  • Loading branch information
Filatelele committed Dec 13, 2023
1 parent 88118f7 commit b005922
Show file tree
Hide file tree
Showing 8 changed files with 233 additions and 17 deletions.
2 changes: 2 additions & 0 deletions baystation12.dme
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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"
Expand Down
4 changes: 4 additions & 0 deletions code/__defines/ces/signals_global.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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"
1 change: 1 addition & 0 deletions code/__defines/subsystem-priority.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
32 changes: 22 additions & 10 deletions code/_helpers/matrices.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
50 changes: 50 additions & 0 deletions code/controllers/subsystems/orbit.dm
Original file line number Diff line number Diff line change
@@ -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
8 changes: 8 additions & 0 deletions code/modules/mob/mob_movement.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
26 changes: 19 additions & 7 deletions code/modules/mob/observer/ghost/ghost.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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, "<span class='notice'>Now following \the [target].</span>")

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)
Expand Down
127 changes: 127 additions & 0 deletions code/modules/orbit/orbit.dm
Original file line number Diff line number Diff line change
@@ -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()

0 comments on commit b005922

Please sign in to comment.