Files
Bubberstation/code/datums/components/orbiter.dm
SkyratBot 7c78418b8a [MIRROR] Updates to orbit ui [READY] (#27742)
* Updates to orbit ui [READY] (#83186)

## About The Pull Request
Pretty big orbit ui refactor.
Check changelog for full list of features.

<details>
<summary>screenshots</summary>

In game
![Screenshot 2024-05-15
133935](https://github.com/tgstation/tgstation/assets/42397676/60c2fc95-9fc5-4417-8477-d6fdae589100)

Sort by department
![Screenshot 2024-05-15
133951](https://github.com/tgstation/tgstation/assets/42397676/96114884-3c10-4b03-a042-c19b25485bf3)

Did you know ninjas had a hud icon that hasn't worked for four years?

![image](https://github.com/tgstation/tgstation/assets/42397676/74f1414e-df57-4586-8cfd-0a154b560b83)

Criticals

![image](https://github.com/tgstation/tgstation/assets/42397676/b6ed9b94-bab3-4878-9a18-345efad9b92d)

Orbit blade

https://github.com/tgstation/tgstation/assets/42397676/99681548-bfb3-4895-9c95-3b650df71107

</details>

## Why It's Good For The Game
Some QoL for the orbit menu, giving more info on where action is and
isn't.
Removes more of the uselocalstate hook which is deprecated anyways
## Changelog
🆑
fix: Fixed an issue preventing space ninjas from having a hud icon
add: ORBIT UI CHANGES:
add: AFK players are greyed out.
add: Living NPCs now display health.
add: Icons displayed are now based on hud icons, which includes icons
for player-visible antagonists
add: You can now sort by job department (click health icon)
add: Round ending "critical" items will be listed at the top.
add: Click the settings button to expand for more info
add: Your current orbit target is highlighted.
/🆑

* Updates to orbit ui [READY]

---------

Co-authored-by: Jeremiah <42397676+jlsnow301@users.noreply.github.com>
2024-05-18 01:31:31 +02:00

186 lines
6.5 KiB
Plaintext

/datum/component/orbiter
can_transfer = TRUE
dupe_mode = COMPONENT_DUPE_UNIQUE_PASSARGS
var/list/orbiter_list
var/datum/movement_detector/tracker
//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
/datum/component/orbiter/Initialize(atom/movable/orbiter, radius, clockwise, rotation_speed, rotation_segments, pre_rotation)
if(!istype(orbiter) || !isatom(parent) || isarea(parent))
return COMPONENT_INCOMPATIBLE
orbiter_list = list()
begin_orbit(orbiter, radius, clockwise, rotation_speed, rotation_segments, pre_rotation)
/datum/component/orbiter/RegisterWithParent()
var/atom/target = parent
target.orbiters = src
if(ismovable(target))
tracker = new(target, CALLBACK(src, PROC_REF(move_react)))
RegisterSignal(parent, COMSIG_MOVABLE_UPDATE_GLIDE_SIZE, PROC_REF(orbiter_glide_size_update))
/datum/component/orbiter/UnregisterFromParent()
UnregisterSignal(parent, COMSIG_MOVABLE_UPDATE_GLIDE_SIZE)
var/atom/target = parent
target.orbiters = null
QDEL_NULL(tracker)
/datum/component/orbiter/Destroy()
var/atom/master = parent
if(master.orbiters == src)
master.orbiters = null
for(var/i in orbiter_list)
end_orbit(i)
orbiter_list = null
return ..()
/datum/component/orbiter/InheritComponent(datum/component/orbiter/newcomp, original, atom/movable/orbiter, radius, clockwise, rotation_speed, rotation_segments, pre_rotation)
if(!newcomp)
begin_orbit(arglist(args.Copy(3)))
return
// The following only happens on component transfers
for(var/o in newcomp.orbiter_list)
var/atom/movable/incoming_orbiter = o
incoming_orbiter.orbiting = src
// It is important to transfer the signals so we don't get locked to the new orbiter component for all time
newcomp.UnregisterSignal(incoming_orbiter, COMSIG_MOVABLE_MOVED)
RegisterSignal(incoming_orbiter, COMSIG_MOVABLE_MOVED, PROC_REF(orbiter_move_react))
orbiter_list += newcomp.orbiter_list
newcomp.orbiter_list = null
/datum/component/orbiter/PostTransfer()
if(!isatom(parent) || isarea(parent) || !get_turf(parent))
return COMPONENT_INCOMPATIBLE
move_react(parent)
/datum/component/orbiter/proc/begin_orbit(atom/movable/orbiter, radius, clockwise, rotation_speed, rotation_segments, pre_rotation)
if(orbiter.orbiting)
if(orbiter.orbiting == src)
orbiter.orbiting.end_orbit(orbiter, TRUE)
else
orbiter.orbiting.end_orbit(orbiter)
orbiter_list[orbiter] = TRUE
orbiter.orbiting = src
ADD_TRAIT(orbiter, TRAIT_NO_FLOATING_ANIM, ORBITING_TRAIT)
RegisterSignal(orbiter, COMSIG_MOVABLE_MOVED, PROC_REF(orbiter_move_react))
SEND_SIGNAL(parent, COMSIG_ATOM_ORBIT_BEGIN, orbiter)
var/matrix/initial_transform = matrix(orbiter.transform)
orbiter_list[orbiter] = initial_transform
// Head first!
if(pre_rotation)
var/matrix/M = matrix(orbiter.transform)
var/pre_rot = 90
if(!clockwise)
pre_rot = -90
M.Turn(pre_rot)
orbiter.transform = M
var/matrix/shift = matrix(orbiter.transform)
shift.Translate(0, radius)
orbiter.transform = shift
orbiter.SpinAnimation(rotation_speed, -1, clockwise, rotation_segments, parallel = FALSE)
if(ismob(orbiter))
var/mob/orbiter_mob = orbiter
orbiter_mob.updating_glide_size = FALSE
if(ismovable(parent))
var/atom/movable/movable_parent = parent
orbiter.glide_size = movable_parent.glide_size
orbiter.abstract_move(get_turf(parent))
to_chat(orbiter, span_notice("Now orbiting [parent]."))
/datum/component/orbiter/proc/end_orbit(atom/movable/orbiter, refreshing=FALSE)
if(!orbiter_list[orbiter])
return
UnregisterSignal(orbiter, COMSIG_MOVABLE_MOVED)
SEND_SIGNAL(parent, COMSIG_ATOM_ORBIT_STOP, orbiter)
orbiter.SpinAnimation(0, 0)
if(istype(orbiter_list[orbiter],/matrix)) //This is ugly.
orbiter.transform = orbiter_list[orbiter]
orbiter_list -= orbiter
orbiter.stop_orbit(src)
orbiter.orbiting = null
if(ismob(orbiter))
var/mob/orbiter_mob = orbiter
orbiter_mob.updating_glide_size = TRUE
orbiter_mob.glide_size = 8
if(isobserver(orbiter))
var/mob/dead/observer/ghostie = orbiter
ghostie.orbiting_ref = null
REMOVE_TRAIT(orbiter, TRAIT_NO_FLOATING_ANIM, ORBITING_TRAIT)
if(!refreshing && !length(orbiter_list) && !QDELING(src))
qdel(src)
// This proc can receive signals by either the thing being directly orbited or anything holding it
/datum/component/orbiter/proc/move_react(atom/movable/master, atom/mover, atom/oldloc, direction)
set waitfor = FALSE // Transfer calls this directly and it doesnt care if the ghosts arent done moving
if(master.loc == oldloc)
return
var/turf/newturf = get_turf(master)
if(!newturf)
qdel(src)
var/atom/curloc = master.loc
for(var/atom/movable/movable_orbiter as anything in orbiter_list)
if(QDELETED(movable_orbiter) || movable_orbiter.loc == newturf)
continue
movable_orbiter.abstract_move(newturf)
if(CHECK_TICK && master.loc != curloc)
// We moved again during the checktick, cancel current operation
break
/datum/component/orbiter/proc/orbiter_move_react(atom/movable/orbiter, atom/oldloc, direction)
SIGNAL_HANDLER
if(orbiter.loc == get_turf(parent))
return
end_orbit(orbiter)
/datum/component/orbiter/proc/orbiter_glide_size_update(datum/source, target)
SIGNAL_HANDLER
for(var/orbiter in orbiter_list)
var/atom/movable/movable_orbiter = orbiter
movable_orbiter.glide_size = target
/////////////////////
/atom/movable/proc/orbit(atom/A, radius = 10, clockwise = FALSE, rotation_speed = 20, rotation_segments = 36, pre_rotation = TRUE)
if(!istype(A) || !get_turf(A) || A == src)
return
if (HAS_TRAIT(A, TRAIT_ORBITING_FORBIDDEN))
// Stealth-mins have an empty name, don't want "You cannot orbit at this time."
to_chat(src, span_notice("You cannot orbit ["[A]" || "them"] at this time."))
return
orbit_target = A
return A.AddComponent(/datum/component/orbiter, src, radius, clockwise, rotation_speed, rotation_segments, pre_rotation)
/atom/movable/proc/stop_orbit(datum/component/orbiter/orbits)
orbit_target = null
return // We're just a simple hook
/atom/proc/transfer_observers_to(atom/target)
if(!orbiters || !istype(target) || !get_turf(target) || target == src)
return
target.TakeComponent(orbiters)