Adds true observing -- letting you see what your target sees (#24855)

* bring it back now y'all

* adds a bunch of other changes

* pretty much works at this point

* grep

* Fixes some bugs, still chasing some down

* Add observers to recursive mob check, ensuring they can see messages from their orbitted atom

* tidy some things up

* resolve

* remove unnecessary checks

* Address todos, minor cleanups

* oh dear

* initial testing

* Fix some vulnerabilities (oops)

* Fix some other bugs from testing

* ci fixes

* Add some contingencies against clicking things you shouldn't

* fix some noted bugs

* Add observe to admin jump, improve messaging

* add a few more fixes that should mostly address some runtimes

* oh this wasn't really great

* one last thing

* Fix suit inventory problem

* Remove merge artifact

* Add some missing parens

* Prevents you from observing other ghosts, and should fix the getting stuck outside of your body issue.

* we love testing

* adds to player panel

* Fix more bugs with observers getting stuck outside of their body

* cleans up player panel, verb offerings

* shit

* Update code/_onclick/hud/alert.dm

welp

Co-authored-by: GDN <96800819+GDNgit@users.noreply.github.com>
Signed-off-by: Luc <89928798+lewcc@users.noreply.github.com>

* Fixes some targeting issues

* Fixes targeting on aobserve

* Fix some weird storage bugs

* clean up some doulbed up follow links

* Update code/modules/admin/player_panel.dm

Signed-off-by: Luc <89928798+lewcc@users.noreply.github.com>

* Adds some checks for ghost observe

* Remove mentor debug log

* ahhhhhhhhh that'll do it

* Cleans up some additional checks that were not quite what they should have been

* better logging, too

* mochi review

* one more thing

* Gets robot huds mostly working

* hopefully fix folks not getting observe

* Fix order of operations causing a runtime on qdeleting

* Remove some unnecessary stack traces

* Apply suggestions from code review

Co-authored-by: 1080pCat <96908085+1080pCat@users.noreply.github.com>
Signed-off-by: Luc <89928798+lewcc@users.noreply.github.com>

* Fix bug with items in your inventory disappearing on changing HUD modes

* Fix some bugs with the observe verbs

* possibly fix nullspace issues

* funny review

* missed one

---------

Signed-off-by: Luc <89928798+lewcc@users.noreply.github.com>
Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: GDN <96800819+GDNgit@users.noreply.github.com>
Co-authored-by: S34N <12197162+S34NW@users.noreply.github.com>
Co-authored-by: 1080pCat <96908085+1080pCat@users.noreply.github.com>
This commit is contained in:
Luc
2024-05-23 11:46:43 -04:00
committed by GitHub
parent 7bf45dce58
commit 23ad8a48fa
38 changed files with 1108 additions and 384 deletions
+174 -47
View File
@@ -20,6 +20,8 @@ GLOBAL_DATUM_INIT(ghost_crew_monitor, /datum/ui_module/crew_monitor/ghost, new)
move_resist = INFINITY // don't get pushed around
invisibility = INVISIBILITY_OBSERVER
blocks_emissive = FALSE // Ghosts are transparent, duh
hud_type = /datum/hud/ghost
speaks_ooc = TRUE
var/can_reenter_corpse
var/bootime = FALSE
var/started_as_observer //This variable is set to 1 when you enter the game as an observer.
@@ -41,8 +43,8 @@ GLOBAL_DATUM_INIT(ghost_crew_monitor, /datum/ui_module/crew_monitor/ghost, new)
var/datum/orbit_menu/orbit_menu
/// The "color" their runechat would have had
var/alive_runechat_color = "#FFFFFF"
hud_type = /datum/hud/ghost
speaks_ooc = TRUE
/// UID of the mob which we are currently observing
var/mob_observed
/mob/dead/observer/New(mob/body=null, flags=1)
set_invisibility(GLOB.observer_default_invisibility)
@@ -120,6 +122,8 @@ GLOBAL_DATUM_INIT(ghost_crew_monitor, /datum/ui_module/crew_monitor/ghost, new)
if(seerads)
STOP_PROCESSING(SSobj, src)
remove_observer_verbs()
if(mob_observed)
cleanup_observe()
return ..()
/mob/dead/observer/examine(mob/user)
@@ -184,25 +188,37 @@ Works together with spawning an observer, noted above.
return 1
/mob/proc/ghostize(flags = GHOST_CAN_REENTER, user_color, ghost_name)
if(key)
if(player_logged) //if they have disconnected we want to remove their SSD overlay
overlays -= image('icons/effects/effects.dmi', icon_state = "zzz_glow")
if(GLOB.non_respawnable_keys[ckey])
flags &= ~GHOST_CAN_REENTER
var/mob/dead/observer/ghost = new(src, flags) //Transfer safety to observer spawning proc.
ghost.timeofdeath = src.timeofdeath //BS12 EDIT
if(ghost.can_reenter_corpse)
ADD_TRAIT(ghost, TRAIT_RESPAWNABLE, GHOSTED)
else
GLOB.non_respawnable_keys[ckey] = 1
if(user_color)
add_atom_colour(user_color, ADMIN_COLOUR_PRIORITY)
ghost.color = user_color
if(ghost_name)
ghost.name = ghost_name
ghost.key = key
ghost.client?.init_verbs()
return ghost
if(!key)
return
if(player_logged) // if they have disconnected we want to remove their SSD overlay
overlays -= image('icons/effects/effects.dmi', icon_state = "zzz_glow")
if(GLOB.non_respawnable_keys[ckey])
flags &= ~GHOST_CAN_REENTER
var/mob/dead/observer/ghost = new(src, flags) // Transfer safety to observer spawning proc.
ghost.timeofdeath = src.timeofdeath // BS12 EDIT
if(ghost.can_reenter_corpse)
ADD_TRAIT(ghost, TRAIT_RESPAWNABLE, GHOSTED)
else
GLOB.non_respawnable_keys[ckey] = 1
// mods, mentors, and the like will have admin observe anyway, so this is moot
if(((key in GLOB.antag_hud_users) || (key in GLOB.roundstart_observer_keys)) && !check_rights(R_MOD | R_ADMIN | R_MENTOR, FALSE, src))
ghost.verbs |= /mob/dead/observer/proc/do_observe
ghost.verbs |= /mob/dead/observer/proc/observe
if(user_color)
add_atom_colour(user_color, ADMIN_COLOUR_PRIORITY)
ghost.color = user_color
if(ghost_name)
ghost.name = ghost_name
ghost.key = key
ghost.client?.init_verbs()
for(var/mob/dead/observer/obs in observers)
obs.cleanup_observe()
return ghost
/*
This is the proc mobs get to turn into a ghost. Forked from ghostize due to compatibility issues.
@@ -492,52 +508,67 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
GLOB.ghost_crew_monitor.ui_interact(src)
/mob/dead/observer/proc/add_observer_verbs()
verbs.Add(/mob/dead/observer/proc/ManualFollow)
verbs.Add(
/mob/dead/observer/proc/ManualFollow,
)
/mob/dead/observer/proc/remove_observer_verbs()
verbs.Remove(/mob/dead/observer/proc/ManualFollow)
verbs.Remove(
/mob/dead/observer/proc/ManualFollow,
// these might not necessarily be here, but we want to make sure they're gonezo anyway
/mob/dead/observer/proc/observe,
/mob/dead/observer/proc/do_observe
)
// This is the ghost's follow verb with an argument
// This is the ghost's follow verb with an argument.
// We need to do the usr check on this verb itself, but the logic follows.
/mob/dead/observer/proc/ManualFollow(atom/movable/target)
set name = "\[Observer\] Orbit"
set desc = "Orbits the specified movable atom."
set category = null
if(!target || !isobserver(usr))
// this usr check is apparently necessary for security
if(!isobserver(usr))
return
return do_manual_follow(target)
// We need to check usr when calling the verb, but we still want this logic to be accessible elsewhere
/mob/dead/observer/proc/do_manual_follow(atom/movable/target)
if(!get_turf(target))
return
if(target != src)
if(src in target.get_orbiters())
return
if(!target || target == src)
return
var/icon/I = icon(target.icon,target.icon_state,target.dir)
if(src in target.get_orbiters())
return
var/orbitsize = (I.Width()+I.Height())*0.5
var/icon/I = icon(target.icon, target.icon_state, target.dir)
if(orbitsize == 0)
orbitsize = 40
var/orbitsize = (I.Width() + I.Height())*0.5
orbitsize -= (orbitsize/world.icon_size)*(world.icon_size*0.25)
if(orbitsize == 0)
orbitsize = 40
var/rot_seg
orbitsize -= (orbitsize/world.icon_size)*(world.icon_size*0.25)
switch(ghost_orbit)
if(GHOST_ORBIT_TRIANGLE)
rot_seg = 3
if(GHOST_ORBIT_SQUARE)
rot_seg = 4
if(GHOST_ORBIT_PENTAGON)
rot_seg = 5
if(GHOST_ORBIT_HEXAGON)
rot_seg = 6
else //Circular
rot_seg = 36 //360/10 bby, smooth enough aproximation of a circle
var/rot_seg
to_chat(src, "<span class='notice'>Now following [target].</span>")
orbit(target,orbitsize, FALSE, 20, rot_seg)
switch(ghost_orbit)
if(GHOST_ORBIT_TRIANGLE)
rot_seg = 3
if(GHOST_ORBIT_SQUARE)
rot_seg = 4
if(GHOST_ORBIT_PENTAGON)
rot_seg = 5
if(GHOST_ORBIT_HEXAGON)
rot_seg = 6
else // Circular
rot_seg = 36 // 360/10 bby, smooth enough aproximation of a circle
to_chat(src, "<span class='notice'>Now following [target].</span>")
orbit(target, orbitsize, FALSE, 20, rot_seg)
/mob/dead/observer/orbit(atom/A, radius = 10, clockwise = FALSE, rotation_speed = 20, rotation_segments = 36, pre_rotation = TRUE, lock_in_orbit = FALSE, force_move = FALSE, orbit_layer = GHOST_LAYER)
setDir(2)//reset dir so the right directional sprites show up
@@ -825,6 +856,102 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
/mob/dead/observer/get_runechat_color()
return alive_runechat_color
/mob/dead/observer/proc/observe()
set name = "Observe"
set desc = "Observe a mob."
set category = "Ghost"
var/list/possible_targets = list()
for(var/mob/living/L in GLOB.player_list)
if(!L.mind)
continue
possible_targets.Add(L)
if(!length(possible_targets))
to_chat(src, "<span class='warning'>There's nobody for you to observe!</span>")
return
var/mob/target = tgui_input_list(usr, "Please, select a player!", "Observe", possible_targets)
if(!istype(target) || QDELETED(target))
return
do_observe(target)
/mob/dead/observer/proc/do_observe(mob/mob_eye)
set name = "\[Observer\] Observe"
set desc = "Observe the target mob."
set category = null
if(isnewplayer(mob_eye))
to_chat(src, "<span class='warning'>You can't observe someone in the lobby.</span>")
return
if(isobserver(mob_eye))
to_chat(src, "<span class='warning'>You can't observe a ghost.</span>")
return
if(!mob_eye.mind)
to_chat(src, "<span class='notice'>You can only observe mobs that have been or are being inhabited by a player!</span>")
return
if(mob_eye == src)
to_chat(src, "<span class='warning'>You can't observe yourself!</span>")
return
if(mob_observed)
// clean up first
stop_orbit()
cleanup_observe()
// Istype so we filter out points of interest that are not mobs
if(client && ismob(mob_eye))
// follow the mob so they're technically right there for visible messages n stuff
// call the sub-proc since the base one checks for usr
do_manual_follow(mob_eye)
client.set_eye(mob_eye)
add_attack_logs(src, mob_eye, "observed", ATKLOG_ALMOSTALL)
client.perspective = EYE_PERSPECTIVE
if(mob_eye.hud_used)
client.clear_screen()
LAZYOR(mob_eye.observers, src)
mob_eye.hud_used?.show_hud(mob_eye.hud_used.hud_version, src)
mob_observed = mob_eye.UID()
// mentor observing grants you this trait, and provides its own signal handler for this
if(!HAS_MIND_TRAIT(src, TRAIT_MENTOR_OBSERVING))
RegisterSignal(src, COMSIG_ATOM_ORBITER_STOP, PROC_REF(on_observer_orbit_end), override = TRUE)
else
if(!check_rights(R_MENTOR, FALSE, src))
log_debug("[key_name(src)] has the the mobserve trait while observing, but isn't a mentor. This is likely an error, and may result in them getting stuck")
/// Clean up observing
/mob/dead/observer/proc/cleanup_observe()
if(isnull(mob_observed))
return
var/mob/target = locateUID(mob_observed)
add_attack_logs(src, target, "un-observed", ATKLOG_ALL)
mob_observed = null
reset_perspective(null)
client?.perspective = initial(client.perspective)
set_sight(initial(sight))
UnregisterSignal(src, COMSIG_ATOM_ORBITER_STOP)
if(s_active)
var/obj/item/storage/bag = s_active
s_active = null
bag.update_viewers(src)
if(!QDELETED(target) && istype(target))
hide_other_mob_action_buttons(target)
target.observers -= src
/mob/dead/observer/proc/on_observer_orbit_end(mob/follower, atom)
SIGNAL_HANDLER // COMSIG_ATOM_ORBITER_STOP
if(HAS_MIND_TRAIT(src, TRAIT_MENTOR_OBSERVING))
log_debug("[key_name(src)] ended up in regular cleanup_observe rather than the mentor cleanup observe despite having TRAIT_MENTOR_OBSERVING. This is likely a bug and may result in them being stuck outside of their bodies.")
cleanup_observe()
/mob/dead/observer/proc/update_dead_radio()
if(get_preference(PREFTOGGLE_CHAT_GHOSTRADIO))
GLOB.deadchat_radio.listeners |= src
@@ -1,6 +1,9 @@
/mob/dead/observer/Logout()
if(client)
client.images -= GLOB.ghost_images
if(mob_observed && ismob(locateUID(mob_observed)))
cleanup_observe()
..()
spawn(0)
if(src && !key) //we've transferred to another mob. This ghost should be deleted.