diff --git a/code/__DEFINES/__game.dm b/code/__DEFINES/__game.dm
index 7cb7440ba8da..ead4c9665c7c 100644
--- a/code/__DEFINES/__game.dm
+++ b/code/__DEFINES/__game.dm
@@ -152,6 +152,7 @@ block( \
//toggles_admin
/// Splits admin tabs in Statpanel
#define SPLIT_ADMIN_TABS (1<<0)
+#define ADMIN_STEALTHMODE (1<<1)
//=================================================
diff --git a/code/game/verbs/who.dm b/code/game/verbs/who.dm
index 9cad56cdabe8..5871fdc7a152 100644
--- a/code/game/verbs/who.dm
+++ b/code/game/verbs/who.dm
@@ -42,6 +42,8 @@
var/list/Lines = list()
if(admin_holder && ((R_ADMIN & admin_holder.rights) || (R_MOD & admin_holder.rights)))
for(var/client/C in GLOB.clients)
+ if(!CLIENT_HAS_RIGHTS(src, R_STEALTH) && (CLIENT_IS_STEALTHED(C)))
+ continue
var/entry = "[C.key]"
if(C.mob) //Juuuust in case
if(istype(C.mob, /mob/new_player))
@@ -139,7 +141,7 @@
else
for(var/client/C in GLOB.clients)
- if(C.admin_holder && C.admin_holder.fakekey)
+ if((C.admin_holder && C.admin_holder.fakekey) || (CLIENT_IS_STEALTHED(C)))
continue
Lines += C.key
@@ -172,6 +174,8 @@
LAZYSET(listings, category, list())
for(var/client/C in GLOB.admins)
+ if(CLIENT_IS_STEALTHED(C) && !CLIENT_HAS_RIGHTS(src, R_STEALTH))
+ continue
if(C.admin_holder?.fakekey && !CLIENT_IS_STAFF(src))
continue
for(var/category in mappings)
@@ -187,7 +191,9 @@
for(var/srank in entry.admin_holder.extra_titles)
dat += " & [srank]"
if(CLIENT_IS_STAFF(src))
- if(entry.admin_holder?.fakekey)
+ if(CLIENT_IS_STEALTHED(entry))
+ dat += " (STEALTHED)"
+ else if(entry.admin_holder?.fakekey)
dat += " (HIDDEN)"
if(istype(entry.mob, /mob/dead/observer))
dat += " - Observing"
diff --git a/code/global.dm b/code/global.dm
index 6847fbd2b7fe..f141dc5d68ac 100644
--- a/code/global.dm
+++ b/code/global.dm
@@ -33,6 +33,7 @@
#define CLIENT_HAS_RIGHTS(cli, flags) ((cli?.admin_holder?.rights & flags) == flags)
#define CLIENT_IS_STAFF(cli) (cli?.admin_holder?.rights & (R_MOD|R_ADMIN))
#define CLIENT_IS_MENTOR(cli) CLIENT_HAS_RIGHTS(cli, R_MENTOR)
+#define CLIENT_IS_STEALTHED(cli) (CLIENT_HAS_RIGHTS(cli, R_STEALTH) && cli.prefs?.toggles_admin & ADMIN_STEALTHMODE)
#define AHOLD_IS_MOD(ahold) (ahold && (ahold.rights & R_MOD))
#define AHOLD_IS_ADMIN(ahold) (ahold && (ahold.rights & R_ADMIN))
diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm
index 85996fca1927..4623df8a5dc5 100644
--- a/code/modules/admin/admin_verbs.dm
+++ b/code/modules/admin/admin_verbs.dm
@@ -250,6 +250,10 @@ GLOBAL_LIST_INIT(admin_verbs_color, list(
/client/proc/set_ooc_color_self
))
+GLOBAL_LIST_INIT(admin_verbs_stealth, list(
+ /client/proc/toggle_admin_stealth
+))
+
GLOBAL_LIST_INIT(admin_mob_event_verbs_hideable, list(
/client/proc/hide_event_mob_verbs,
/client/proc/cmd_admin_select_mob_rank,
@@ -341,6 +345,8 @@ GLOBAL_LIST_INIT(roundstart_mod_verbs, list(
add_verb(src, GLOB.admin_verbs_sounds)
if(CLIENT_HAS_RIGHTS(src, R_SPAWN))
add_verb(src, GLOB.admin_verbs_spawn)
+ if(CLIENT_HAS_RIGHTS(src, R_STEALTH))
+ add_verb(src, GLOB.admin_verbs_stealth)
if(GLOB.RoleAuthority && (GLOB.RoleAuthority.roles_whitelist[ckey] & WHITELIST_YAUTJA_LEADER))
add_verb(src, GLOB.clan_verbs)
@@ -370,6 +376,7 @@ GLOBAL_LIST_INIT(roundstart_mod_verbs, list(
GLOB.admin_mob_event_verbs_hideable,
GLOB.admin_verbs_hideable,
GLOB.debug_verbs,
+ GLOB.admin_verbs_stealth,
))
/client/proc/jobbans()
@@ -595,6 +602,14 @@ GLOBAL_LIST_INIT(roundstart_mod_verbs, list(
else
to_chat(usr, SPAN_BOLDNOTICE("You will no longer hear an audio cue for ARES and Prayer messages."))
+/client/proc/toggle_admin_stealth()
+ set name = "Toggle Admin Stealth"
+ set category = "Preferences"
+ prefs.toggles_admin ^= ADMIN_STEALTHMODE
+ if(prefs.toggles_admin & ADMIN_STEALTHMODE)
+ to_chat(usr, SPAN_BOLDNOTICE("You enabled admin stealth mode."))
+ else
+ to_chat(usr, SPAN_BOLDNOTICE("You disabled admin stealth mode."))
#undef MAX_WARNS
#undef AUTOBANTIME
diff --git a/code/modules/admin/player_panel/player_panel.dm b/code/modules/admin/player_panel/player_panel.dm
index bead55f994ab..0fef0415bb38 100644
--- a/code/modules/admin/player_panel/player_panel.dm
+++ b/code/modules/admin/player_panel/player_panel.dm
@@ -188,6 +188,8 @@
for(var/mob/M in mobs)
if(!M.ckey)
continue
+ if(!CLIENT_HAS_RIGHTS(usr.client, R_STEALTH) && (M.client && (CLIENT_IS_STEALTHED(M.client))))
+ continue
var/color = i % 2 == 0 ? "#6289b7" : "#48709d"
diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm
index a085cb7634d6..3dfe2d38d81f 100644
--- a/code/modules/client/client_procs.dm
+++ b/code/modules/client/client_procs.dm
@@ -330,7 +330,6 @@ GLOBAL_LIST_INIT(whitelisted_client_procs, list(
admin_holder = GLOB.admin_datums[ckey]
if(admin_holder)
admin_holder.associate(src)
- notify_login()
add_pref_verbs()
//preferences datum - also holds some persistent data for the client (because we may as well keep these datums to a minimum)
@@ -343,6 +342,8 @@ GLOBAL_LIST_INIT(whitelisted_client_procs, list(
prefs.last_id = computer_id //these are gonna be used for banning
fps = prefs.fps
+ notify_login()
+
load_xeno_name()
human_name_ban = prefs.human_name_ban
@@ -476,7 +477,7 @@ GLOBAL_LIST_INIT(whitelisted_client_procs, list(
SSping.currentrun -= src
log_access("Logout: [key_name(src)]")
- if(CLIENT_IS_STAFF(src))
+ if(CLIENT_IS_STAFF(src) && !CLIENT_IS_STEALTHED(src))
message_admins("Admin logout: [key_name(src)]")
var/list/adm = get_admin_counts(R_MOD)
@@ -493,7 +494,7 @@ GLOBAL_LIST_INIT(whitelisted_client_procs, list(
/// Handles login-related logging and associated notifications
/client/proc/notify_login()
log_access("Login: [key_name(src)] from [address ? address : "localhost"]-[computer_id] || BYOND v[byond_version].[byond_build]")
- if(CLIENT_IS_STAFF(src))
+ if(CLIENT_IS_STAFF(src) && !CLIENT_IS_STEALTHED(src))
message_admins("Admin login: [key_name(src)]")
var/list/adm = get_admin_counts(R_MOD)