mirror of
https://github.com/Citadel-Station-13/Citadel-Station-13-RP.git
synced 2026-08-24 00:47:03 +01:00
refactors admin narrate (#7319)
This commit is contained in:
@@ -3,6 +3,7 @@ var/list/admin_datums = list()
|
||||
GLOBAL_VAR_INIT(href_token, GenerateToken())
|
||||
GLOBAL_PROTECT(href_token)
|
||||
|
||||
// todo: /datum/admin_holder
|
||||
/datum/admins
|
||||
var/rank = "Temporary Admin"
|
||||
var/client/owner = null
|
||||
@@ -36,27 +37,97 @@ GLOBAL_PROTECT(href_token)
|
||||
spawn(-1)
|
||||
UNTIL(SSmapping.loaded_station)
|
||||
admincaster_signature = "[(LEGACY_MAP_DATUM).company_name] Officer #[rand(0,9)][rand(0,9)][rand(0,9)]"
|
||||
..()
|
||||
|
||||
// todo: assertions on this are too weak
|
||||
/datum/admins/proc/associate(client/C)
|
||||
if(istype(C))
|
||||
owner = C
|
||||
owner.holder = src
|
||||
owner.add_admin_verbs() //TODO
|
||||
GLOB.admins |= C
|
||||
if(owner == C)
|
||||
return
|
||||
if(owner)
|
||||
disassociate()
|
||||
if(!istype(C))
|
||||
return
|
||||
owner = C
|
||||
owner.holder = src
|
||||
GLOB.admins |= C
|
||||
// add verbs
|
||||
add_admin_verbs()
|
||||
|
||||
// todo: assertions on this are too weak
|
||||
/datum/admins/proc/disassociate()
|
||||
if(owner)
|
||||
GLOB.admins -= owner
|
||||
owner.remove_admin_verbs()
|
||||
owner.deadmin_holder = owner.holder
|
||||
owner.holder = null
|
||||
if(!owner)
|
||||
return
|
||||
// for now, destroy all modals
|
||||
QDEL_LIST(admin_modals)
|
||||
// obliterate verbs
|
||||
remove_admin_verbs()
|
||||
GLOB.admins -= owner
|
||||
owner.deadmin_holder = owner.holder
|
||||
owner.holder = null
|
||||
owner = null
|
||||
|
||||
/datum/admins/proc/reassociate()
|
||||
if(owner)
|
||||
GLOB.admins |= owner
|
||||
owner.holder = src
|
||||
owner.deadmin_holder = null
|
||||
owner.add_admin_verbs()
|
||||
/datum/admins/add_admin_verbs()
|
||||
..()
|
||||
if(!owner)
|
||||
return
|
||||
var/list/verbs_to_add = list()
|
||||
verbs_to_add += admin_verbs_default
|
||||
if(rights & R_BUILDMODE)
|
||||
verbs_to_add += /client/proc/togglebuildmodeself
|
||||
if(rights & R_ADMIN)
|
||||
verbs_to_add += admin_verbs_admin
|
||||
if(rights & R_BAN)
|
||||
verbs_to_add += admin_verbs_ban
|
||||
if(rights & R_FUN)
|
||||
verbs_to_add += admin_verbs_fun
|
||||
if(rights & R_SERVER)
|
||||
verbs_to_add += admin_verbs_server
|
||||
if(rights & R_DEBUG)
|
||||
verbs_to_add += admin_verbs_debug
|
||||
if(rights & R_POSSESS)
|
||||
verbs_to_add += admin_verbs_possess
|
||||
if(rights & R_PERMISSIONS)
|
||||
verbs_to_add += admin_verbs_permissions
|
||||
if(rights & R_STEALTH)
|
||||
verbs_to_add += /client/proc/stealth
|
||||
if(rights & R_REJUVINATE)
|
||||
verbs_to_add += admin_verbs_rejuv
|
||||
if(rights & R_SOUNDS)
|
||||
verbs_to_add += admin_verbs_sounds
|
||||
if(rights & R_SPAWN)
|
||||
verbs_to_add += admin_verbs_spawn
|
||||
if(rights & R_MOD)
|
||||
verbs_to_add += admin_verbs_mod
|
||||
if(rights & R_EVENT)
|
||||
verbs_to_add += admin_verbs_event_manager
|
||||
add_verb(
|
||||
owner,
|
||||
verbs_to_add,
|
||||
)
|
||||
|
||||
/datum/admins/remove_admin_verbs()
|
||||
..()
|
||||
if(!owner)
|
||||
return
|
||||
remove_verb(
|
||||
owner,
|
||||
list(
|
||||
admin_verbs_default,
|
||||
/client/proc/togglebuildmodeself,
|
||||
admin_verbs_admin,
|
||||
admin_verbs_ban,
|
||||
admin_verbs_fun,
|
||||
admin_verbs_server,
|
||||
admin_verbs_debug,
|
||||
admin_verbs_possess,
|
||||
admin_verbs_permissions,
|
||||
/client/proc/stealth,
|
||||
admin_verbs_rejuv,
|
||||
admin_verbs_sounds,
|
||||
admin_verbs_spawn,
|
||||
debug_verbs,
|
||||
),
|
||||
)
|
||||
|
||||
/*
|
||||
checks if usr is an admin with at least ONE of the flags in rights_required. (Note, they don't need all the flags)
|
||||
@@ -119,7 +190,6 @@ NOTE: It checks usr by default. Supply the "user" argument if you wish to check
|
||||
/client/proc/deadmin()
|
||||
if(holder)
|
||||
holder.disassociate()
|
||||
//qdel(holder)
|
||||
return 1
|
||||
|
||||
/proc/GenerateToken()
|
||||
@@ -159,12 +229,3 @@ NOTE: It checks usr by default. Supply the "user" argument if you wish to check
|
||||
return TRUE
|
||||
log_admin_private("[key_name(usr)] clicked an href with [msg] authorization key! [href]")
|
||||
*/
|
||||
|
||||
/datum/admins/vv_edit_var(var_name, var_value)
|
||||
#ifdef TESTING
|
||||
return ..()
|
||||
#else
|
||||
if(var_name == NAMEOF(src, rank) || var_name == NAMEOF(src, rights))
|
||||
return FALSE
|
||||
return ..()
|
||||
#endif
|
||||
@@ -0,0 +1,76 @@
|
||||
//* This file is explicitly licensed under the MIT license. *//
|
||||
//* Copyright (c) 2025 Citadel Station Developers *//
|
||||
|
||||
/datum/admins
|
||||
/// lazy list of active admin modals
|
||||
///
|
||||
/// todo: re-open these on reconnect.
|
||||
var/list/datum/admin_modal/admin_modals
|
||||
|
||||
/// owning ckey
|
||||
var/ckey
|
||||
|
||||
/datum/admins/Destroy()
|
||||
QDEL_LIST(admin_modals)
|
||||
return ..()
|
||||
|
||||
/datum/admins/New(initial_rank, initial_rights, ckey)
|
||||
src.ckey = ckey
|
||||
..()
|
||||
|
||||
/datum/admins/proc/add_admin_verbs()
|
||||
if(!owner)
|
||||
return
|
||||
var/list/verbs_to_add = list()
|
||||
for(var/datum/admin_verb_descriptor/descriptor in global.admin_verb_descriptors)
|
||||
if((rights & descriptor.required_rights) != descriptor.required_rights)
|
||||
continue
|
||||
verbs_to_add += descriptor.verb_path
|
||||
add_verb(
|
||||
owner,
|
||||
verbs_to_add,
|
||||
)
|
||||
world.SetConfig("APP/admin", ckey, "role=admin")
|
||||
|
||||
/datum/admins/proc/remove_admin_verbs()
|
||||
var/list/verbs_to_remove = list()
|
||||
for(var/datum/admin_verb_descriptor/descriptor in global.admin_verb_descriptors)
|
||||
verbs_to_remove += descriptor.verb_path
|
||||
remove_verb(
|
||||
owner,
|
||||
verbs_to_remove,
|
||||
)
|
||||
world.SetConfig("APP/admin", ckey, "null")
|
||||
|
||||
//* Admin Modals *//
|
||||
|
||||
/datum/admins/proc/open_admin_modal(path, ...)
|
||||
ASSERT(ispath(path, /datum/admin_modal))
|
||||
var/datum/admin_modal/modal = new path(src)
|
||||
if(!modal.Initialize(arglist(args.Copy(2))))
|
||||
qdel(modal)
|
||||
message_admins("Failed to initialize an admin modal. Check runtimes for more details.")
|
||||
stack_trace("failed to initialize an admin modal; this means someone passed in bad args.")
|
||||
return null
|
||||
modal.open()
|
||||
return modal
|
||||
|
||||
//* -- SECURITY -- *//
|
||||
//* Do not touch this section unless you know what you are doing. *//
|
||||
|
||||
/datum/admins/vv_edit_var(var_name, var_value)
|
||||
#ifdef TESTING
|
||||
return ..()
|
||||
#else
|
||||
if(var_name == NAMEOF(src, rank) || var_name == NAMEOF(src, rights))
|
||||
return FALSE
|
||||
return ..()
|
||||
#endif
|
||||
|
||||
/datum/admins/CanProcCall(procname)
|
||||
switch(procname)
|
||||
if(NAMEOF_PROC(src, open_admin_modal))
|
||||
return FALSE
|
||||
if(findtext(procname, "verb__") == 1)
|
||||
return FALSE
|
||||
return ..()
|
||||
@@ -0,0 +1,70 @@
|
||||
//* This file is explicitly licensed under the MIT license. *//
|
||||
//* Copyright (c) 2024 Citadel Station Developers *//
|
||||
|
||||
//* Modal *//
|
||||
|
||||
/**
|
||||
* Base type of admin modals, which tend to be standalone panels.
|
||||
*/
|
||||
VV_PROTECT_READONLY(/datum/admin_modal)
|
||||
/datum/admin_modal
|
||||
/// Our name, for UI / output purposes
|
||||
var/name = "Unknown Modal"
|
||||
/// The admin datum that opened us
|
||||
var/datum/admins/owner
|
||||
/// TGUI ID; this will always be loaded from `tgui/interfaces/admin_modal` if possible.
|
||||
var/tgui_interface
|
||||
/// Do we autoupdate?
|
||||
var/tgui_update = TRUE
|
||||
/// are we initialized?
|
||||
var/initialized = FALSE
|
||||
|
||||
/datum/admin_modal/New(datum/admins/for_owner)
|
||||
owner = for_owner
|
||||
LAZYADD(owner.admin_modals, src)
|
||||
|
||||
/datum/admin_modal/Destroy()
|
||||
LAZYREMOVE(owner.admin_modals, src)
|
||||
return ..()
|
||||
|
||||
/datum/admin_modal/ui_interact(mob/user, datum/tgui/ui, datum/tgui/parent_ui)
|
||||
ui = SStgui.try_update_ui(user, src, ui)
|
||||
if(!ui)
|
||||
ui = new(user, src, "admin_modal/[tgui_interface]")
|
||||
ui.set_autoupdate(tgui_update)
|
||||
ui.open()
|
||||
|
||||
/datum/admin_modal/ui_state()
|
||||
return GLOB.ui_admin_modal_state
|
||||
|
||||
// TODO: don't destroy if it's part of a disconnect, restore after?
|
||||
/datum/admin_modal/on_ui_close(mob/user, datum/tgui/ui, embedded)
|
||||
..()
|
||||
qdel(src)
|
||||
|
||||
/**
|
||||
* Just a wrapper for opening our UI. Ensures everything is initialized.
|
||||
*/
|
||||
/datum/admin_modal/proc/open()
|
||||
if(!initialized)
|
||||
CRASH("attempted to open an uninitialized admin modal")
|
||||
if(!owner?.owner?.mob)
|
||||
return
|
||||
ui_interact(owner.owner.mob)
|
||||
|
||||
/**
|
||||
* Called with args to open_admin_modal().
|
||||
*/
|
||||
/datum/admin_modal/proc/Initialize(...)
|
||||
initialized = TRUE
|
||||
return TRUE
|
||||
|
||||
/**
|
||||
* Call to complain about a failure in doing something.
|
||||
*
|
||||
* * Failures should generally be done UI-side, this is for when something is seriously wrong.
|
||||
*/
|
||||
/datum/admin_modal/proc/loud_rejection(message)
|
||||
// todo: make this fancier
|
||||
to_chat(owner.owner, "<font color='red'><b>[name]</b>: [message]</font>")
|
||||
|
||||
@@ -0,0 +1,361 @@
|
||||
//* This file is explicitly licensed under the MIT license. *//
|
||||
//* Copyright (c) 2025 Citadel Station Developers *//
|
||||
|
||||
/datum/admin_modal/admin_narrate
|
||||
name = "Admin Narrate"
|
||||
/**
|
||||
* Target. Also determines what modes we can change to.
|
||||
* ### in global, lobby mode
|
||||
* Ignored.
|
||||
* ### in overmap mode
|
||||
* Can be atom, map_level, map, or overmap entity
|
||||
* ### in sector mode
|
||||
* Can be atom, map_level, map
|
||||
* ### in level mode
|
||||
* Can be atom, map_level
|
||||
* ### in range mode
|
||||
* Can be atom
|
||||
* ### in direct mode
|
||||
* Can be movable that is a or can hold mob(s), or client
|
||||
*/
|
||||
var/atom/target
|
||||
|
||||
//* i'm so sorry lohikar but i don't believe in define spam for this *//
|
||||
var/const/M_GLOBAL = "global"
|
||||
var/const/M_SECTOR = "sector"
|
||||
var/const/M_OVERMAP = "overmap"
|
||||
var/const/M_LEVEL = "level"
|
||||
var/const/M_RANGE = "range"
|
||||
var/const/M_DIRECT = "direct"
|
||||
var/const/M_LOBBY = "lobby"
|
||||
|
||||
/// text to send (raw html)
|
||||
var/unsafe_raw_html_to_send
|
||||
|
||||
/// narrate mode
|
||||
var/mode
|
||||
|
||||
/// use line of sight when doing in range?
|
||||
var/use_los = FALSE
|
||||
/// use range in tiles; in overmaps, this is multiples of WORLD_ICON_SIZE
|
||||
var/use_range = 14
|
||||
|
||||
var/const/MAX_LOS_RANGE = 35
|
||||
var/const/MAX_ANY_RANGE = 1024
|
||||
|
||||
/// our identifying color
|
||||
var/narrate_visual_color
|
||||
/// our target image
|
||||
// TODO: visualize it
|
||||
// var/image/narrate_target_image
|
||||
|
||||
/datum/admin_modal/admin_narrate/Initialize(atom/target)
|
||||
. = ..()
|
||||
if(!.)
|
||||
return
|
||||
set_target(target)
|
||||
src.narrate_visual_color = rgb(arglist(hsl2rgb(rand(0, 360), rand(0, 360), rand(125, 360))))
|
||||
|
||||
var/list/possible_modes = resolve_modes()
|
||||
if(length(possible_modes))
|
||||
src.mode = possible_modes[1]
|
||||
else
|
||||
return FALSE
|
||||
|
||||
/datum/admin_modal/admin_narrate/Destroy()
|
||||
set_target(null)
|
||||
return ..()
|
||||
|
||||
/datum/admin_modal/admin_narrate/proc/set_target(atom/target)
|
||||
if(src.target)
|
||||
on_unset_target(src.target)
|
||||
src.target = target
|
||||
if(src.target)
|
||||
on_set_target(src.target)
|
||||
|
||||
/datum/admin_modal/admin_narrate/proc/on_unset_target(atom/new_target)
|
||||
// TODO: visualize it
|
||||
UnregisterSignal(target, COMSIG_PARENT_QDELETING)
|
||||
|
||||
/datum/admin_modal/admin_narrate/proc/on_set_target(atom/new_target)
|
||||
// TODO: visualize it
|
||||
RegisterSignal(target, COMSIG_PARENT_QDELETING, PROC_REF(on_target_del))
|
||||
|
||||
/datum/admin_modal/admin_narrate/proc/on_target_del(datum/source)
|
||||
if(source != target)
|
||||
CRASH("how was target del signal invoked by something that is not our target?")
|
||||
set_target(null)
|
||||
|
||||
/**
|
||||
* @return list of M_ modes
|
||||
*/
|
||||
/datum/admin_modal/admin_narrate/proc/resolve_modes()
|
||||
var/datum/resolved = target
|
||||
if(!resolved)
|
||||
return list(
|
||||
/datum/admin_modal/admin_narrate::M_GLOBAL,
|
||||
/datum/admin_modal/admin_narrate::M_LOBBY,
|
||||
)
|
||||
. = list()
|
||||
|
||||
if(istype(resolved, /obj/overmap/entity))
|
||||
. += /datum/admin_modal/admin_narrate::M_DIRECT
|
||||
. += /datum/admin_modal/admin_narrate::M_OVERMAP
|
||||
else if(isatom(resolved))
|
||||
if(ismovable(resolved))
|
||||
var/atom/movable/casted = resolved
|
||||
if(casted.admin_resolve_narrate())
|
||||
// compatible with direct-narrate
|
||||
. += /datum/admin_modal/admin_narrate::M_DIRECT
|
||||
. += /datum/admin_modal/admin_narrate::M_RANGE
|
||||
. += /datum/admin_modal/admin_narrate::M_LEVEL
|
||||
. += /datum/admin_modal/admin_narrate::M_SECTOR
|
||||
. += /datum/admin_modal/admin_narrate::M_OVERMAP
|
||||
else if(istype(resolved, /datum/map_level))
|
||||
var/datum/map_level/casted = resolved
|
||||
. += /datum/admin_modal/admin_narrate::M_LEVEL
|
||||
. += /datum/admin_modal/admin_narrate::M_SECTOR
|
||||
if(get_overmap_sector(locate(1, 1, casted.z_index)))
|
||||
. += /datum/admin_modal/admin_narrate::M_OVERMAP
|
||||
else if(istype(resolved, /datum/map))
|
||||
. += /datum/admin_modal/admin_narrate::M_SECTOR
|
||||
// TODO: check for overmap binding & add overmap if it's there
|
||||
|
||||
/**
|
||||
* @return list of mobs, or null if invalid
|
||||
*/
|
||||
/datum/admin_modal/admin_narrate/proc/resolve_hearers()
|
||||
// Clients are not allowed in output list; we want mobs, not clients.
|
||||
. = list()
|
||||
switch(mode)
|
||||
if(M_GLOBAL)
|
||||
// do not use client refs directly to prevent gc issues if delayed
|
||||
for(var/client/C as anything in GLOB.clients)
|
||||
. += C.mob
|
||||
if(M_SECTOR)
|
||||
var/atom/resolved = target
|
||||
var/turf/maybe_target_turf = get_turf(resolved)
|
||||
var/datum/map_level/maybe_target_map_level
|
||||
if(maybe_target_turf)
|
||||
maybe_target_map_level = SSmapping.ordered_levels[maybe_target_turf.z]
|
||||
if(!maybe_target_map_level)
|
||||
return null
|
||||
for(var/client/C as anything in GLOB.clients)
|
||||
var/turf/maybe_turf = get_turf(C.mob)
|
||||
if(maybe_turf)
|
||||
var/datum/map_level/maybe_map_level = SSmapping.ordered_levels[maybe_turf.z]
|
||||
if(maybe_map_level?.parent_map == maybe_target_map_level.parent_map)
|
||||
. += C.mob
|
||||
if(M_OVERMAP)
|
||||
var/datum/resolved = target
|
||||
var/obj/overmap/entity/resolved_entity
|
||||
if(istype(resolved, /obj/overmap/entity))
|
||||
resolved_entity = resolved
|
||||
else if(istype(resolved, /atom))
|
||||
resolved_entity = get_overmap_sector(get_turf(resolved))
|
||||
else if(istype(resolved, /datum/map_level))
|
||||
var/datum/map_level/casted = resolved
|
||||
resolved_entity = get_overmap_sector(locate(1, 1, casted.z_index))
|
||||
// TODO: do something
|
||||
else if(istype(resolved, /datum/map))
|
||||
pass()
|
||||
// TODO: do something
|
||||
if(!resolved_entity)
|
||||
return null
|
||||
for(var/client/C as anything in GLOB.clients)
|
||||
var/turf/maybe_turf = get_turf(C.mob)
|
||||
var/obj/overmap/entity/maybe_entity = get_overmap_sector(maybe_turf)
|
||||
if(maybe_entity == resolved_entity)
|
||||
. += C.mob
|
||||
if(M_LEVEL)
|
||||
var/datum/resolved = target
|
||||
var/datum/map_level/resolved_level
|
||||
if(istype(resolved, /atom))
|
||||
var/turf/maybe_turf = get_turf(resolved)
|
||||
if(maybe_turf)
|
||||
resolved_level = SSmapping.ordered_levels[maybe_turf.z]
|
||||
else if(istype(resolved, /datum/map_level))
|
||||
resolved_level = resolved
|
||||
for(var/client/C as anything in GLOB.clients)
|
||||
var/turf/maybe_turf = get_turf(C.mob)
|
||||
if(maybe_turf?.z == resolved_level.z_index)
|
||||
. += C.mob
|
||||
if(M_RANGE)
|
||||
var/atom/resolved = target
|
||||
if(!istype(resolved))
|
||||
return null
|
||||
else
|
||||
if(use_los)
|
||||
for(var/mob/maybe_viewing in viewers(min(MAX_LOS_RANGE, use_range), resolved))
|
||||
if(!maybe_viewing.client)
|
||||
continue
|
||||
. += maybe_viewing
|
||||
else
|
||||
var/our_z = get_z(resolved)
|
||||
for(var/client/C as anything in GLOB.clients)
|
||||
var/mob/maybe_viewing = C.mob
|
||||
if((get_z(maybe_viewing) != our_z) || (get_dist(maybe_viewing, resolved) > use_range))
|
||||
continue
|
||||
if(!maybe_viewing.client)
|
||||
continue
|
||||
. += maybe_viewing
|
||||
if(M_DIRECT)
|
||||
var/atom/movable/resolved = target
|
||||
if(!istype(resolved))
|
||||
return null
|
||||
. += resolved.admin_resolve_narrate()
|
||||
if(M_LOBBY)
|
||||
// do not use client refs directly to prevent gc issues if delayed
|
||||
for(var/client/C as anything in GLOB.clients)
|
||||
if(isnewplayer(C.mob))
|
||||
. += C.mob
|
||||
|
||||
/datum/admin_modal/admin_narrate/proc/get_target_data()
|
||||
var/datum/resolved = target
|
||||
if(!is_target_valid(resolved, mode))
|
||||
return null
|
||||
|
||||
var/turf/maybe_turf
|
||||
var/datum/map_level/maybe_level
|
||||
var/datum/map/maybe_map
|
||||
var/obj/overmap/entity/maybe_entity
|
||||
|
||||
if(istype(resolved, /obj/overmap/entity))
|
||||
maybe_entity = resolved
|
||||
else if(isatom(resolved))
|
||||
maybe_turf = get_turf(resolved)
|
||||
else if(istype(resolved, /datum/map_level))
|
||||
maybe_level = resolved
|
||||
else if(istype(resolved, /datum/map))
|
||||
maybe_map = resolved
|
||||
|
||||
if(maybe_turf)
|
||||
maybe_level = SSmapping.ordered_levels[maybe_turf.z]
|
||||
if(maybe_level)
|
||||
maybe_map = maybe_level.parent_map
|
||||
maybe_entity = get_overmap_sector(maybe_turf)
|
||||
|
||||
. = list()
|
||||
if(maybe_turf)
|
||||
.["coords"] = list(maybe_turf.x, maybe_turf.y, maybe_turf.z)
|
||||
if(maybe_level)
|
||||
.["level"] = list("index" = maybe_level.z_index, "name" = maybe_level.name)
|
||||
if(maybe_map)
|
||||
.["sector"] = list("name" = maybe_map.name)
|
||||
if(maybe_entity)
|
||||
.["overmap"] = list(
|
||||
"name" = maybe_entity.name,
|
||||
"x" = maybe_entity.x,
|
||||
"y" = maybe_entity.y,
|
||||
"map" = maybe_entity.overmap?.name,
|
||||
)
|
||||
|
||||
/datum/admin_modal/admin_narrate/proc/is_target_valid(datum/target, mode)
|
||||
switch(mode)
|
||||
if(M_GLOBAL)
|
||||
return TRUE
|
||||
if(M_LOBBY)
|
||||
return TRUE
|
||||
if(M_SECTOR)
|
||||
return isturf(target) || ismovable(target) || istype(target, /datum/map_level) || istype(target, /datum/map)
|
||||
if(M_OVERMAP)
|
||||
return isturf(target) || ismovable(target) || istype(target, /datum/map_level) || istype(target, /datum/map)
|
||||
if(M_LEVEL)
|
||||
return isturf(target) || ismovable(target) || istype(target, /datum/map_level)
|
||||
if(M_RANGE)
|
||||
return isturf(target) || ismovable(target)
|
||||
if(M_DIRECT)
|
||||
var/atom/movable/casted = target
|
||||
return istype(casted) && casted.admin_resolve_narrate()
|
||||
|
||||
/datum/admin_modal/admin_narrate/proc/narrate()
|
||||
if(!length(unsafe_raw_html_to_send))
|
||||
return
|
||||
var/list/mob/targets = resolve_hearers()
|
||||
|
||||
var/emit = unsafe_raw_html_to_send
|
||||
var/list/view_target_to_list = list()
|
||||
for(var/mob/viewing in targets)
|
||||
view_target_to_list += "[key_name(viewing)]"
|
||||
tim_sort(view_target_to_list, /proc/cmp_text_asc)
|
||||
var/view_target_list = jointext(view_target_to_list, ", ")
|
||||
message_admins("[key_name(owner.owner.mob)] sent a [SPAN_TOOLTIP("[html_encode(emit)]", "global narrate")] to [SPAN_TOOLTIP("[view_target_list]", "[length(targets)] target(s)")].")
|
||||
log_admin("[key_name(owner.owner.mob)] sent a global narrate to [length(targets)] targets; VIEWERS: '[view_target_list]'', TEXT: '[emit]'")
|
||||
|
||||
for(var/mob/viewing in targets)
|
||||
to_chat(viewing, emit)
|
||||
|
||||
/datum/admin_modal/admin_narrate/ui_act(action, list/params, datum/tgui/ui)
|
||||
. = ..()
|
||||
if(.)
|
||||
return
|
||||
switch(action)
|
||||
if("setOutput")
|
||||
unsafe_raw_html_to_send = params["target"]
|
||||
return TRUE
|
||||
if("setRange")
|
||||
use_range = params["target"]
|
||||
if(use_los)
|
||||
use_range = clamp(use_range, 0, MAX_LOS_RANGE)
|
||||
else
|
||||
use_range = clamp(use_range, 0, MAX_ANY_RANGE)
|
||||
return TRUE
|
||||
if("setMode")
|
||||
mode = params["mode"]
|
||||
return TRUE
|
||||
if("setLos")
|
||||
use_los = !!params["target"]
|
||||
if(use_los)
|
||||
use_range = clamp(use_range, 0, MAX_LOS_RANGE)
|
||||
else
|
||||
use_range = clamp(use_range, 0, MAX_ANY_RANGE)
|
||||
return TRUE
|
||||
if("narrate")
|
||||
if(params["html"])
|
||||
unsafe_raw_html_to_send = params["html"]
|
||||
narrate()
|
||||
qdel(src)
|
||||
return TRUE
|
||||
if("cancel")
|
||||
qdel(src)
|
||||
return TRUE
|
||||
if("preview")
|
||||
var/emit = params["html"]
|
||||
var/list/mob/targets = resolve_hearers()
|
||||
var/list/rendered_viewers_list = list()
|
||||
for(var/mob/target as anything in targets)
|
||||
rendered_viewers_list += "[target.name][target.real_name != target.name ? " ([target.real_name])" : ""]"
|
||||
tim_sort(rendered_viewers_list, /proc/cmp_text_asc)
|
||||
var/rendered_viewers = jointext(rendered_viewers_list, "<br>")
|
||||
var/list/html = list(
|
||||
"<hr>",
|
||||
SPAN_BLOCKQUOTE(emit, null),
|
||||
"<hr>",
|
||||
"<center>[SPAN_ADMIN("^^^ Narrate Preview; [length(rendered_viewers) ? SPAN_TOOLTIP(rendered_viewers, "Viewers..."): "No Viewers!"] ^^^")]</center>",
|
||||
)
|
||||
to_chat(owner.owner, jointext(html, ""))
|
||||
return TRUE
|
||||
|
||||
/datum/admin_modal/admin_narrate/ui_interact(mob/user, datum/tgui/ui, datum/tgui/parent_ui)
|
||||
ui = SStgui.try_update_ui(user, src, ui)
|
||||
if(!ui)
|
||||
ui = new(user, src, "admin_modal/AdminNarrate")
|
||||
ui.open()
|
||||
|
||||
/datum/admin_modal/admin_narrate/ui_static_data(mob/user, datum/tgui/ui)
|
||||
. = ..()
|
||||
// UI-authoritative, so this is only even sent to server
|
||||
// to keep the UI persistent across reconnects.
|
||||
.["rawHtml"] = unsafe_raw_html_to_send
|
||||
.["visualColor"] = narrate_visual_color
|
||||
.["possibleModes"] = resolve_modes()
|
||||
|
||||
/datum/admin_modal/admin_narrate/ui_data(mob/user, datum/tgui/ui)
|
||||
. = ..()
|
||||
// This however needs to update
|
||||
.["target"] = get_target_data()
|
||||
.["mode"] = mode
|
||||
.["useLos"] = use_los
|
||||
.["useRange"] = use_range
|
||||
.["maxRangeLos"] = MAX_LOS_RANGE
|
||||
.["maxRangeAny"] = MAX_ANY_RANGE
|
||||
@@ -0,0 +1,45 @@
|
||||
//* This file is explicitly licensed under the MIT license. *//
|
||||
//* Copyright (c) 2024 Citadel Station Developers *//
|
||||
|
||||
GLOBAL_REAL_PROTECT(admin_verbs)
|
||||
GLOBAL_REAL_LIST(admin_verb_descriptors) = zz__prepare_admin_verb_descriptors()
|
||||
|
||||
/proc/zz__prepare_admin_verb_descriptors()
|
||||
. = list()
|
||||
for(var/datum/admin_verb_descriptor/path as anything in subtypesof(/datum/admin_verb_descriptor))
|
||||
. += new path
|
||||
|
||||
/**
|
||||
* Describes an admin verb.
|
||||
*
|
||||
* When admin verbs should be used:
|
||||
* * Core debug functions (VV, SDQL, similar). These should **never** be abstracted to complex
|
||||
* systems like TGUI, as we need thse to debug said complex systems!
|
||||
* * Anything that invokes on right click
|
||||
* * Anything that is very simple to invoke ('spawn') and is used enough that removing it
|
||||
* for a panel makes no sense as it would make the lives of people using the in-game
|
||||
* command line worse!
|
||||
*/
|
||||
/datum/admin_verb_descriptor
|
||||
/// our unique ID; this is based on verb path!
|
||||
var/id
|
||||
/// our verb path
|
||||
var/verb_path
|
||||
/// the reflection path to read from
|
||||
var/reflection_path
|
||||
/// required rights flags
|
||||
var/required_rights
|
||||
|
||||
//* detected from verb *//
|
||||
|
||||
/// our name
|
||||
var/name
|
||||
/// our desc
|
||||
var/desc
|
||||
|
||||
/datum/admin_verb_descriptor/New()
|
||||
if(!verb_path)
|
||||
return
|
||||
var/procpath/cast_verb = reflection_path
|
||||
name = cast_verb.name
|
||||
desc = cast_verb.desc
|
||||
@@ -51,10 +51,6 @@ var/list/admin_verbs_admin = list(
|
||||
/client/proc/jumptoturf, //allows us to jump to a specific turf,
|
||||
/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/cmd_admin_direct_narrate, //send text directly to a player with no padding. Useful for narratives and fluff-text,
|
||||
/client/proc/cmd_admin_local_narrate,
|
||||
/client/proc/cmd_admin_world_narrate,
|
||||
/client/proc/cmd_admin_z_narrate, //sends text to all players on a z-level.When Global is too much
|
||||
/client/proc/cmd_admin_create_centcom_report,
|
||||
/client/proc/check_words, //displays cult-words,
|
||||
/client/proc/check_ai_laws, //shows AI and borg laws,
|
||||
@@ -282,10 +278,6 @@ var/list/admin_verbs_hideable = list(
|
||||
/datum/admins/proc/access_news_network,
|
||||
/client/proc/admin_call_shuttle,
|
||||
/client/proc/admin_cancel_shuttle,
|
||||
/client/proc/cmd_admin_direct_narrate,
|
||||
/client/proc/cmd_admin_local_narrate,
|
||||
/client/proc/cmd_admin_world_narrate,
|
||||
/client/proc/cmd_admin_z_narrate,
|
||||
/client/proc/check_words,
|
||||
/client/proc/play_local_sound,
|
||||
/client/proc/play_sound,
|
||||
@@ -364,7 +356,6 @@ var/list/admin_verbs_mod = list(
|
||||
/client/proc/cmd_admin_subtle_message, //send an message to somebody as a 'voice in their head',
|
||||
/client/proc/cmd_admin_icsubtle_message,
|
||||
/datum/admins/proc/paralyze_mob,
|
||||
/client/proc/cmd_admin_direct_narrate,
|
||||
/client/proc/allow_character_respawn, // Allows a ghost to respawn ,
|
||||
/datum/admins/proc/sendFax,
|
||||
/client/proc/addbunkerbypass,
|
||||
@@ -386,7 +377,6 @@ var/list/admin_verbs_event_manager = list(
|
||||
/client/proc/aooc,
|
||||
/client/proc/cmd_admin_clear_mobs,
|
||||
/datum/admins/proc/paralyze_mob,
|
||||
/client/proc/cmd_admin_direct_narrate,
|
||||
/client/proc/allow_character_respawn,
|
||||
/datum/admins/proc/sendFax,
|
||||
/client/proc/respawn_character,
|
||||
@@ -776,7 +766,7 @@ var/list/admin_verbs_event_manager = list(
|
||||
set category = "Admin"
|
||||
|
||||
if(deadmin_holder)
|
||||
deadmin_holder.reassociate()
|
||||
deadmin_holder.associate(src)
|
||||
log_admin("[src] re-admined themself.")
|
||||
message_admins("[src] re-admined themself.", 1)
|
||||
to_chat(src, "<span class='interface'>You now have the keys to control the planet, or atleast a small space station</span>")
|
||||
|
||||
@@ -1574,7 +1574,7 @@
|
||||
if(!check_rights(R_ADMIN)) return
|
||||
|
||||
var/mob/M = locate(href_list["narrateto"])
|
||||
usr.client.cmd_admin_direct_narrate(M)
|
||||
usr.client.holder.open_admin_modal(/datum/admin_modal/admin_narrate, M)
|
||||
|
||||
else if(href_list["subtlemessage"])
|
||||
if(!check_rights(R_MOD,0) && !check_rights(R_ADMIN)) return
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
/client/proc/cmd_admin_delete(atom/A as obj|mob|turf in world)
|
||||
set category = "Admin"
|
||||
set name = "Delete"
|
||||
|
||||
if(!check_rights(R_SPAWN|R_DEBUG|R_ADMIN))
|
||||
return
|
||||
|
||||
admin_delete(A)
|
||||
@@ -1,5 +1,5 @@
|
||||
/client/proc/Debug2()
|
||||
set category = ADMIN_CATEGORY_DEBUG
|
||||
set category = VERB_CATEGORY_DEBUG
|
||||
set name = "Debug-Game"
|
||||
set desc = "Toggles game debugging."
|
||||
|
||||
@@ -118,7 +118,7 @@
|
||||
return types[key]
|
||||
|
||||
/client/proc/cmd_del_all(object as text)
|
||||
set category = ADMIN_CATEGORY_DEBUG
|
||||
set category = VERB_CATEGORY_DEBUG
|
||||
set name = "Del-All"
|
||||
set desc = "Delete all datums with the specified type."
|
||||
|
||||
@@ -138,7 +138,7 @@
|
||||
message_admins("[key_name_admin(src)] has deleted all ([counter]) instances of [type_to_del].")
|
||||
|
||||
/client/proc/cmd_del_all_force(object as text)
|
||||
set category = ADMIN_CATEGORY_DEBUG
|
||||
set category = VERB_CATEGORY_DEBUG
|
||||
set name = "Force-Del-All"
|
||||
set desc = "Forcibly delete all datums with the specified type."
|
||||
|
||||
@@ -158,7 +158,7 @@
|
||||
message_admins("[key_name_admin(src)] has force-deleted all ([counter]) instances of [type_to_del].")
|
||||
|
||||
/client/proc/cmd_del_all_hard(object as text)
|
||||
set category = ADMIN_CATEGORY_DEBUG
|
||||
set category = VERB_CATEGORY_DEBUG
|
||||
set name = "Hard-Del-All"
|
||||
set desc = "Hard delete all datums with the specified type."
|
||||
|
||||
@@ -203,7 +203,7 @@
|
||||
message_admins("[key_name_admin(src)] has hard deleted all ([counter]) instances of [type_to_del].")
|
||||
|
||||
/client/proc/cmd_debug_make_powernets()
|
||||
set category = ADMIN_CATEGORY_DEBUG
|
||||
set category = VERB_CATEGORY_DEBUG
|
||||
set name = "Make Powernets"
|
||||
set desc = "Regenerates all powernets for all cables."
|
||||
|
||||
@@ -215,7 +215,7 @@
|
||||
feedback_add_details("admin_verb","MPWN") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
/client/proc/cmd_admin_grantfullaccess(var/mob/M in GLOB.mob_list)
|
||||
set category = ADMIN_CATEGORY_DEBUG
|
||||
set category = VERB_CATEGORY_DEBUG
|
||||
set name = "Grant Full Access"
|
||||
set desc = "Grant full access to a mob."
|
||||
|
||||
@@ -250,7 +250,7 @@
|
||||
message_admins(SPAN_ADMINNOTICE("[key_name_admin(usr)] has granted [M.key] full access."))
|
||||
|
||||
/client/proc/cmd_debug_mob_lists()
|
||||
set category = ADMIN_CATEGORY_DEBUG
|
||||
set category = VERB_CATEGORY_DEBUG
|
||||
set name = "Debug Mob Lists"
|
||||
set desc = "For when you just gotta know"
|
||||
|
||||
@@ -274,7 +274,7 @@
|
||||
to_chat(usr, jointext(GLOB.clients,","), confidential = TRUE)
|
||||
|
||||
/client/proc/cmd_display_del_log()
|
||||
set category = ADMIN_CATEGORY_DEBUG
|
||||
set category = VERB_CATEGORY_DEBUG
|
||||
set name = "Display del() Log"
|
||||
set desc = "Display del's log of everything that's passed through it."
|
||||
|
||||
@@ -307,7 +307,7 @@
|
||||
browser.open()
|
||||
|
||||
/client/proc/cmd_display_overlay_log()
|
||||
set category = ADMIN_CATEGORY_DEBUG
|
||||
set category = VERB_CATEGORY_DEBUG
|
||||
set name = "Display overlay Log"
|
||||
set desc = "Display SSoverlays log of everything that's passed through it."
|
||||
|
||||
@@ -316,7 +316,7 @@
|
||||
render_stats(SSoverlays.stats, src)
|
||||
|
||||
/client/proc/cmd_display_init_log()
|
||||
set category = ADMIN_CATEGORY_DEBUG
|
||||
set category = VERB_CATEGORY_DEBUG
|
||||
set name = "Display Initialize() Log"
|
||||
set desc = "Displays a list of things that didn't handle Initialize() properly"
|
||||
|
||||
@@ -327,7 +327,7 @@
|
||||
browser.open()
|
||||
|
||||
/datum/admins/proc/view_runtimes()
|
||||
set category = ADMIN_CATEGORY_DEBUG
|
||||
set category = VERB_CATEGORY_DEBUG
|
||||
set name = "View Runtimes"
|
||||
set desc = "Open the Runtime Viewer"
|
||||
|
||||
@@ -345,7 +345,7 @@
|
||||
alert(src, "[warning]. Proceed with caution. If you really need to see the runtimes, download the runtime log and view it in a text editor.", "HEED THIS WARNING CAREFULLY MORTAL")
|
||||
|
||||
/client/proc/check_timer_sources()
|
||||
set category = ADMIN_CATEGORY_DEBUG
|
||||
set category = VERB_CATEGORY_DEBUG
|
||||
set name = "Check Timer Sources"
|
||||
set desc = "Checks the sources of running timers."
|
||||
|
||||
@@ -365,7 +365,7 @@
|
||||
browser.open()
|
||||
|
||||
/client/proc/toggle_browser_inspect()
|
||||
set category = ADMIN_CATEGORY_DEBUG
|
||||
set category = VERB_CATEGORY_DEBUG
|
||||
set name = "Toggle Browser Inspect"
|
||||
set desc = "Toggle browser debugging via inspect"
|
||||
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
//* This file is explicitly licensed under the MIT license. *//
|
||||
//* Copyright (c) 2025 Citadel Station Developers *//
|
||||
|
||||
ADMIN_VERB_DEF(narrate, R_ADMIN, "Narrate", "Perform narration.", VERB_CATEGORY_GAME, atom/target as null|obj|mob|turf in world)
|
||||
caller.holder.open_admin_modal(/datum/admin_modal/admin_narrate, target)
|
||||
@@ -0,0 +1,95 @@
|
||||
//* This file is explicitly licensed under the MIT license. *//
|
||||
//* Copyright (c) 2025 Citadel Station Developers *//
|
||||
|
||||
ADMIN_VERB_DEF(narrate_quick, R_ADMIN, "Narrate (Quick)", "Perform narration.", VERB_CATEGORY_GAME, atom/target as null|obj|mob|turf in world)
|
||||
|
||||
var/use_global
|
||||
var/datum/weakref/use_viewers
|
||||
var/datum/weakref/use_overmap
|
||||
var/datum/weakref/use_direct
|
||||
|
||||
var/target_name_descriptor = "ERROR"
|
||||
var/target_long_descriptor = "ERROR"
|
||||
|
||||
if(isnull(target))
|
||||
use_global = TRUE
|
||||
target_name_descriptor = "(Everyone)"
|
||||
target_long_descriptor = "Narrate to the entire server, even those in lobby."
|
||||
else if(istype(target, /obj/overmap/entity))
|
||||
use_overmap = WEAKREF(target)
|
||||
target_name_descriptor = "([target])"
|
||||
target_long_descriptor = "Narrate to everyone aboard / present on \the [target]."
|
||||
else if(isturf(target))
|
||||
use_viewers = WEAKREF(target)
|
||||
target_name_descriptor = "(viewing [target] @ [target.x], [target.y], [target.y])"
|
||||
target_long_descriptor = "Narrate to everyone who can view the turf ([target]) at [target.x], [target.y], [target.z]."
|
||||
else if(ismovable(target))
|
||||
var/atom/movable/target_movable = target
|
||||
if(target_movable.admin_resolve_narrate())
|
||||
use_direct = WEAKREF(target_movable)
|
||||
target_name_descriptor = "([target])"
|
||||
target_long_descriptor = "Narrate to everyone inside / the person at / the target of '[target]' (currently at \the [get_area(target)])."
|
||||
else
|
||||
use_viewers = WEAKREF(target_movable)
|
||||
target_name_descriptor = "(viewing [target])"
|
||||
target_long_descriptor = "Narrate to everyone who can see '[target]' (currently at \the [get_area(target)])."
|
||||
|
||||
var/emit = tgui_input_text(caller, target_long_descriptor, "Narrate to [target_name_descriptor]", "", 65535, TRUE, FALSE)
|
||||
if(!emit)
|
||||
return
|
||||
|
||||
var/list/mob/targets = list()
|
||||
var/reject
|
||||
|
||||
if(use_global)
|
||||
for(var/client/C as anything in GLOB.clients)
|
||||
targets += C.mob
|
||||
else if(use_viewers)
|
||||
var/atom/resolved = use_viewers.resolve()
|
||||
if(!istype(resolved))
|
||||
reject = "use_viewers failed atom resolution; this is a bug."
|
||||
else
|
||||
for(var/mob/maybe_viewing in viewers(35, resolved))
|
||||
if(!maybe_viewing.client)
|
||||
continue
|
||||
targets += maybe_viewing
|
||||
else if(use_overmap)
|
||||
var/obj/overmap/entity/resolved = use_viewers.resolve()
|
||||
if(!istype(resolved))
|
||||
reject = "use_overmap failed entity resolution; this is a bug."
|
||||
else
|
||||
// sigh make this better later
|
||||
for(var/client/C as anything in GLOB.clients)
|
||||
if(get_overmap_sector(C.mob) == resolved)
|
||||
targets += C.mob
|
||||
else if(use_direct)
|
||||
var/atom/movable/resolved = use_viewers.resolve()
|
||||
if(!istype(resolved))
|
||||
reject = "use_direct failed target resolution; this is a bug."
|
||||
else
|
||||
targets = resolved.admin_resolve_narrate()
|
||||
else
|
||||
reject = "no narrate mode resolved; this is a bug."
|
||||
|
||||
if(!reject && !length(targets))
|
||||
reject = "no targets in range; skipping narrate sequence."
|
||||
|
||||
if(reject)
|
||||
var/list/html = list(
|
||||
"<hr>",
|
||||
SPAN_BLOCKQUOTE(emit, null),
|
||||
"<hr>",
|
||||
"<center><span style='font-weight: bold; color: red;'>^^^ ERROR: The above was not sent; [reject] ^^^</span></center>",
|
||||
)
|
||||
to_chat(caller, jointext(html, ""))
|
||||
return
|
||||
|
||||
var/list/view_target_to_list = list()
|
||||
for(var/mob/viewing in targets)
|
||||
view_target_to_list += "[key_name(viewing)]"
|
||||
var/view_target_list = jointext(view_target_to_list, ", ")
|
||||
message_admins("[key_name(caller)] sent a [SPAN_TOOLTIP("[html_encode(emit)]", "global narrate")] to [SPAN_TOOLTIP("[view_target_list]", "[length(targets)] target(s)")].")
|
||||
log_admin("[key_name(caller)] sent a global narrate to [length(targets)] targets; VIEWERS: '[view_target_list]'', TEXT: '[emit]'")
|
||||
|
||||
for(var/mob/viewing in targets)
|
||||
to_chat(viewing, emit)
|
||||
@@ -101,86 +101,6 @@
|
||||
admin_ticket_log(M, msg)
|
||||
feedback_add_details("admin_verb","SMS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
/client/proc/cmd_admin_world_narrate()
|
||||
set category = "Special Verbs"
|
||||
set name = "Global Narrate"
|
||||
|
||||
if(!check_rights(R_ADMIN))
|
||||
return
|
||||
|
||||
var/msg = input("Message:", "Enter the text you wish to appear to everyone:") as text|null
|
||||
|
||||
if (!msg)
|
||||
return
|
||||
to_chat(world, "[msg]")
|
||||
log_admin("GlobalNarrate: [key_name(usr)] : [msg]")
|
||||
message_admins("<span class='adminnotice'>[key_name_admin(usr)] Sent a global narrate</span>")
|
||||
// SSblackbox.record_feedback("tally", "admin_verb", 1, "Global Narrate") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
/client/proc/cmd_admin_direct_narrate(mob/M)
|
||||
set category = "Special Verbs"
|
||||
set name = "Direct Narrate"
|
||||
|
||||
if(!check_rights(R_ADMIN))
|
||||
return
|
||||
|
||||
if(!M)
|
||||
M = input("Direct narrate to whom?", "Active Players") as null|anything in GLOB.player_list
|
||||
|
||||
if(!M)
|
||||
return
|
||||
|
||||
var/msg = input("Message:", "Enter the text you wish to appear to your target:") as text|null
|
||||
|
||||
if( !msg )
|
||||
return
|
||||
|
||||
to_chat(M, msg)
|
||||
log_admin("DirectNarrate: [key_name(usr)] to ([M.name]/[M.key]): [msg]")
|
||||
msg = "<span class='adminnotice'><b> DirectNarrate: [key_name(usr)] to ([M.name]/[M.key]):</b> [msg]<BR></span>"
|
||||
message_admins(msg)
|
||||
admin_ticket_log(M, msg)
|
||||
// SSblackbox.record_feedback("tally", "admin_verb", 1, "Direct Narrate") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
/client/proc/cmd_admin_local_narrate(atom/A)
|
||||
set category = "Special Verbs"
|
||||
set name = "Local Narrate"
|
||||
|
||||
if(!check_rights(R_ADMIN))
|
||||
return
|
||||
if(!A)
|
||||
return
|
||||
var/range = input("Range:", "Narrate to mobs within how many tiles:", 7) as num|null
|
||||
if(!range)
|
||||
return
|
||||
var/msg = input("Message:", "Enter the text you wish to appear to everyone within view:") as text|null
|
||||
if (!msg)
|
||||
return
|
||||
for(var/mob/M in view(range,A))
|
||||
to_chat(M, msg)
|
||||
|
||||
log_admin("LocalNarrate: [key_name(usr)] at [AREACOORD(A)]: [msg]")
|
||||
message_admins("<span class='adminnotice'><b> LocalNarrate: [key_name_admin(usr)] at [ADMIN_COORDJMP(A)]:</b> [msg]<BR></span>")
|
||||
// SSblackbox.record_feedback("tally", "admin_verb", 1, "Local Narrate") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
/client/proc/cmd_admin_z_narrate()
|
||||
set category = "Special Verbs"
|
||||
set name = "Z Narrate"
|
||||
|
||||
if(!check_rights(R_ADMIN))
|
||||
return
|
||||
|
||||
var/msg = input("Enter the text you wish to show an entire Z level:", "Z Narrate:") as text|null
|
||||
|
||||
if (!msg)
|
||||
return
|
||||
|
||||
for(var/mob/M in range(192)) //Yes this is lazy
|
||||
to_chat(M, msg)
|
||||
|
||||
log_admin("ZNarrate: [key_name(usr)] at [ADMIN_COORDJMP(usr)]: [msg]")
|
||||
message_admins("<span class='adminnotice'><b> ZNarrate: [key_name_admin(usr)] at [ADMIN_COORDJMP(usr)]:</b> [msg]<BR></span>")
|
||||
|
||||
|
||||
/client/proc/cmd_admin_godmode(mob/M as mob in GLOB.mob_list)
|
||||
set category = "Special Verbs"
|
||||
@@ -632,15 +552,6 @@ Traitors and the like can also be revived with the previous role mostly intact.
|
||||
message_admins("[key_name_admin(src)] has created a command report", 1)
|
||||
feedback_add_details("admin_verb","CCR") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
/client/proc/cmd_admin_delete(atom/A as obj|mob|turf in world)
|
||||
set category = "Admin"
|
||||
set name = "Delete"
|
||||
|
||||
if(!check_rights(R_SPAWN|R_DEBUG|R_ADMIN))
|
||||
return
|
||||
|
||||
admin_delete(A)
|
||||
|
||||
/client/proc/cmd_admin_list_open_jobs()
|
||||
set category = "Admin"
|
||||
set name = "List free slots"
|
||||
|
||||
Reference in New Issue
Block a user