diff --git a/code/__DEFINES/preferences.dm b/code/__DEFINES/preferences.dm
index e0947d2d420..56a93164143 100644
--- a/code/__DEFINES/preferences.dm
+++ b/code/__DEFINES/preferences.dm
@@ -15,7 +15,6 @@
#define DEADMIN_POSITION_SECURITY (1<<18)
#define DEADMIN_POSITION_SILICON (1<<19)
#define ADMIN_IGNORE_CULT_GHOST (1<<21)
-#define SPLIT_ADMIN_TABS (1<<23)
#define TOGGLES_DEFAULT (SOUND_ADMINHELP|MEMBER_PUBLIC|SOUND_PRAYERS)
diff --git a/code/__DEFINES/subsystems.dm b/code/__DEFINES/subsystems.dm
index a35dab5dd89..e789597cfbb 100644
--- a/code/__DEFINES/subsystems.dm
+++ b/code/__DEFINES/subsystems.dm
@@ -135,12 +135,13 @@
#define INIT_ORDER_SERVER_MAINT 93
#define INIT_ORDER_INPUT 85
#define INIT_ORDER_SOUNDS 83
-#define INIT_ORDER_INSTRUMENTS 82
-#define INIT_ORDER_GREYSCALE 81
-#define INIT_ORDER_VIS 80
-#define INIT_ORDER_SECURITY_LEVEL 79 // We need to load before events so that it has a security level to choose from.
-#define INIT_ORDER_DISCORD 78
-#define INIT_ORDER_ACHIEVEMENTS 77
+#define INIT_ORDER_ADMIN_VERBS 82
+#define INIT_ORDER_INSTRUMENTS 81
+#define INIT_ORDER_GREYSCALE 80
+#define INIT_ORDER_VIS 79
+#define INIT_ORDER_SECURITY_LEVEL 78 // We need to load before events so that it has a security level to choose from.
+#define INIT_ORDER_DISCORD 77
+#define INIT_ORDER_ACHIEVEMENTS 76
#define INIT_ORDER_STATION 74 //This is high priority because it manipulates a lot of the subsystems that will initialize after it.
#define INIT_ORDER_QUIRKS 73
#define INIT_ORDER_REAGENTS 72 //HAS to be before mapping and assets - both create objects, which creates reagents, which relies on lists made in this subsystem
diff --git a/code/__HELPERS/_lists.dm b/code/__HELPERS/_lists.dm
index 4a4bc828cf8..589693abd29 100644
--- a/code/__HELPERS/_lists.dm
+++ b/code/__HELPERS/_lists.dm
@@ -271,6 +271,20 @@
if(typecache_include[atom_checked.type] && !typecache_exclude[atom_checked.type])
. += atom_checked
+/// Returns a typecache for the direct descendants of the path
+/proc/typecache_next_level(path)
+ if(isnull(path))
+ return
+ var/list/paths = list()
+ if(ispath(path))
+ var/path_text = "[path]"
+ for(var/subtype in subtypesof(path))
+ var/subtype_text = replacetext("[subtype]", path_text, "")
+ if(findlasttext(subtype_text, "/") != 1)
+ continue
+ paths[subtype] = TRUE
+ return paths
+
/**
* Like typesof() or subtypesof(), but returns a typecache instead of a list.
*
diff --git a/code/__HELPERS/admin_verb.dm b/code/__HELPERS/admin_verb.dm
new file mode 100644
index 00000000000..5862b41c11e
--- /dev/null
+++ b/code/__HELPERS/admin_verb.dm
@@ -0,0 +1,56 @@
+/**
+ * Creates an admin verb with the specified module(category) name, desc, permissions, and parameters as needed.
+ */
+#define ADMIN_VERB(module, verb_name, verb_desc, permissions, params...) \
+/mob/admin_module_holder/##module/##verb_name/verb/invoke(##params){ \
+ set src in usr.group; \
+ set name = #verb_name; \
+ set desc = verb_desc; \
+ if(datum_flags & DF_VAR_EDITED) { \
+ message_admins("[key_name_admin(usr)] attempted to elevate permissions by executing from a var edited admin verb holder!"); \
+ del(src); \
+ return; \
+ } \
+ if(IsAdminAdvancedProcCall()) { \
+ message_admins("[key_name_admin(usr)] attempted to elevate permissions by executing an admin verb using ProcCall!"); \
+ return; \
+ } \
+ if(check_rights_for(usr.client, permissions)) { \
+ _##verb_name(arglist(args)); \
+ SSblackbox.record_feedback("tally", "admin_verb", 1, "[#module]/[#verb_name]"); \
+ } else { \
+ to_chat(usr, span_warning("You lack the permissions ([rights2text(permissions, " ")]) for this verb!")); \
+ } \
+} \
+/mob/admin_module_holder/##module/##verb_name/dynamic_map_generate(){ \
+ return list(#module, #verb_name, verb_desc, permissions); \
+} \
+/mob/admin_module_holder/##module/##verb_name/proc/_##verb_name(##params)
+
+/**
+ * Creates a context menu entry for the client. The source of this proc will be the client!
+ */
+#define ADMIN_CONTEXT_ENTRY(context_id, context_name, permissions, params...) \
+/client/proc/admin_context_wrapper_##context_id(##params){ \
+ if(check_rights_for(src, permissions)) { \
+ __admin_context_verb_##context_id(arglist(args)); \
+ SSblackbox.record_feedback("tally", "admin_context", 1, "[#context_id]/[context_name]"); \
+ } else { \
+ to_chat(usr, span_warning("You lack the permissions ([rights2text(permissions, " ")]) for this context menu action!")); \
+ } \
+} \
+/datum/controller/subsystem/admin_verbs/populate_context_map(list/context_map){ \
+ ..(); \
+ context_map[/client/proc/admin_context_wrapper_##context_id] = list(context_name, permissions); \
+} \
+/client/proc/__admin_context_verb_##context_id(##params)
+
+// THIS IS DONE HERE TO ENSURE IT ALWAYS MATCHES THE ABOVE MACRO.
+// IF YOU CHANGE THE MACRO MAKE SURE THIS STILL WORKS CORRECTLY!! -Zephyr
+
+/client/CanProcCall(procname)
+ if(findtext(procname, "admin_context_wrapper_") == 1)
+ return FALSE
+ if(findtext(procname, "__admin_context_verb") == 1)
+ return FALSE
+ return ..()
diff --git a/code/__HELPERS/hallucinations.dm b/code/__HELPERS/hallucinations.dm
index 809ff475fc9..b2c7ad6d9b5 100644
--- a/code/__HELPERS/hallucinations.dm
+++ b/code/__HELPERS/hallucinations.dm
@@ -113,50 +113,6 @@ GLOBAL_LIST_INIT(random_hallucination_weighted_list, generate_hallucination_weig
to_chat(usr, span_boldnotice("The total weight of the hallucination weighted list is [total_weight]."))
return total_weight
-/// Debug verb for getting the weight of each distinct type within the random_hallucination_weighted_list
-/client/proc/debug_hallucination_weighted_list_per_type()
- set name = "Show Hallucination Weights"
- set category = "Debug"
-
- var/header = "
| Type | Weight | Percent | "
-
- var/total_weight = debug_hallucination_weighted_list()
- var/list/all_weights = list()
- var/datum/hallucination/last_type
- var/last_type_weight = 0
- for(var/datum/hallucination/hallucination_type as anything in GLOB.random_hallucination_weighted_list)
- var/this_weight = GLOB.random_hallucination_weighted_list[hallucination_type]
- // Last_type is the abstract parent of the last hallucination type we iterated over
- if(last_type)
- // If this hallucination is the same path as the last type (subtype), add it to the total of the last type weight
- if(ispath(hallucination_type, last_type))
- last_type_weight += this_weight
- continue
-
- // Otherwise we moved onto the next hallucination subtype so we can stop
- else
- all_weights["
|---|
| [last_type] | [last_type_weight] / [total_weight] | [round(100 * (last_type_weight / total_weight), 0.01)]% chance |
"] = last_type_weight
-
- // Set last_type to the abstract parent of this hallucination
- last_type = initial(hallucination_type.abstract_hallucination_parent)
- // If last_type is the base hallucination it has no distinct subtypes so we can total it up immediately
- if(last_type == /datum/hallucination)
- all_weights["| [hallucination_type] | [this_weight] / [total_weight] | [round(100 * (this_weight / total_weight), 0.01)]% chance |
"] = this_weight
- last_type = null
-
- // Otherwise we start the weight sum for the next entry here
- else
- last_type_weight = this_weight
-
- // Sort by weight descending, where weight is the values (not the keys). We assoc_to_keys later to get JUST the text
- all_weights = sortTim(all_weights, GLOBAL_PROC_REF(cmp_numeric_dsc), associative = TRUE)
-
- var/page_style = ""
- var/page_contents = "[page_style][header][jointext(assoc_to_keys(all_weights), "")]
"
- var/datum/browser/popup = new(mob, "hallucinationdebug", "Hallucination Weights", 600, 400)
- popup.set_content(page_contents)
- popup.open()
-
/// Gets a random subtype of the passed hallucination type that has a random_hallucination_weight > 0.
/// If no subtype is passed, it will get any random hallucination subtype that is not abstract and has weight > 0.
/// This can be used instead of picking from the global weighted list to just get a random valid hallucination.
diff --git a/code/_onclick/hud/movable_screen_objects.dm b/code/_onclick/hud/movable_screen_objects.dm
index b1f40f76294..2d2a92388d4 100644
--- a/code/_onclick/hud/movable_screen_objects.dm
+++ b/code/_onclick/hud/movable_screen_objects.dm
@@ -43,41 +43,3 @@
offset[1] += x_off
offset[2] += y_off
return offset_to_screen_loc(offset[1], offset[2], our_client?.view)
-
-//Debug procs
-/client/proc/test_movable_UI()
- set category = "Debug"
- set name = "Spawn Movable UI Object"
-
- var/atom/movable/screen/movable/M = new()
- M.name = "Movable UI Object"
- M.icon_state = "block"
- M.maptext = MAPTEXT("Movable")
- M.maptext_width = 64
-
- var/screen_l = input(usr,"Where on the screen? (Formatted as 'X,Y' e.g: '1,1' for bottom left)","Spawn Movable UI Object") as text|null
- if(!screen_l)
- return
-
- M.screen_loc = screen_l
-
- screen += M
-
-
-/client/proc/test_snap_UI()
- set category = "Debug"
- set name = "Spawn Snap UI Object"
-
- var/atom/movable/screen/movable/snap/S = new()
- S.name = "Snap UI Object"
- S.icon_state = "block"
- S.maptext = MAPTEXT("Snap")
- S.maptext_width = 64
-
- var/screen_l = input(usr,"Where on the screen? (Formatted as 'X,Y' e.g: '1,1' for bottom left)","Spawn Snap UI Object") as text|null
- if(!screen_l)
- return
-
- S.screen_loc = screen_l
-
- screen += S
diff --git a/code/controllers/admin.dm b/code/controllers/admin.dm
index 0d90319f863..618e0c80da9 100644
--- a/code/controllers/admin.dm
+++ b/code/controllers/admin.dm
@@ -41,51 +41,5 @@ INITIALIZE_IMMEDIATE(/obj/effect/statclick)
else
class = "unknown"
- usr.client.debug_variables(target)
+ SSadmin_verbs.dynamic_invoke_admin_verb(usr.client, /mob/admin_module_holder/debug/view_variables, target)
message_admins("Admin [key_name_admin(usr)] is debugging the [target] [class].")
-
-
-// Debug verbs.
-/client/proc/restart_controller(controller in list("Master", "Failsafe"))
- set category = "Debug"
- set name = "Restart Controller"
- set desc = "Restart one of the various periodic loop controllers for the game (be careful!)"
-
- if(!holder)
- return
- switch(controller)
- if("Master")
- Recreate_MC()
- SSblackbox.record_feedback("tally", "admin_verb", 1, "Restart Master Controller")
- if("Failsafe")
- new /datum/controller/failsafe()
- SSblackbox.record_feedback("tally", "admin_verb", 1, "Restart Failsafe Controller")
-
- message_admins("Admin [key_name_admin(usr)] has restarted the [controller] controller.")
-
-/client/proc/debug_controller()
- set category = "Debug"
- set name = "Debug Controller"
- set desc = "Debug the various periodic loop controllers for the game (be careful!)"
-
- if(!holder)
- return
-
- var/list/controllers = list()
- var/list/controller_choices = list()
-
- for (var/datum/controller/controller in world)
- if (istype(controller, /datum/controller/subsystem))
- continue
- controllers["[controller] (controller.type)"] = controller //we use an associated list to ensure clients can't hold references to controllers
- controller_choices += "[controller] (controller.type)"
-
- var/datum/controller/controller_string = input("Select controller to debug", "Debug Controller") as null|anything in controller_choices
- var/datum/controller/controller = controllers[controller_string]
-
- if (!istype(controller))
- return
- debug_variables(controller)
-
- SSblackbox.record_feedback("tally", "admin_verb", 1, "Restart Failsafe Controller")
- message_admins("Admin [key_name_admin(usr)] is debugging the [controller] controller.")
diff --git a/code/controllers/subsystem/admin_verbs.dm b/code/controllers/subsystem/admin_verbs.dm
new file mode 100644
index 00000000000..99d7bfaec2d
--- /dev/null
+++ b/code/controllers/subsystem/admin_verbs.dm
@@ -0,0 +1,197 @@
+#define VERB_MAP_MODULE 1
+#define VERB_MAP_NAME 2
+#define VERB_MAP_DESCRIPTION 3
+#define VERB_MAP_PERMISSIONS 4
+
+#define CONTEXT_MAP_NAME 1
+#define CONTEXT_MAP_PERMISSIONS 2
+
+#define LINKUPMAP_LOGOUT 1
+#define LINKUPMAP_LOGIN 2
+#define LINKUPMAP_CONTEXT_MAP 3
+
+SUBSYSTEM_DEF(admin_verbs)
+ name = "Admin Verbs"
+ flags = SS_NO_FIRE
+ init_order = INIT_ORDER_ADMIN_VERBS
+ VAR_PRIVATE/list/admin_verb_map
+ VAR_PRIVATE/list/holder_map
+ VAR_PRIVATE/list/context_map
+ VAR_PRIVATE/list/admin_linkup_map
+
+ var/list/waiting_to_assosciate = list()
+ var/list/assosciations_by_ckey
+
+// DO NOT MERGE BEFORE UNCOMMENTING -- GENERAL_PROTECT_DATUM(/datum/controller/subsystem/admin_verbs)
+
+/datum/controller/subsystem/admin_verbs/Recover()
+ admin_verb_map = SSadmin_verbs.admin_verb_map
+ holder_map = SSadmin_verbs.holder_map
+ context_map = SSadmin_verbs.context_map
+ admin_linkup_map = SSadmin_verbs.admin_linkup_map
+ assosciations_by_ckey = SSadmin_verbs.assosciations_by_ckey
+
+/datum/controller/subsystem/admin_verbs/Initialize()
+ RegisterSignal(src, COMSIG_SUBSYSTEM_POST_INITIALIZE, PROC_REF(assosciate_with_waiting))
+ admin_verb_map = list()
+ admin_linkup_map = list()
+ generate_holder_map()
+ context_map = list()
+ populate_context_map(context_map)
+ assosciations_by_ckey = list()
+ return SS_INIT_SUCCESS
+
+/datum/controller/subsystem/admin_verbs/proc/generate_stat_data(client/target)
+ var/static/list/abbreviations = list(
+ "ERT"
+ )
+ var/static/list/cached_formats = list()
+
+ if(!initialized || !target.holder)
+ return list()
+
+ var/list/stat_data = list()
+ for(var/verb_type in assosciations_by_ckey[target.ckey])
+ var/list/verb_information = admin_verb_map[verb_type]
+ var/verb_permissions = verb_information[VERB_MAP_PERMISSIONS]
+ if(!check_rights_for(target, verb_permissions))
+ continue
+
+ var/verb_module = lowertext(verb_information[VERB_MAP_MODULE])
+ if(!verb_module || verb_module == "null")
+ continue
+
+ if(!cached_formats[verb_module])
+ var/verb_module_formatted = ""
+ for(var/verb_module_part in splittext(verb_module, "_"))
+ if(verb_module_part in abbreviations)
+ verb_module_formatted += "[uppertext(verb_module_part)] "
+ else
+ verb_module_formatted += "[capitalize(verb_module_part)] "
+ verb_module_formatted = copytext(verb_module_formatted, 1, -1)
+ cached_formats[verb_module] = verb_module_formatted
+
+ var/original_name = verb_information[VERB_MAP_NAME]
+ if(!cached_formats[original_name])
+ var/formatted_name = ""
+ for(var/name_part in splittext(original_name, "_"))
+ if(name_part in abbreviations)
+ formatted_name += "[uppertext(name_part)] "
+ else
+ formatted_name += "[capitalize(name_part)] "
+ formatted_name = copytext(formatted_name, 1, -1)
+ cached_formats[original_name] = formatted_name
+
+ var/verb_desc = verb_information[VERB_MAP_DESCRIPTION]
+ if(!stat_data[cached_formats[verb_module]])
+ stat_data[cached_formats[verb_module]] = list()
+ stat_data[cached_formats[verb_module]] += list(list(cached_formats[original_name], verb_desc, original_name))
+ var/sorted_stat_data = list()
+ for(var/verb_category in stat_data)
+ sorted_stat_data[verb_category] = sort_list(stat_data[verb_category], GLOBAL_PROC_REF(cmp_admin_verb_name))
+ return sorted_stat_data
+
+/proc/cmp_admin_verb_name(list/info_left, list/info_right)
+ return sorttext(info_right[1], info_left[1])
+
+/datum/controller/subsystem/admin_verbs/proc/populate_context_map(list/context_map)
+ return
+
+/datum/controller/subsystem/admin_verbs/proc/generate_holder_map()
+ admin_verb_map = list()
+ holder_map = list()
+ var/list/processing = typecacheof(sort_list(subtypesof(/mob/admin_module_holder), GLOBAL_PROC_REF(cmp_typepaths_asc)))
+ processing -= typecache_next_level(/mob/admin_module_holder)
+ for(var/mob/admin_module_holder/holder_type as anything in processing)
+ var/mob/admin_module_holder/holder = new holder_type
+ holder_map[holder_type] = holder
+ admin_verb_map[holder_type] = holder.dynamic_map_generate()
+
+/datum/controller/subsystem/admin_verbs/proc/dynamic_invoke_admin_verb(mob/target, verb_type, ...)
+ if(IsAdminAdvancedProcCall())
+ return
+
+ var/mob/admin_module_holder/holder = holder_map[verb_type]
+ if(!istype(holder))
+ to_chat(usr, span_big("Attempted to dynamic invoke an admin verb that didnt exist, this is a really bad problem!"))
+ CRASH("Admin Verb Holder '[verb_type]' did not exist when an attempt to access the dynmap occured.")
+
+ if(IS_CLIENT_OR_MOCK(target))
+ var/client/clientele = target
+ target = clientele.mob
+
+ usr = target
+ var/holder_proc = text2path("[verb_type]/verb/invoke")
+ var/list/arguments = args.Copy(3)
+ call(holder, holder_proc)(arglist(arguments))
+
+/datum/controller/subsystem/admin_verbs/proc/link_admin(mob/admin)
+ assosciations_by_ckey[admin.ckey] = list()
+ for(var/mob/admin_module_holder/holder as anything in holder_map)
+ holder = holder_map[holder]
+ if(check_rights_for(admin.client, admin_verb_map[holder.type][VERB_MAP_PERMISSIONS]))
+ admin.group |= holder
+ assosciations_by_ckey[admin.ckey] |= list(holder.type)
+
+ var/list/client_context_verbs = admin_linkup_map[admin.ckey][LINKUPMAP_CONTEXT_MAP]
+ for(var/context_entry in context_map)
+ var/list/context_information = context_map[context_entry]
+
+ var/procpath/existing = client_context_verbs[context_entry]
+ if(existing)
+ admin.client.verbs -= existing
+
+ if(!check_rights_for(admin.client, context_information[CONTEXT_MAP_PERMISSIONS]))
+ continue
+ client_context_verbs[context_entry] = new context_entry(admin.client, context_information[CONTEXT_MAP_NAME])
+
+/datum/controller/subsystem/admin_verbs/proc/unlink_admin(mob/adwas)
+ for(var/mob/admin_module_holder/holder as anything in holder_map)
+ holder = holder_map[holder]
+ adwas.group -= holder
+ assosciations_by_ckey -= adwas.canon_client.ckey
+
+ // we use canon_client here because ckey will already have moved when this is called
+ var/list/client_context_verbs = admin_linkup_map[adwas.canon_client.ckey][LINKUPMAP_CONTEXT_MAP]
+ for(var/context_entry in context_map)
+ adwas.canon_client.verbs -= client_context_verbs[context_entry]
+
+/datum/controller/subsystem/admin_verbs/proc/assosciate_admin(client/admin)
+ if(!initialized)
+ to_chat_immediate(admin, span_admin("SSadmin_verbs has either not begun or has not finished initialization procedures, please wait!"))
+ waiting_to_assosciate |= admin.ckey
+ return
+
+ var/list/existing_map = admin_linkup_map[admin.ckey]
+ if(existing_map)
+ admin.player_details.post_login_callbacks -= existing_map[LINKUPMAP_LOGIN]
+
+ var/datum/callback/old_logout = existing_map[LINKUPMAP_LOGOUT]
+ admin.player_details.post_logout_callbacks -= old_logout
+ old_logout.Invoke(admin.mob)
+
+ var/on_login = CALLBACK(src, PROC_REF(link_admin))
+ var/on_logout = CALLBACK(src, PROC_REF(unlink_admin))
+ admin_linkup_map[admin.ckey] = list(on_logout, on_login, list())
+
+ admin.player_details.post_login_callbacks += list(on_login)
+ admin.player_details.post_logout_callbacks += list(on_logout)
+ link_admin(admin.mob)
+ SSstatpanels.set_admin_verb_tab(admin)
+
+/datum/controller/subsystem/admin_verbs/proc/deassosciate_admin(client/adwas)
+ unlink_admin(adwas.mob) // we unlink before clearing the linkup map because unlink checks the map for context entries to remove
+ admin_linkup_map -= list(adwas.ckey)
+ SSstatpanels.set_admin_verb_tab(adwas)
+
+/datum/controller/subsystem/admin_verbs/proc/assosciate_with_waiting()
+ for(var/waiting in waiting_to_assosciate)
+ if(waiting in GLOB.directory)
+ assosciate_admin(GLOB.directory[waiting])
+ waiting_to_assosciate.Cut()
+
+/datum/controller/subsystem/admin_verbs/proc/handle_admin_holder_topic(client/user, href, href_list)
+ if(href_list["adminchecklaws"])
+ dynamic_invoke_admin_verb(user, /mob/admin_module_holder/game/check_ai_laws)
+ return TRUE
+ return FALSE
diff --git a/code/controllers/subsystem/explosions.dm b/code/controllers/subsystem/explosions.dm
index ef14d078fb4..42621b534d2 100644
--- a/code/controllers/subsystem/explosions.dm
+++ b/code/controllers/subsystem/explosions.dm
@@ -81,12 +81,9 @@ SUBSYSTEM_DEF(explosions)
flameturf -= T
throwturf -= T
-/client/proc/check_bomb_impacts()
- set name = "Check Bomb Impact"
- set category = "Debug"
-
+ADMIN_VERB(debug, check_bomb_impact, "", R_DEBUG)
var/newmode = tgui_alert(usr, "Use reactionary explosions?","Check Bomb Impact", list("Yes", "No"))
- var/turf/epicenter = get_turf(mob)
+ var/turf/epicenter = get_turf(usr)
if(!epicenter)
return
diff --git a/code/controllers/subsystem/mapping.dm b/code/controllers/subsystem/mapping.dm
index 80beabfcbe9..4312087b7f1 100644
--- a/code/controllers/subsystem/mapping.dm
+++ b/code/controllers/subsystem/mapping.dm
@@ -603,15 +603,7 @@ GLOBAL_LIST_EMPTY(the_station_areas)
holodeck_templates[holo_template.template_id] = holo_template
-//Manual loading of away missions.
-/client/proc/admin_away()
- set name = "Load Away Mission"
- set category = "Admin.Events"
-
- if(!holder || !check_rights(R_FUN))
- return
-
-
+ADMIN_VERB(events, load_away_mission, "", R_FUN)
if(!GLOB.the_gateway)
if(tgui_alert(usr, "There's no home gateway on the station. You sure you want to continue ?", "Uh oh", list("Yes", "No")) != "Yes")
return
@@ -622,14 +614,14 @@ GLOBAL_LIST_EMPTY(the_station_areas)
var/secret = FALSE
if(tgui_alert(usr, "Do you want your mission secret? (This will prevent ghosts from looking at your map in any way other than through a living player's eyes.)", "Are you $$$ekret?", list("Yes", "No")) == "Yes")
secret = TRUE
- var/answer = input("What kind?","Away") as null|anything in possible_options
+ var/answer = input(usr, "What kind?","Away") as null|anything in possible_options
switch(answer)
if("Custom")
- var/mapfile = input("Pick file:", "File") as null|file
+ var/mapfile = input(usr, "Pick file:", "File") as null|file
if(!mapfile)
return
away_name = "[mapfile] custom"
- to_chat(usr,span_notice("Loading [away_name]..."))
+ to_chat(usr, span_notice("Loading [away_name]..."))
var/datum/map_template/template = new(mapfile, "Away Mission")
away_level = template.load_new_z(secret)
else
@@ -645,7 +637,6 @@ GLOBAL_LIST_EMPTY(the_station_areas)
log_admin("Admin [key_name(usr)] has loaded [away_name] away mission.")
if(!away_level)
message_admins("Loading [away_name] failed!")
- return
/datum/controller/subsystem/mapping/proc/RequestBlockReservation(width, height, z, type = /datum/turf_reservation, turf_type_override)
UNTIL((!z || reservation_ready["[z]"]) && !clearing_reserved_turfs)
diff --git a/code/controllers/subsystem/statpanel.dm b/code/controllers/subsystem/statpanel.dm
index 8c56e601f2b..fd6caa550aa 100644
--- a/code/controllers/subsystem/statpanel.dm
+++ b/code/controllers/subsystem/statpanel.dm
@@ -54,8 +54,6 @@ SUBSYSTEM_DEF(statpanels)
if(!target.holder)
target.stat_panel.send_message("remove_admin_tabs")
else
- target.stat_panel.send_message("update_split_admin_tabs", !!(target.prefs.toggles & SPLIT_ADMIN_TABS))
-
if(!("MC" in target.panel_tabs) || !("Tickets" in target.panel_tabs))
target.stat_panel.send_message("add_admin_tabs", target.holder.href_token)
@@ -146,6 +144,13 @@ SUBSYSTEM_DEF(statpanels)
// Push update
target.stat_panel.send_message("update_interviews", data)
+/datum/controller/subsystem/statpanels/proc/set_admin_verb_tab(client/target)
+ var/list/admin_verb_stat_data = SSadmin_verbs.generate_stat_data(target)
+ if(length(admin_verb_stat_data))
+ target.stat_panel.send_message("update_admin_verbs", admin_verb_stat_data)
+ else
+ target.stat_panel.send_message("remove_admin_verbs")
+
/datum/controller/subsystem/statpanels/proc/set_SDQL2_tab(client/target)
var/list/sdql2A = list()
sdql2A[++sdql2A.len] = list("", "Access Global SDQL2 List", REF(GLOB.sdql2_vv_statobj))
@@ -293,6 +298,9 @@ SUBSYSTEM_DEF(statpanels)
set_tickets_tab(target)
return TRUE
+ if(target.stat_tab == "Admin Verbs")
+ set_admin_verb_tab(target)
+
if(!length(GLOB.sdql2_queries) && ("SDQL2" in target.panel_tabs))
target.stat_panel.send_message("remove_sdql2")
diff --git a/code/controllers/subsystem/tcgsetup.dm b/code/controllers/subsystem/tcgsetup.dm
index e52474adac5..511837cf075 100644
--- a/code/controllers/subsystem/tcgsetup.dm
+++ b/code/controllers/subsystem/tcgsetup.dm
@@ -118,10 +118,10 @@ SUBSYSTEM_DEF(trading_card_game)
///Prints all the cards names
/datum/controller/subsystem/trading_card_game/proc/printAllCards()
for(var/card_set in cached_cards)
- message_admins("Printing the [card_set] set")
+ to_chat(usr, span_admin("Printing the [card_set] set"))
for(var/card in cached_cards[card_set]["ALL"])
var/datum/card/toPrint = cached_cards[card_set]["ALL"][card]
- message_admins(toPrint.name)
+ to_chat(usr, span_admin("[toPrint.name]"))
///Checks the passed type list for missing raritys, or raritys out of bounds
/datum/controller/subsystem/trading_card_game/proc/check_cardpacks(card_pack_list)
@@ -186,5 +186,5 @@ SUBSYSTEM_DEF(trading_card_game)
if(id)
var/datum/card/template = cached_cards[pack.series]["ALL"][id]
toSend += "\nID:[id] [template.name] [(cardsByCount[id] * 100) / totalCards]% Total:[cardsByCount[id]]"
- message_admins(toSend)
+ to_chat(usr, toSend)
qdel(pack)
diff --git a/code/datums/brain_damage/imaginary_friend.dm b/code/datums/brain_damage/imaginary_friend.dm
index 3d2af7012b9..4dd2188e767 100644
--- a/code/datums/brain_damage/imaginary_friend.dm
+++ b/code/datums/brain_damage/imaginary_friend.dm
@@ -203,11 +203,11 @@
message = capitalize(message)
if(message_mods[RADIO_EXTENSION] == MODE_ADMIN)
- client?.cmd_admin_say(message)
+ SSadmin_verbs.dynamic_invoke_admin_verb(client, /mob/admin_module_holder/admin/admin_say, message)
return
if(message_mods[RADIO_EXTENSION] == MODE_DEADMIN)
- client?.dsay(message)
+ SSadmin_verbs.dynamic_invoke_admin_verb(client, /mob/admin_module_holder/game/dead_say, message)
return
if(check_emote(message, forced))
diff --git a/code/datums/components/puzzgrid.dm b/code/datums/components/puzzgrid.dm
index 0f54fce9929..4324101e3cc 100644
--- a/code/datums/components/puzzgrid.dm
+++ b/code/datums/components/puzzgrid.dm
@@ -276,10 +276,7 @@
/// Debug verb for validating that all puzzgrids can be created successfully.
/// Locked behind a verb because it's fairly slow and memory intensive.
-/client/proc/validate_puzzgrids()
- set name = "Validate Puzzgrid Config"
- set category = "Debug"
-
+ADMIN_VERB(debug, validate_puzzgrids, "", R_DEBUG)
var/line_number = 0
for (var/line in world.file2list(PUZZGRID_CONFIG))
@@ -290,16 +287,16 @@
var/line_json_decoded = safe_json_decode(line)
if (isnull(line_json_decoded))
- to_chat(src, span_warning("Line [line_number] in puzzgrids.txt is not a JSON: [line]"))
+ to_chat(usr, span_warning("Line [line_number] in puzzgrids.txt is not a JSON: [line]"))
continue
var/datum/puzzgrid/puzzgrid = new
var/populate_result = puzzgrid.populate(line_json_decoded)
if (populate_result != TRUE)
- to_chat(src, span_warning("Line [line_number] in puzzgrids.txt is not formatted correctly: [populate_result]"))
+ to_chat(usr, span_warning("Line [line_number] in puzzgrids.txt is not formatted correctly: [populate_result]"))
- to_chat(src, span_notice("Validated. If you did not see any errors, you're in the clear."))
+ to_chat(usr, span_notice("Validated. If you did not see any errors, you're in the clear."))
#undef PUZZGRID_CONFIG
#undef PUZZGRID_GROUP_COUNT
diff --git a/code/datums/keybinding/admin.dm b/code/datums/keybinding/admin.dm
index 39fa27f40c3..39165e68609 100644
--- a/code/datums/keybinding/admin.dm
+++ b/code/datums/keybinding/admin.dm
@@ -16,7 +16,7 @@
. = ..()
if(.)
return
- user.get_admin_say()
+ SSadmin_verbs.dynamic_invoke_admin_verb(user, /mob/admin_module_holder/admin/admin_say)
return TRUE
/datum/keybinding/admin/admin_ghost
@@ -30,7 +30,7 @@
. = ..()
if(.)
return
- user.admin_ghost()
+ SSadmin_verbs.dynamic_invoke_admin_verb(user, /mob/admin_module_holder/game/aghost)
return TRUE
/datum/keybinding/admin/player_panel_new
@@ -58,7 +58,7 @@
. = ..()
if(.)
return
- user.togglebuildmodeself()
+ SSadmin_verbs.dynamic_invoke_admin_verb(user, /mob/admin_module_holder/build_mode/toggle_build_mode_self)
return TRUE
/datum/keybinding/admin/stealthmode
@@ -72,7 +72,7 @@
. = ..()
if(.)
return
- user.stealth()
+ SSadmin_verbs.dynamic_invoke_admin_verb(user, /mob/admin_module_holder/admin/stealth_mode)
return TRUE
/datum/keybinding/admin/invisimin
@@ -86,7 +86,7 @@
. = ..()
if(.)
return
- user.invisimin()
+ SSadmin_verbs.dynamic_invoke_admin_verb(user, /mob/admin_module_holder/game/invisimin)
return TRUE
/datum/keybinding/admin/deadsay
@@ -100,7 +100,7 @@
. = ..()
if(.)
return
- user.get_dead_say()
+ SSadmin_verbs.dynamic_invoke_admin_verb(user, /mob/admin_module_holder/game/dead_say)
return TRUE
/datum/keybinding/admin/deadmin
@@ -114,7 +114,7 @@
. = ..()
if(.)
return
- user.deadmin()
+ SSadmin_verbs.dynamic_invoke_admin_verb(user, /mob/admin_module_holder/admin/deadmin)
return TRUE
/datum/keybinding/admin/readmin
diff --git a/code/datums/mind/_mind.dm b/code/datums/mind/_mind.dm
index f0fe1db1e26..dfd3aa009a9 100644
--- a/code/datums/mind/_mind.dm
+++ b/code/datums/mind/_mind.dm
@@ -221,7 +221,7 @@
if(!istype(to_vv))
to_chat(usr, span_warning("Invalid antagonist ref to be vv'd."))
return
- usr.client?.debug_variables(to_vv)
+ SSadmin_verbs.dynamic_invoke_admin_verb(usr.client, /mob/admin_module_holder/debug/view_variables, to_vv)
if (href_list["role_edit"])
var/new_role = input("Select new role", "Assigned role", assigned_role.title) as null|anything in sort_list(SSjob.name_occupations)
diff --git a/code/datums/station_traits/admin_panel.dm b/code/datums/station_traits/admin_panel.dm
index 02eca48b54f..e81f1455068 100644
--- a/code/datums/station_traits/admin_panel.dm
+++ b/code/datums/station_traits/admin_panel.dm
@@ -1,9 +1,9 @@
/// Opens the station traits admin panel
-/datum/admins/proc/station_traits_panel()
- set name = "Modify Station Traits"
- set category = "Admin.Events"
- var/static/datum/station_traits_panel/station_traits_panel = new
+ADMIN_VERB(events, modify_station_traits, "", R_FUN)
+ var/static/datum/station_traits_panel/station_traits_panel
+ if(!station_traits_panel)
+ station_traits_panel = new
station_traits_panel.ui_interact(usr)
/datum/station_traits_panel
diff --git a/code/datums/weakrefs.dm b/code/datums/weakrefs.dm
index 27bd5d94330..f78019c3735 100644
--- a/code/datums/weakrefs.dm
+++ b/code/datums/weakrefs.dm
@@ -105,4 +105,4 @@
return
var/datum/R = resolve()
if(R)
- usr.client.debug_variables(R)
+ SSadmin_verbs.dynamic_invoke_admin_verb(usr.client, /mob/admin_module_holder/debug/view_variables, R)
diff --git a/code/game/atoms.dm b/code/game/atoms.dm
index 9ac44b4460d..f229b39a58c 100644
--- a/code/game/atoms.dm
+++ b/code/game/atoms.dm
@@ -1283,11 +1283,11 @@
log_admin("[key_name(usr)] has added [amount] units of [chosen_id] to [src]")
message_admins(span_notice("[key_name(usr)] has added [amount] units of [chosen_id] to [src]"))
- if(href_list[VV_HK_TRIGGER_EXPLOSION] && check_rights(R_FUN))
- usr.client.cmd_admin_explosion(src)
+ if(href_list[VV_HK_TRIGGER_EXPLOSION])
+ SSadmin_verbs.dynamic_invoke_admin_verb(usr, /mob/admin_module_holder/game/explosion, src)
- if(href_list[VV_HK_TRIGGER_EMP] && check_rights(R_FUN))
- usr.client.cmd_admin_emp(src)
+ if(href_list[VV_HK_TRIGGER_EMP])
+ SSadmin_verbs.dynamic_invoke_admin_verb(usr, /mob/admin_module_holder/game/emp, src)
if(href_list[VV_HK_SHOW_HIDDENPRINTS] && check_rights(R_ADMIN))
usr.client.cmd_show_hiddenprints(src)
diff --git a/code/game/gamemodes/dynamic/dynamic_simulations.dm b/code/game/gamemodes/dynamic/dynamic_simulations.dm
index 63da54becee..7c64859c3ee 100644
--- a/code/game/gamemodes/dynamic/dynamic_simulations.dm
+++ b/code/game/gamemodes/dynamic/dynamic_simulations.dm
@@ -68,10 +68,8 @@
/// Optional, force this threat level instead of picking randomly through the lorentz distribution
var/forced_threat_level
-/client/proc/run_dynamic_simulations()
- set name = "Run Dynamic Simulations"
- set category = "Debug"
-
+#ifdef TESTING
+ADMIN_VERB(debug, run_dynamic_simulations, "", R_DEBUG)
var/simulations = input(usr, "Enter number of simulations") as num
var/roundstart_players = input(usr, "Enter number of round start players") as num
var/forced_threat_level = input(usr, "Enter forced threat level, if you want one") as num | null
@@ -100,6 +98,7 @@
message_admins("Writing file...")
WRITE_FILE(file("[GLOB.log_directory]/dynamic_simulations.json"), json_encode(outputs))
message_admins("Writing complete.")
+#endif
/proc/export_dynamic_json_of(ruleset_list)
var/list/export = list()
diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm
index 67c2ecae9c1..8d8aa1dceff 100644
--- a/code/game/objects/objs.dm
+++ b/code/game/objects/objs.dm
@@ -237,8 +237,7 @@ GLOBAL_LIST_EMPTY(objects_by_id_tag)
if(!(. = ..()))
return
if(href_list[VV_HK_OSAY])
- if(check_rights(R_FUN, FALSE))
- usr.client.object_say(src)
+ return // AVD TODO
if(href_list[VV_HK_MASS_DEL_TYPE])
if(check_rights(R_DEBUG|R_SERVER))
diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm
index 33431a5783b..c189a398b55 100644
--- a/code/modules/admin/admin.dm
+++ b/code/modules/admin/admin.dm
@@ -75,14 +75,7 @@
log_admin("[key_name(usr)] spawned [amount] x [chosen] at [AREACOORD(usr)]")
SSblackbox.record_feedback("tally", "admin_verb", 1, "Spawn Atom") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
-/datum/admins/proc/podspawn_atom(object as text)
- set category = "Debug"
- set desc = "(atom path) Spawn an atom via supply drop"
- set name = "Podspawn"
-
- if(!check_rights(R_SPAWN))
- return
-
+ADMIN_VERB(debug, podspawn_atom, "Spawn an atom typepath via supply drop", R_SPAWN, object as text)
var/chosen = pick_closest_path(object)
if(!chosen)
return
@@ -98,27 +91,22 @@
//we need to set the admin spawn flag for the spawned items so we do it outside of the podspawn proc
var/atom/A = new chosen(pod)
A.flags_1 |= ADMIN_SPAWNED_1
-
log_admin("[key_name(usr)] pod-spawned [chosen] at [AREACOORD(usr)]")
- SSblackbox.record_feedback("tally", "admin_verb", 1, "Podspawn Atom") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
-
-/datum/admins/proc/spawn_cargo(object as text)
- set category = "Debug"
- set desc = "(atom path) Spawn a cargo crate"
- set name = "Spawn Cargo"
-
- if(!check_rights(R_SPAWN))
- return
+ADMIN_VERB(debug, spawn_cargo_crate, "Spawn a cargo crate", R_SPAWN, object as text)
var/chosen = pick_closest_path(object, make_types_fancy(subtypesof(/datum/supply_pack)))
if(!chosen)
return
var/datum/supply_pack/S = new chosen
S.admin_spawned = TRUE
S.generate(get_turf(usr))
-
log_admin("[key_name(usr)] spawned cargo pack [chosen] at [AREACOORD(usr)]")
- SSblackbox.record_feedback("tally", "admin_verb", 1, "Spawn Cargo") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
+
+ADMIN_VERB(debug, toggle_tinted_welding_helmets, "Reduces view range when wearing welding helmets", R_DEBUG)
+ GLOB.tinted_weldhelh = !GLOB.tinted_weldhelh
+ to_chat(world, span_bold("Welding Helmet tinting has been [(GLOB.tinted_weldhelh ? "enabled" : "disabled")]"))
+ log_admin("[key_name(usr)] toggled tinted_weldhelh.")
+ message_admins("[key_name_admin(usr)] toggled tinted_weldhelh.")
/datum/admins/proc/dynamic_mode_options(mob/user)
var/dat = {"
@@ -143,9 +131,7 @@
user << browse(dat, "window=dyn_mode_options;size=900x650")
-/datum/admins/proc/create_or_modify_area()
- set category = "Debug"
- set name = "Create or modify area"
+ADMIN_VERB(debug, create_or_modify_area, "", R_DEBUG)
create_area(usr)
//Kicks all the clients currently in the lobby. The second parameter (kick_only_afk) determins if an is_afk() check is ran, or if all clients are kicked
diff --git a/code/modules/admin/admin_fax_panel.dm b/code/modules/admin/admin_fax_panel.dm
index e4126d89c5d..af7fb248958 100644
--- a/code/modules/admin/admin_fax_panel.dm
+++ b/code/modules/admin/admin_fax_panel.dm
@@ -98,7 +98,7 @@
if("follow")
if(!isobserver(usr))
- usr.client?.admin_ghost()
+ SSadmin_verbs.dynamic_invoke_admin_verb(usr.client, /mob/admin_module_holder/game/aghost)
usr.client?.admin_follow(action_fax)
diff --git a/code/modules/admin/admin_investigate.dm b/code/modules/admin/admin_investigate.dm
index 94ea182891f..e69de29bb2d 100644
--- a/code/modules/admin/admin_investigate.dm
+++ b/code/modules/admin/admin_investigate.dm
@@ -1,67 +0,0 @@
-/atom/proc/investigate_log(message, subject)
- if(!message || !subject)
- return
- var/F = file("[GLOB.log_directory]/[subject].html")
- var/source = "[src]"
-
- if(isliving(src))
- var/mob/living/source_mob = src
- source += " ([source_mob.ckey ? source_mob.ckey : "*no key*"])"
-
- WRITE_FILE(F, "[time_stamp(format = "YYYY-MM-DD hh:mm:ss")] [REF(src)] ([x],[y],[z]) || [source] [message]
")
-
-/client/proc/investigate_show()
- set name = "Investigate"
- set category = "Admin.Game"
- if(!holder)
- return
-
- var/list/investigates = list(
- INVESTIGATE_ACCESSCHANGES,
- INVESTIGATE_ATMOS,
- INVESTIGATE_BOTANY,
- INVESTIGATE_CARGO,
- INVESTIGATE_CRAFTING,
- INVESTIGATE_DEATHS,
- INVESTIGATE_ENGINE,
- INVESTIGATE_EXPERIMENTOR,
- INVESTIGATE_GRAVITY,
- INVESTIGATE_HALLUCINATIONS,
- INVESTIGATE_HYPERTORUS,
- INVESTIGATE_PORTAL,
- INVESTIGATE_PRESENTS,
- INVESTIGATE_RADIATION,
- INVESTIGATE_RECORDS,
- INVESTIGATE_RESEARCH,
- INVESTIGATE_WIRES,
- )
-
- var/list/logs_present = list("notes, memos, watchlist")
- var/list/logs_missing = list("---")
-
- for(var/subject in investigates)
- var/temp_file = file("[GLOB.log_directory]/[subject].html")
- if(fexists(temp_file))
- logs_present += subject
- else
- logs_missing += "[subject] (empty)"
-
- var/list/combined = sort_list(logs_present) + sort_list(logs_missing)
-
- var/selected = tgui_input_list(src, "Investigate what?", "Investigation", combined)
- if(isnull(selected))
- return
- if(!(selected in combined) || selected == "---")
- return
-
- selected = replacetext(selected, " (empty)", "")
-
- if(selected == "notes, memos, watchlist" && check_rights(R_ADMIN))
- browse_messages()
- return
-
- var/F = file("[GLOB.log_directory]/[selected].html")
- if(!fexists(F))
- to_chat(src, span_danger("No [selected] logfile was found."), confidential = TRUE)
- return
- src << browse(F,"window=investigate[selected];size=800x300")
diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm
index c8aa474f15d..9ca15b2d2a9 100644
--- a/code/modules/admin/admin_verbs.dm
+++ b/code/modules/admin/admin_verbs.dm
@@ -1,304 +1,18 @@
-//admin verb groups - They can overlap if you so wish. Only one of each verb will exist in the verbs list regardless
-//the procs are cause you can't put the comments in the GLOB var define
-GLOBAL_LIST_INIT(admin_verbs_default, world.AVerbsDefault())
-GLOBAL_PROTECT(admin_verbs_default)
-/world/proc/AVerbsDefault()
- return list(
- /client/proc/cmd_admin_pm_context, /*right-click adminPM interface*/
- /client/proc/cmd_admin_pm_panel, /*admin-pm list*/
- /client/proc/cmd_admin_say, /*admin-only ooc chat*/
- /client/proc/deadmin, /*destroys our own admin datum so we can play as a regular player*/
- /client/proc/debugstatpanel,
- /client/proc/debug_variables, /*allows us to -see- the variables of any instance in the game. +VAREDIT needed to modify*/
- /client/proc/dsay, /*talk in deadchat using our ckey/fakekey*/
- /client/proc/fix_air, /*resets air in designated radius to its default atmos composition*/
- /client/proc/hide_verbs, /*hides all our adminverbs*/
- /client/proc/investigate_show, /*various admintools for investigation. Such as a singulo grief-log*/
- /client/proc/mark_datum_mapview,
- /client/proc/reestablish_db_connection, /*reattempt a connection to the database*/
- /client/proc/reload_admins,
- /client/proc/requests,
- /client/proc/secrets,
- /client/proc/stop_sounds,
- /client/proc/tag_datum_mapview,
- )
-GLOBAL_LIST_INIT(admin_verbs_admin, world.AVerbsAdmin())
-GLOBAL_PROTECT(admin_verbs_admin)
-/world/proc/AVerbsAdmin()
- return list(
-// Admin datums
- /datum/admins/proc/access_news_network, /*allows access of newscasters*/
- /datum/admins/proc/announce, /*priority announce something to all clients.*/
- /datum/admins/proc/display_tags,
- /datum/admins/proc/fishing_calculator,
- /datum/admins/proc/known_alts_panel,
- /datum/admins/proc/show_lag_switch_panel,
- /datum/admins/proc/open_borgopanel,
- /datum/admins/proc/open_shuttlepanel, /* Opens shuttle manipulator UI */
- /datum/admins/proc/paintings_manager,
- /datum/admins/proc/set_admin_notice, /*announcement all clients see when joining the server.*/
- /datum/admins/proc/show_player_panel, /*shows an interface for individual players, with various links (links require additional flags*/
- /datum/admins/proc/toggleenter, /*toggles whether people can join the current game*/
- /datum/admins/proc/toggleguests, /*toggles whether guests can join the current game*/
- /datum/admins/proc/toggleooc, /*toggles ooc on/off for everyone*/
- /datum/admins/proc/toggleoocdead, /*toggles ooc on/off for everyone who is dead*/
- /datum/admins/proc/trophy_manager,
- /datum/admins/proc/view_all_circuits,
- /datum/verbs/menu/Admin/verb/playerpanel, /* It isn't /datum/admin but it fits no less */
-// Client procs
- /client/proc/admin_call_shuttle, /*allows us to call the emergency shuttle*/
- /client/proc/admin_cancel_shuttle, /*allows us to cancel the emergency shuttle, sending it back to centcom*/
- /client/proc/admin_disable_shuttle, /*allows us to disable the emergency shuttle admin-wise so that it cannot be called*/
- /client/proc/admin_enable_shuttle, /*undoes the above*/
- /client/proc/admin_ghost, /*allows us to ghost/reenter body at will*/
- /client/proc/admin_hostile_environment, /*Allows admins to prevent the emergency shuttle from leaving, also lets admins clear hostile environments if theres one stuck*/
- /client/proc/cmd_admin_check_contents, /*displays the contents of an instance*/
- /client/proc/cmd_admin_check_player_exp, /* shows players by playtime */
- /client/proc/cmd_admin_create_centcom_report,
- /client/proc/cmd_admin_delete, /*delete an instance/object/mob/etc*/
- /client/proc/cmd_admin_direct_narrate, /*send text directly to a player with no padding. Useful for narratives and fluff-text*/
- /client/proc/cmd_admin_headset_message, /*send a message to somebody through their headset as CentCom*/
- /client/proc/cmd_admin_local_narrate, /*sends text to all mobs within view of atom*/
- /client/proc/cmd_admin_subtle_message, /*send a message to somebody as a 'voice in their head'*/
- /client/proc/cmd_admin_world_narrate, /*sends text to all players with no padding*/
- /client/proc/cmd_change_command_name,
- /client/proc/centcom_podlauncher,/*Open a window to launch a Supplypod and configure it or it's contents*/
- /client/proc/check_ai_laws, /*shows AI and borg laws*/
- /client/proc/check_antagonists, /*shows all antags*/
- /client/proc/fax_panel, /*send a paper to fax*/
- /client/proc/force_load_lazy_template,
- /client/proc/game_panel, /*game panel, allows to change game-mode etc*/
- /client/proc/Getmob, /*teleports a mob to our location*/
- /client/proc/Getkey, /*teleports a mob with a certain ckey to our location*/
- /client/proc/getserverlogs, /*for accessing server logs*/
- /client/proc/getcurrentlogs, /*for accessing server logs for the current round*/
- /client/proc/ghost_pool_protection, /*opens a menu for toggling ghost roles*/
- /client/proc/invisimin, /*allows our mob to go invisible/visible*/
- /client/proc/jumptoarea,
- /client/proc/jumptokey, /*allows us to jump to the location of a mob with a certain ckey*/
- /client/proc/jumptomob, /*allows us to jump to a specific mob*/
- /client/proc/jumptoturf, /*allows us to jump to a specific turf*/
- /client/proc/jumptocoord, /*we ghost and jump to a coordinate*/
- /client/proc/list_bombers,
- /client/proc/list_dna,
- /client/proc/list_fingerprints,
- /client/proc/list_law_changes,
- /client/proc/list_signalers,
- /client/proc/message_pda, /*send a message to somebody on PDA*/
- /client/proc/respawn_character,
- /client/proc/show_manifest,
- /client/proc/toggle_AI_interact, /*toggle admin ability to interact with machines as an AI*/
- /client/proc/toggle_combo_hud, /* toggle display of the combination pizza antag and taco sci/med/eng hud */
- /client/proc/toggle_view_range, /*changes how far we can see*/
- )
-GLOBAL_LIST_INIT(admin_verbs_ban, list(/client/proc/unban_panel, /client/proc/ban_panel, /client/proc/stickybanpanel))
-GLOBAL_PROTECT(admin_verbs_ban)
-GLOBAL_LIST_INIT(admin_verbs_sounds, list(/client/proc/play_local_sound, /client/proc/play_direct_mob_sound, /client/proc/play_sound, /client/proc/set_round_end_sound))
-GLOBAL_PROTECT(admin_verbs_sounds)
-GLOBAL_LIST_INIT(admin_verbs_fun, list(
-// Admin datums
- /datum/admins/proc/station_traits_panel,
-// Client procs
- /client/proc/admin_away,
- /client/proc/add_mob_ability,
- /client/proc/admin_change_sec_level,
- /client/proc/cinematic,
- /client/proc/cmd_admin_add_freeform_ai_law,
- /client/proc/cmd_admin_gib_self,
- /client/proc/cmd_select_equipment,
- /client/proc/command_report_footnote,
- /client/proc/delay_command_report,
- /client/proc/drop_bomb,
- /client/proc/drop_dynex_bomb,
- /client/proc/forceEvent,
- /client/proc/mass_zombie_cure,
- /client/proc/mass_zombie_infection,
- /client/proc/object_say,
- /client/proc/polymorph_all,
- /client/proc/remove_mob_ability,
- /client/proc/reset_ooc,
- /client/proc/run_weather,
- /client/proc/set_dynex_scale,
- /client/proc/set_ooc,
- /client/proc/show_tip,
- /client/proc/smite,
- /client/proc/summon_ert,
- /client/proc/toggle_nuke,
- /client/proc/toggle_random_events,
- ))
-GLOBAL_PROTECT(admin_verbs_fun)
-GLOBAL_LIST_INIT(admin_verbs_spawn, list(/datum/admins/proc/spawn_atom, /datum/admins/proc/podspawn_atom, /datum/admins/proc/spawn_cargo, /datum/admins/proc/spawn_objasmob, /client/proc/respawn_character, /datum/admins/proc/beaker_panel))
-GLOBAL_PROTECT(admin_verbs_spawn)
-GLOBAL_LIST_INIT(admin_verbs_server, world.AVerbsServer())
-GLOBAL_PROTECT(admin_verbs_server)
-/world/proc/AVerbsServer()
- return list(
-// Admin datums
- /datum/admins/proc/delay,
- /datum/admins/proc/delay_round_end,
- /datum/admins/proc/end_round,
- /datum/admins/proc/restart,
- /datum/admins/proc/startnow,
- /datum/admins/proc/toggleaban,
- /datum/admins/proc/toggleAI,
-// Client procs
- /client/proc/adminchangemap,
- /client/proc/cmd_admin_delete, /*delete an instance/object/mob/etc*/
- /client/proc/cmd_debug_del_all,
- /client/proc/cmd_debug_force_del_all,
- /client/proc/cmd_debug_hard_del_all,
- /client/proc/everyone_random,
- /client/proc/forcerandomrotate,
- /client/proc/generate_job_config,
- /client/proc/panicbunker,
- /client/proc/toggle_cdn,
- /client/proc/toggle_hub,
- /client/proc/toggle_interviews,
- /client/proc/toggle_random_events,
- )
-GLOBAL_LIST_INIT(admin_verbs_debug, world.AVerbsDebug())
-GLOBAL_PROTECT(admin_verbs_debug)
-/world/proc/AVerbsDebug()
- return list(
- #ifdef TESTING /* Keep these at the top to not make the list look fugly */
- /client/proc/check_missing_sprites,
- /client/proc/run_dynamic_simulations,
- #endif
- /proc/machine_upgrade,
- /datum/admins/proc/create_or_modify_area,
- /client/proc/adventure_manager,
- /client/proc/atmos_control,
- /client/proc/callproc,
- /client/proc/callproc_datum,
- /client/proc/check_bomb_impacts,
- /client/proc/check_timer_sources,
- /client/proc/clear_dynamic_transit,
- /client/proc/cmd_admin_debug_traitor_objectives,
- /client/proc/cmd_admin_delete,
- /client/proc/cmd_admin_list_open_jobs,
- /client/proc/cmd_admin_toggle_fov,
- /client/proc/cmd_debug_del_all,
- /client/proc/cmd_debug_force_del_all,
- /client/proc/cmd_debug_hard_del_all,
- /client/proc/cmd_debug_make_powernets,
- /client/proc/cmd_debug_mob_lists,
- /client/proc/cmd_display_del_log,
- /client/proc/cmd_display_init_log,
- /client/proc/cmd_display_overlay_log,
- /client/proc/Debug2,
- /client/proc/debug_controller,
- /client/proc/debug_hallucination_weighted_list_per_type,
- /client/proc/debug_huds,
- /client/proc/debugNatureMapGenerator,
- /client/proc/debug_plane_masters,
- /client/proc/debug_spell_requirements,
- /client/proc/display_sendmaps,
- /client/proc/enable_mapping_verbs,
- /client/proc/generate_wikichem_list,
- /client/proc/get_dynex_power, /*debug verbs for dynex explosions.*/
- /client/proc/get_dynex_range, /*debug verbs for dynex explosions.*/
- /client/proc/jump_to_ruin,
- /client/proc/load_circuit,
- /client/proc/map_template_load,
- /client/proc/map_template_upload,
- /client/proc/modify_goals,
- /client/proc/open_colorblind_test,
- /client/proc/open_lua_editor,
- /client/proc/outfit_manager,
- /client/proc/populate_world,
- /client/proc/pump_random_event,
- /client/proc/print_cards,
- /client/proc/reload_cards,
- /client/proc/reload_configuration,
- /client/proc/restart_controller,
- /client/proc/run_empty_query,
- /client/proc/SDQL2_query,
- /client/proc/set_dynex_scale,
- /client/proc/spawn_debug_full_crew,
- /client/proc/test_cardpack_distribution,
- /client/proc/test_movable_UI,
- /client/proc/test_snap_UI,
- /client/proc/toggle_cdn,
- /client/proc/toggle_medal_disable,
- /client/proc/unload_ctf,
- /client/proc/validate_cards,
- /client/proc/validate_puzzgrids,
- /client/proc/view_runtimes,
- )
-GLOBAL_LIST_INIT(admin_verbs_possess, list(/proc/possess, /proc/release))
-GLOBAL_PROTECT(admin_verbs_possess)
-GLOBAL_LIST_INIT(admin_verbs_permissions, list(/client/proc/edit_admin_permissions))
-GLOBAL_PROTECT(admin_verbs_permissions)
-GLOBAL_LIST_INIT(admin_verbs_poll, list(/client/proc/poll_panel))
-GLOBAL_PROTECT(admin_verbs_poll)
-
/client/proc/add_admin_verbs()
- if(holder)
- control_freak = CONTROL_FREAK_SKIN | CONTROL_FREAK_MACROS
-
- var/rights = holder.rank_flags()
- add_verb(src, GLOB.admin_verbs_default)
- if(rights & R_BUILD)
- add_verb(src, /client/proc/togglebuildmodeself)
- if(rights & R_ADMIN)
- add_verb(src, GLOB.admin_verbs_admin)
- if(rights & R_BAN)
- add_verb(src, GLOB.admin_verbs_ban)
- if(rights & R_FUN)
- add_verb(src, GLOB.admin_verbs_fun)
- if(rights & R_SERVER)
- add_verb(src, GLOB.admin_verbs_server)
- if(rights & R_DEBUG)
- add_verb(src, GLOB.admin_verbs_debug)
- if(rights & R_POSSESS)
- add_verb(src, GLOB.admin_verbs_possess)
- if(rights & R_PERMISSIONS)
- add_verb(src, GLOB.admin_verbs_permissions)
- if(rights & R_STEALTH)
- add_verb(src, /client/proc/stealth)
- if(rights & R_ADMIN)
- add_verb(src, GLOB.admin_verbs_poll)
- if(rights & R_SOUND)
- add_verb(src, GLOB.admin_verbs_sounds)
- if(CONFIG_GET(string/invoke_youtubedl))
- add_verb(src, /client/proc/play_web_sound)
- if(rights & R_SPAWN)
- add_verb(src, GLOB.admin_verbs_spawn)
+ if(!holder)
+ CRASH("called add_admin_verbs on a client without a holder?")
+ control_freak = CONTROL_FREAK_SKIN | CONTROL_FREAK_MACROS
+ SSadmin_verbs.assosciate_admin(src)
/client/proc/remove_admin_verbs()
- remove_verb(src, list(
- GLOB.admin_verbs_default,
- /client/proc/togglebuildmodeself,
- GLOB.admin_verbs_admin,
- GLOB.admin_verbs_ban,
- GLOB.admin_verbs_fun,
- GLOB.admin_verbs_server,
- GLOB.admin_verbs_debug,
- GLOB.admin_verbs_possess,
- GLOB.admin_verbs_permissions,
- /client/proc/stealth,
- GLOB.admin_verbs_poll,
- GLOB.admin_verbs_sounds,
- /client/proc/play_web_sound,
- GLOB.admin_verbs_spawn,
- /*Debug verbs added by "show debug verbs"*/
- GLOB.admin_verbs_debug_mapping,
- /client/proc/disable_mapping_verbs,
- /client/proc/readmin
- ))
+ SSadmin_verbs.deassosciate_admin(src)
-/client/proc/hide_verbs()
- set name = "Adminverbs - Hide All"
- set category = "Admin"
+ADMIN_VERB(admin, hide_all_verbs, "Hide all of your Admin Verbs", NONE)
+ usr.client.remove_admin_verbs()
+ add_verb(usr.client, /client/proc/show_verbs)
+ to_chat(usr, span_admin("Almost all of your adminverbs have been hidden."))
- remove_admin_verbs()
- add_verb(src, /client/proc/show_verbs)
-
- to_chat(src, span_interface("Almost all of your adminverbs have been hidden."), confidential = TRUE)
- SSblackbox.record_feedback("tally", "admin_verb", 1, "Hide All Adminverbs") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
- return
-
-/client/proc/show_verbs()
+/client/proc/show_verbs() // This is not an ADMIN_VERB for a reason
set name = "Adminverbs - Show"
set category = "Admin"
@@ -308,143 +22,75 @@ GLOBAL_PROTECT(admin_verbs_poll)
to_chat(src, span_interface("All of your adminverbs are now visible."), confidential = TRUE)
SSblackbox.record_feedback("tally", "admin_verb", 1, "Show Adminverbs") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
-
-
-
-/client/proc/admin_ghost()
- set category = "Admin.Game"
- set name = "Aghost"
- if(!holder)
+ADMIN_VERB(game, aghost, "Observe without leaving the game", R_ADMIN)
+ if(isnewplayer(usr))
+ to_chat(usr, span_red("Error: AGhost: Cannot admin-ghost wile in the lobby. Join or Observe first."))
return
- . = TRUE
- if(isobserver(mob))
- //re-enter
- var/mob/dead/observer/ghost = mob
- if(!ghost.mind || !ghost.mind.current) //won't do anything if there is no body
- return FALSE
- if(!ghost.can_reenter_corpse)
+
+ if(isobserver(usr))
+ var/mob/dead/observer/admin_ghost = usr
+ if(!admin_ghost.mind?.current)
+ to_chat(usr, span_red("Error: AGhost: You do not have a body to return to!"))
+ return
+ if(!admin_ghost.can_reenter_corpse)
log_admin("[key_name(usr)] re-entered corpse")
message_admins("[key_name_admin(usr)] re-entered corpse")
- ghost.can_reenter_corpse = 1 //force re-entering even when otherwise not possible
- ghost.reenter_corpse()
- SSblackbox.record_feedback("tally", "admin_verb", 1, "Admin Reenter") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
- else if(isnewplayer(mob))
- to_chat(src, "Error: Aghost: Can't admin-ghost whilst in the lobby. Join or Observe first.", confidential = TRUE)
- return FALSE
+ admin_ghost.can_reenter_corpse = TRUE
+ admin_ghost.reenter_corpse()
+ return
+
+ log_admin("[key_name(usr)] admin ghosted.")
+ message_admins("[key_name_admin(usr)] admin ghosted.")
+ usr.ghostize(TRUE)
+ if(usr && !usr.key)
+ usr.key = "@[key]" // If the key starts with '@' it designates an admin ghost
+
+ADMIN_VERB(game, invisimin, "Toggles ghost-like invisibility", R_ADMIN)
+ if(initial(usr.invisibility) == INVISIBILITY_OBSERVER)
+ to_chat(usr, span_boldannounce("Invisimin toggle failed. You are already an invisible mob like a ghost."), confidential = TRUE)
+ return
+ if(usr.invisibility == INVISIBILITY_OBSERVER)
+ usr.invisibility = initial(usr.invisibility)
+ to_chat(usr, span_boldannounce("Invisimin off. Invisibility reset."), confidential = TRUE)
else
- //ghostize
- log_admin("[key_name(usr)] admin ghosted.")
- message_admins("[key_name_admin(usr)] admin ghosted.")
- var/mob/body = mob
- body.ghostize(TRUE)
- init_verbs()
- if(body && !body.key)
- body.key = "@[key]" //Haaaaaaaack. But the people have spoken. If it breaks; blame adminbus
- SSblackbox.record_feedback("tally", "admin_verb", 1, "Admin Ghost") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
+ usr.invisibility = INVISIBILITY_OBSERVER
+ to_chat(usr, span_adminnotice("Invisimin on. You are now as invisible as a ghost."), confidential = TRUE)
-/client/proc/invisimin()
- set name = "Invisimin"
- set category = "Admin.Game"
- set desc = "Toggles ghost-like invisibility (Don't abuse this)"
- if(holder && mob)
- if(initial(mob.invisibility) == INVISIBILITY_OBSERVER)
- to_chat(mob, span_boldannounce("Invisimin toggle failed. You are already an invisible mob like a ghost."), confidential = TRUE)
- return
- if(mob.invisibility == INVISIBILITY_OBSERVER)
- mob.invisibility = initial(mob.invisibility)
- to_chat(mob, span_boldannounce("Invisimin off. Invisibility reset."), confidential = TRUE)
- else
- mob.invisibility = INVISIBILITY_OBSERVER
- to_chat(mob, span_adminnotice("Invisimin on. You are now as invisible as a ghost."), confidential = TRUE)
+ADMIN_VERB(game, check_antagonists, "", R_ADMIN)
+ usr.client.holder.check_antagonists()
+ log_admin("[key_name(usr)] checked antagonists.") //for tsar~ get a room you two
+ if(!isobserver(usr) && SSticker.HasRoundStarted())
+ message_admins("[key_name_admin(usr)] checked antagonists.")
-/client/proc/check_antagonists()
- set name = "Check Antagonists"
- set category = "Admin.Game"
- if(holder)
- holder.check_antagonists()
- log_admin("[key_name(usr)] checked antagonists.") //for tsar~
- if(!isobserver(usr) && SSticker.HasRoundStarted())
- message_admins("[key_name_admin(usr)] checked antagonists.")
- SSblackbox.record_feedback("tally", "admin_verb", 1, "Check Antagonists") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
+ADMIN_VERB(game, list_bombers, "", R_ADMIN)
+ usr.client.holder.list_bombers()
-/client/proc/list_bombers()
- set name = "List Bombers"
- set category = "Admin.Game"
- if(!holder)
- return
- holder.list_bombers()
- SSblackbox.record_feedback("tally", "admin_verb", 1, "List Bombers") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
+ADMIN_VERB(game, list_signalers, "", R_ADMIN)
+ usr.client.holder.list_signalers()
-/client/proc/list_signalers()
- set name = "List Signalers"
- set category = "Admin.Game"
- if(!holder)
- return
- holder.list_signalers()
- SSblackbox.record_feedback("tally", "admin_verb", 1, "List Signalers") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
+ADMIN_VERB(game, list_law_changes, "", R_ADMIN)
+ usr.client.holder.list_law_changes()
-/client/proc/list_law_changes()
- set name = "List Law Changes"
- set category = "Debug"
- if(!holder)
- return
- holder.list_law_changes()
- SSblackbox.record_feedback("tally", "admin_verb", 1, "List Law Changes") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
+ADMIN_VERB(game, show_manifest, "", R_ADMIN)
+ usr.client.holder.show_manifest()
-/client/proc/show_manifest()
- set name = "Show Manifest"
- set category = "Debug"
- if(!holder)
- return
- holder.show_manifest()
- SSblackbox.record_feedback("tally", "admin_verb", 1, "Show Manifest") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
+ADMIN_VERB(game, list_dna, "", R_ADMIN)
+ usr.client.holder.list_dna()
-/client/proc/list_dna()
- set name = "List DNA"
- set category = "Debug"
- if(!holder)
- return
- holder.list_dna()
- SSblackbox.record_feedback("tally", "admin_verb", 1, "List DNA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
+ADMIN_VERB(game, list_fingerprints, "", R_ADMIN)
+ usr.client.holder.list_fingerprints()
-/client/proc/list_fingerprints()
- set name = "List Fingerprints"
- set category = "Debug"
- if(!holder)
- return
- holder.list_fingerprints()
- SSblackbox.record_feedback("tally", "admin_verb", 1, "List Fingerprints") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
+ADMIN_VERB(admin, banning_panel, "", R_BAN)
+ usr.client.holder.ban_panel()
-/client/proc/ban_panel()
- set name = "Banning Panel"
- set category = "Admin"
- if(!check_rights(R_BAN))
- return
- holder.ban_panel()
- SSblackbox.record_feedback("tally", "admin_verb", 1, "Banning Panel") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
+ADMIN_VERB(admin, unbanning_panel, "", R_BAN)
+ usr.client.holder.unban_panel()
-/client/proc/unban_panel()
- set name = "Unbanning Panel"
- set category = "Admin"
- if(!check_rights(R_BAN))
- return
- holder.unban_panel()
- SSblackbox.record_feedback("tally", "admin_verb", 1, "Unbanning Panel") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
+ADMIN_VERB(game, game_panel, "", NONE)
+ usr.client.holder.Game()
-/client/proc/game_panel()
- set name = "Game Panel"
- set category = "Admin.Game"
- if(holder)
- holder.Game()
- SSblackbox.record_feedback("tally", "admin_verb", 1, "Game Panel") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
-
-/client/proc/poll_panel()
- set name = "Server Poll Management"
- set category = "Admin"
- if(!check_rights(R_POLL))
- return
- holder.poll_list_panel()
- SSblackbox.record_feedback("tally", "admin_verb", 1, "Server Poll Management") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
+ADMIN_VERB(admin, server_poll_management, "", R_POLL)
+ usr.client.holder.poll_list_panel()
/// Returns this client's stealthed ckey
/client/proc/getStealthKey()
@@ -479,18 +125,11 @@ GLOBAL_PROTECT(admin_verbs_poll)
/client/proc/createStealthKey()
GLOB.stealthminID["[ckey]"] = generateStealthCkey()
-/client/proc/stealth()
- set category = "Admin"
- set name = "Stealth Mode"
- if(!holder)
- return
-
- if(holder.fakekey)
- disable_stealth_mode()
+ADMIN_VERB(admin, stealth_mode, "Makes you unable to be seen through most means", R_STEALTH)
+ if(usr.client.holder.fakekey)
+ usr.client.disable_stealth_mode()
else
- enable_stealth_mode()
-
- SSblackbox.record_feedback("tally", "admin_verb", 1, "Stealth Mode") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
+ usr.client.enable_stealth_mode()
#define STEALTH_MODE_TRAIT "stealth_mode"
@@ -533,26 +172,22 @@ GLOBAL_PROTECT(admin_verbs_poll)
#undef STEALTH_MODE_TRAIT
-/client/proc/drop_bomb()
- set category = "Admin.Fun"
- set name = "Drop Bomb"
- set desc = "Cause an explosion of varying strength at your location."
-
+ADMIN_VERB(fun, drop_bomb, "Cause an explosion of varying strength at your location", R_FUN)
var/list/choices = list("Small Bomb (1, 2, 3, 3)", "Medium Bomb (2, 3, 4, 4)", "Big Bomb (3, 5, 7, 5)", "Maxcap", "Custom Bomb")
- var/choice = tgui_input_list(src, "What size explosion would you like to produce? NOTE: You can do all this rapidly and in an IC manner (using cruise missiles!) with the Config/Launch Supplypod verb. WARNING: These ignore the maxcap", "Drop Bomb", choices)
+ var/choice = tgui_input_list(usr, "What size explosion would you like to produce? NOTE: You can do all this rapidly and in an IC manner (using cruise missiles!) with the Config/Launch Supplypod verb. WARNING: These ignore the maxcap", "Drop Bomb", choices)
if(isnull(choice))
return
- var/turf/epicenter = mob.loc
+ var/turf/epicenter = get_turf(usr)
switch(choice)
if("Small Bomb (1, 2, 3, 3)")
- explosion(epicenter, devastation_range = 1, heavy_impact_range = 2, light_impact_range = 3, flash_range = 3, adminlog = TRUE, ignorecap = TRUE, explosion_cause = mob)
+ explosion(epicenter, devastation_range = 1, heavy_impact_range = 2, light_impact_range = 3, flash_range = 3, adminlog = TRUE, ignorecap = TRUE, explosion_cause = usr)
if("Medium Bomb (2, 3, 4, 4)")
- explosion(epicenter, devastation_range = 2, heavy_impact_range = 3, light_impact_range = 4, flash_range = 4, adminlog = TRUE, ignorecap = TRUE, explosion_cause = mob)
+ explosion(epicenter, devastation_range = 2, heavy_impact_range = 3, light_impact_range = 4, flash_range = 4, adminlog = TRUE, ignorecap = TRUE, explosion_cause = usr)
if("Big Bomb (3, 5, 7, 5)")
- explosion(epicenter, devastation_range = 3, heavy_impact_range = 5, light_impact_range = 7, flash_range = 5, adminlog = TRUE, ignorecap = TRUE, explosion_cause = mob)
+ explosion(epicenter, devastation_range = 3, heavy_impact_range = 5, light_impact_range = 7, flash_range = 5, adminlog = TRUE, ignorecap = TRUE, explosion_cause = usr)
if("Maxcap")
- explosion(epicenter, devastation_range = GLOB.MAX_EX_DEVESTATION_RANGE, heavy_impact_range = GLOB.MAX_EX_HEAVY_RANGE, light_impact_range = GLOB.MAX_EX_LIGHT_RANGE, flash_range = GLOB.MAX_EX_FLASH_RANGE, adminlog = TRUE, ignorecap = TRUE, explosion_cause = mob)
+ explosion(epicenter, devastation_range = GLOB.MAX_EX_DEVESTATION_RANGE, heavy_impact_range = GLOB.MAX_EX_HEAVY_RANGE, light_impact_range = GLOB.MAX_EX_LIGHT_RANGE, flash_range = GLOB.MAX_EX_FLASH_RANGE, adminlog = TRUE, ignorecap = TRUE, explosion_cause = usr)
if("Custom Bomb")
var/range_devastation = input("Devastation range (in tiles):") as null|num
if(range_devastation == null)
@@ -569,118 +204,85 @@ GLOBAL_PROTECT(admin_verbs_poll)
if(range_devastation > GLOB.MAX_EX_DEVESTATION_RANGE || range_heavy > GLOB.MAX_EX_HEAVY_RANGE || range_light > GLOB.MAX_EX_LIGHT_RANGE || range_flash > GLOB.MAX_EX_FLASH_RANGE)
if(tgui_alert(usr, "Bomb is bigger than the maxcap. Continue?",,list("Yes","No")) != "Yes")
return
- epicenter = mob.loc //We need to reupdate as they may have moved again
- explosion(epicenter, devastation_range = range_devastation, heavy_impact_range = range_heavy, light_impact_range = range_light, flash_range = range_flash, adminlog = TRUE, ignorecap = TRUE, explosion_cause = mob)
+ epicenter = get_turf(usr) //We need to reupdate as they may have moved again
+ explosion(epicenter, devastation_range = range_devastation, heavy_impact_range = range_heavy, light_impact_range = range_light, flash_range = range_flash, adminlog = TRUE, ignorecap = TRUE, explosion_cause = usr)
message_admins("[ADMIN_LOOKUPFLW(usr)] creating an admin explosion at [epicenter.loc].")
log_admin("[key_name(usr)] created an admin explosion at [epicenter.loc].")
- SSblackbox.record_feedback("tally", "admin_verb", 1, "Drop Bomb") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
-/client/proc/drop_dynex_bomb()
- set category = "Admin.Fun"
- set name = "Drop DynEx Bomb"
- set desc = "Cause an explosion of varying strength at your location."
-
- var/ex_power = input("Explosive Power:") as null|num
- var/turf/epicenter = mob.loc
+ADMIN_VERB(fun, drop_dynex_bomb, "Cause an explosion of varting strength at your location", R_FUN)
+ var/ex_power = input(usr, "Explosive Power:") as null|num
+ var/turf/epicenter = get_turf(usr)
if(ex_power && epicenter)
dyn_explosion(epicenter, ex_power)
message_admins("[ADMIN_LOOKUPFLW(usr)] creating an admin explosion at [epicenter.loc].")
log_admin("[key_name(usr)] created an admin explosion at [epicenter.loc].")
- SSblackbox.record_feedback("tally", "admin_verb", 1, "Drop Dynamic Bomb") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
-/client/proc/get_dynex_range()
- set category = "Debug"
- set name = "Get DynEx Range"
- set desc = "Get the estimated range of a bomb, using explosive power."
-
- var/ex_power = input("Explosive Power:") as null|num
+ADMIN_VERB(debug, get_dynex_range, "Get the estimated range of a bomb, using explosive power", R_FUN)
+ var/ex_power = input(usr, "Explosive Power:") as null|num
if (isnull(ex_power))
return
var/range = round((2 * ex_power)**GLOB.DYN_EX_SCALE)
- to_chat(usr, "Estimated Explosive Range: (Devastation: [round(range*0.25)], Heavy: [round(range*0.5)], Light: [round(range)])", confidential = TRUE)
+ to_chat(usr, "Estimated Explosive Range: (Devastation: [round(range*0.25)], Heavy: [round(range*0.5)], Light: [round(range)])")
-/client/proc/get_dynex_power()
- set category = "Debug"
- set name = "Get DynEx Power"
- set desc = "Get the estimated required power of a bomb, to reach a specific range."
-
- var/ex_range = input("Light Explosion Range:") as null|num
+ADMIN_VERB(debug, get_dynex_power, "Get the estimated power of a bomb, to reach the specific range", R_FUN)
+ var/ex_range = input(usr, "Light Explosion Range:") as null|num
if (isnull(ex_range))
return
var/power = (0.5 * ex_range)**(1/GLOB.DYN_EX_SCALE)
- to_chat(usr, "Estimated Explosive Power: [power]", confidential = TRUE)
-
-/client/proc/set_dynex_scale()
- set category = "Debug"
- set name = "Set DynEx Scale"
- set desc = "Set the scale multiplier of dynex explosions. The default is 0.5."
+ to_chat(usr, "Estimated Explosive Power: [power]")
+ADMIN_VERB(debug, set_dynex_scale, "Set the scale multiplier on dynex explosions. Default of 0.5", R_FUN)
var/ex_scale = input("New DynEx Scale:") as null|num
- if(!ex_scale)
+ if(isnull(ex_scale))
return
GLOB.DYN_EX_SCALE = ex_scale
log_admin("[key_name(usr)] has modified Dynamic Explosion Scale: [ex_scale]")
message_admins("[key_name_admin(usr)] has modified Dynamic Explosion Scale: [ex_scale]")
-/client/proc/atmos_control()
- set name = "Atmos Control Panel"
- set category = "Debug"
- if(!check_rights(R_DEBUG))
- return
- SSair.ui_interact(mob)
+ADMIN_VERB(debug, atmos_control_panel, "", R_DEBUG)
+ SSair.ui_interact(usr)
-/client/proc/reload_cards()
- set name = "Reload Cards"
- set category = "Debug"
- if(!check_rights(R_DEBUG))
- return
+ADMIN_VERB(trading_card_game, reload_cards, "", R_DEBUG)
if(!SStrading_card_game.loaded)
- message_admins("The card subsystem is not currently loaded")
+ to_chat(usr, span_admin("The card subsystem is not currently loaded!"))
return
+ message_admins("[key_name_admin(usr)] manually reloaded SStrading_card_game.")
SStrading_card_game.reloadAllCardFiles()
-/client/proc/validate_cards()
- set name = "Validate Cards"
- set category = "Debug"
- if(!check_rights(R_DEBUG))
- return
+ADMIN_VERB(trading_card_game, validate_cards, "", R_DEBUG)
if(!SStrading_card_game.loaded)
- message_admins("The card subsystem is not currently loaded")
+ to_chat(usr, span_admin("The card subsystem is not currently loaded!"))
return
+
var/message = SStrading_card_game.check_cardpacks(SStrading_card_game.card_packs)
message += SStrading_card_game.check_card_datums()
if(message)
- message_admins(message)
+ to_chat(usr, span_admin(message))
else
- message_admins("No errors found in card rarities or overrides.")
+ to_chat(usr, span_admin("No errors found in card rarities or overrides."))
-/client/proc/test_cardpack_distribution()
- set name = "Test Cardpack Distribution"
- set category = "Debug"
- if(!check_rights(R_DEBUG))
- return
+ADMIN_VERB(trading_card_game, test_cardpack_distribution, "", R_DEBUG)
if(!SStrading_card_game.loaded)
- message_admins("The card subsystem is not currently loaded")
+ to_chat(usr, span_admin("The card subsystem is not currently loaded!"))
return
+
var/pack = tgui_input_list(usr, "Which pack should we test?", "You fucked it didn't you", sort_list(SStrading_card_game.card_packs))
if(!pack)
return
+
var/batch_count = tgui_input_number(usr, "How many times should we open it?", "Don't worry, I understand")
var/batch_size = tgui_input_number(usr, "How many cards per batch?", "I hope you remember to check the validation")
var/guar = tgui_input_number(usr, "Should we use the pack's guaranteed rarity? If so, how many?", "We've all been there. Man you should have seen the old system")
-
SStrading_card_game.check_card_distribution(pack, batch_size, batch_count, guar)
-/client/proc/print_cards()
- set name = "Print Cards"
- set category = "Debug"
+ADMIN_VERB(trading_card_game, print_cards, "", R_DEBUG)
+ if(!SStrading_card_game.loaded)
+ to_chat(usr, span_admin("The card subsystem is not currently loaded!"))
+ return
+
SStrading_card_game.printAllCards()
-/client/proc/give_spell(mob/spell_recipient in GLOB.mob_list)
- set category = "Admin.Fun"
- set name = "Give Spell"
- set desc = "Gives a spell to a mob."
-
+ADMIN_VERB(fun, give_mob_spell, "", R_FUN, mob/spell_recipient in GLOB.mob_list)
var/which = tgui_alert(usr, "Chose by name or by type path?", "Chose option", list("Name", "Typepath"))
if(!which)
return
@@ -712,26 +314,18 @@ GLOBAL_PROTECT(admin_verbs_poll)
to_chat(usr, span_warning("The intended spell recipient no longer exists."))
return
- SSblackbox.record_feedback("tally", "admin_verb", 1, "Give Spell") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
log_admin("[key_name(usr)] gave [key_name(spell_recipient)] the spell [chosen_spell][robeless ? " (Forced robeless)" : ""].")
message_admins("[key_name_admin(usr)] gave [key_name_admin(spell_recipient)] the spell [chosen_spell][robeless ? " (Forced robeless)" : ""].")
-
var/datum/action/cooldown/spell/new_spell = new spell_path(spell_recipient.mind || spell_recipient)
-
if(robeless)
new_spell.spell_requirements &= ~SPELL_REQUIRES_WIZARD_GARB
new_spell.Grant(spell_recipient)
-
if(!spell_recipient.mind)
to_chat(usr, span_userdanger("Spells given to mindless mobs will belong to the mob and not their mind, \
and as such will not be transferred if their mind changes body (Such as from Mindswap)."))
-/client/proc/remove_spell(mob/removal_target in GLOB.mob_list)
- set category = "Admin.Fun"
- set name = "Remove Spell"
- set desc = "Remove a spell from the selected mob."
-
+ADMIN_VERB(fun, remove_spell, "", R_FUN, mob/removal_target in GLOB.mob_list)
var/list/target_spell_list = list()
for(var/datum/action/cooldown/spell/spell in removal_target.actions)
target_spell_list[spell.name] = spell
@@ -749,65 +343,57 @@ GLOBAL_PROTECT(admin_verbs_poll)
qdel(to_remove)
log_admin("[key_name(usr)] removed the spell [chosen_spell] from [key_name(removal_target)].")
message_admins("[key_name_admin(usr)] removed the spell [chosen_spell] from [key_name_admin(removal_target)].")
- SSblackbox.record_feedback("tally", "admin_verb", 1, "Remove Spell") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
-/client/proc/give_disease(mob/living/T in GLOB.mob_living_list)
- set category = "Admin.Fun"
- set name = "Give Disease"
- set desc = "Gives a Disease to a mob."
- if(!istype(T))
- to_chat(src, span_notice("You can only give a disease to a mob of type /mob/living."), confidential = TRUE)
+ADMIN_VERB(fun, give_disease, "Give Disease", R_FUN, mob/living/victim in GLOB.mob_living_list)
+ var/datum/disease/disease_type = input(usr, "Choose the disease to give to that guy", "ACHOO") as null|anything in sort_list(SSdisease.diseases, GLOBAL_PROC_REF(cmp_typepaths_asc))
+ if(!disease_type)
return
- var/datum/disease/D = input("Choose the disease to give to that guy", "ACHOO") as null|anything in sort_list(SSdisease.diseases, GLOBAL_PROC_REF(cmp_typepaths_asc))
- if(!D)
- return
- T.ForceContractDisease(new D, FALSE, TRUE)
- SSblackbox.record_feedback("tally", "admin_verb", 1, "Give Disease") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
- log_admin("[key_name(usr)] gave [key_name(T)] the disease [D].")
- message_admins(span_adminnotice("[key_name_admin(usr)] gave [key_name_admin(T)] the disease [D]."))
+ victim.ForceContractDisease(new disease_type, FALSE, TRUE)
-/client/proc/object_say(obj/O in world)
- set category = "Admin.Events"
- set name = "OSay"
- set desc = "Makes an object say something."
+ log_admin("[key_name(usr)] gave [key_name(victim)] the disease [disease_type].")
+ message_admins(span_adminnotice("[key_name_admin(usr)] gave [key_name_admin(victim)] the disease [disease_type]."))
+
+ADMIN_CONTEXT_ENTRY(context_object_say, "Object Say", R_FUN, obj/target in world)
var/message = tgui_input_text(usr, "What do you want the message to be?", "Make Sound", encode = FALSE)
if(!message)
return
- O.say(message, sanitize = FALSE)
- log_admin("[key_name(usr)] made [O] at [AREACOORD(O)] say \"[message]\"")
- message_admins(span_adminnotice("[key_name_admin(usr)] made [O] at [AREACOORD(O)]. say \"[message]\""))
- SSblackbox.record_feedback("tally", "admin_verb", 1, "Object Say") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
-/client/proc/togglebuildmodeself()
- set name = "Toggle Build Mode Self"
- set category = "Admin.Events"
- if (!(holder.rank_flags() & R_BUILD))
- return
- if(src.mob)
- togglebuildmode(src.mob)
- SSblackbox.record_feedback("tally", "admin_verb", 1, "Toggle Build Mode") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
+ target.say(message, sanitize = FALSE)
+ log_admin("[key_name(usr)] made [target] at [AREACOORD(target)] say \"[message]\"")
+ message_admins(span_adminnotice("[key_name_admin(usr)] made [target] at [AREACOORD(target)]. say \"[message]\""))
-/client/proc/check_ai_laws()
- set name = "Check AI Laws"
- set category = "Admin.Game"
- if(holder)
- src.holder.output_ai_laws()
+ADMIN_VERB(build_mode, toggle_build_mode_self, "", R_BUILD)
+ togglebuildmode(usr)
-/client/proc/deadmin()
- set name = "Deadmin"
- set category = "Admin"
- set desc = "Shed your admin powers."
+ADMIN_VERB(game, check_ai_laws, "", R_ADMIN)
+ var/law_bound_entities = 0
+ for(var/mob/living/silicon/subject as anything in GLOB.silicon_mobs)
+ law_bound_entities++
- if(!holder)
- return
+ var/message = ""
- holder.deactivate()
+ if(isAI(subject))
+ message += "AI [key_name(subject, usr)]'s laws:"
+ else if(iscyborg(subject))
+ var/mob/living/silicon/robot/borg = subject
+ message += "CYBORG [key_name(subject, usr)] [borg.connected_ai?"(Slaved to: [key_name(borg.connected_ai)])":"(Independent)"]: laws:"
+ else if (ispAI(subject))
+ message += "pAI [key_name(subject, usr)]'s laws:"
+ else
+ message += "SOMETHING SILICON [key_name(subject, usr)]'s laws:"
- to_chat(src, span_interface("You are now a normal player."))
- log_admin("[src] deadminned themselves.")
- message_admins("[src] deadminned themselves.")
- SSblackbox.record_feedback("tally", "admin_verb", 1, "Deadmin")
+ message += "
"
-/client/proc/readmin()
+ if (!subject.laws)
+ message += "[key_name(subject, usr)]'s laws are null?? Contact a coder."
+ else
+ message += jointext(subject.laws.get_law_list(include_zeroth = TRUE), "
")
+
+ to_chat(usr, message, confidential = TRUE)
+
+ if(!law_bound_entities)
+ to_chat(usr, "No law bound entities located", confidential = TRUE)
+
+/client/proc/readmin() // not an ADMIN_VERB for a reason
set name = "Readmin"
set category = "Admin"
set desc = "Regain your admin powers."
@@ -832,47 +418,30 @@ GLOBAL_PROTECT(admin_verbs_poll)
log_admin("[src] re-adminned themselves.")
SSblackbox.record_feedback("tally", "admin_verb", 1, "Readmin")
-/client/proc/populate_world(amount = 50)
- set name = "Populate World"
- set category = "Debug"
- set desc = "(\"Amount of mobs to create\") Populate the world with test mobs."
-
+ADMIN_VERB(debug, populate_world, "Populate the world with the given number of test mobs", R_DEBUG, amount = 50 as num)
for (var/i in 1 to amount)
var/turf/tile = get_safe_random_station_turf()
var/mob/living/carbon/human/hooman = new(tile)
hooman.equipOutfit(pick(subtypesof(/datum/outfit)))
testing("Spawned test mob at [get_area_name(tile, TRUE)] ([tile.x],[tile.y],[tile.z])")
-/client/proc/toggle_AI_interact()
- set name = "Toggle Admin AI Interact"
- set category = "Admin.Game"
- set desc = "Allows you to interact with most machines as an AI would as a ghost"
+ADMIN_VERB(game, toggle_admin_ai_interaction, "Allows you to interact with most machines as an AI would as a ghost", R_ADMIN)
+ usr.client.AI_Interact = !usr.client.AI_Interact
+ if(usr && isAdminGhostAI(usr))
+ usr.has_unlimited_silicon_privilege = usr.client.AI_Interact
- AI_Interact = !AI_Interact
- if(mob && isAdminGhostAI(mob))
- mob.has_unlimited_silicon_privilege = AI_Interact
+ log_admin("[key_name(usr)] has [usr.client.AI_Interact ? "activated" : "deactivated"] Admin AI Interact")
+ message_admins("[key_name_admin(usr)] has [usr.client.AI_Interact ? "activated" : "deactivated"] their AI interaction")
- log_admin("[key_name(usr)] has [AI_Interact ? "activated" : "deactivated"] Admin AI Interact")
- message_admins("[key_name_admin(usr)] has [AI_Interact ? "activated" : "deactivated"] their AI interaction")
-
-/client/proc/debugstatpanel()
- set name = "Debug Stat Panel"
- set category = "Debug"
-
- src.stat_panel.send_message("create_debug")
-
-/client/proc/admin_2fa_verify()
+/client/proc/admin_2fa_verify() // not an ADMIN_VERB for a reason
set name = "Verify Admin"
set category = "Admin"
var/datum/admins/admin = GLOB.admin_datums[ckey]
admin?.associate(src)
-/client/proc/display_sendmaps()
- set name = "Send Maps Profile"
- set category = "Debug"
-
- src << link("?debug=profile&type=sendmaps&window=test")
+ADMIN_VERB(debug, send_maps_profile, "", R_DEBUG)
+ usr.client << link("?debug=profile&type=sendmaps&window=test")
/**
* Debug verb that spawns human crewmembers
@@ -882,32 +451,24 @@ GLOBAL_PROTECT(admin_verbs_poll)
* This spawns humans with minds and jobs, but does NOT make them 'players'.
* They're all clientles mobs with minds / jobs.
*/
-/client/proc/spawn_debug_full_crew()
- set name = "Spawn Debug Full Crew"
- set desc = "Creates a full crew for the station, filling the datacore and assigning them all minds / jobs. Don't do this on live"
- set category = "Debug"
-
- if(!check_rights(R_DEBUG))
- return
-
- var/mob/admin = usr
+ADMIN_VERB(debug, spawn_debug_full_crew, "Creates a full crew for the station, filling the datacore and assigning them all minds/jobs. Don't do this on live", R_DEBUG)
if(SSticker.current_state != GAME_STATE_PLAYING)
- to_chat(admin, "You should only be using this after a round has setup and started.")
+ to_chat(usr, "You should only be using this after a round has setup and started.")
return
// Two input checks here to make sure people are certain when they're using this.
- if(tgui_alert(admin, "This command will create a bunch of dummy crewmembers with minds, job, and datacore entries, which will take a while and fill the manifest.", "Spawn Crew", list("Yes", "Cancel")) != "Yes")
+ if(tgui_alert(usr, "This command will create a bunch of dummy crewmembers with minds, job, and datacore entries, which will take a while and fill the manifest.", "Spawn Crew", list("Yes", "Cancel")) != "Yes")
return
- if(tgui_alert(admin, "I sure hope you aren't doing this on live. Are you sure?", "Spawn Crew (Be certain)", list("Yes", "Cancel")) != "Yes")
+ if(tgui_alert(usr, "I sure hope you aren't doing this on live. Are you sure?", "Spawn Crew (Be certain)", list("Yes", "Cancel")) != "Yes")
return
// Find the observer spawn, so we have a place to dump the dummies.
var/obj/effect/landmark/observer_start/observer_point = locate(/obj/effect/landmark/observer_start) in GLOB.landmarks_list
var/turf/destination = get_turf(observer_point)
if(!destination)
- to_chat(admin, "Failed to find the observer spawn to send the dummies.")
+ to_chat(usr, "Failed to find the observer spawn to send the dummies.")
return
// Okay, now go through all nameable occupations.
@@ -929,7 +490,7 @@ GLOBAL_PROTECT(admin_verbs_poll)
// Assign the rank to the new player dummy.
if(!SSjob.AssignRole(new_guy, job))
qdel(new_guy)
- to_chat(admin, "[rank] wasn't able to be spawned.")
+ to_chat(usr, "[rank] wasn't able to be spawned.")
continue
// It's got a job, spawn in a human and shove it in the human.
@@ -950,13 +511,11 @@ GLOBAL_PROTECT(admin_verbs_poll)
number_made++
CHECK_TICK
- to_chat(admin, "[number_made] crewmembers have been created.")
+ to_chat(usr, "[number_made] crewmembers have been created.")
/// Debug verb for seeing at a glance what all spells have as set requirements
-/client/proc/debug_spell_requirements()
- set name = "Show Spell Requirements"
- set category = "Debug"
+ADMIN_VERB(debug, show_spell_requirements, "seeing at a glance what all spells have as set requirements", R_DEBUG)
var/header = "| Name | Requirements | "
var/all_requirements = list()
for(var/datum/action/cooldown/spell/spell as anything in typesof(/datum/action/cooldown/spell))
@@ -986,16 +545,11 @@ GLOBAL_PROTECT(admin_verbs_poll)
var/page_style = ""
var/page_contents = "[page_style][header][jointext(all_requirements, "")]
"
- var/datum/browser/popup = new(mob, "spellreqs", "Spell Requirements", 600, 400)
+ var/datum/browser/popup = new(usr, "spellreqs", "Spell Requirements", 600, 400)
popup.set_content(page_contents)
popup.open()
-/client/proc/force_load_lazy_template()
- set name = "Load/Jump Lazy Template"
- set category = "Admin.Events"
- if(!check_rights(R_ADMIN))
- return
-
+ADMIN_VERB(events, load_jump_lazy_template, "", R_ADMIN)
var/list/choices = LAZY_TEMPLATE_KEY_LIST_ALL()
var/choice = tgui_input_list(usr, "Key?", "Lazy Loader", choices)
if(!choice)
@@ -1017,7 +571,7 @@ GLOBAL_PROTECT(admin_verbs_poll)
return
if(!isobserver(usr))
- admin_ghost()
+ SSadmin_verbs.dynamic_invoke_admin_verb(usr.client, /mob/admin_module_holder/game/aghost)
usr.forceMove(coords2turf(reservation.bottom_left_coords))
message_admins("[key_name_admin(usr)] has loaded lazy template '[choice]'")
diff --git a/code/modules/admin/adminmenu.dm b/code/modules/admin/adminmenu.dm
deleted file mode 100644
index b7d85ccff51..00000000000
--- a/code/modules/admin/adminmenu.dm
+++ /dev/null
@@ -1,11 +0,0 @@
-/datum/verbs/menu/Admin/Generate_list(client/C)
- if (C.holder)
- . = ..()
-
-/datum/verbs/menu/Admin/verb/playerpanel()
- set name = "Player Panel"
- set desc = "Player Panel"
- set category = "Admin.Game"
- if(usr.client.holder)
- usr.client.holder.player_panel_new()
- SSblackbox.record_feedback("tally", "admin_verb", 1, "Player Panel New") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
diff --git a/code/modules/admin/callproc/callproc.dm b/code/modules/admin/callproc/callproc.dm
index 25a2ee4a72c..5a765cb8f21 100644
--- a/code/modules/admin/callproc/callproc.dm
+++ b/code/modules/admin/callproc/callproc.dm
@@ -92,11 +92,8 @@ GLOBAL_PROTECT(AdminProcCallHandler)
usr = lastusr
handler.remove_caller(user)
-/client/proc/callproc()
- set category = "Debug"
- set name = "Advanced ProcCall"
- set waitfor = FALSE
- callproc_blocking()
+ADMIN_VERB(debug, advanced_proccall, "", R_DEBUG)
+ usr.client.callproc_blocking()
/client/proc/callproc_blocking(list/get_retval)
if(!check_rights(R_DEBUG))
@@ -230,34 +227,29 @@ GLOBAL_PROTECT(LastAdminCalledProc)
return (GLOB.AdminProcCaller && GLOB.AdminProcCaller == usr?.client?.ckey) || (GLOB.AdminProcCallHandler && usr == GLOB.AdminProcCallHandler)
#endif
-/client/proc/callproc_datum(datum/A as null|area|mob|obj|turf)
- set category = "Debug"
- set name = "Atom ProcCall"
- set waitfor = FALSE
-
- if(!check_rights(R_DEBUG))
- return
+ADMIN_CONTEXT_ENTRY(context_callproc, "Atom ProcCall", R_DEBUG, atom/target in world)
+ set waitfor = 0
var/procname = input("Proc name, eg: fake_blood","Proc:", null) as text|null
if(!procname)
return
- if(!hascall(A,procname))
- to_chat(usr, "Error: callproc_datum(): type [A.type] has no proc named [procname].", confidential = TRUE)
+ if(!hascall(target, procname))
+ to_chat(usr, "Error: callproc_datum(): type [target.type] has no proc named [procname].", confidential = TRUE)
return
var/list/lst = get_callproc_args()
if(!lst)
return
- if(!A || !is_valid_src(A))
+ if(!target || !is_valid_src(target))
to_chat(usr, span_warning("Error: callproc_datum(): owner of proc no longer exists."), confidential = TRUE)
return
- log_admin("[key_name(src)] called [A]'s [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"].")
- var/msg = "[key_name(src)] called [A]'s [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"]."
- message_admins(msg)
- admin_ticket_log(A, msg)
- SSblackbox.record_feedback("tally", "admin_verb", 1, "Atom ProcCall") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
- var/returnval = WrapAdminProcCall(A, procname, lst) // Pass the lst as an argument list to the proc
+ log_admin("[key_name(usr)] called [target]'s [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"].")
+ var/msg = "[key_name(usr)] called [target]'s [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"]."
+ message_admins(msg)
+ admin_ticket_log(target, msg)
+
+ var/returnval = WrapAdminProcCall(target, procname, lst) // Pass the lst as an argument list to the proc
. = get_callproc_returnval(returnval,procname)
if(.)
to_chat(usr, ., confidential = TRUE)
diff --git a/code/modules/admin/force_event.dm b/code/modules/admin/force_event.dm
index d0d332b7d6c..13de1c0da7d 100644
--- a/code/modules/admin/force_event.dm
+++ b/code/modules/admin/force_event.dm
@@ -1,18 +1,9 @@
///Allows an admin to force an event
-/client/proc/forceEvent()
- set name = "Trigger Event"
- set category = "Admin.Events"
-
- if(!holder || !check_rights(R_FUN))
- return
-
- holder.forceEvent()
+ADMIN_VERB(events, trigger_event, "", R_FUN)
+ usr.client.holder.forceEvent()
///Opens up the Force Event Panel
/datum/admins/proc/forceEvent()
- if(!check_rights(R_FUN))
- return
-
var/datum/force_event/ui = new(usr)
ui.ui_interact(usr)
diff --git a/code/modules/admin/known_alts.dm b/code/modules/admin/known_alts.dm
index d6486f77bd7..75564269695 100644
--- a/code/modules/admin/known_alts.dm
+++ b/code/modules/admin/known_alts.dm
@@ -186,9 +186,3 @@ GLOBAL_DATUM_INIT(known_alts, /datum/known_alts, new)
"}
client << browse(html, "window=known_alts;size=700x400")
-
-/datum/admins/proc/known_alts_panel()
- set name = "Known Alts Panel"
- set category = "Admin"
-
- GLOB.known_alts.show_panel(usr.client)
diff --git a/code/modules/admin/outfit_editor.dm b/code/modules/admin/outfit_editor.dm
index 67196c54bd4..87a7275ea99 100644
--- a/code/modules/admin/outfit_editor.dm
+++ b/code/modules/admin/outfit_editor.dm
@@ -110,7 +110,7 @@
GLOB.custom_outfits -= drip
SStgui.update_user_uis(owner.mob)
if("vv")
- owner.debug_variables(drip)
+ SSadmin_verbs.dynamic_invoke_admin_verb(owner, /mob/admin_module_holder/debug/view_variables, drip)
/datum/outfit_editor/proc/set_item(slot, obj/item/choice)
diff --git a/code/modules/admin/outfit_manager.dm b/code/modules/admin/outfit_manager.dm
index fcb41b3f2f1..963af54e161 100644
--- a/code/modules/admin/outfit_manager.dm
+++ b/code/modules/admin/outfit_manager.dm
@@ -1,13 +1,7 @@
-/client/proc/outfit_manager()
- set category = "Debug"
- set name = "Outfit Manager"
-
- if(!check_rights(R_DEBUG))
- return
+ADMIN_VERB(debug, outfit_manager, "", R_DEBUG)
var/datum/outfit_manager/ui = new(usr)
ui.ui_interact(usr)
-
/datum/outfit_manager
var/client/owner
diff --git a/code/modules/admin/permissionedit.dm b/code/modules/admin/permissionedit.dm
index 5544f90fb74..477e8986073 100644
--- a/code/modules/admin/permissionedit.dm
+++ b/code/modules/admin/permissionedit.dm
@@ -1,9 +1,4 @@
-/client/proc/edit_admin_permissions()
- set category = "Admin"
- set name = "Permissions Panel"
- set desc = "Edit admin permissions"
- if(!check_rights(R_PERMISSIONS))
- return
+ADMIN_VERB(admin, permissions_panel, "Edit/Set admin permissions", R_PERMISSIONS)
usr.client.holder.edit_admin_permissions()
/datum/admins/proc/edit_admin_permissions(action, target, operation, page)
diff --git a/code/modules/admin/stickyban.dm b/code/modules/admin/stickyban.dm
index 537e0b92acb..bd88cf10231 100644
--- a/code/modules/admin/stickyban.dm
+++ b/code/modules/admin/stickyban.dm
@@ -481,10 +481,5 @@
. = list2params(.)
-
-/client/proc/stickybanpanel()
- set name = "Sticky Ban Panel"
- set category = "Admin"
- if (!holder)
- return
- holder.stickyban_show()
+ADMIN_VERB(admin, sticky_ban_panel, "", R_BAN)
+ usr.client.holder.stickyban_show()
diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm
index d3c2a4011fa..8d3413f3d71 100644
--- a/code/modules/admin/topic.dm
+++ b/code/modules/admin/topic.dm
@@ -13,7 +13,6 @@
/datum/admins/Topic(href, href_list)
..()
-
if(usr.client != src.owner || !check_rights(0))
message_admins("[usr.key] has attempted to override the admin panel!")
log_admin("[key_name(usr)] tried to use the admin panel without authorization.")
@@ -22,6 +21,9 @@
if(!CheckAdminHref(href, href_list))
return
+ if(SSadmin_verbs.handle_admin_holder_topic(usr.client, href, href_list))
+ return
+
if(href_list["ahelp"])
if(!check_rights(R_ADMIN, TRUE))
return
@@ -111,10 +113,7 @@
minor_announce("The emergency shuttle will reach its destination in [DisplayTimeText(timer SECONDS)].")
message_admins(span_adminnotice("[key_name_admin(usr)] edited the Emergency Shuttle's timeleft to [timer] seconds."))
else if(href_list["trigger_centcom_recall"])
- if(!check_rights(R_ADMIN))
- return
-
- usr.client.trigger_centcom_recall()
+ SSadmin_verbs.dynamic_invoke_admin_verb(usr, /mob/admin_module_holder/events/recall_shuttle)
else if(href_list["move_shuttle"])
if(!check_rights(R_ADMIN))
@@ -136,8 +135,7 @@
to_chat(usr, "[shuttle_console] was [shuttle_console.admin_controlled ? "locked" : "unlocked"].", confidential = TRUE)
else if(href_list["delay_round_end"])
- // Permissions are checked in delay_round_end
- delay_round_end()
+ SSadmin_verbs.dynamic_invoke_admin_verb(usr.client, /mob/admin_module_holder/server/delay_round_end)
else if(href_list["undelay_round_end"])
if(!check_rights(R_SERVER))
@@ -709,12 +707,10 @@
if(iscyborg(our_mob))
to_chat(usr, "That's already a cyborg.", confidential = TRUE)
return
-
- usr.client.cmd_admin_robotize(our_mob)
+ SSadmin_verbs.dynamic_invoke_admin_verb(usr, /mob/admin_module_holder/debug/make_cyborg, our_mob)
else if(href_list["adminplayeropts"])
- var/mob/M = locate(href_list["adminplayeropts"])
- show_player_panel(M)
+ usr.client.admin_context_wrapper_context_player_panel(locate(href_list["adminplayeropts"]))
else if(href_list["ppbyckey"])
var/target_ckey = href_list["ppbyckey"]
@@ -729,7 +725,7 @@
return
to_chat(usr, span_notice("Jumping to [target_ckey]'s new mob: [target_mob]!"))
- show_player_panel(target_mob)
+ usr.client.admin_context_wrapper_context_player_panel(target_mob)
else if(href_list["adminplayerobservefollow"])
if(!isobserver(usr) && !check_rights(R_ADMIN))
@@ -757,14 +753,9 @@
var/client/C = usr.client
if(!isobserver(usr))
- C.admin_ghost()
+ SSadmin_verbs.dynamic_invoke_admin_verb(C, /mob/admin_module_holder/game/aghost)
sleep(0.2 SECONDS)
- C.jumptocoord(x,y,z)
-
- else if(href_list["adminchecklaws"])
- if(!check_rights(R_ADMIN))
- return
- output_ai_laws()
+ SSadmin_verbs.dynamic_invoke_admin_verb(C, /mob/admin_module_holder/game/jump_to_coordinate, x, y, z)
else if(href_list["adminmoreinfo"])
var/mob/subject = locate(href_list["adminmoreinfo"]) in GLOB.mob_list
@@ -968,28 +959,24 @@
to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human", confidential = TRUE)
return
- usr.client.smite(H)
+ usr.client.admin_context_wrapper_context_smite(H)
else if(href_list["CentComReply"])
- if(!check_rights(R_ADMIN))
- return
-
- var/mob/M = locate(href_list["CentComReply"])
- usr.client.admin_headset_message(M, RADIO_CHANNEL_CENTCOM)
+ usr.client.admin_context_wrapper_contexxt_headset_message(
+ locate(href_list["CentComReply"]),
+ RADIO_CHANNEL_CENTCOM,
+ )
else if(href_list["SyndicateReply"])
- if(!check_rights(R_ADMIN))
- return
-
- var/mob/M = locate(href_list["SyndicateReply"])
- usr.client.admin_headset_message(M, RADIO_CHANNEL_SYNDICATE)
+ usr.client.admin_context_wrapper_contexxt_headset_message(
+ locate(href_list["SyndicateReply"]),
+ RADIO_CHANNEL_SYNDICATE,
+ )
else if(href_list["HeadsetMessage"])
- if(!check_rights(R_ADMIN))
- return
-
- var/mob/M = locate(href_list["HeadsetMessage"])
- usr.client.admin_headset_message(M)
+ usr.client.admin_context_wrapper_contexxt_headset_message(
+ locate(href_list["HeadsetMessage"]),
+ )
else if(href_list["reject_custom_name"])
if(!check_rights(R_ADMIN))
@@ -997,12 +984,12 @@
var/obj/item/station_charter/charter = locate(href_list["reject_custom_name"])
if(istype(charter))
charter.reject_proposed(usr)
+
else if(href_list["jumpto"])
if(!isobserver(usr) && !check_rights(R_ADMIN))
return
- var/mob/M = locate(href_list["jumpto"])
- usr.client.jumptomob(M)
+ SSadmin_verbs.dynamic_invoke_admin_verb(usr, /mob/admin_module_holder/game/jump_to_mob, locate(href_list["jumpto"]))
else if(href_list["getmob"])
if(!check_rights(R_ADMIN))
@@ -1010,38 +997,29 @@
if(tgui_alert(usr, "Confirm?", "Message", list("Yes", "No")) != "Yes")
return
- var/mob/M = locate(href_list["getmob"])
- usr.client.Getmob(M)
+ SSadmin_verbs.dynamic_invoke_admin_verb(usr, /mob/admin_module_holder/game/get_mob, locate(href_list["getmob"]))
else if(href_list["sendmob"])
- if(!check_rights(R_ADMIN))
- return
-
- var/mob/M = locate(href_list["sendmob"])
- usr.client.sendmob(M)
+ usr.client.admin_context_wrapper_context_sendmob(locate(href_list["sendmob"]))
else if(href_list["narrateto"])
- if(!check_rights(R_ADMIN))
- return
-
- var/mob/M = locate(href_list["narrateto"])
- usr.client.cmd_admin_direct_narrate(M)
+ usr.client.admin_context_wrapper_context_direct_narrate(locate(href_list["narrateto"]))
else if(href_list["subtlemessage"])
- if(!check_rights(R_ADMIN))
- return
-
- var/mob/M = locate(href_list["subtlemessage"])
- usr.client.cmd_admin_subtle_message(M)
+ usr.client.admin_context_wrapper_context_subtle_message(locate(href_list["subtlemessage"]))
else if(href_list["playsoundto"])
if(!check_rights(R_SOUND))
return
var/mob/M = locate(href_list["playsoundto"])
+ if(QDELETED(M))
+ to_chat(usr, span_warning("target mob no longer exists!"))
+ return
+
var/S = input("", "Select a sound file",) as null|sound
if(S)
- usr.client.play_direct_mob_sound(S, M)
+ SSadmin_verbs.dynamic_invoke_admin_verb(usr.client, /mob/admin_module_holder/fun/play_direct_mob_sound, S, M)
else if(href_list["individuallog"])
if(!check_rights(R_ADMIN))
@@ -1081,37 +1059,13 @@
else
D.traitor_panel()
else
- show_traitor_panel(M)
+ SSadmin_verbs.dynamic_invoke_admin_verb(usr, /mob/admin_module_holder/game/traitor_panel, M)
else if(href_list["skill"])
- if(!check_rights(R_ADMIN))
- return
-
- if(!SSticker.HasRoundStarted())
- tgui_alert(usr,"The game hasn't started yet!")
- return
-
- var/target = locate(href_list["skill"])
- var/datum/mind/target_mind
- if(ismob(target))
- var/mob/target_mob = target
- target_mind = target_mob.mind
- else if (istype(target, /datum/mind))
- target_mind = target
- else
- to_chat(usr, "This can only be used on instances of type /mob and /mind", confidential = TRUE)
- return
- show_skill_panel(target_mind)
+ SSadmin_verbs.dynamic_invoke_admin_verb(usr, /mob/admin_module_holder/game/skill_panel, locate(href_list["skill"]))
else if(href_list["borgpanel"])
- if(!check_rights(R_ADMIN))
- return
-
- var/mob/M = locate(href_list["borgpanel"])
- if(!iscyborg(M))
- to_chat(usr, "This can only be used on cyborgs", confidential = TRUE)
- else
- open_borgopanel(M)
+ usr.client.admin_context_wrapper_context_borg_panel(locate(href_list["borgpanel"]))
else if(href_list["initmind"])
if(!check_rights(R_ADMIN))
@@ -1274,9 +1228,7 @@
return
else if(href_list["check_antagonist"])
- if(!check_rights(R_ADMIN))
- return
- usr.client.check_antagonists()
+ SSadmin_verbs.dynamic_invoke_admin_verb(usr.client, /mob/admin_module_holder/game/check_antagonists)
else if(href_list["kick_all_from_lobby"])
if(!check_rights(R_ADMIN))
@@ -1324,7 +1276,7 @@
G.report_message = description
message_admins("[key_name(usr)] created \"[G.name]\" station goal.")
GLOB.station_goals += G
- modify_goals()
+ SSadmin_verbs.dynamic_invoke_admin_verb(usr, /mob/admin_module_holder/)
else if(href_list["change_lag_switch"])
if(!check_rights(R_ADMIN))
@@ -1347,7 +1299,7 @@
log_admin("[key_name(usr)] turned a Lag Switch measure at index ([switch_index]) [LAZYACCESS(SSlag_switch.measures, switch_index) ? "ON" : "OFF"]")
message_admins("[key_name_admin(usr)] turned a Lag Switch measure [LAZYACCESS(SSlag_switch.measures, switch_index) ? "ON" : "OFF"]")
- src.show_lag_switch_panel()
+ SSadmin_verbs.dynamic_invoke_admin_verb(usr, /mob/admin_module_holder/game/show_lag_switches)
else if(href_list["change_lag_switch_option"])
if(!check_rights(R_ADMIN))
@@ -1376,7 +1328,7 @@
log_admin("[key_name(usr)] set the Lag Switch slowmode cooldown to [new_num] seconds.")
message_admins("[key_name_admin(usr)] set the Lag Switch slowmode cooldown to [new_num] seconds.")
- src.show_lag_switch_panel()
+ SSadmin_verbs.dynamic_invoke_admin_verb(usr, /mob/admin_module_holder/game/show_lag_switches)
else if(href_list["viewruntime"])
var/datum/error_viewer/error_viewer = locate(href_list["viewruntime"])
@@ -1496,7 +1448,7 @@
if(confirm == "No")
return
if(confirm == "Yes")
- restart()
+ SSadmin_verbs.dynamic_invoke_admin_verb(usr.client, /mob/admin_module_holder/server/reboot_world)
else if(href_list["check_teams"])
if(!check_rights(R_ADMIN))
@@ -1748,3 +1700,5 @@
if(!paper_to_show)
return
paper_to_show.ui_interact(usr)
+
+ stack_trace("Unknown admin topic [href]-'[list2params(href_list)]'")
diff --git a/code/modules/admin/verbs/SDQL2/SDQL_2.dm b/code/modules/admin/verbs/SDQL2/SDQL_2.dm
index bd5a3ea7b33..6f9be4ca45f 100644
--- a/code/modules/admin/verbs/SDQL2/SDQL_2.dm
+++ b/code/modules/admin/verbs/SDQL2/SDQL_2.dm
@@ -198,17 +198,11 @@
state = SDQL2_STATE_ERROR;\
CRASH("SDQL2 fatal error");};
-/client/proc/SDQL2_query(query_text as message)
- set category = "Debug"
- if(!check_rights(R_DEBUG)) //Shouldn't happen... but just to be safe.
- message_admins(span_danger("ERROR: Non-admin [key_name(usr)] attempted to execute a SDQL query!"))
- usr.log_message("non-admin attempted to execute a SDQL query!", LOG_ADMIN)
- return FALSE
+ADMIN_VERB(debug, query_text, "", R_DEBUG, query_text as message)
var/list/results = world.SDQL2_query(query_text, key_name_admin(usr), "[key_name(usr)]")
if(length(results) == 3)
for(var/I in 1 to 3)
to_chat(usr, results[I], confidential = TRUE)
- SSblackbox.record_feedback("nested tally", "SDQL query", 1, list(ckey, query_text))
/world/proc/SDQL2_query(query_text, log_entry1, log_entry2, silent = FALSE)
var/query_log = "executed SDQL query(s): \"[query_text]\"."
@@ -1230,4 +1224,4 @@ GLOBAL_DATUM_INIT(sdql2_vv_statobj, /obj/effect/statclick/sdql2_vv_all, new(null
message_admins("[key_name_admin(usr)] non-holder clicked on a statclick! ([src])")
usr.log_message("non-holder clicked on a statclick! ([src])", LOG_ADMIN)
return
- usr.client.debug_variables(GLOB.sdql2_queries)
+ SSadmin_verbs.dynamic_invoke_admin_verb(usr.client, /mob/admin_module_holder/debug/view_variables, GLOB.sdql2_queries)
diff --git a/code/modules/admin/verbs/admin.dm b/code/modules/admin/verbs/admin.dm
index 2558606499a..6480302e100 100644
--- a/code/modules/admin/verbs/admin.dm
+++ b/code/modules/admin/verbs/admin.dm
@@ -1,19 +1,12 @@
// Admin Tab - Admin Verbs
-/client/proc/show_tip()
- set category = "Admin"
- set name = "Show Tip"
- set desc = "Sends a tip (that you specify) to all players. After all \
- you're the experienced player here."
-
- if(!check_rights(R_ADMIN))
- return
-
+ADMIN_VERB(admin, show_tip, "Sends a tip, which you specify, to all players", R_ADMIN)
var/input = input(usr, "Please specify your tip that you want to send to the players.", "Tip", "") as message|null
if(!input)
return
- if(!SSticker)
+ if(!SSticker.initialized)
+ to_chat(usr, span_warning("Please wait for the game to initialize!"))
return
// If we've already tipped, then send it straight away.
@@ -24,40 +17,16 @@
message_admins("[key_name_admin(usr)] sent a tip of the round.")
log_admin("[key_name(usr)] sent \"[input]\" as the Tip of the Round.")
- SSblackbox.record_feedback("tally", "admin_verb", 1, "Show Tip")
-/datum/admins/proc/announce()
- set category = "Admin"
- set name = "Announce"
- set desc="Announce your desires to the world"
- if(!check_rights(0))
- return
-
- var/message = input("Global message to send:", "Admin Announce", null, null) as message|null
- if(message)
- if(!check_rights(R_SERVER,0))
- message = adminscrub(message,500)
- to_chat(world, "[span_adminnotice("[usr.client.holder.fakekey ? "Administrator" : usr.key] Announces:")]\n \t [message]", confidential = TRUE)
- log_admin("Announce: [key_name(usr)] : [message]")
- SSblackbox.record_feedback("tally", "admin_verb", 1, "Announce") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
-
-/datum/admins/proc/unprison(mob/M in GLOB.mob_list)
- set category = "Admin"
- set name = "Unprison"
- if (is_centcom_level(M.z))
- SSjob.SendToLateJoin(M)
- message_admins("[key_name_admin(usr)] has unprisoned [key_name_admin(M)]")
- log_admin("[key_name(usr)] has unprisoned [key_name(M)]")
+ADMIN_VERB(admin, release_from_prison, "", R_ADMIN, mob/freeing in world)
+ if(is_centcom_level(freeing.z))
+ SSjob.SendToLateJoin(freeing)
+ message_admins("[key_name_admin(usr)] has unprisoned [key_name_admin(freeing)]")
+ log_admin("[key_name(usr)] has unprisoned [key_name(freeing)]")
else
- tgui_alert(usr,"[M.name] is not prisoned.")
- SSblackbox.record_feedback("tally", "admin_verb", 1, "Unprison") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
-
-/client/proc/cmd_admin_check_player_exp() //Allows admins to determine who the newer players are.
- set category = "Admin"
- set name = "Player Playtime"
- if(!check_rights(R_ADMIN))
- return
+ tgui_alert(usr,"[freeing.name] is not prisoned.")
+ADMIN_VERB(admin, player_playtime, "Check the playtime for connected players", R_ADMIN)
if(!CONFIG_GET(flag/use_exp_tracking))
to_chat(usr, span_warning("Tracking is disabled in the server configuration file."), confidential = TRUE)
return
@@ -67,13 +36,11 @@
for(var/client/client in sort_list(GLOB.clients, GLOBAL_PROC_REF(cmp_playtime_asc)))
msg += " [ADMIN_PP(client.mob)] [key_name_admin(client)]: " + client.get_exp_living() + ""
msg += "
|---|