mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-07-18 11:36:24 +01:00
Force LF line endings with gitattributes and convert repo (#52266)
Co-authored-by: Aleksej Komarov <stylemistake@gmail.com>
This commit is contained in:
+243
-243
@@ -1,243 +1,243 @@
|
||||
//Blocks an attempt to connect before even creating our client datum thing.
|
||||
|
||||
//How many new ckey matches before we revert the stickyban to it's roundstart state
|
||||
//These are exclusive, so once it goes over one of these numbers, it reverts the ban
|
||||
#define STICKYBAN_MAX_MATCHES 15
|
||||
#define STICKYBAN_MAX_EXISTING_USER_MATCHES 3 //ie, users who were connected before the ban triggered
|
||||
#define STICKYBAN_MAX_ADMIN_MATCHES 1
|
||||
|
||||
/world/IsBanned(key, address, computer_id, type, real_bans_only=FALSE)
|
||||
debug_world_log("isbanned(): '[args.Join("', '")]'")
|
||||
if (!key || (!real_bans_only && (!address || !computer_id)))
|
||||
if(real_bans_only)
|
||||
return FALSE
|
||||
log_access("Failed Login (invalid data): [key] [address]-[computer_id]")
|
||||
return list("reason"="invalid login data", "desc"="Error: Could not check ban status, Please try again. Error message: Your computer provided invalid or blank information to the server on connection (byond username, IP, and Computer ID.) Provided information for reference: Username:'[key]' IP:'[address]' Computer ID:'[computer_id]'. (If you continue to get this error, please restart byond or contact byond support.)")
|
||||
|
||||
if (type == "world")
|
||||
return ..() //shunt world topic banchecks to purely to byond's internal ban system
|
||||
|
||||
var/admin = FALSE
|
||||
var/ckey = ckey(key)
|
||||
|
||||
var/client/C = GLOB.directory[ckey]
|
||||
if (C && ckey == C.ckey && computer_id == C.computer_id && address == C.address)
|
||||
return //don't recheck connected clients.
|
||||
|
||||
//IsBanned can get re-called on a user in certain situations, this prevents that leading to repeated messages to admins.
|
||||
var/static/list/checkedckeys = list()
|
||||
//magic voodo to check for a key in a list while also adding that key to the list without having to do two associated lookups
|
||||
var/message = !checkedckeys[ckey]++
|
||||
|
||||
if(GLOB.admin_datums[ckey] || GLOB.deadmins[ckey])
|
||||
admin = TRUE
|
||||
|
||||
|
||||
//Whitelist
|
||||
if(!real_bans_only && !C && CONFIG_GET(flag/usewhitelist))
|
||||
if(!check_whitelist(ckey))
|
||||
if (admin)
|
||||
log_admin("The admin [key] has been allowed to bypass the whitelist")
|
||||
if (message)
|
||||
message_admins("<span class='adminnotice'>The admin [key] has been allowed to bypass the whitelist</span>")
|
||||
addclientmessage(ckey,"<span class='adminnotice'>You have been allowed to bypass the whitelist</span>")
|
||||
else
|
||||
log_access("Failed Login: [key] - Not on whitelist")
|
||||
return list("reason"="whitelist", "desc" = "\nReason: You are not on the white list for this server")
|
||||
|
||||
//Guest Checking
|
||||
if(!real_bans_only && !C && IsGuestKey(key))
|
||||
if (CONFIG_GET(flag/guest_ban))
|
||||
log_access("Failed Login: [key] - Guests not allowed")
|
||||
return list("reason"="guest", "desc"="\nReason: Guests not allowed. Please sign in with a byond account.")
|
||||
if (CONFIG_GET(flag/panic_bunker) && SSdbcore.Connect())
|
||||
log_access("Failed Login: [key] - Guests not allowed during panic bunker")
|
||||
return list("reason"="guest", "desc"="\nReason: Sorry but the server is currently not accepting connections from never before seen players or guests. If you have played on this server with a byond account before, please log in to the byond account you have played from.")
|
||||
|
||||
//Population Cap Checking
|
||||
var/extreme_popcap = CONFIG_GET(number/extreme_popcap)
|
||||
if(!real_bans_only && !C && extreme_popcap && !admin)
|
||||
var/popcap_value = GLOB.clients.len
|
||||
if(popcap_value >= extreme_popcap && !GLOB.joined_player_list.Find(ckey))
|
||||
if(!CONFIG_GET(flag/byond_member_bypass_popcap) || !world.IsSubscribed(ckey, "BYOND"))
|
||||
log_access("Failed Login: [key] - Population cap reached")
|
||||
return list("reason"="popcap", "desc"= "\nReason: [CONFIG_GET(string/extreme_popcap_message)]")
|
||||
|
||||
if(CONFIG_GET(flag/sql_enabled))
|
||||
if(!SSdbcore.Connect())
|
||||
var/msg = "Ban database connection failure. Key [ckey] not checked"
|
||||
log_world(msg)
|
||||
if (message)
|
||||
message_admins(msg)
|
||||
else
|
||||
var/list/ban_details = is_banned_from_with_details(ckey, address, computer_id, "Server")
|
||||
for(var/i in ban_details)
|
||||
if(admin)
|
||||
if(text2num(i["applies_to_admins"]))
|
||||
var/msg = "Admin [key] is admin banned, and has been disallowed access."
|
||||
log_admin(msg)
|
||||
if (message)
|
||||
message_admins(msg)
|
||||
else
|
||||
var/msg = "Admin [key] has been allowed to bypass a matching non-admin ban on [i["key"]] [i["ip"]]-[i["computerid"]]."
|
||||
log_admin(msg)
|
||||
if (message)
|
||||
message_admins(msg)
|
||||
addclientmessage(ckey,"<span class='adminnotice'>Admin [key] has been allowed to bypass a matching non-admin ban on [i["key"]] [i["ip"]]-[i["computerid"]].</span>")
|
||||
continue
|
||||
var/expires = "This is a permanent ban."
|
||||
if(i["expiration_time"])
|
||||
expires = " The ban is for [DisplayTimeText(text2num(i["duration"]) MINUTES)] and expires on [i["expiration_time"]] (server time)."
|
||||
var/desc = {"You, or another user of this computer or connection ([i["key"]]) is banned from playing here.
|
||||
The ban reason is: [i["reason"]]
|
||||
This ban (BanID #[i["id"]]) was applied by [i["admin_key"]] on [i["bantime"]] during round ID [i["round_id"]].
|
||||
[expires]"}
|
||||
log_access("Failed Login: [key] [computer_id] [address] - Banned (#[i["id"]])")
|
||||
return list("reason"="Banned","desc"="[desc]")
|
||||
if (admin)
|
||||
if (GLOB.directory[ckey])
|
||||
return
|
||||
|
||||
//oh boy, so basically, because of a bug in byond, sometimes stickyban matches don't trigger here, so we can't exempt admins.
|
||||
// Whitelisting the ckey with the byond whitelist field doesn't work.
|
||||
// So we instead have to remove every stickyban than later re-add them.
|
||||
if (!length(GLOB.stickybanadminexemptions))
|
||||
for (var/banned_ckey in world.GetConfig("ban"))
|
||||
GLOB.stickybanadmintexts[banned_ckey] = world.GetConfig("ban", banned_ckey)
|
||||
world.SetConfig("ban", banned_ckey, null)
|
||||
if (!SSstickyban.initialized)
|
||||
return
|
||||
GLOB.stickybanadminexemptions[ckey] = world.time
|
||||
stoplag() // sleep a byond tick
|
||||
GLOB.stickbanadminexemptiontimerid = addtimer(CALLBACK(GLOBAL_PROC, /proc/restore_stickybans), 5 SECONDS, TIMER_STOPPABLE|TIMER_UNIQUE|TIMER_OVERRIDE)
|
||||
return
|
||||
var/list/ban = ..() //default pager ban stuff
|
||||
|
||||
if (ban)
|
||||
if (!admin)
|
||||
. = ban
|
||||
if (real_bans_only)
|
||||
return
|
||||
var/bannedckey = "ERROR"
|
||||
if (ban["ckey"])
|
||||
bannedckey = ban["ckey"]
|
||||
|
||||
var/newmatch = FALSE
|
||||
var/list/cachedban = SSstickyban.cache[bannedckey]
|
||||
//rogue ban in the process of being reverted.
|
||||
if (cachedban && (cachedban["reverting"] || cachedban["timeout"]))
|
||||
world.SetConfig("ban", bannedckey, null)
|
||||
return null
|
||||
|
||||
if (cachedban && ckey != bannedckey)
|
||||
newmatch = TRUE
|
||||
if (cachedban["keys"])
|
||||
if (cachedban["keys"][ckey])
|
||||
newmatch = FALSE
|
||||
if (cachedban["matches_this_round"][ckey])
|
||||
newmatch = FALSE
|
||||
|
||||
if (newmatch && cachedban)
|
||||
var/list/newmatches = cachedban["matches_this_round"]
|
||||
var/list/pendingmatches = cachedban["matches_this_round"]
|
||||
var/list/newmatches_connected = cachedban["existing_user_matches_this_round"]
|
||||
var/list/newmatches_admin = cachedban["admin_matches_this_round"]
|
||||
|
||||
if (C)
|
||||
newmatches_connected[ckey] = ckey
|
||||
newmatches_connected = cachedban["existing_user_matches_this_round"]
|
||||
pendingmatches[ckey] = ckey
|
||||
sleep(STICKYBAN_ROGUE_CHECK_TIME)
|
||||
pendingmatches -= ckey
|
||||
if (admin)
|
||||
newmatches_admin[ckey] = ckey
|
||||
|
||||
if (cachedban["reverting"] || cachedban["timeout"])
|
||||
return null
|
||||
|
||||
newmatches[ckey] = ckey
|
||||
|
||||
|
||||
if (\
|
||||
newmatches.len+pendingmatches.len > STICKYBAN_MAX_MATCHES || \
|
||||
newmatches_connected.len > STICKYBAN_MAX_EXISTING_USER_MATCHES || \
|
||||
newmatches_admin.len > STICKYBAN_MAX_ADMIN_MATCHES \
|
||||
)
|
||||
|
||||
var/action
|
||||
if (ban["fromdb"])
|
||||
cachedban["timeout"] = TRUE
|
||||
action = "putting it on timeout for the remainder of the round"
|
||||
else
|
||||
cachedban["reverting"] = TRUE
|
||||
action = "reverting to its roundstart state"
|
||||
|
||||
world.SetConfig("ban", bannedckey, null)
|
||||
|
||||
//we always report this
|
||||
log_game("Stickyban on [bannedckey] detected as rogue, [action]")
|
||||
message_admins("Stickyban on [bannedckey] detected as rogue, [action]")
|
||||
//do not convert to timer.
|
||||
spawn (5)
|
||||
world.SetConfig("ban", bannedckey, null)
|
||||
sleep(1)
|
||||
world.SetConfig("ban", bannedckey, null)
|
||||
if (!ban["fromdb"])
|
||||
cachedban = cachedban.Copy() //so old references to the list still see the ban as reverting
|
||||
cachedban["matches_this_round"] = list()
|
||||
cachedban["existing_user_matches_this_round"] = list()
|
||||
cachedban["admin_matches_this_round"] = list()
|
||||
cachedban -= "reverting"
|
||||
SSstickyban.cache[bannedckey] = cachedban
|
||||
world.SetConfig("ban", bannedckey, list2stickyban(cachedban))
|
||||
return null
|
||||
|
||||
if (ban["fromdb"])
|
||||
if(SSdbcore.Connect())
|
||||
INVOKE_ASYNC(SSdbcore, /datum/controller/subsystem/dbcore/proc.QuerySelect, list(
|
||||
SSdbcore.NewQuery(
|
||||
"INSERT INTO [format_table_name("stickyban_matched_ckey")] (matched_ckey, stickyban) VALUES (:ckey, :bannedckey) ON DUPLICATE KEY UPDATE last_matched = now()",
|
||||
list("ckey" = ckey, "bannedckey" = bannedckey)
|
||||
),
|
||||
SSdbcore.NewQuery(
|
||||
"INSERT INTO [format_table_name("stickyban_matched_ip")] (matched_ip, stickyban) VALUES (INET_ATON(:address), :bannedckey) ON DUPLICATE KEY UPDATE last_matched = now()",
|
||||
list("address" = address, "bannedckey" = bannedckey)
|
||||
),
|
||||
SSdbcore.NewQuery(
|
||||
"INSERT INTO [format_table_name("stickyban_matched_cid")] (matched_cid, stickyban) VALUES (:computer_id, :bannedckey) ON DUPLICATE KEY UPDATE last_matched = now()",
|
||||
list("computer_id" = computer_id, "bannedckey" = bannedckey)
|
||||
)
|
||||
), FALSE, TRUE)
|
||||
|
||||
|
||||
//byond will not trigger isbanned() for "global" host bans,
|
||||
//ie, ones where the "apply to this game only" checkbox is not checked (defaults to not checked)
|
||||
//So it's safe to let admins walk thru host/sticky bans here
|
||||
if (admin)
|
||||
log_admin("The admin [key] has been allowed to bypass a matching host/sticky ban on [bannedckey]")
|
||||
if (message)
|
||||
message_admins("<span class='adminnotice'>The admin [key] has been allowed to bypass a matching host/sticky ban on [bannedckey]</span>")
|
||||
addclientmessage(ckey,"<span class='adminnotice'>You have been allowed to bypass a matching host/sticky ban on [bannedckey]</span>")
|
||||
return null
|
||||
|
||||
if (C) //user is already connected!.
|
||||
to_chat(C, "<span class='redtext'>You are about to get disconnected for matching a sticky ban after you connected. If this turns out to be the ban evasion detection system going haywire, we will automatically detect this and revert the matches. if you feel that this is the case, please wait EXACTLY 6 seconds then reconnect using file -> reconnect to see if the match was automatically reversed.</span>", confidential = TRUE)
|
||||
|
||||
var/desc = "\nReason:(StickyBan) You, or another user of this computer or connection ([bannedckey]) is banned from playing here. The ban reason is:\n[ban["message"]]\nThis ban was applied by [ban["admin"]]\nThis is a BanEvasion Detection System ban, if you think this ban is a mistake, please wait EXACTLY 6 seconds, then try again before filing an appeal.\n"
|
||||
. = list("reason" = "Stickyban", "desc" = desc)
|
||||
log_access("Failed Login: [key] [computer_id] [address] - StickyBanned [ban["message"]] Target Username: [bannedckey] Placed by [ban["admin"]]")
|
||||
|
||||
return .
|
||||
|
||||
/proc/restore_stickybans()
|
||||
for (var/banned_ckey in GLOB.stickybanadmintexts)
|
||||
world.SetConfig("ban", banned_ckey, GLOB.stickybanadmintexts[banned_ckey])
|
||||
GLOB.stickybanadminexemptions = list()
|
||||
GLOB.stickybanadmintexts = list()
|
||||
if (GLOB.stickbanadminexemptiontimerid)
|
||||
deltimer(GLOB.stickbanadminexemptiontimerid)
|
||||
GLOB.stickbanadminexemptiontimerid = null
|
||||
|
||||
#undef STICKYBAN_MAX_MATCHES
|
||||
#undef STICKYBAN_MAX_EXISTING_USER_MATCHES
|
||||
#undef STICKYBAN_MAX_ADMIN_MATCHES
|
||||
//Blocks an attempt to connect before even creating our client datum thing.
|
||||
|
||||
//How many new ckey matches before we revert the stickyban to it's roundstart state
|
||||
//These are exclusive, so once it goes over one of these numbers, it reverts the ban
|
||||
#define STICKYBAN_MAX_MATCHES 15
|
||||
#define STICKYBAN_MAX_EXISTING_USER_MATCHES 3 //ie, users who were connected before the ban triggered
|
||||
#define STICKYBAN_MAX_ADMIN_MATCHES 1
|
||||
|
||||
/world/IsBanned(key, address, computer_id, type, real_bans_only=FALSE)
|
||||
debug_world_log("isbanned(): '[args.Join("', '")]'")
|
||||
if (!key || (!real_bans_only && (!address || !computer_id)))
|
||||
if(real_bans_only)
|
||||
return FALSE
|
||||
log_access("Failed Login (invalid data): [key] [address]-[computer_id]")
|
||||
return list("reason"="invalid login data", "desc"="Error: Could not check ban status, Please try again. Error message: Your computer provided invalid or blank information to the server on connection (byond username, IP, and Computer ID.) Provided information for reference: Username:'[key]' IP:'[address]' Computer ID:'[computer_id]'. (If you continue to get this error, please restart byond or contact byond support.)")
|
||||
|
||||
if (type == "world")
|
||||
return ..() //shunt world topic banchecks to purely to byond's internal ban system
|
||||
|
||||
var/admin = FALSE
|
||||
var/ckey = ckey(key)
|
||||
|
||||
var/client/C = GLOB.directory[ckey]
|
||||
if (C && ckey == C.ckey && computer_id == C.computer_id && address == C.address)
|
||||
return //don't recheck connected clients.
|
||||
|
||||
//IsBanned can get re-called on a user in certain situations, this prevents that leading to repeated messages to admins.
|
||||
var/static/list/checkedckeys = list()
|
||||
//magic voodo to check for a key in a list while also adding that key to the list without having to do two associated lookups
|
||||
var/message = !checkedckeys[ckey]++
|
||||
|
||||
if(GLOB.admin_datums[ckey] || GLOB.deadmins[ckey])
|
||||
admin = TRUE
|
||||
|
||||
|
||||
//Whitelist
|
||||
if(!real_bans_only && !C && CONFIG_GET(flag/usewhitelist))
|
||||
if(!check_whitelist(ckey))
|
||||
if (admin)
|
||||
log_admin("The admin [key] has been allowed to bypass the whitelist")
|
||||
if (message)
|
||||
message_admins("<span class='adminnotice'>The admin [key] has been allowed to bypass the whitelist</span>")
|
||||
addclientmessage(ckey,"<span class='adminnotice'>You have been allowed to bypass the whitelist</span>")
|
||||
else
|
||||
log_access("Failed Login: [key] - Not on whitelist")
|
||||
return list("reason"="whitelist", "desc" = "\nReason: You are not on the white list for this server")
|
||||
|
||||
//Guest Checking
|
||||
if(!real_bans_only && !C && IsGuestKey(key))
|
||||
if (CONFIG_GET(flag/guest_ban))
|
||||
log_access("Failed Login: [key] - Guests not allowed")
|
||||
return list("reason"="guest", "desc"="\nReason: Guests not allowed. Please sign in with a byond account.")
|
||||
if (CONFIG_GET(flag/panic_bunker) && SSdbcore.Connect())
|
||||
log_access("Failed Login: [key] - Guests not allowed during panic bunker")
|
||||
return list("reason"="guest", "desc"="\nReason: Sorry but the server is currently not accepting connections from never before seen players or guests. If you have played on this server with a byond account before, please log in to the byond account you have played from.")
|
||||
|
||||
//Population Cap Checking
|
||||
var/extreme_popcap = CONFIG_GET(number/extreme_popcap)
|
||||
if(!real_bans_only && !C && extreme_popcap && !admin)
|
||||
var/popcap_value = GLOB.clients.len
|
||||
if(popcap_value >= extreme_popcap && !GLOB.joined_player_list.Find(ckey))
|
||||
if(!CONFIG_GET(flag/byond_member_bypass_popcap) || !world.IsSubscribed(ckey, "BYOND"))
|
||||
log_access("Failed Login: [key] - Population cap reached")
|
||||
return list("reason"="popcap", "desc"= "\nReason: [CONFIG_GET(string/extreme_popcap_message)]")
|
||||
|
||||
if(CONFIG_GET(flag/sql_enabled))
|
||||
if(!SSdbcore.Connect())
|
||||
var/msg = "Ban database connection failure. Key [ckey] not checked"
|
||||
log_world(msg)
|
||||
if (message)
|
||||
message_admins(msg)
|
||||
else
|
||||
var/list/ban_details = is_banned_from_with_details(ckey, address, computer_id, "Server")
|
||||
for(var/i in ban_details)
|
||||
if(admin)
|
||||
if(text2num(i["applies_to_admins"]))
|
||||
var/msg = "Admin [key] is admin banned, and has been disallowed access."
|
||||
log_admin(msg)
|
||||
if (message)
|
||||
message_admins(msg)
|
||||
else
|
||||
var/msg = "Admin [key] has been allowed to bypass a matching non-admin ban on [i["key"]] [i["ip"]]-[i["computerid"]]."
|
||||
log_admin(msg)
|
||||
if (message)
|
||||
message_admins(msg)
|
||||
addclientmessage(ckey,"<span class='adminnotice'>Admin [key] has been allowed to bypass a matching non-admin ban on [i["key"]] [i["ip"]]-[i["computerid"]].</span>")
|
||||
continue
|
||||
var/expires = "This is a permanent ban."
|
||||
if(i["expiration_time"])
|
||||
expires = " The ban is for [DisplayTimeText(text2num(i["duration"]) MINUTES)] and expires on [i["expiration_time"]] (server time)."
|
||||
var/desc = {"You, or another user of this computer or connection ([i["key"]]) is banned from playing here.
|
||||
The ban reason is: [i["reason"]]
|
||||
This ban (BanID #[i["id"]]) was applied by [i["admin_key"]] on [i["bantime"]] during round ID [i["round_id"]].
|
||||
[expires]"}
|
||||
log_access("Failed Login: [key] [computer_id] [address] - Banned (#[i["id"]])")
|
||||
return list("reason"="Banned","desc"="[desc]")
|
||||
if (admin)
|
||||
if (GLOB.directory[ckey])
|
||||
return
|
||||
|
||||
//oh boy, so basically, because of a bug in byond, sometimes stickyban matches don't trigger here, so we can't exempt admins.
|
||||
// Whitelisting the ckey with the byond whitelist field doesn't work.
|
||||
// So we instead have to remove every stickyban than later re-add them.
|
||||
if (!length(GLOB.stickybanadminexemptions))
|
||||
for (var/banned_ckey in world.GetConfig("ban"))
|
||||
GLOB.stickybanadmintexts[banned_ckey] = world.GetConfig("ban", banned_ckey)
|
||||
world.SetConfig("ban", banned_ckey, null)
|
||||
if (!SSstickyban.initialized)
|
||||
return
|
||||
GLOB.stickybanadminexemptions[ckey] = world.time
|
||||
stoplag() // sleep a byond tick
|
||||
GLOB.stickbanadminexemptiontimerid = addtimer(CALLBACK(GLOBAL_PROC, /proc/restore_stickybans), 5 SECONDS, TIMER_STOPPABLE|TIMER_UNIQUE|TIMER_OVERRIDE)
|
||||
return
|
||||
var/list/ban = ..() //default pager ban stuff
|
||||
|
||||
if (ban)
|
||||
if (!admin)
|
||||
. = ban
|
||||
if (real_bans_only)
|
||||
return
|
||||
var/bannedckey = "ERROR"
|
||||
if (ban["ckey"])
|
||||
bannedckey = ban["ckey"]
|
||||
|
||||
var/newmatch = FALSE
|
||||
var/list/cachedban = SSstickyban.cache[bannedckey]
|
||||
//rogue ban in the process of being reverted.
|
||||
if (cachedban && (cachedban["reverting"] || cachedban["timeout"]))
|
||||
world.SetConfig("ban", bannedckey, null)
|
||||
return null
|
||||
|
||||
if (cachedban && ckey != bannedckey)
|
||||
newmatch = TRUE
|
||||
if (cachedban["keys"])
|
||||
if (cachedban["keys"][ckey])
|
||||
newmatch = FALSE
|
||||
if (cachedban["matches_this_round"][ckey])
|
||||
newmatch = FALSE
|
||||
|
||||
if (newmatch && cachedban)
|
||||
var/list/newmatches = cachedban["matches_this_round"]
|
||||
var/list/pendingmatches = cachedban["matches_this_round"]
|
||||
var/list/newmatches_connected = cachedban["existing_user_matches_this_round"]
|
||||
var/list/newmatches_admin = cachedban["admin_matches_this_round"]
|
||||
|
||||
if (C)
|
||||
newmatches_connected[ckey] = ckey
|
||||
newmatches_connected = cachedban["existing_user_matches_this_round"]
|
||||
pendingmatches[ckey] = ckey
|
||||
sleep(STICKYBAN_ROGUE_CHECK_TIME)
|
||||
pendingmatches -= ckey
|
||||
if (admin)
|
||||
newmatches_admin[ckey] = ckey
|
||||
|
||||
if (cachedban["reverting"] || cachedban["timeout"])
|
||||
return null
|
||||
|
||||
newmatches[ckey] = ckey
|
||||
|
||||
|
||||
if (\
|
||||
newmatches.len+pendingmatches.len > STICKYBAN_MAX_MATCHES || \
|
||||
newmatches_connected.len > STICKYBAN_MAX_EXISTING_USER_MATCHES || \
|
||||
newmatches_admin.len > STICKYBAN_MAX_ADMIN_MATCHES \
|
||||
)
|
||||
|
||||
var/action
|
||||
if (ban["fromdb"])
|
||||
cachedban["timeout"] = TRUE
|
||||
action = "putting it on timeout for the remainder of the round"
|
||||
else
|
||||
cachedban["reverting"] = TRUE
|
||||
action = "reverting to its roundstart state"
|
||||
|
||||
world.SetConfig("ban", bannedckey, null)
|
||||
|
||||
//we always report this
|
||||
log_game("Stickyban on [bannedckey] detected as rogue, [action]")
|
||||
message_admins("Stickyban on [bannedckey] detected as rogue, [action]")
|
||||
//do not convert to timer.
|
||||
spawn (5)
|
||||
world.SetConfig("ban", bannedckey, null)
|
||||
sleep(1)
|
||||
world.SetConfig("ban", bannedckey, null)
|
||||
if (!ban["fromdb"])
|
||||
cachedban = cachedban.Copy() //so old references to the list still see the ban as reverting
|
||||
cachedban["matches_this_round"] = list()
|
||||
cachedban["existing_user_matches_this_round"] = list()
|
||||
cachedban["admin_matches_this_round"] = list()
|
||||
cachedban -= "reverting"
|
||||
SSstickyban.cache[bannedckey] = cachedban
|
||||
world.SetConfig("ban", bannedckey, list2stickyban(cachedban))
|
||||
return null
|
||||
|
||||
if (ban["fromdb"])
|
||||
if(SSdbcore.Connect())
|
||||
INVOKE_ASYNC(SSdbcore, /datum/controller/subsystem/dbcore/proc.QuerySelect, list(
|
||||
SSdbcore.NewQuery(
|
||||
"INSERT INTO [format_table_name("stickyban_matched_ckey")] (matched_ckey, stickyban) VALUES (:ckey, :bannedckey) ON DUPLICATE KEY UPDATE last_matched = now()",
|
||||
list("ckey" = ckey, "bannedckey" = bannedckey)
|
||||
),
|
||||
SSdbcore.NewQuery(
|
||||
"INSERT INTO [format_table_name("stickyban_matched_ip")] (matched_ip, stickyban) VALUES (INET_ATON(:address), :bannedckey) ON DUPLICATE KEY UPDATE last_matched = now()",
|
||||
list("address" = address, "bannedckey" = bannedckey)
|
||||
),
|
||||
SSdbcore.NewQuery(
|
||||
"INSERT INTO [format_table_name("stickyban_matched_cid")] (matched_cid, stickyban) VALUES (:computer_id, :bannedckey) ON DUPLICATE KEY UPDATE last_matched = now()",
|
||||
list("computer_id" = computer_id, "bannedckey" = bannedckey)
|
||||
)
|
||||
), FALSE, TRUE)
|
||||
|
||||
|
||||
//byond will not trigger isbanned() for "global" host bans,
|
||||
//ie, ones where the "apply to this game only" checkbox is not checked (defaults to not checked)
|
||||
//So it's safe to let admins walk thru host/sticky bans here
|
||||
if (admin)
|
||||
log_admin("The admin [key] has been allowed to bypass a matching host/sticky ban on [bannedckey]")
|
||||
if (message)
|
||||
message_admins("<span class='adminnotice'>The admin [key] has been allowed to bypass a matching host/sticky ban on [bannedckey]</span>")
|
||||
addclientmessage(ckey,"<span class='adminnotice'>You have been allowed to bypass a matching host/sticky ban on [bannedckey]</span>")
|
||||
return null
|
||||
|
||||
if (C) //user is already connected!.
|
||||
to_chat(C, "<span class='redtext'>You are about to get disconnected for matching a sticky ban after you connected. If this turns out to be the ban evasion detection system going haywire, we will automatically detect this and revert the matches. if you feel that this is the case, please wait EXACTLY 6 seconds then reconnect using file -> reconnect to see if the match was automatically reversed.</span>", confidential = TRUE)
|
||||
|
||||
var/desc = "\nReason:(StickyBan) You, or another user of this computer or connection ([bannedckey]) is banned from playing here. The ban reason is:\n[ban["message"]]\nThis ban was applied by [ban["admin"]]\nThis is a BanEvasion Detection System ban, if you think this ban is a mistake, please wait EXACTLY 6 seconds, then try again before filing an appeal.\n"
|
||||
. = list("reason" = "Stickyban", "desc" = desc)
|
||||
log_access("Failed Login: [key] [computer_id] [address] - StickyBanned [ban["message"]] Target Username: [bannedckey] Placed by [ban["admin"]]")
|
||||
|
||||
return .
|
||||
|
||||
/proc/restore_stickybans()
|
||||
for (var/banned_ckey in GLOB.stickybanadmintexts)
|
||||
world.SetConfig("ban", banned_ckey, GLOB.stickybanadmintexts[banned_ckey])
|
||||
GLOB.stickybanadminexemptions = list()
|
||||
GLOB.stickybanadmintexts = list()
|
||||
if (GLOB.stickbanadminexemptiontimerid)
|
||||
deltimer(GLOB.stickbanadminexemptiontimerid)
|
||||
GLOB.stickbanadminexemptiontimerid = null
|
||||
|
||||
#undef STICKYBAN_MAX_MATCHES
|
||||
#undef STICKYBAN_MAX_EXISTING_USER_MATCHES
|
||||
#undef STICKYBAN_MAX_ADMIN_MATCHES
|
||||
|
||||
+992
-992
File diff suppressed because it is too large
Load Diff
@@ -1,42 +1,42 @@
|
||||
/atom/proc/investigate_log(message, subject)
|
||||
if(!message || !subject)
|
||||
return
|
||||
var/F = file("[GLOB.log_directory]/[subject].html")
|
||||
WRITE_FILE(F, "[time_stamp()] [REF(src)] ([x],[y],[z]) || [src] [message]<br>")
|
||||
|
||||
/client/proc/investigate_show()
|
||||
set name = "Investigate"
|
||||
set category = "Admin - Game"
|
||||
if(!holder)
|
||||
return
|
||||
|
||||
var/list/investigates = list(INVESTIGATE_RESEARCH, INVESTIGATE_EXONET, INVESTIGATE_PORTAL, INVESTIGATE_SINGULO, INVESTIGATE_WIRES, INVESTIGATE_TELESCI, INVESTIGATE_GRAVITY, INVESTIGATE_RECORDS, INVESTIGATE_CARGO, INVESTIGATE_SUPERMATTER, INVESTIGATE_ATMOS, INVESTIGATE_EXPERIMENTOR, INVESTIGATE_BOTANY, INVESTIGATE_HALLUCINATIONS, INVESTIGATE_RADIATION, INVESTIGATE_NANITES, INVESTIGATE_PRESENTS)
|
||||
|
||||
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 = sortList(logs_present) + sortList(logs_missing)
|
||||
|
||||
var/selected = input("Investigate what?", "Investigate") as null|anything in combined
|
||||
|
||||
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 class='danger'>No [selected] logfile was found.</span>", confidential = TRUE)
|
||||
return
|
||||
src << browse(F,"window=investigate[selected];size=800x300")
|
||||
/atom/proc/investigate_log(message, subject)
|
||||
if(!message || !subject)
|
||||
return
|
||||
var/F = file("[GLOB.log_directory]/[subject].html")
|
||||
WRITE_FILE(F, "[time_stamp()] [REF(src)] ([x],[y],[z]) || [src] [message]<br>")
|
||||
|
||||
/client/proc/investigate_show()
|
||||
set name = "Investigate"
|
||||
set category = "Admin - Game"
|
||||
if(!holder)
|
||||
return
|
||||
|
||||
var/list/investigates = list(INVESTIGATE_RESEARCH, INVESTIGATE_EXONET, INVESTIGATE_PORTAL, INVESTIGATE_SINGULO, INVESTIGATE_WIRES, INVESTIGATE_TELESCI, INVESTIGATE_GRAVITY, INVESTIGATE_RECORDS, INVESTIGATE_CARGO, INVESTIGATE_SUPERMATTER, INVESTIGATE_ATMOS, INVESTIGATE_EXPERIMENTOR, INVESTIGATE_BOTANY, INVESTIGATE_HALLUCINATIONS, INVESTIGATE_RADIATION, INVESTIGATE_NANITES, INVESTIGATE_PRESENTS)
|
||||
|
||||
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 = sortList(logs_present) + sortList(logs_missing)
|
||||
|
||||
var/selected = input("Investigate what?", "Investigate") as null|anything in combined
|
||||
|
||||
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 class='danger'>No [selected] logfile was found.</span>", confidential = TRUE)
|
||||
return
|
||||
src << browse(F,"window=investigate[selected];size=800x300")
|
||||
|
||||
+292
-292
@@ -1,292 +1,292 @@
|
||||
GLOBAL_LIST_EMPTY(admin_ranks) //list of all admin_rank datums
|
||||
GLOBAL_PROTECT(admin_ranks)
|
||||
|
||||
GLOBAL_LIST_EMPTY(protected_ranks) //admin ranks loaded from txt
|
||||
GLOBAL_PROTECT(protected_ranks)
|
||||
|
||||
/datum/admin_rank
|
||||
var/name = "NoRank"
|
||||
var/rights = R_DEFAULT
|
||||
var/exclude_rights = 0
|
||||
var/include_rights = 0
|
||||
var/can_edit_rights = 0
|
||||
|
||||
/datum/admin_rank/New(init_name, init_rights, init_exclude_rights, init_edit_rights)
|
||||
if(IsAdminAdvancedProcCall())
|
||||
var/msg = " has tried to elevate permissions!"
|
||||
message_admins("[key_name_admin(usr)][msg]")
|
||||
log_admin("[key_name(usr)][msg]")
|
||||
if (name == "NoRank") //only del if this is a true creation (and not just a New() proc call), other wise trialmins/coders could abuse this to deadmin other admins
|
||||
QDEL_IN(src, 0)
|
||||
CRASH("Admin proc call creation of admin datum")
|
||||
return
|
||||
name = init_name
|
||||
if(!name)
|
||||
qdel(src)
|
||||
CRASH("Admin rank created without name.")
|
||||
if(init_rights)
|
||||
rights = init_rights
|
||||
include_rights = rights
|
||||
if(init_exclude_rights)
|
||||
exclude_rights = init_exclude_rights
|
||||
rights &= ~exclude_rights
|
||||
if(init_edit_rights)
|
||||
can_edit_rights = init_edit_rights
|
||||
|
||||
/datum/admin_rank/Destroy()
|
||||
if(IsAdminAdvancedProcCall())
|
||||
var/msg = " has tried to elevate permissions!"
|
||||
message_admins("[key_name_admin(usr)][msg]")
|
||||
log_admin("[key_name(usr)][msg]")
|
||||
return QDEL_HINT_LETMELIVE
|
||||
. = ..()
|
||||
|
||||
/datum/admin_rank/vv_edit_var(var_name, var_value)
|
||||
return FALSE
|
||||
|
||||
// Adds/removes rights to this admin_rank
|
||||
/datum/admin_rank/proc/process_keyword(group, group_count, datum/admin_rank/previous_rank)
|
||||
if(IsAdminAdvancedProcCall())
|
||||
var/msg = " has tried to elevate permissions!"
|
||||
message_admins("[key_name_admin(usr)][msg]")
|
||||
log_admin("[key_name(usr)][msg]")
|
||||
return
|
||||
var/list/keywords = splittext(group, " ")
|
||||
var/flag = 0
|
||||
for(var/k in keywords)
|
||||
switch(k)
|
||||
if("BUILD")
|
||||
flag = R_BUILD
|
||||
if("ADMIN")
|
||||
flag = R_ADMIN
|
||||
if("BAN")
|
||||
flag = R_BAN
|
||||
if("FUN")
|
||||
flag = R_FUN
|
||||
if("SERVER")
|
||||
flag = R_SERVER
|
||||
if("DEBUG")
|
||||
flag = R_DEBUG
|
||||
if("PERMISSIONS")
|
||||
flag = R_PERMISSIONS
|
||||
if("POSSESS")
|
||||
flag = R_POSSESS
|
||||
if("STEALTH")
|
||||
flag = R_STEALTH
|
||||
if("POLL")
|
||||
flag = R_POLL
|
||||
if("VAREDIT")
|
||||
flag = R_VAREDIT
|
||||
if("EVERYTHING")
|
||||
flag = R_EVERYTHING
|
||||
if("SOUND")
|
||||
flag = R_SOUND
|
||||
if("SPAWN")
|
||||
flag = R_SPAWN
|
||||
if("AUTOADMIN")
|
||||
flag = R_AUTOADMIN
|
||||
if("DBRANKS")
|
||||
flag = R_DBRANKS
|
||||
if("@")
|
||||
if(previous_rank)
|
||||
switch(group_count)
|
||||
if(1)
|
||||
flag = previous_rank.include_rights
|
||||
if(2)
|
||||
flag = previous_rank.exclude_rights
|
||||
if(3)
|
||||
flag = previous_rank.can_edit_rights
|
||||
else
|
||||
continue
|
||||
switch(group_count)
|
||||
if(1)
|
||||
rights |= flag
|
||||
include_rights |= flag
|
||||
if(2)
|
||||
rights &= ~flag
|
||||
exclude_rights |= flag
|
||||
if(3)
|
||||
can_edit_rights |= flag
|
||||
|
||||
/proc/sync_ranks_with_db()
|
||||
set waitfor = FALSE
|
||||
|
||||
if(IsAdminAdvancedProcCall())
|
||||
to_chat(usr, "<span class='admin prefix'>Admin rank DB Sync blocked: Advanced ProcCall detected.</span>", confidential = TRUE)
|
||||
return
|
||||
|
||||
var/list/sql_ranks = list()
|
||||
for(var/datum/admin_rank/R in GLOB.protected_ranks)
|
||||
sql_ranks += list(list("rank" = R.name, "flags" = R.include_rights, "exclude_flags" = R.exclude_rights, "can_edit_flags" = R.can_edit_rights))
|
||||
SSdbcore.MassInsert(format_table_name("admin_ranks"), sql_ranks, duplicate_key = TRUE)
|
||||
|
||||
//load our rank - > rights associations
|
||||
/proc/load_admin_ranks(dbfail, no_update)
|
||||
if(IsAdminAdvancedProcCall())
|
||||
to_chat(usr, "<span class='admin prefix'>Admin Reload blocked: Advanced ProcCall detected.</span>", confidential = TRUE)
|
||||
return
|
||||
GLOB.admin_ranks.Cut()
|
||||
GLOB.protected_ranks.Cut()
|
||||
//load text from file and process each entry
|
||||
var/ranks_text = file2text("[global.config.directory]/admin_ranks.txt")
|
||||
var/datum/admin_rank/previous_rank
|
||||
var/regex/admin_ranks_regex = new(@"^Name\s*=\s*(.+?)\s*\n+Include\s*=\s*([\l @]*?)\s*\n+Exclude\s*=\s*([\l @]*?)\s*\n+Edit\s*=\s*([\l @]*?)\s*\n*$", "gm")
|
||||
while(admin_ranks_regex.Find(ranks_text))
|
||||
var/datum/admin_rank/R = new(admin_ranks_regex.group[1])
|
||||
if(!R)
|
||||
continue
|
||||
var/count = 1
|
||||
for(var/i in admin_ranks_regex.group - admin_ranks_regex.group[1])
|
||||
if(i)
|
||||
R.process_keyword(i, count, previous_rank)
|
||||
count++
|
||||
GLOB.admin_ranks += R
|
||||
GLOB.protected_ranks += R
|
||||
previous_rank = R
|
||||
if(!CONFIG_GET(flag/admin_legacy_system) || dbfail)
|
||||
if(CONFIG_GET(flag/load_legacy_ranks_only))
|
||||
if(!no_update)
|
||||
sync_ranks_with_db()
|
||||
else
|
||||
var/datum/db_query/query_load_admin_ranks = SSdbcore.NewQuery("SELECT `rank`, flags, exclude_flags, can_edit_flags FROM [format_table_name("admin_ranks")]")
|
||||
if(!query_load_admin_ranks.Execute())
|
||||
message_admins("Error loading admin ranks from database. Loading from backup.")
|
||||
log_sql("Error loading admin ranks from database. Loading from backup.")
|
||||
dbfail = 1
|
||||
else
|
||||
while(query_load_admin_ranks.NextRow())
|
||||
var/skip
|
||||
var/rank_name = query_load_admin_ranks.item[1]
|
||||
for(var/datum/admin_rank/R in GLOB.admin_ranks)
|
||||
if(R.name == rank_name) //this rank was already loaded from txt override
|
||||
skip = 1
|
||||
break
|
||||
if(!skip)
|
||||
var/rank_flags = text2num(query_load_admin_ranks.item[2])
|
||||
var/rank_exclude_flags = text2num(query_load_admin_ranks.item[3])
|
||||
var/rank_can_edit_flags = text2num(query_load_admin_ranks.item[4])
|
||||
var/datum/admin_rank/R = new(rank_name, rank_flags, rank_exclude_flags, rank_can_edit_flags)
|
||||
if(!R)
|
||||
continue
|
||||
GLOB.admin_ranks += R
|
||||
qdel(query_load_admin_ranks)
|
||||
//load ranks from backup file
|
||||
if(dbfail)
|
||||
var/backup_file = file2text("data/admins_backup.json")
|
||||
if(backup_file == null)
|
||||
log_world("Unable to locate admins backup file.")
|
||||
return FALSE
|
||||
var/list/json = json_decode(backup_file)
|
||||
for(var/J in json["ranks"])
|
||||
var/skip
|
||||
for(var/datum/admin_rank/R in GLOB.admin_ranks)
|
||||
if(R.name == "[J]") //this rank was already loaded from txt override
|
||||
skip = TRUE
|
||||
if(skip)
|
||||
continue
|
||||
var/datum/admin_rank/R = new("[J]", json["ranks"]["[J]"]["include rights"], json["ranks"]["[J]"]["exclude rights"], json["ranks"]["[J]"]["can edit rights"])
|
||||
if(!R)
|
||||
continue
|
||||
GLOB.admin_ranks += R
|
||||
return json
|
||||
#ifdef TESTING
|
||||
var/msg = "Permission Sets Built:\n"
|
||||
for(var/datum/admin_rank/R in GLOB.admin_ranks)
|
||||
msg += "\t[R.name]"
|
||||
var/rights = rights2text(R.rights,"\n\t\t")
|
||||
if(rights)
|
||||
msg += "\t\t[rights]\n"
|
||||
testing(msg)
|
||||
#endif
|
||||
|
||||
/proc/load_admins(no_update)
|
||||
var/dbfail
|
||||
if(!CONFIG_GET(flag/admin_legacy_system) && !SSdbcore.Connect())
|
||||
message_admins("Failed to connect to database while loading admins. Loading from backup.")
|
||||
log_sql("Failed to connect to database while loading admins. Loading from backup.")
|
||||
dbfail = 1
|
||||
//clear the datums references
|
||||
GLOB.admin_datums.Cut()
|
||||
for(var/client/C in GLOB.admins)
|
||||
C.remove_admin_verbs()
|
||||
C.holder = null
|
||||
GLOB.admins.Cut()
|
||||
GLOB.protected_admins.Cut()
|
||||
GLOB.deadmins.Cut()
|
||||
var/list/backup_file_json = load_admin_ranks(dbfail, no_update)
|
||||
dbfail = backup_file_json != null
|
||||
//Clear profile access
|
||||
for(var/A in world.GetConfig("admin"))
|
||||
world.SetConfig("APP/admin", A, null)
|
||||
var/list/rank_names = list()
|
||||
for(var/datum/admin_rank/R in GLOB.admin_ranks)
|
||||
rank_names[R.name] = R
|
||||
//ckeys listed in admins.txt are always made admins before sql loading is attempted
|
||||
var/admins_text = file2text("[global.config.directory]/admins.txt")
|
||||
var/regex/admins_regex = new(@"^(?!#)(.+?)\s+=\s+(.+)", "gm")
|
||||
while(admins_regex.Find(admins_text))
|
||||
new /datum/admins(rank_names[admins_regex.group[2]], ckey(admins_regex.group[1]), FALSE, TRUE)
|
||||
if(!CONFIG_GET(flag/admin_legacy_system) || dbfail)
|
||||
var/datum/db_query/query_load_admins = SSdbcore.NewQuery("SELECT ckey, `rank` FROM [format_table_name("admin")] ORDER BY `rank`")
|
||||
if(!query_load_admins.Execute())
|
||||
message_admins("Error loading admins from database. Loading from backup.")
|
||||
log_sql("Error loading admins from database. Loading from backup.")
|
||||
dbfail = 1
|
||||
else
|
||||
while(query_load_admins.NextRow())
|
||||
var/admin_ckey = ckey(query_load_admins.item[1])
|
||||
var/admin_rank = query_load_admins.item[2]
|
||||
var/skip
|
||||
if(rank_names[admin_rank] == null)
|
||||
message_admins("[admin_ckey] loaded with invalid admin rank [admin_rank].")
|
||||
skip = 1
|
||||
if(GLOB.admin_datums[admin_ckey] || GLOB.deadmins[admin_ckey])
|
||||
skip = 1
|
||||
if(!skip)
|
||||
new /datum/admins(rank_names[admin_rank], admin_ckey)
|
||||
qdel(query_load_admins)
|
||||
//load admins from backup file
|
||||
if(dbfail)
|
||||
if(!backup_file_json)
|
||||
if(backup_file_json != null)
|
||||
//already tried
|
||||
return
|
||||
var/backup_file = file2text("data/admins_backup.json")
|
||||
if(backup_file == null)
|
||||
log_world("Unable to locate admins backup file.")
|
||||
return
|
||||
backup_file_json = json_decode(backup_file)
|
||||
for(var/J in backup_file_json["admins"])
|
||||
var/skip
|
||||
for(var/A in GLOB.admin_datums + GLOB.deadmins)
|
||||
if(A == "[J]") //this admin was already loaded from txt override
|
||||
skip = TRUE
|
||||
if(skip)
|
||||
continue
|
||||
new /datum/admins(rank_names[backup_file_json["admins"]["[J]"]], ckey("[J]"))
|
||||
#ifdef TESTING
|
||||
var/msg = "Admins Built:\n"
|
||||
for(var/ckey in GLOB.admin_datums)
|
||||
var/datum/admins/D = GLOB.admin_datums[ckey]
|
||||
msg += "\t[ckey] - [D.rank.name]\n"
|
||||
testing(msg)
|
||||
#endif
|
||||
return dbfail
|
||||
|
||||
#ifdef TESTING
|
||||
/client/verb/changerank(newrank in GLOB.admin_ranks)
|
||||
if(holder)
|
||||
holder.rank = newrank
|
||||
else
|
||||
holder = new /datum/admins(newrank, ckey)
|
||||
remove_admin_verbs()
|
||||
holder.associate(src)
|
||||
|
||||
/client/verb/changerights(newrights as num)
|
||||
if(holder)
|
||||
holder.rank.rights = newrights
|
||||
else
|
||||
holder = new /datum/admins("testing", newrights, ckey)
|
||||
remove_admin_verbs()
|
||||
holder.associate(src)
|
||||
#endif
|
||||
GLOBAL_LIST_EMPTY(admin_ranks) //list of all admin_rank datums
|
||||
GLOBAL_PROTECT(admin_ranks)
|
||||
|
||||
GLOBAL_LIST_EMPTY(protected_ranks) //admin ranks loaded from txt
|
||||
GLOBAL_PROTECT(protected_ranks)
|
||||
|
||||
/datum/admin_rank
|
||||
var/name = "NoRank"
|
||||
var/rights = R_DEFAULT
|
||||
var/exclude_rights = 0
|
||||
var/include_rights = 0
|
||||
var/can_edit_rights = 0
|
||||
|
||||
/datum/admin_rank/New(init_name, init_rights, init_exclude_rights, init_edit_rights)
|
||||
if(IsAdminAdvancedProcCall())
|
||||
var/msg = " has tried to elevate permissions!"
|
||||
message_admins("[key_name_admin(usr)][msg]")
|
||||
log_admin("[key_name(usr)][msg]")
|
||||
if (name == "NoRank") //only del if this is a true creation (and not just a New() proc call), other wise trialmins/coders could abuse this to deadmin other admins
|
||||
QDEL_IN(src, 0)
|
||||
CRASH("Admin proc call creation of admin datum")
|
||||
return
|
||||
name = init_name
|
||||
if(!name)
|
||||
qdel(src)
|
||||
CRASH("Admin rank created without name.")
|
||||
if(init_rights)
|
||||
rights = init_rights
|
||||
include_rights = rights
|
||||
if(init_exclude_rights)
|
||||
exclude_rights = init_exclude_rights
|
||||
rights &= ~exclude_rights
|
||||
if(init_edit_rights)
|
||||
can_edit_rights = init_edit_rights
|
||||
|
||||
/datum/admin_rank/Destroy()
|
||||
if(IsAdminAdvancedProcCall())
|
||||
var/msg = " has tried to elevate permissions!"
|
||||
message_admins("[key_name_admin(usr)][msg]")
|
||||
log_admin("[key_name(usr)][msg]")
|
||||
return QDEL_HINT_LETMELIVE
|
||||
. = ..()
|
||||
|
||||
/datum/admin_rank/vv_edit_var(var_name, var_value)
|
||||
return FALSE
|
||||
|
||||
// Adds/removes rights to this admin_rank
|
||||
/datum/admin_rank/proc/process_keyword(group, group_count, datum/admin_rank/previous_rank)
|
||||
if(IsAdminAdvancedProcCall())
|
||||
var/msg = " has tried to elevate permissions!"
|
||||
message_admins("[key_name_admin(usr)][msg]")
|
||||
log_admin("[key_name(usr)][msg]")
|
||||
return
|
||||
var/list/keywords = splittext(group, " ")
|
||||
var/flag = 0
|
||||
for(var/k in keywords)
|
||||
switch(k)
|
||||
if("BUILD")
|
||||
flag = R_BUILD
|
||||
if("ADMIN")
|
||||
flag = R_ADMIN
|
||||
if("BAN")
|
||||
flag = R_BAN
|
||||
if("FUN")
|
||||
flag = R_FUN
|
||||
if("SERVER")
|
||||
flag = R_SERVER
|
||||
if("DEBUG")
|
||||
flag = R_DEBUG
|
||||
if("PERMISSIONS")
|
||||
flag = R_PERMISSIONS
|
||||
if("POSSESS")
|
||||
flag = R_POSSESS
|
||||
if("STEALTH")
|
||||
flag = R_STEALTH
|
||||
if("POLL")
|
||||
flag = R_POLL
|
||||
if("VAREDIT")
|
||||
flag = R_VAREDIT
|
||||
if("EVERYTHING")
|
||||
flag = R_EVERYTHING
|
||||
if("SOUND")
|
||||
flag = R_SOUND
|
||||
if("SPAWN")
|
||||
flag = R_SPAWN
|
||||
if("AUTOADMIN")
|
||||
flag = R_AUTOADMIN
|
||||
if("DBRANKS")
|
||||
flag = R_DBRANKS
|
||||
if("@")
|
||||
if(previous_rank)
|
||||
switch(group_count)
|
||||
if(1)
|
||||
flag = previous_rank.include_rights
|
||||
if(2)
|
||||
flag = previous_rank.exclude_rights
|
||||
if(3)
|
||||
flag = previous_rank.can_edit_rights
|
||||
else
|
||||
continue
|
||||
switch(group_count)
|
||||
if(1)
|
||||
rights |= flag
|
||||
include_rights |= flag
|
||||
if(2)
|
||||
rights &= ~flag
|
||||
exclude_rights |= flag
|
||||
if(3)
|
||||
can_edit_rights |= flag
|
||||
|
||||
/proc/sync_ranks_with_db()
|
||||
set waitfor = FALSE
|
||||
|
||||
if(IsAdminAdvancedProcCall())
|
||||
to_chat(usr, "<span class='admin prefix'>Admin rank DB Sync blocked: Advanced ProcCall detected.</span>", confidential = TRUE)
|
||||
return
|
||||
|
||||
var/list/sql_ranks = list()
|
||||
for(var/datum/admin_rank/R in GLOB.protected_ranks)
|
||||
sql_ranks += list(list("rank" = R.name, "flags" = R.include_rights, "exclude_flags" = R.exclude_rights, "can_edit_flags" = R.can_edit_rights))
|
||||
SSdbcore.MassInsert(format_table_name("admin_ranks"), sql_ranks, duplicate_key = TRUE)
|
||||
|
||||
//load our rank - > rights associations
|
||||
/proc/load_admin_ranks(dbfail, no_update)
|
||||
if(IsAdminAdvancedProcCall())
|
||||
to_chat(usr, "<span class='admin prefix'>Admin Reload blocked: Advanced ProcCall detected.</span>", confidential = TRUE)
|
||||
return
|
||||
GLOB.admin_ranks.Cut()
|
||||
GLOB.protected_ranks.Cut()
|
||||
//load text from file and process each entry
|
||||
var/ranks_text = file2text("[global.config.directory]/admin_ranks.txt")
|
||||
var/datum/admin_rank/previous_rank
|
||||
var/regex/admin_ranks_regex = new(@"^Name\s*=\s*(.+?)\s*\n+Include\s*=\s*([\l @]*?)\s*\n+Exclude\s*=\s*([\l @]*?)\s*\n+Edit\s*=\s*([\l @]*?)\s*\n*$", "gm")
|
||||
while(admin_ranks_regex.Find(ranks_text))
|
||||
var/datum/admin_rank/R = new(admin_ranks_regex.group[1])
|
||||
if(!R)
|
||||
continue
|
||||
var/count = 1
|
||||
for(var/i in admin_ranks_regex.group - admin_ranks_regex.group[1])
|
||||
if(i)
|
||||
R.process_keyword(i, count, previous_rank)
|
||||
count++
|
||||
GLOB.admin_ranks += R
|
||||
GLOB.protected_ranks += R
|
||||
previous_rank = R
|
||||
if(!CONFIG_GET(flag/admin_legacy_system) || dbfail)
|
||||
if(CONFIG_GET(flag/load_legacy_ranks_only))
|
||||
if(!no_update)
|
||||
sync_ranks_with_db()
|
||||
else
|
||||
var/datum/db_query/query_load_admin_ranks = SSdbcore.NewQuery("SELECT `rank`, flags, exclude_flags, can_edit_flags FROM [format_table_name("admin_ranks")]")
|
||||
if(!query_load_admin_ranks.Execute())
|
||||
message_admins("Error loading admin ranks from database. Loading from backup.")
|
||||
log_sql("Error loading admin ranks from database. Loading from backup.")
|
||||
dbfail = 1
|
||||
else
|
||||
while(query_load_admin_ranks.NextRow())
|
||||
var/skip
|
||||
var/rank_name = query_load_admin_ranks.item[1]
|
||||
for(var/datum/admin_rank/R in GLOB.admin_ranks)
|
||||
if(R.name == rank_name) //this rank was already loaded from txt override
|
||||
skip = 1
|
||||
break
|
||||
if(!skip)
|
||||
var/rank_flags = text2num(query_load_admin_ranks.item[2])
|
||||
var/rank_exclude_flags = text2num(query_load_admin_ranks.item[3])
|
||||
var/rank_can_edit_flags = text2num(query_load_admin_ranks.item[4])
|
||||
var/datum/admin_rank/R = new(rank_name, rank_flags, rank_exclude_flags, rank_can_edit_flags)
|
||||
if(!R)
|
||||
continue
|
||||
GLOB.admin_ranks += R
|
||||
qdel(query_load_admin_ranks)
|
||||
//load ranks from backup file
|
||||
if(dbfail)
|
||||
var/backup_file = file2text("data/admins_backup.json")
|
||||
if(backup_file == null)
|
||||
log_world("Unable to locate admins backup file.")
|
||||
return FALSE
|
||||
var/list/json = json_decode(backup_file)
|
||||
for(var/J in json["ranks"])
|
||||
var/skip
|
||||
for(var/datum/admin_rank/R in GLOB.admin_ranks)
|
||||
if(R.name == "[J]") //this rank was already loaded from txt override
|
||||
skip = TRUE
|
||||
if(skip)
|
||||
continue
|
||||
var/datum/admin_rank/R = new("[J]", json["ranks"]["[J]"]["include rights"], json["ranks"]["[J]"]["exclude rights"], json["ranks"]["[J]"]["can edit rights"])
|
||||
if(!R)
|
||||
continue
|
||||
GLOB.admin_ranks += R
|
||||
return json
|
||||
#ifdef TESTING
|
||||
var/msg = "Permission Sets Built:\n"
|
||||
for(var/datum/admin_rank/R in GLOB.admin_ranks)
|
||||
msg += "\t[R.name]"
|
||||
var/rights = rights2text(R.rights,"\n\t\t")
|
||||
if(rights)
|
||||
msg += "\t\t[rights]\n"
|
||||
testing(msg)
|
||||
#endif
|
||||
|
||||
/proc/load_admins(no_update)
|
||||
var/dbfail
|
||||
if(!CONFIG_GET(flag/admin_legacy_system) && !SSdbcore.Connect())
|
||||
message_admins("Failed to connect to database while loading admins. Loading from backup.")
|
||||
log_sql("Failed to connect to database while loading admins. Loading from backup.")
|
||||
dbfail = 1
|
||||
//clear the datums references
|
||||
GLOB.admin_datums.Cut()
|
||||
for(var/client/C in GLOB.admins)
|
||||
C.remove_admin_verbs()
|
||||
C.holder = null
|
||||
GLOB.admins.Cut()
|
||||
GLOB.protected_admins.Cut()
|
||||
GLOB.deadmins.Cut()
|
||||
var/list/backup_file_json = load_admin_ranks(dbfail, no_update)
|
||||
dbfail = backup_file_json != null
|
||||
//Clear profile access
|
||||
for(var/A in world.GetConfig("admin"))
|
||||
world.SetConfig("APP/admin", A, null)
|
||||
var/list/rank_names = list()
|
||||
for(var/datum/admin_rank/R in GLOB.admin_ranks)
|
||||
rank_names[R.name] = R
|
||||
//ckeys listed in admins.txt are always made admins before sql loading is attempted
|
||||
var/admins_text = file2text("[global.config.directory]/admins.txt")
|
||||
var/regex/admins_regex = new(@"^(?!#)(.+?)\s+=\s+(.+)", "gm")
|
||||
while(admins_regex.Find(admins_text))
|
||||
new /datum/admins(rank_names[admins_regex.group[2]], ckey(admins_regex.group[1]), FALSE, TRUE)
|
||||
if(!CONFIG_GET(flag/admin_legacy_system) || dbfail)
|
||||
var/datum/db_query/query_load_admins = SSdbcore.NewQuery("SELECT ckey, `rank` FROM [format_table_name("admin")] ORDER BY `rank`")
|
||||
if(!query_load_admins.Execute())
|
||||
message_admins("Error loading admins from database. Loading from backup.")
|
||||
log_sql("Error loading admins from database. Loading from backup.")
|
||||
dbfail = 1
|
||||
else
|
||||
while(query_load_admins.NextRow())
|
||||
var/admin_ckey = ckey(query_load_admins.item[1])
|
||||
var/admin_rank = query_load_admins.item[2]
|
||||
var/skip
|
||||
if(rank_names[admin_rank] == null)
|
||||
message_admins("[admin_ckey] loaded with invalid admin rank [admin_rank].")
|
||||
skip = 1
|
||||
if(GLOB.admin_datums[admin_ckey] || GLOB.deadmins[admin_ckey])
|
||||
skip = 1
|
||||
if(!skip)
|
||||
new /datum/admins(rank_names[admin_rank], admin_ckey)
|
||||
qdel(query_load_admins)
|
||||
//load admins from backup file
|
||||
if(dbfail)
|
||||
if(!backup_file_json)
|
||||
if(backup_file_json != null)
|
||||
//already tried
|
||||
return
|
||||
var/backup_file = file2text("data/admins_backup.json")
|
||||
if(backup_file == null)
|
||||
log_world("Unable to locate admins backup file.")
|
||||
return
|
||||
backup_file_json = json_decode(backup_file)
|
||||
for(var/J in backup_file_json["admins"])
|
||||
var/skip
|
||||
for(var/A in GLOB.admin_datums + GLOB.deadmins)
|
||||
if(A == "[J]") //this admin was already loaded from txt override
|
||||
skip = TRUE
|
||||
if(skip)
|
||||
continue
|
||||
new /datum/admins(rank_names[backup_file_json["admins"]["[J]"]], ckey("[J]"))
|
||||
#ifdef TESTING
|
||||
var/msg = "Admins Built:\n"
|
||||
for(var/ckey in GLOB.admin_datums)
|
||||
var/datum/admins/D = GLOB.admin_datums[ckey]
|
||||
msg += "\t[ckey] - [D.rank.name]\n"
|
||||
testing(msg)
|
||||
#endif
|
||||
return dbfail
|
||||
|
||||
#ifdef TESTING
|
||||
/client/verb/changerank(newrank in GLOB.admin_ranks)
|
||||
if(holder)
|
||||
holder.rank = newrank
|
||||
else
|
||||
holder = new /datum/admins(newrank, ckey)
|
||||
remove_admin_verbs()
|
||||
holder.associate(src)
|
||||
|
||||
/client/verb/changerights(newrights as num)
|
||||
if(holder)
|
||||
holder.rank.rights = newrights
|
||||
else
|
||||
holder = new /datum/admins("testing", newrights, ckey)
|
||||
remove_admin_verbs()
|
||||
holder.associate(src)
|
||||
#endif
|
||||
|
||||
+109
-109
@@ -1,109 +1,109 @@
|
||||
#define TGS_STATUS_THROTTLE 5
|
||||
|
||||
/datum/tgs_chat_command/tgsstatus
|
||||
name = "status"
|
||||
help_text = "Gets the admincount, playercount, gamemode, and true game mode of the server"
|
||||
admin_only = TRUE
|
||||
|
||||
/datum/tgs_chat_command/tgsstatus/Run(datum/tgs_chat_user/sender, params)
|
||||
var/list/adm = get_admin_counts()
|
||||
var/list/allmins = adm["total"]
|
||||
var/status = "Admins: [allmins.len] (Active: [english_list(adm["present"])] AFK: [english_list(adm["afk"])] Stealth: [english_list(adm["stealth"])] Skipped: [english_list(adm["noflags"])]). "
|
||||
status += "Players: [GLOB.clients.len] (Active: [get_active_player_count(0,1,0)]). Mode: [SSticker.mode ? SSticker.mode.name : "Not started"]."
|
||||
return status
|
||||
|
||||
/datum/tgs_chat_command/tgscheck
|
||||
name = "check"
|
||||
help_text = "Gets the playercount, gamemode, and address of the server"
|
||||
|
||||
/datum/tgs_chat_command/tgscheck/Run(datum/tgs_chat_user/sender, params)
|
||||
var/server = CONFIG_GET(string/server)
|
||||
return "[GLOB.round_id ? "Round #[GLOB.round_id]: " : ""][GLOB.clients.len] players on [SSmapping.config.map_name], Mode: [GLOB.master_mode]; Round [SSticker.HasRoundStarted() ? (SSticker.IsRoundInProgress() ? "Active" : "Finishing") : "Starting"] -- [server ? server : "[world.internet_address]:[world.port]"]"
|
||||
|
||||
/datum/tgs_chat_command/ahelp
|
||||
name = "ahelp"
|
||||
help_text = "<ckey|ticket #> <message|ticket <close|resolve|icissue|reject|reopen <ticket #>|list>>"
|
||||
admin_only = TRUE
|
||||
|
||||
/datum/tgs_chat_command/ahelp/Run(datum/tgs_chat_user/sender, params)
|
||||
var/list/all_params = splittext(params, " ")
|
||||
if(all_params.len < 2)
|
||||
return "Insufficient parameters"
|
||||
var/target = all_params[1]
|
||||
all_params.Cut(1, 2)
|
||||
var/id = text2num(target)
|
||||
if(id != null)
|
||||
var/datum/admin_help/AH = GLOB.ahelp_tickets.TicketByID(id)
|
||||
if(AH)
|
||||
target = AH.initiator_ckey
|
||||
else
|
||||
return "Ticket #[id] not found!"
|
||||
var/res = TgsPm(target, all_params.Join(" "), sender.friendly_name)
|
||||
if(res != "Message Successful")
|
||||
return res
|
||||
|
||||
/datum/tgs_chat_command/namecheck
|
||||
name = "namecheck"
|
||||
help_text = "Returns info on the specified target"
|
||||
admin_only = TRUE
|
||||
|
||||
/datum/tgs_chat_command/namecheck/Run(datum/tgs_chat_user/sender, params)
|
||||
params = trim(params)
|
||||
if(!params)
|
||||
return "Insufficient parameters"
|
||||
log_admin("Chat Name Check: [sender.friendly_name] on [params]")
|
||||
message_admins("Name checking [params] from [sender.friendly_name]")
|
||||
return keywords_lookup(params, 1)
|
||||
|
||||
/datum/tgs_chat_command/adminwho
|
||||
name = "adminwho"
|
||||
help_text = "Lists administrators currently on the server"
|
||||
admin_only = TRUE
|
||||
|
||||
/datum/tgs_chat_command/adminwho/Run(datum/tgs_chat_user/sender, params)
|
||||
return tgsadminwho()
|
||||
|
||||
GLOBAL_LIST(round_end_notifiees)
|
||||
|
||||
/datum/tgs_chat_command/endnotify
|
||||
name = "endnotify"
|
||||
help_text = "Pings the invoker when the round ends"
|
||||
admin_only = TRUE
|
||||
|
||||
/datum/tgs_chat_command/endnotify/Run(datum/tgs_chat_user/sender, params)
|
||||
if(!SSticker.IsRoundInProgress() && SSticker.HasRoundStarted())
|
||||
return "[sender.mention], the round has already ended!"
|
||||
LAZYINITLIST(GLOB.round_end_notifiees)
|
||||
GLOB.round_end_notifiees[sender.mention] = TRUE
|
||||
return "I will notify [sender.mention] when the round ends."
|
||||
|
||||
/datum/tgs_chat_command/sdql
|
||||
name = "sdql"
|
||||
help_text = "Runs an SDQL query"
|
||||
admin_only = TRUE
|
||||
|
||||
/datum/tgs_chat_command/sdql/Run(datum/tgs_chat_user/sender, params)
|
||||
if(GLOB.AdminProcCaller)
|
||||
return "Unable to run query, another admin proc call is in progress. Try again later."
|
||||
GLOB.AdminProcCaller = "CHAT_[sender.friendly_name]" //_ won't show up in ckeys so it'll never match with a real admin
|
||||
var/list/results = world.SDQL2_query(params, GLOB.AdminProcCaller, GLOB.AdminProcCaller)
|
||||
GLOB.AdminProcCaller = null
|
||||
if(!results)
|
||||
return "Query produced no output"
|
||||
var/list/text_res = results.Copy(1, 3)
|
||||
var/list/refs = results.len > 3 ? results.Copy(4) : null
|
||||
. = "[text_res.Join("\n")][refs ? "\nRefs: [refs.Join(" ")]" : ""]"
|
||||
|
||||
/datum/tgs_chat_command/reload_admins
|
||||
name = "reload_admins"
|
||||
help_text = "Forces the server to reload admins."
|
||||
admin_only = TRUE
|
||||
|
||||
/datum/tgs_chat_command/reload_admins/Run(datum/tgs_chat_user/sender, params)
|
||||
ReloadAsync()
|
||||
log_admin("[sender.friendly_name] reloaded admins via chat command.")
|
||||
return "Admins reloaded."
|
||||
|
||||
/datum/tgs_chat_command/reload_admins/proc/ReloadAsync()
|
||||
set waitfor = FALSE
|
||||
load_admins()
|
||||
#define TGS_STATUS_THROTTLE 5
|
||||
|
||||
/datum/tgs_chat_command/tgsstatus
|
||||
name = "status"
|
||||
help_text = "Gets the admincount, playercount, gamemode, and true game mode of the server"
|
||||
admin_only = TRUE
|
||||
|
||||
/datum/tgs_chat_command/tgsstatus/Run(datum/tgs_chat_user/sender, params)
|
||||
var/list/adm = get_admin_counts()
|
||||
var/list/allmins = adm["total"]
|
||||
var/status = "Admins: [allmins.len] (Active: [english_list(adm["present"])] AFK: [english_list(adm["afk"])] Stealth: [english_list(adm["stealth"])] Skipped: [english_list(adm["noflags"])]). "
|
||||
status += "Players: [GLOB.clients.len] (Active: [get_active_player_count(0,1,0)]). Mode: [SSticker.mode ? SSticker.mode.name : "Not started"]."
|
||||
return status
|
||||
|
||||
/datum/tgs_chat_command/tgscheck
|
||||
name = "check"
|
||||
help_text = "Gets the playercount, gamemode, and address of the server"
|
||||
|
||||
/datum/tgs_chat_command/tgscheck/Run(datum/tgs_chat_user/sender, params)
|
||||
var/server = CONFIG_GET(string/server)
|
||||
return "[GLOB.round_id ? "Round #[GLOB.round_id]: " : ""][GLOB.clients.len] players on [SSmapping.config.map_name], Mode: [GLOB.master_mode]; Round [SSticker.HasRoundStarted() ? (SSticker.IsRoundInProgress() ? "Active" : "Finishing") : "Starting"] -- [server ? server : "[world.internet_address]:[world.port]"]"
|
||||
|
||||
/datum/tgs_chat_command/ahelp
|
||||
name = "ahelp"
|
||||
help_text = "<ckey|ticket #> <message|ticket <close|resolve|icissue|reject|reopen <ticket #>|list>>"
|
||||
admin_only = TRUE
|
||||
|
||||
/datum/tgs_chat_command/ahelp/Run(datum/tgs_chat_user/sender, params)
|
||||
var/list/all_params = splittext(params, " ")
|
||||
if(all_params.len < 2)
|
||||
return "Insufficient parameters"
|
||||
var/target = all_params[1]
|
||||
all_params.Cut(1, 2)
|
||||
var/id = text2num(target)
|
||||
if(id != null)
|
||||
var/datum/admin_help/AH = GLOB.ahelp_tickets.TicketByID(id)
|
||||
if(AH)
|
||||
target = AH.initiator_ckey
|
||||
else
|
||||
return "Ticket #[id] not found!"
|
||||
var/res = TgsPm(target, all_params.Join(" "), sender.friendly_name)
|
||||
if(res != "Message Successful")
|
||||
return res
|
||||
|
||||
/datum/tgs_chat_command/namecheck
|
||||
name = "namecheck"
|
||||
help_text = "Returns info on the specified target"
|
||||
admin_only = TRUE
|
||||
|
||||
/datum/tgs_chat_command/namecheck/Run(datum/tgs_chat_user/sender, params)
|
||||
params = trim(params)
|
||||
if(!params)
|
||||
return "Insufficient parameters"
|
||||
log_admin("Chat Name Check: [sender.friendly_name] on [params]")
|
||||
message_admins("Name checking [params] from [sender.friendly_name]")
|
||||
return keywords_lookup(params, 1)
|
||||
|
||||
/datum/tgs_chat_command/adminwho
|
||||
name = "adminwho"
|
||||
help_text = "Lists administrators currently on the server"
|
||||
admin_only = TRUE
|
||||
|
||||
/datum/tgs_chat_command/adminwho/Run(datum/tgs_chat_user/sender, params)
|
||||
return tgsadminwho()
|
||||
|
||||
GLOBAL_LIST(round_end_notifiees)
|
||||
|
||||
/datum/tgs_chat_command/endnotify
|
||||
name = "endnotify"
|
||||
help_text = "Pings the invoker when the round ends"
|
||||
admin_only = TRUE
|
||||
|
||||
/datum/tgs_chat_command/endnotify/Run(datum/tgs_chat_user/sender, params)
|
||||
if(!SSticker.IsRoundInProgress() && SSticker.HasRoundStarted())
|
||||
return "[sender.mention], the round has already ended!"
|
||||
LAZYINITLIST(GLOB.round_end_notifiees)
|
||||
GLOB.round_end_notifiees[sender.mention] = TRUE
|
||||
return "I will notify [sender.mention] when the round ends."
|
||||
|
||||
/datum/tgs_chat_command/sdql
|
||||
name = "sdql"
|
||||
help_text = "Runs an SDQL query"
|
||||
admin_only = TRUE
|
||||
|
||||
/datum/tgs_chat_command/sdql/Run(datum/tgs_chat_user/sender, params)
|
||||
if(GLOB.AdminProcCaller)
|
||||
return "Unable to run query, another admin proc call is in progress. Try again later."
|
||||
GLOB.AdminProcCaller = "CHAT_[sender.friendly_name]" //_ won't show up in ckeys so it'll never match with a real admin
|
||||
var/list/results = world.SDQL2_query(params, GLOB.AdminProcCaller, GLOB.AdminProcCaller)
|
||||
GLOB.AdminProcCaller = null
|
||||
if(!results)
|
||||
return "Query produced no output"
|
||||
var/list/text_res = results.Copy(1, 3)
|
||||
var/list/refs = results.len > 3 ? results.Copy(4) : null
|
||||
. = "[text_res.Join("\n")][refs ? "\nRefs: [refs.Join(" ")]" : ""]"
|
||||
|
||||
/datum/tgs_chat_command/reload_admins
|
||||
name = "reload_admins"
|
||||
help_text = "Forces the server to reload admins."
|
||||
admin_only = TRUE
|
||||
|
||||
/datum/tgs_chat_command/reload_admins/Run(datum/tgs_chat_user/sender, params)
|
||||
ReloadAsync()
|
||||
log_admin("[sender.friendly_name] reloaded admins via chat command.")
|
||||
return "Admins reloaded."
|
||||
|
||||
/datum/tgs_chat_command/reload_admins/proc/ReloadAsync()
|
||||
set waitfor = FALSE
|
||||
load_admins()
|
||||
|
||||
@@ -1,41 +1,41 @@
|
||||
|
||||
/datum/admins/proc/create_mob(mob/user)
|
||||
var/static/create_mob_html
|
||||
if (!create_mob_html)
|
||||
var/mobjs = null
|
||||
mobjs = jointext(typesof(/mob), ";")
|
||||
create_mob_html = file2text('html/create_object.html')
|
||||
create_mob_html = replacetext(create_mob_html, "Create Object", "Create Mob")
|
||||
create_mob_html = replacetext(create_mob_html, "null /* object types */", "\"[mobjs]\"")
|
||||
|
||||
user << browse(create_panel_helper(create_mob_html), "window=create_mob;size=425x475")
|
||||
|
||||
/proc/randomize_human(mob/living/carbon/human/H)
|
||||
H.gender = pick(MALE, FEMALE)
|
||||
H.body_type = H.gender
|
||||
H.real_name = random_unique_name(H.gender)
|
||||
H.name = H.real_name
|
||||
H.underwear = random_underwear(H.gender)
|
||||
H.underwear_color = random_short_color()
|
||||
H.skin_tone = random_skin_tone()
|
||||
H.hairstyle = random_hairstyle(H.gender)
|
||||
H.facial_hairstyle = random_facial_hairstyle(H.gender)
|
||||
H.hair_color = random_short_color()
|
||||
H.facial_hair_color = H.hair_color
|
||||
H.eye_color = random_eye_color()
|
||||
H.dna.blood_type = random_blood_type()
|
||||
|
||||
// Mutant randomizing, doesn't affect the mob appearance unless it's the specific mutant.
|
||||
H.dna.features["mcolor"] = random_short_color()
|
||||
H.dna.features["ethcolor"] = GLOB.color_list_ethereal[pick(GLOB.color_list_ethereal)]
|
||||
H.dna.features["tail_lizard"] = pick(GLOB.tails_list_lizard)
|
||||
H.dna.features["snout"] = pick(GLOB.snouts_list)
|
||||
H.dna.features["horns"] = pick(GLOB.horns_list)
|
||||
H.dna.features["frills"] = pick(GLOB.frills_list)
|
||||
H.dna.features["spines"] = pick(GLOB.spines_list)
|
||||
H.dna.features["body_markings"] = pick(GLOB.body_markings_list)
|
||||
H.dna.features["moth_wings"] = pick(GLOB.moth_wings_list)
|
||||
|
||||
H.update_body()
|
||||
H.update_hair()
|
||||
H.update_body_parts()
|
||||
|
||||
/datum/admins/proc/create_mob(mob/user)
|
||||
var/static/create_mob_html
|
||||
if (!create_mob_html)
|
||||
var/mobjs = null
|
||||
mobjs = jointext(typesof(/mob), ";")
|
||||
create_mob_html = file2text('html/create_object.html')
|
||||
create_mob_html = replacetext(create_mob_html, "Create Object", "Create Mob")
|
||||
create_mob_html = replacetext(create_mob_html, "null /* object types */", "\"[mobjs]\"")
|
||||
|
||||
user << browse(create_panel_helper(create_mob_html), "window=create_mob;size=425x475")
|
||||
|
||||
/proc/randomize_human(mob/living/carbon/human/H)
|
||||
H.gender = pick(MALE, FEMALE)
|
||||
H.body_type = H.gender
|
||||
H.real_name = random_unique_name(H.gender)
|
||||
H.name = H.real_name
|
||||
H.underwear = random_underwear(H.gender)
|
||||
H.underwear_color = random_short_color()
|
||||
H.skin_tone = random_skin_tone()
|
||||
H.hairstyle = random_hairstyle(H.gender)
|
||||
H.facial_hairstyle = random_facial_hairstyle(H.gender)
|
||||
H.hair_color = random_short_color()
|
||||
H.facial_hair_color = H.hair_color
|
||||
H.eye_color = random_eye_color()
|
||||
H.dna.blood_type = random_blood_type()
|
||||
|
||||
// Mutant randomizing, doesn't affect the mob appearance unless it's the specific mutant.
|
||||
H.dna.features["mcolor"] = random_short_color()
|
||||
H.dna.features["ethcolor"] = GLOB.color_list_ethereal[pick(GLOB.color_list_ethereal)]
|
||||
H.dna.features["tail_lizard"] = pick(GLOB.tails_list_lizard)
|
||||
H.dna.features["snout"] = pick(GLOB.snouts_list)
|
||||
H.dna.features["horns"] = pick(GLOB.horns_list)
|
||||
H.dna.features["frills"] = pick(GLOB.frills_list)
|
||||
H.dna.features["spines"] = pick(GLOB.spines_list)
|
||||
H.dna.features["body_markings"] = pick(GLOB.body_markings_list)
|
||||
H.dna.features["moth_wings"] = pick(GLOB.moth_wings_list)
|
||||
|
||||
H.update_body()
|
||||
H.update_hair()
|
||||
H.update_body_parts()
|
||||
|
||||
@@ -1,32 +1,32 @@
|
||||
/datum/admins/proc/create_panel_helper(template)
|
||||
var/final_html = replacetext(template, "/* ref src */", "[REF(src)];[HrefToken()]")
|
||||
final_html = replacetext(final_html,"/* hreftokenfield */","[HrefTokenFormField()]")
|
||||
return final_html
|
||||
|
||||
/datum/admins/proc/create_object(mob/user)
|
||||
var/static/create_object_html = null
|
||||
if (!create_object_html)
|
||||
var/objectjs = null
|
||||
objectjs = jointext(typesof(/obj), ";")
|
||||
create_object_html = file2text('html/create_object.html')
|
||||
create_object_html = replacetext(create_object_html, "null /* object types */", "\"[objectjs]\"")
|
||||
|
||||
user << browse(create_panel_helper(create_object_html), "window=create_object;size=425x475")
|
||||
|
||||
/datum/admins/proc/quick_create_object(mob/user)
|
||||
var/static/list/create_object_forms = list(
|
||||
/obj, /obj/structure, /obj/machinery, /obj/effect,
|
||||
/obj/item, /obj/item/clothing, /obj/item/stack, /obj/item,
|
||||
/obj/item/reagent_containers, /obj/item/gun)
|
||||
|
||||
var/path = input("Select the path of the object you wish to create.", "Path", /obj) in sortList(create_object_forms, /proc/cmp_typepaths_asc)
|
||||
var/html_form = create_object_forms[path]
|
||||
|
||||
if (!html_form)
|
||||
var/objectjs = jointext(typesof(path), ";")
|
||||
html_form = file2text('html/create_object.html')
|
||||
html_form = replacetext(html_form, "Create Object", "Create [path]")
|
||||
html_form = replacetext(html_form, "null /* object types */", "\"[objectjs]\"")
|
||||
create_object_forms[path] = html_form
|
||||
|
||||
user << browse(create_panel_helper(html_form), "window=qco[path];size=425x475")
|
||||
/datum/admins/proc/create_panel_helper(template)
|
||||
var/final_html = replacetext(template, "/* ref src */", "[REF(src)];[HrefToken()]")
|
||||
final_html = replacetext(final_html,"/* hreftokenfield */","[HrefTokenFormField()]")
|
||||
return final_html
|
||||
|
||||
/datum/admins/proc/create_object(mob/user)
|
||||
var/static/create_object_html = null
|
||||
if (!create_object_html)
|
||||
var/objectjs = null
|
||||
objectjs = jointext(typesof(/obj), ";")
|
||||
create_object_html = file2text('html/create_object.html')
|
||||
create_object_html = replacetext(create_object_html, "null /* object types */", "\"[objectjs]\"")
|
||||
|
||||
user << browse(create_panel_helper(create_object_html), "window=create_object;size=425x475")
|
||||
|
||||
/datum/admins/proc/quick_create_object(mob/user)
|
||||
var/static/list/create_object_forms = list(
|
||||
/obj, /obj/structure, /obj/machinery, /obj/effect,
|
||||
/obj/item, /obj/item/clothing, /obj/item/stack, /obj/item,
|
||||
/obj/item/reagent_containers, /obj/item/gun)
|
||||
|
||||
var/path = input("Select the path of the object you wish to create.", "Path", /obj) in sortList(create_object_forms, /proc/cmp_typepaths_asc)
|
||||
var/html_form = create_object_forms[path]
|
||||
|
||||
if (!html_form)
|
||||
var/objectjs = jointext(typesof(path), ";")
|
||||
html_form = file2text('html/create_object.html')
|
||||
html_form = replacetext(html_form, "Create Object", "Create [path]")
|
||||
html_form = replacetext(html_form, "null /* object types */", "\"[objectjs]\"")
|
||||
create_object_forms[path] = html_form
|
||||
|
||||
user << browse(create_panel_helper(html_form), "window=qco[path];size=425x475")
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
/datum/admins/proc/create_turf(mob/user)
|
||||
var/static/create_turf_html
|
||||
if (!create_turf_html)
|
||||
var/turfjs = null
|
||||
turfjs = jointext(typesof(/turf), ";")
|
||||
create_turf_html = file2text('html/create_object.html')
|
||||
create_turf_html = replacetext(create_turf_html, "Create Object", "Create Turf")
|
||||
create_turf_html = replacetext(create_turf_html, "null /* object types */", "\"[turfjs]\"")
|
||||
|
||||
user << browse(create_panel_helper(create_turf_html), "window=create_turf;size=425x475")
|
||||
/datum/admins/proc/create_turf(mob/user)
|
||||
var/static/create_turf_html
|
||||
if (!create_turf_html)
|
||||
var/turfjs = null
|
||||
turfjs = jointext(typesof(/turf), ";")
|
||||
create_turf_html = file2text('html/create_object.html')
|
||||
create_turf_html = replacetext(create_turf_html, "Create Object", "Create Turf")
|
||||
create_turf_html = replacetext(create_turf_html, "null /* object types */", "\"[turfjs]\"")
|
||||
|
||||
user << browse(create_panel_helper(create_turf_html), "window=create_turf;size=425x475")
|
||||
|
||||
+208
-208
@@ -1,208 +1,208 @@
|
||||
GLOBAL_LIST_EMPTY(admin_datums)
|
||||
GLOBAL_PROTECT(admin_datums)
|
||||
GLOBAL_LIST_EMPTY(protected_admins)
|
||||
GLOBAL_PROTECT(protected_admins)
|
||||
|
||||
GLOBAL_VAR_INIT(href_token, GenerateToken())
|
||||
GLOBAL_PROTECT(href_token)
|
||||
|
||||
/datum/admins
|
||||
var/datum/admin_rank/rank
|
||||
|
||||
var/target
|
||||
var/name = "nobody's admin datum (no rank)" //Makes for better runtimes
|
||||
var/client/owner = null
|
||||
var/fakekey = null
|
||||
|
||||
var/datum/marked_datum
|
||||
|
||||
var/spamcooldown = 0
|
||||
|
||||
var/admincaster_screen = 0 //TODO: remove all these 5 variables, they are completly unacceptable
|
||||
var/datum/newscaster/feed_message/admincaster_feed_message = new /datum/newscaster/feed_message
|
||||
var/datum/newscaster/wanted_message/admincaster_wanted_message = new /datum/newscaster/wanted_message
|
||||
var/datum/newscaster/feed_channel/admincaster_feed_channel = new /datum/newscaster/feed_channel
|
||||
var/admin_signature
|
||||
|
||||
var/href_token
|
||||
|
||||
var/deadmined
|
||||
|
||||
/datum/admins/New(datum/admin_rank/R, ckey, force_active = FALSE, protected)
|
||||
if(IsAdminAdvancedProcCall())
|
||||
var/msg = " has tried to elevate permissions!"
|
||||
message_admins("[key_name_admin(usr)][msg]")
|
||||
log_admin("[key_name(usr)][msg]")
|
||||
if (!target) //only del if this is a true creation (and not just a New() proc call), other wise trialmins/coders could abuse this to deadmin other admins
|
||||
QDEL_IN(src, 0)
|
||||
CRASH("Admin proc call creation of admin datum")
|
||||
return
|
||||
if(!ckey)
|
||||
QDEL_IN(src, 0)
|
||||
CRASH("Admin datum created without a ckey")
|
||||
if(!istype(R))
|
||||
QDEL_IN(src, 0)
|
||||
CRASH("Admin datum created without a rank")
|
||||
target = ckey
|
||||
name = "[ckey]'s admin datum ([R])"
|
||||
rank = R
|
||||
admin_signature = "Nanotrasen Officer #[rand(0,9)][rand(0,9)][rand(0,9)]"
|
||||
href_token = GenerateToken()
|
||||
if(R.rights & R_DEBUG) //grant profile access
|
||||
world.SetConfig("APP/admin", ckey, "role=admin")
|
||||
//only admins with +ADMIN start admined
|
||||
if(protected)
|
||||
GLOB.protected_admins[target] = src
|
||||
if (force_active || (R.rights & R_AUTOADMIN))
|
||||
activate()
|
||||
else
|
||||
deactivate()
|
||||
|
||||
/datum/admins/Destroy()
|
||||
if(IsAdminAdvancedProcCall())
|
||||
var/msg = " has tried to elevate permissions!"
|
||||
message_admins("[key_name_admin(usr)][msg]")
|
||||
log_admin("[key_name(usr)][msg]")
|
||||
return QDEL_HINT_LETMELIVE
|
||||
. = ..()
|
||||
|
||||
/datum/admins/proc/activate()
|
||||
if(IsAdminAdvancedProcCall())
|
||||
var/msg = " has tried to elevate permissions!"
|
||||
message_admins("[key_name_admin(usr)][msg]")
|
||||
log_admin("[key_name(usr)][msg]")
|
||||
return
|
||||
GLOB.deadmins -= target
|
||||
GLOB.admin_datums[target] = src
|
||||
deadmined = FALSE
|
||||
if (GLOB.directory[target])
|
||||
associate(GLOB.directory[target]) //find the client for a ckey if they are connected and associate them with us
|
||||
|
||||
|
||||
/datum/admins/proc/deactivate()
|
||||
if(IsAdminAdvancedProcCall())
|
||||
var/msg = " has tried to elevate permissions!"
|
||||
message_admins("[key_name_admin(usr)][msg]")
|
||||
log_admin("[key_name(usr)][msg]")
|
||||
return
|
||||
GLOB.deadmins[target] = src
|
||||
GLOB.admin_datums -= target
|
||||
deadmined = TRUE
|
||||
var/client/C
|
||||
if ((C = owner) || (C = GLOB.directory[target]))
|
||||
disassociate()
|
||||
C.verbs += /client/proc/readmin
|
||||
|
||||
/datum/admins/proc/associate(client/C)
|
||||
if(IsAdminAdvancedProcCall())
|
||||
var/msg = " has tried to elevate permissions!"
|
||||
message_admins("[key_name_admin(usr)][msg]")
|
||||
log_admin("[key_name(usr)][msg]")
|
||||
return
|
||||
|
||||
if(istype(C))
|
||||
if(C.ckey != target)
|
||||
var/msg = " has attempted to associate with [target]'s admin datum"
|
||||
message_admins("[key_name_admin(C)][msg]")
|
||||
log_admin("[key_name(C)][msg]")
|
||||
return
|
||||
if (deadmined)
|
||||
activate()
|
||||
owner = C
|
||||
owner.holder = src
|
||||
owner.add_admin_verbs() //TODO <--- todo what? the proc clearly exists and works since its the backbone to our entire admin system
|
||||
owner.verbs -= /client/proc/readmin
|
||||
GLOB.admins |= C
|
||||
|
||||
/datum/admins/proc/disassociate()
|
||||
if(IsAdminAdvancedProcCall())
|
||||
var/msg = " has tried to elevate permissions!"
|
||||
message_admins("[key_name_admin(usr)][msg]")
|
||||
log_admin("[key_name(usr)][msg]")
|
||||
return
|
||||
if(owner)
|
||||
GLOB.admins -= owner
|
||||
owner.remove_admin_verbs()
|
||||
owner.holder = null
|
||||
owner = null
|
||||
|
||||
/datum/admins/proc/check_for_rights(rights_required)
|
||||
if(rights_required && !(rights_required & rank.rights))
|
||||
return 0
|
||||
return 1
|
||||
|
||||
|
||||
/datum/admins/proc/check_if_greater_rights_than_holder(datum/admins/other)
|
||||
if(!other)
|
||||
return 1 //they have no rights
|
||||
if(rank.rights == R_EVERYTHING)
|
||||
return 1 //we have all the rights
|
||||
if(src == other)
|
||||
return 1 //you always have more rights than yourself
|
||||
if(rank.rights != other.rank.rights)
|
||||
if( (rank.rights & other.rank.rights) == other.rank.rights )
|
||||
return 1 //we have all the rights they have and more
|
||||
return 0
|
||||
|
||||
/datum/admins/vv_edit_var(var_name, var_value)
|
||||
return FALSE //nice try trialmin
|
||||
|
||||
/*
|
||||
checks if usr is an admin with at least ONE of the flags in rights_required. (Note, they don't need all the flags)
|
||||
if rights_required == 0, then it simply checks if they are an admin.
|
||||
if it doesn't return 1 and show_msg=1 it will prints a message explaining why the check has failed
|
||||
generally it would be used like so:
|
||||
|
||||
/proc/admin_proc()
|
||||
if(!check_rights(R_ADMIN))
|
||||
return
|
||||
to_chat(world, "you have enough rights!", confidential = TRUE)
|
||||
|
||||
NOTE: it checks usr! not src! So if you're checking somebody's rank in a proc which they did not call
|
||||
you will have to do something like if(client.rights & R_ADMIN) yourself.
|
||||
*/
|
||||
/proc/check_rights(rights_required, show_msg=1)
|
||||
if(usr && usr.client)
|
||||
if (check_rights_for(usr.client, rights_required))
|
||||
return 1
|
||||
else
|
||||
if(show_msg)
|
||||
to_chat(usr, "<font color='red'>Error: You do not have sufficient rights to do that. You require one of the following flags:[rights2text(rights_required," ")].</font>", confidential = TRUE)
|
||||
return 0
|
||||
|
||||
//probably a bit iffy - will hopefully figure out a better solution
|
||||
/proc/check_if_greater_rights_than(client/other)
|
||||
if(usr && usr.client)
|
||||
if(usr.client.holder)
|
||||
if(!other || !other.holder)
|
||||
return 1
|
||||
return usr.client.holder.check_if_greater_rights_than_holder(other.holder)
|
||||
return 0
|
||||
|
||||
//This proc checks whether subject has at least ONE of the rights specified in rights_required.
|
||||
/proc/check_rights_for(client/subject, rights_required)
|
||||
if(subject && subject.holder)
|
||||
return subject.holder.check_for_rights(rights_required)
|
||||
return 0
|
||||
|
||||
/proc/GenerateToken()
|
||||
. = ""
|
||||
for(var/I in 1 to 32)
|
||||
. += "[rand(10)]"
|
||||
|
||||
/proc/RawHrefToken(forceGlobal = FALSE)
|
||||
var/tok = GLOB.href_token
|
||||
if(!forceGlobal && usr)
|
||||
var/client/C = usr.client
|
||||
if(!C)
|
||||
CRASH("No client for HrefToken()!")
|
||||
var/datum/admins/holder = C.holder
|
||||
if(holder)
|
||||
tok = holder.href_token
|
||||
return tok
|
||||
|
||||
/proc/HrefToken(forceGlobal = FALSE)
|
||||
return "admin_token=[RawHrefToken(forceGlobal)]"
|
||||
|
||||
/proc/HrefTokenFormField(forceGlobal = FALSE)
|
||||
return "<input type='hidden' name='admin_token' value='[RawHrefToken(forceGlobal)]'>"
|
||||
GLOBAL_LIST_EMPTY(admin_datums)
|
||||
GLOBAL_PROTECT(admin_datums)
|
||||
GLOBAL_LIST_EMPTY(protected_admins)
|
||||
GLOBAL_PROTECT(protected_admins)
|
||||
|
||||
GLOBAL_VAR_INIT(href_token, GenerateToken())
|
||||
GLOBAL_PROTECT(href_token)
|
||||
|
||||
/datum/admins
|
||||
var/datum/admin_rank/rank
|
||||
|
||||
var/target
|
||||
var/name = "nobody's admin datum (no rank)" //Makes for better runtimes
|
||||
var/client/owner = null
|
||||
var/fakekey = null
|
||||
|
||||
var/datum/marked_datum
|
||||
|
||||
var/spamcooldown = 0
|
||||
|
||||
var/admincaster_screen = 0 //TODO: remove all these 5 variables, they are completly unacceptable
|
||||
var/datum/newscaster/feed_message/admincaster_feed_message = new /datum/newscaster/feed_message
|
||||
var/datum/newscaster/wanted_message/admincaster_wanted_message = new /datum/newscaster/wanted_message
|
||||
var/datum/newscaster/feed_channel/admincaster_feed_channel = new /datum/newscaster/feed_channel
|
||||
var/admin_signature
|
||||
|
||||
var/href_token
|
||||
|
||||
var/deadmined
|
||||
|
||||
/datum/admins/New(datum/admin_rank/R, ckey, force_active = FALSE, protected)
|
||||
if(IsAdminAdvancedProcCall())
|
||||
var/msg = " has tried to elevate permissions!"
|
||||
message_admins("[key_name_admin(usr)][msg]")
|
||||
log_admin("[key_name(usr)][msg]")
|
||||
if (!target) //only del if this is a true creation (and not just a New() proc call), other wise trialmins/coders could abuse this to deadmin other admins
|
||||
QDEL_IN(src, 0)
|
||||
CRASH("Admin proc call creation of admin datum")
|
||||
return
|
||||
if(!ckey)
|
||||
QDEL_IN(src, 0)
|
||||
CRASH("Admin datum created without a ckey")
|
||||
if(!istype(R))
|
||||
QDEL_IN(src, 0)
|
||||
CRASH("Admin datum created without a rank")
|
||||
target = ckey
|
||||
name = "[ckey]'s admin datum ([R])"
|
||||
rank = R
|
||||
admin_signature = "Nanotrasen Officer #[rand(0,9)][rand(0,9)][rand(0,9)]"
|
||||
href_token = GenerateToken()
|
||||
if(R.rights & R_DEBUG) //grant profile access
|
||||
world.SetConfig("APP/admin", ckey, "role=admin")
|
||||
//only admins with +ADMIN start admined
|
||||
if(protected)
|
||||
GLOB.protected_admins[target] = src
|
||||
if (force_active || (R.rights & R_AUTOADMIN))
|
||||
activate()
|
||||
else
|
||||
deactivate()
|
||||
|
||||
/datum/admins/Destroy()
|
||||
if(IsAdminAdvancedProcCall())
|
||||
var/msg = " has tried to elevate permissions!"
|
||||
message_admins("[key_name_admin(usr)][msg]")
|
||||
log_admin("[key_name(usr)][msg]")
|
||||
return QDEL_HINT_LETMELIVE
|
||||
. = ..()
|
||||
|
||||
/datum/admins/proc/activate()
|
||||
if(IsAdminAdvancedProcCall())
|
||||
var/msg = " has tried to elevate permissions!"
|
||||
message_admins("[key_name_admin(usr)][msg]")
|
||||
log_admin("[key_name(usr)][msg]")
|
||||
return
|
||||
GLOB.deadmins -= target
|
||||
GLOB.admin_datums[target] = src
|
||||
deadmined = FALSE
|
||||
if (GLOB.directory[target])
|
||||
associate(GLOB.directory[target]) //find the client for a ckey if they are connected and associate them with us
|
||||
|
||||
|
||||
/datum/admins/proc/deactivate()
|
||||
if(IsAdminAdvancedProcCall())
|
||||
var/msg = " has tried to elevate permissions!"
|
||||
message_admins("[key_name_admin(usr)][msg]")
|
||||
log_admin("[key_name(usr)][msg]")
|
||||
return
|
||||
GLOB.deadmins[target] = src
|
||||
GLOB.admin_datums -= target
|
||||
deadmined = TRUE
|
||||
var/client/C
|
||||
if ((C = owner) || (C = GLOB.directory[target]))
|
||||
disassociate()
|
||||
C.verbs += /client/proc/readmin
|
||||
|
||||
/datum/admins/proc/associate(client/C)
|
||||
if(IsAdminAdvancedProcCall())
|
||||
var/msg = " has tried to elevate permissions!"
|
||||
message_admins("[key_name_admin(usr)][msg]")
|
||||
log_admin("[key_name(usr)][msg]")
|
||||
return
|
||||
|
||||
if(istype(C))
|
||||
if(C.ckey != target)
|
||||
var/msg = " has attempted to associate with [target]'s admin datum"
|
||||
message_admins("[key_name_admin(C)][msg]")
|
||||
log_admin("[key_name(C)][msg]")
|
||||
return
|
||||
if (deadmined)
|
||||
activate()
|
||||
owner = C
|
||||
owner.holder = src
|
||||
owner.add_admin_verbs() //TODO <--- todo what? the proc clearly exists and works since its the backbone to our entire admin system
|
||||
owner.verbs -= /client/proc/readmin
|
||||
GLOB.admins |= C
|
||||
|
||||
/datum/admins/proc/disassociate()
|
||||
if(IsAdminAdvancedProcCall())
|
||||
var/msg = " has tried to elevate permissions!"
|
||||
message_admins("[key_name_admin(usr)][msg]")
|
||||
log_admin("[key_name(usr)][msg]")
|
||||
return
|
||||
if(owner)
|
||||
GLOB.admins -= owner
|
||||
owner.remove_admin_verbs()
|
||||
owner.holder = null
|
||||
owner = null
|
||||
|
||||
/datum/admins/proc/check_for_rights(rights_required)
|
||||
if(rights_required && !(rights_required & rank.rights))
|
||||
return 0
|
||||
return 1
|
||||
|
||||
|
||||
/datum/admins/proc/check_if_greater_rights_than_holder(datum/admins/other)
|
||||
if(!other)
|
||||
return 1 //they have no rights
|
||||
if(rank.rights == R_EVERYTHING)
|
||||
return 1 //we have all the rights
|
||||
if(src == other)
|
||||
return 1 //you always have more rights than yourself
|
||||
if(rank.rights != other.rank.rights)
|
||||
if( (rank.rights & other.rank.rights) == other.rank.rights )
|
||||
return 1 //we have all the rights they have and more
|
||||
return 0
|
||||
|
||||
/datum/admins/vv_edit_var(var_name, var_value)
|
||||
return FALSE //nice try trialmin
|
||||
|
||||
/*
|
||||
checks if usr is an admin with at least ONE of the flags in rights_required. (Note, they don't need all the flags)
|
||||
if rights_required == 0, then it simply checks if they are an admin.
|
||||
if it doesn't return 1 and show_msg=1 it will prints a message explaining why the check has failed
|
||||
generally it would be used like so:
|
||||
|
||||
/proc/admin_proc()
|
||||
if(!check_rights(R_ADMIN))
|
||||
return
|
||||
to_chat(world, "you have enough rights!", confidential = TRUE)
|
||||
|
||||
NOTE: it checks usr! not src! So if you're checking somebody's rank in a proc which they did not call
|
||||
you will have to do something like if(client.rights & R_ADMIN) yourself.
|
||||
*/
|
||||
/proc/check_rights(rights_required, show_msg=1)
|
||||
if(usr && usr.client)
|
||||
if (check_rights_for(usr.client, rights_required))
|
||||
return 1
|
||||
else
|
||||
if(show_msg)
|
||||
to_chat(usr, "<font color='red'>Error: You do not have sufficient rights to do that. You require one of the following flags:[rights2text(rights_required," ")].</font>", confidential = TRUE)
|
||||
return 0
|
||||
|
||||
//probably a bit iffy - will hopefully figure out a better solution
|
||||
/proc/check_if_greater_rights_than(client/other)
|
||||
if(usr && usr.client)
|
||||
if(usr.client.holder)
|
||||
if(!other || !other.holder)
|
||||
return 1
|
||||
return usr.client.holder.check_if_greater_rights_than_holder(other.holder)
|
||||
return 0
|
||||
|
||||
//This proc checks whether subject has at least ONE of the rights specified in rights_required.
|
||||
/proc/check_rights_for(client/subject, rights_required)
|
||||
if(subject && subject.holder)
|
||||
return subject.holder.check_for_rights(rights_required)
|
||||
return 0
|
||||
|
||||
/proc/GenerateToken()
|
||||
. = ""
|
||||
for(var/I in 1 to 32)
|
||||
. += "[rand(10)]"
|
||||
|
||||
/proc/RawHrefToken(forceGlobal = FALSE)
|
||||
var/tok = GLOB.href_token
|
||||
if(!forceGlobal && usr)
|
||||
var/client/C = usr.client
|
||||
if(!C)
|
||||
CRASH("No client for HrefToken()!")
|
||||
var/datum/admins/holder = C.holder
|
||||
if(holder)
|
||||
tok = holder.href_token
|
||||
return tok
|
||||
|
||||
/proc/HrefToken(forceGlobal = FALSE)
|
||||
return "admin_token=[RawHrefToken(forceGlobal)]"
|
||||
|
||||
/proc/HrefTokenFormField(forceGlobal = FALSE)
|
||||
return "<input type='hidden' name='admin_token' value='[RawHrefToken(forceGlobal)]'>"
|
||||
|
||||
+328
-328
@@ -1,328 +1,328 @@
|
||||
/datum/admins/proc/player_panel_new()//The new one
|
||||
if(!check_rights())
|
||||
return
|
||||
log_admin("[key_name(usr)] checked the player panel.")
|
||||
var/dat = "<html><head><meta http-equiv='X-UA-Compatible' content='IE=edge; charset=UTF-8'/><title>Player Panel</title></head>"
|
||||
|
||||
//javascript, the part that does most of the work~
|
||||
dat += {"
|
||||
|
||||
<head>
|
||||
<script type='text/javascript'>
|
||||
|
||||
var locked_tabs = new Array();
|
||||
|
||||
function updateSearch(){
|
||||
|
||||
|
||||
var filter_text = document.getElementById('filter');
|
||||
var filter = filter_text.value.toLowerCase();
|
||||
|
||||
if(complete_list != null && complete_list != ""){
|
||||
var mtbl = document.getElementById("maintable_data_archive");
|
||||
mtbl.innerHTML = complete_list;
|
||||
}
|
||||
|
||||
if(filter.value == ""){
|
||||
return;
|
||||
}else{
|
||||
|
||||
var maintable_data = document.getElementById('maintable_data');
|
||||
var ltr = maintable_data.getElementsByTagName("tr");
|
||||
for ( var i = 0; i < ltr.length; ++i )
|
||||
{
|
||||
try{
|
||||
var tr = ltr\[i\];
|
||||
if(tr.getAttribute("id").indexOf("data") != 0){
|
||||
continue;
|
||||
}
|
||||
var ltd = tr.getElementsByTagName("td");
|
||||
var td = ltd\[0\];
|
||||
var lsearch = td.getElementsByClassName("filter_data");
|
||||
var search = lsearch\[0\];
|
||||
if ( search.innerText.toLowerCase().indexOf(filter) == -1 )
|
||||
{
|
||||
tr.innerHTML = "";
|
||||
i--;
|
||||
}
|
||||
}catch(err) { }
|
||||
}
|
||||
}
|
||||
|
||||
var count = 0;
|
||||
var index = -1;
|
||||
var debug = document.getElementById("debug");
|
||||
|
||||
locked_tabs = new Array();
|
||||
}
|
||||
|
||||
function expand(data_id,target_id){
|
||||
|
||||
job = document.getElementById(data_id+"_job").textContent
|
||||
name = document.getElementById(data_id+"_name").textContent
|
||||
real_name = document.getElementById(data_id+"_rname").textContent
|
||||
old_names = document.getElementById(data_id+"_prevnames").textContent
|
||||
key = document.getElementById(data_id+"_key").textContent
|
||||
ip = document.getElementById(data_id+"_lastip").textContent
|
||||
antagonist = document.getElementById(data_id+"_isantag").textContent
|
||||
ref = document.getElementById(data_id+"_ref").textContent
|
||||
|
||||
clearAll();
|
||||
|
||||
var span = document.getElementById(target_id);
|
||||
var ckey = key.toLowerCase().replace(/\[^a-z@0-9\]+/g,"");
|
||||
|
||||
body = "<table><tr><td>";
|
||||
|
||||
body += "</td><td align='center'>";
|
||||
|
||||
body += "<font size='2'><b>"+job+" "+name+"</b><br><b>Real name "+real_name+"</b><br><b>Played by "+key+" ("+ip+")</b><br><b>Old names :"+old_names+"</b></font>";
|
||||
|
||||
body += "</td><td align='center'>";
|
||||
|
||||
body += "<a href='?_src_=holder;[HrefToken()];adminplayeropts="+ref+"'>PP</a> - "
|
||||
body += "<a href='?_src_=holder;[HrefToken()];showmessageckey="+ckey+"'>N</a> - "
|
||||
body += "<a href='?_src_=vars;[HrefToken()];Vars="+ref+"'>VV</a> - "
|
||||
body += "<a href='?_src_=vars;[HrefToken()];skill="+ref+"'>SP</a> - "
|
||||
body += "<a href='?_src_=holder;[HrefToken()];traitor="+ref+"'>TP</a> - "
|
||||
if (job == "Cyborg")
|
||||
body += "<a href='?_src_=holder;[HrefToken()];borgpanel="+ref+"'>BP</a> - "
|
||||
body += "<a href='?priv_msg="+ckey+"'>PM</a> - "
|
||||
body += "<a href='?_src_=holder;[HrefToken()];subtlemessage="+ref+"'>SM</a> - "
|
||||
body += "<a href='?_src_=holder;[HrefToken()];adminplayerobservefollow="+ref+"'>FLW</a> - "
|
||||
body += "<a href='?_src_=holder;[HrefToken()];individuallog="+ref+"'>LOGS</a><br>"
|
||||
if(antagonist > 0)
|
||||
body += "<font size='2'><a href='?_src_=holder;[HrefToken()];check_antagonist=1'><font color='red'><b>Antagonist</b></font></a></font>";
|
||||
|
||||
body += "</td></tr></table>";
|
||||
|
||||
|
||||
span.innerHTML = body
|
||||
}
|
||||
|
||||
function clearAll(){
|
||||
var spans = document.getElementsByTagName('span');
|
||||
for(var i = 0; i < spans.length; i++){
|
||||
var span = spans\[i\];
|
||||
|
||||
var id = span.getAttribute("id");
|
||||
|
||||
if(!id || !(id.indexOf("item")==0))
|
||||
continue;
|
||||
|
||||
var pass = 1;
|
||||
|
||||
for(var j = 0; j < locked_tabs.length; j++){
|
||||
if(locked_tabs\[j\]==id){
|
||||
pass = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(pass != 1)
|
||||
continue;
|
||||
|
||||
|
||||
|
||||
|
||||
span.innerHTML = "";
|
||||
}
|
||||
}
|
||||
|
||||
function addToLocked(id,link_id,notice_span_id){
|
||||
var link = document.getElementById(link_id);
|
||||
var decision = link.getAttribute("name");
|
||||
if(decision == "1"){
|
||||
link.setAttribute("name","2");
|
||||
}else{
|
||||
link.setAttribute("name","1");
|
||||
removeFromLocked(id,link_id,notice_span_id);
|
||||
return;
|
||||
}
|
||||
|
||||
var pass = 1;
|
||||
for(var j = 0; j < locked_tabs.length; j++){
|
||||
if(locked_tabs\[j\]==id){
|
||||
pass = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!pass)
|
||||
return;
|
||||
locked_tabs.push(id);
|
||||
var notice_span = document.getElementById(notice_span_id);
|
||||
notice_span.innerHTML = "<font color='red'>Locked</font> ";
|
||||
}
|
||||
|
||||
function attempt(ab){
|
||||
return ab;
|
||||
}
|
||||
|
||||
function removeFromLocked(id,link_id,notice_span_id){
|
||||
//document.write("a");
|
||||
var index = 0;
|
||||
var pass = 0;
|
||||
for(var j = 0; j < locked_tabs.length; j++){
|
||||
if(locked_tabs\[j\]==id){
|
||||
pass = 1;
|
||||
index = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!pass)
|
||||
return;
|
||||
locked_tabs\[index\] = "";
|
||||
var notice_span = document.getElementById(notice_span_id);
|
||||
notice_span.innerHTML = "";
|
||||
}
|
||||
|
||||
function selectTextField(){
|
||||
var filter_text = document.getElementById('filter');
|
||||
filter_text.focus();
|
||||
filter_text.select();
|
||||
}
|
||||
|
||||
</script>
|
||||
</head>
|
||||
|
||||
|
||||
"}
|
||||
|
||||
//body tag start + onload and onkeypress (onkeyup) javascript event calls
|
||||
dat += "<body onload='selectTextField(); updateSearch();' onkeyup='updateSearch();'>"
|
||||
|
||||
//title + search bar
|
||||
dat += {"
|
||||
|
||||
<table width='560' align='center' cellspacing='0' cellpadding='5' id='maintable'>
|
||||
<tr id='title_tr'>
|
||||
<td align='center'>
|
||||
<font size='5'><b>Player panel</b></font><br>
|
||||
Hover over a line to see more information - <a href='?_src_=holder;[HrefToken()];check_antagonist=1'>Check antagonists</a> - Kick <a href='?_src_=holder;[HrefToken()];kick_all_from_lobby=1;afkonly=0'>everyone</a>/<a href='?_src_=holder;[HrefToken()];kick_all_from_lobby=1;afkonly=1'>AFKers</a> in lobby
|
||||
<p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr id='search_tr'>
|
||||
<td align='center'>
|
||||
<b>Search:</b> <input type='text' id='filter' value='' style='width:300px;'>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
"}
|
||||
|
||||
//player table header
|
||||
dat += {"
|
||||
<span id='maintable_data_archive'>
|
||||
<table width='560' align='center' cellspacing='0' cellpadding='5' id='maintable_data'>"}
|
||||
|
||||
var/list/mobs = sortmobs()
|
||||
var/i = 1
|
||||
for(var/mob/M in mobs)
|
||||
if(M.ckey)
|
||||
|
||||
var/color = "#e6e6e6"
|
||||
if(i%2 == 0)
|
||||
color = "#f2f2f2"
|
||||
var/is_antagonist = is_special_character(M)
|
||||
|
||||
var/M_job = ""
|
||||
|
||||
if(isliving(M))
|
||||
|
||||
if(iscarbon(M)) //Carbon stuff
|
||||
if(ishuman(M))
|
||||
M_job = M.job
|
||||
else if(ismonkey(M))
|
||||
M_job = "Monkey"
|
||||
else if(isalien(M)) //aliens
|
||||
if(islarva(M))
|
||||
M_job = "Alien larva"
|
||||
else
|
||||
M_job = ROLE_ALIEN
|
||||
else
|
||||
M_job = "Carbon-based"
|
||||
|
||||
else if(issilicon(M)) //silicon
|
||||
if(isAI(M))
|
||||
M_job = "AI"
|
||||
else if(ispAI(M))
|
||||
M_job = ROLE_PAI
|
||||
else if(iscyborg(M))
|
||||
M_job = "Cyborg"
|
||||
else
|
||||
M_job = "Silicon-based"
|
||||
|
||||
else if(isanimal(M)) //simple animals
|
||||
if(iscorgi(M))
|
||||
M_job = "Corgi"
|
||||
else if(isslime(M))
|
||||
M_job = "slime"
|
||||
else
|
||||
M_job = "Animal"
|
||||
|
||||
else
|
||||
M_job = "Living"
|
||||
|
||||
else if(isnewplayer(M))
|
||||
M_job = "New player"
|
||||
|
||||
else if(isobserver(M))
|
||||
var/mob/dead/observer/O = M
|
||||
if(O.started_as_observer)//Did they get BTFO or are they just not trying?
|
||||
M_job = "Observer"
|
||||
else
|
||||
M_job = "Ghost"
|
||||
|
||||
var/M_name = html_encode(M.name)
|
||||
var/M_rname = html_encode(M.real_name)
|
||||
var/M_key = html_encode(M.key)
|
||||
var/previous_names = ""
|
||||
if(M_key)
|
||||
var/datum/player_details/P = GLOB.player_details[ckey(M_key)]
|
||||
if(P)
|
||||
previous_names = P.played_names.Join(",")
|
||||
previous_names = html_encode(previous_names)
|
||||
|
||||
//output for each mob
|
||||
dat += {"
|
||||
|
||||
<tr id='data[i]' name='[i]' onClick="addToLocked('item[i]','data[i]','notice_span[i]')">
|
||||
<td align='center' bgcolor='[color]'>
|
||||
<span id='notice_span[i]'></span>
|
||||
<a id='link[i]'
|
||||
onmouseover='expand("data[i]","item[i]")'
|
||||
>
|
||||
<b id='search[i]'>[M_name] - [M_rname] - [M_key] ([M_job])</b>
|
||||
<span hidden class='filter_data'>[M_name] [M_rname] [M_key] [M_job] [previous_names]</span>
|
||||
<span hidden id="data[i]_name">[M_name]</span>
|
||||
<span hidden id="data[i]_job">[M_job]</span>
|
||||
<span hidden id="data[i]_rname">[M_rname]</span>
|
||||
<span hidden id="data[i]_prevnames">[previous_names]</span>
|
||||
<span hidden id="data[i]_key">[M_key]</span>
|
||||
<span hidden id="data[i]_lastip">[M.lastKnownIP]</span>
|
||||
<span hidden id="data[i]_isantag">[is_antagonist]</span>
|
||||
<span hidden id="data[i]_ref">[REF(M)]</span>
|
||||
</a>
|
||||
<br><span id='item[i]'></span>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
"}
|
||||
|
||||
i++
|
||||
|
||||
|
||||
//player table ending
|
||||
dat += {"
|
||||
</table>
|
||||
</span>
|
||||
|
||||
<script type='text/javascript'>
|
||||
var maintable = document.getElementById("maintable_data_archive");
|
||||
var complete_list = maintable.innerHTML;
|
||||
</script>
|
||||
</body></html>
|
||||
"}
|
||||
|
||||
usr << browse(dat, "window=players;size=600x480")
|
||||
/datum/admins/proc/player_panel_new()//The new one
|
||||
if(!check_rights())
|
||||
return
|
||||
log_admin("[key_name(usr)] checked the player panel.")
|
||||
var/dat = "<html><head><meta http-equiv='X-UA-Compatible' content='IE=edge; charset=UTF-8'/><title>Player Panel</title></head>"
|
||||
|
||||
//javascript, the part that does most of the work~
|
||||
dat += {"
|
||||
|
||||
<head>
|
||||
<script type='text/javascript'>
|
||||
|
||||
var locked_tabs = new Array();
|
||||
|
||||
function updateSearch(){
|
||||
|
||||
|
||||
var filter_text = document.getElementById('filter');
|
||||
var filter = filter_text.value.toLowerCase();
|
||||
|
||||
if(complete_list != null && complete_list != ""){
|
||||
var mtbl = document.getElementById("maintable_data_archive");
|
||||
mtbl.innerHTML = complete_list;
|
||||
}
|
||||
|
||||
if(filter.value == ""){
|
||||
return;
|
||||
}else{
|
||||
|
||||
var maintable_data = document.getElementById('maintable_data');
|
||||
var ltr = maintable_data.getElementsByTagName("tr");
|
||||
for ( var i = 0; i < ltr.length; ++i )
|
||||
{
|
||||
try{
|
||||
var tr = ltr\[i\];
|
||||
if(tr.getAttribute("id").indexOf("data") != 0){
|
||||
continue;
|
||||
}
|
||||
var ltd = tr.getElementsByTagName("td");
|
||||
var td = ltd\[0\];
|
||||
var lsearch = td.getElementsByClassName("filter_data");
|
||||
var search = lsearch\[0\];
|
||||
if ( search.innerText.toLowerCase().indexOf(filter) == -1 )
|
||||
{
|
||||
tr.innerHTML = "";
|
||||
i--;
|
||||
}
|
||||
}catch(err) { }
|
||||
}
|
||||
}
|
||||
|
||||
var count = 0;
|
||||
var index = -1;
|
||||
var debug = document.getElementById("debug");
|
||||
|
||||
locked_tabs = new Array();
|
||||
}
|
||||
|
||||
function expand(data_id,target_id){
|
||||
|
||||
job = document.getElementById(data_id+"_job").textContent
|
||||
name = document.getElementById(data_id+"_name").textContent
|
||||
real_name = document.getElementById(data_id+"_rname").textContent
|
||||
old_names = document.getElementById(data_id+"_prevnames").textContent
|
||||
key = document.getElementById(data_id+"_key").textContent
|
||||
ip = document.getElementById(data_id+"_lastip").textContent
|
||||
antagonist = document.getElementById(data_id+"_isantag").textContent
|
||||
ref = document.getElementById(data_id+"_ref").textContent
|
||||
|
||||
clearAll();
|
||||
|
||||
var span = document.getElementById(target_id);
|
||||
var ckey = key.toLowerCase().replace(/\[^a-z@0-9\]+/g,"");
|
||||
|
||||
body = "<table><tr><td>";
|
||||
|
||||
body += "</td><td align='center'>";
|
||||
|
||||
body += "<font size='2'><b>"+job+" "+name+"</b><br><b>Real name "+real_name+"</b><br><b>Played by "+key+" ("+ip+")</b><br><b>Old names :"+old_names+"</b></font>";
|
||||
|
||||
body += "</td><td align='center'>";
|
||||
|
||||
body += "<a href='?_src_=holder;[HrefToken()];adminplayeropts="+ref+"'>PP</a> - "
|
||||
body += "<a href='?_src_=holder;[HrefToken()];showmessageckey="+ckey+"'>N</a> - "
|
||||
body += "<a href='?_src_=vars;[HrefToken()];Vars="+ref+"'>VV</a> - "
|
||||
body += "<a href='?_src_=vars;[HrefToken()];skill="+ref+"'>SP</a> - "
|
||||
body += "<a href='?_src_=holder;[HrefToken()];traitor="+ref+"'>TP</a> - "
|
||||
if (job == "Cyborg")
|
||||
body += "<a href='?_src_=holder;[HrefToken()];borgpanel="+ref+"'>BP</a> - "
|
||||
body += "<a href='?priv_msg="+ckey+"'>PM</a> - "
|
||||
body += "<a href='?_src_=holder;[HrefToken()];subtlemessage="+ref+"'>SM</a> - "
|
||||
body += "<a href='?_src_=holder;[HrefToken()];adminplayerobservefollow="+ref+"'>FLW</a> - "
|
||||
body += "<a href='?_src_=holder;[HrefToken()];individuallog="+ref+"'>LOGS</a><br>"
|
||||
if(antagonist > 0)
|
||||
body += "<font size='2'><a href='?_src_=holder;[HrefToken()];check_antagonist=1'><font color='red'><b>Antagonist</b></font></a></font>";
|
||||
|
||||
body += "</td></tr></table>";
|
||||
|
||||
|
||||
span.innerHTML = body
|
||||
}
|
||||
|
||||
function clearAll(){
|
||||
var spans = document.getElementsByTagName('span');
|
||||
for(var i = 0; i < spans.length; i++){
|
||||
var span = spans\[i\];
|
||||
|
||||
var id = span.getAttribute("id");
|
||||
|
||||
if(!id || !(id.indexOf("item")==0))
|
||||
continue;
|
||||
|
||||
var pass = 1;
|
||||
|
||||
for(var j = 0; j < locked_tabs.length; j++){
|
||||
if(locked_tabs\[j\]==id){
|
||||
pass = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(pass != 1)
|
||||
continue;
|
||||
|
||||
|
||||
|
||||
|
||||
span.innerHTML = "";
|
||||
}
|
||||
}
|
||||
|
||||
function addToLocked(id,link_id,notice_span_id){
|
||||
var link = document.getElementById(link_id);
|
||||
var decision = link.getAttribute("name");
|
||||
if(decision == "1"){
|
||||
link.setAttribute("name","2");
|
||||
}else{
|
||||
link.setAttribute("name","1");
|
||||
removeFromLocked(id,link_id,notice_span_id);
|
||||
return;
|
||||
}
|
||||
|
||||
var pass = 1;
|
||||
for(var j = 0; j < locked_tabs.length; j++){
|
||||
if(locked_tabs\[j\]==id){
|
||||
pass = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!pass)
|
||||
return;
|
||||
locked_tabs.push(id);
|
||||
var notice_span = document.getElementById(notice_span_id);
|
||||
notice_span.innerHTML = "<font color='red'>Locked</font> ";
|
||||
}
|
||||
|
||||
function attempt(ab){
|
||||
return ab;
|
||||
}
|
||||
|
||||
function removeFromLocked(id,link_id,notice_span_id){
|
||||
//document.write("a");
|
||||
var index = 0;
|
||||
var pass = 0;
|
||||
for(var j = 0; j < locked_tabs.length; j++){
|
||||
if(locked_tabs\[j\]==id){
|
||||
pass = 1;
|
||||
index = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!pass)
|
||||
return;
|
||||
locked_tabs\[index\] = "";
|
||||
var notice_span = document.getElementById(notice_span_id);
|
||||
notice_span.innerHTML = "";
|
||||
}
|
||||
|
||||
function selectTextField(){
|
||||
var filter_text = document.getElementById('filter');
|
||||
filter_text.focus();
|
||||
filter_text.select();
|
||||
}
|
||||
|
||||
</script>
|
||||
</head>
|
||||
|
||||
|
||||
"}
|
||||
|
||||
//body tag start + onload and onkeypress (onkeyup) javascript event calls
|
||||
dat += "<body onload='selectTextField(); updateSearch();' onkeyup='updateSearch();'>"
|
||||
|
||||
//title + search bar
|
||||
dat += {"
|
||||
|
||||
<table width='560' align='center' cellspacing='0' cellpadding='5' id='maintable'>
|
||||
<tr id='title_tr'>
|
||||
<td align='center'>
|
||||
<font size='5'><b>Player panel</b></font><br>
|
||||
Hover over a line to see more information - <a href='?_src_=holder;[HrefToken()];check_antagonist=1'>Check antagonists</a> - Kick <a href='?_src_=holder;[HrefToken()];kick_all_from_lobby=1;afkonly=0'>everyone</a>/<a href='?_src_=holder;[HrefToken()];kick_all_from_lobby=1;afkonly=1'>AFKers</a> in lobby
|
||||
<p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr id='search_tr'>
|
||||
<td align='center'>
|
||||
<b>Search:</b> <input type='text' id='filter' value='' style='width:300px;'>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
"}
|
||||
|
||||
//player table header
|
||||
dat += {"
|
||||
<span id='maintable_data_archive'>
|
||||
<table width='560' align='center' cellspacing='0' cellpadding='5' id='maintable_data'>"}
|
||||
|
||||
var/list/mobs = sortmobs()
|
||||
var/i = 1
|
||||
for(var/mob/M in mobs)
|
||||
if(M.ckey)
|
||||
|
||||
var/color = "#e6e6e6"
|
||||
if(i%2 == 0)
|
||||
color = "#f2f2f2"
|
||||
var/is_antagonist = is_special_character(M)
|
||||
|
||||
var/M_job = ""
|
||||
|
||||
if(isliving(M))
|
||||
|
||||
if(iscarbon(M)) //Carbon stuff
|
||||
if(ishuman(M))
|
||||
M_job = M.job
|
||||
else if(ismonkey(M))
|
||||
M_job = "Monkey"
|
||||
else if(isalien(M)) //aliens
|
||||
if(islarva(M))
|
||||
M_job = "Alien larva"
|
||||
else
|
||||
M_job = ROLE_ALIEN
|
||||
else
|
||||
M_job = "Carbon-based"
|
||||
|
||||
else if(issilicon(M)) //silicon
|
||||
if(isAI(M))
|
||||
M_job = "AI"
|
||||
else if(ispAI(M))
|
||||
M_job = ROLE_PAI
|
||||
else if(iscyborg(M))
|
||||
M_job = "Cyborg"
|
||||
else
|
||||
M_job = "Silicon-based"
|
||||
|
||||
else if(isanimal(M)) //simple animals
|
||||
if(iscorgi(M))
|
||||
M_job = "Corgi"
|
||||
else if(isslime(M))
|
||||
M_job = "slime"
|
||||
else
|
||||
M_job = "Animal"
|
||||
|
||||
else
|
||||
M_job = "Living"
|
||||
|
||||
else if(isnewplayer(M))
|
||||
M_job = "New player"
|
||||
|
||||
else if(isobserver(M))
|
||||
var/mob/dead/observer/O = M
|
||||
if(O.started_as_observer)//Did they get BTFO or are they just not trying?
|
||||
M_job = "Observer"
|
||||
else
|
||||
M_job = "Ghost"
|
||||
|
||||
var/M_name = html_encode(M.name)
|
||||
var/M_rname = html_encode(M.real_name)
|
||||
var/M_key = html_encode(M.key)
|
||||
var/previous_names = ""
|
||||
if(M_key)
|
||||
var/datum/player_details/P = GLOB.player_details[ckey(M_key)]
|
||||
if(P)
|
||||
previous_names = P.played_names.Join(",")
|
||||
previous_names = html_encode(previous_names)
|
||||
|
||||
//output for each mob
|
||||
dat += {"
|
||||
|
||||
<tr id='data[i]' name='[i]' onClick="addToLocked('item[i]','data[i]','notice_span[i]')">
|
||||
<td align='center' bgcolor='[color]'>
|
||||
<span id='notice_span[i]'></span>
|
||||
<a id='link[i]'
|
||||
onmouseover='expand("data[i]","item[i]")'
|
||||
>
|
||||
<b id='search[i]'>[M_name] - [M_rname] - [M_key] ([M_job])</b>
|
||||
<span hidden class='filter_data'>[M_name] [M_rname] [M_key] [M_job] [previous_names]</span>
|
||||
<span hidden id="data[i]_name">[M_name]</span>
|
||||
<span hidden id="data[i]_job">[M_job]</span>
|
||||
<span hidden id="data[i]_rname">[M_rname]</span>
|
||||
<span hidden id="data[i]_prevnames">[previous_names]</span>
|
||||
<span hidden id="data[i]_key">[M_key]</span>
|
||||
<span hidden id="data[i]_lastip">[M.lastKnownIP]</span>
|
||||
<span hidden id="data[i]_isantag">[is_antagonist]</span>
|
||||
<span hidden id="data[i]_ref">[REF(M)]</span>
|
||||
</a>
|
||||
<br><span id='item[i]'></span>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
"}
|
||||
|
||||
i++
|
||||
|
||||
|
||||
//player table ending
|
||||
dat += {"
|
||||
</table>
|
||||
</span>
|
||||
|
||||
<script type='text/javascript'>
|
||||
var maintable = document.getElementById("maintable_data_archive");
|
||||
var complete_list = maintable.innerHTML;
|
||||
</script>
|
||||
</body></html>
|
||||
"}
|
||||
|
||||
usr << browse(dat, "window=players;size=600x480")
|
||||
|
||||
@@ -1,55 +1,55 @@
|
||||
/datum/skill_panel
|
||||
var/datum/mind/targetmind
|
||||
var/client/holder //client of whoever is using this datum
|
||||
|
||||
/datum/skill_panel/New(user, datum/mind/mind)//H can either be a client or a mob due to byondcode(tm)
|
||||
targetmind = mind
|
||||
if (istype(user,/client))
|
||||
var/client/userClient = user
|
||||
holder = userClient //if its a client, assign it to holder
|
||||
else
|
||||
var/mob/userMob = user
|
||||
holder = userMob.client //if its a mob, assign the mob's client to holder
|
||||
|
||||
/datum/skill_panel/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, \
|
||||
force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.admin_state)//ui_interact is called when the client verb is called.
|
||||
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
|
||||
if(!ui)
|
||||
ui = new(user, src, ui_key, "SkillPanel", "Manage Skills", 600, 500, master_ui, state)
|
||||
ui.open()
|
||||
|
||||
/datum/skill_panel/ui_data(mob/user) //Sends info about the skills to UI
|
||||
. = list()
|
||||
for (var/type in GLOB.skill_types)
|
||||
var/datum/skill/S = GetSkillRef(type)
|
||||
var/lvl_num = targetmind.get_skill_level(type)
|
||||
var/lvl_name = uppertext(targetmind.get_skill_level_name(type))
|
||||
var/exp = targetmind.get_skill_exp(type)
|
||||
var/xp_prog_to_level = targetmind.exp_needed_to_level_up(type)
|
||||
var/xp_req_to_level = 0
|
||||
if (xp_prog_to_level)//is it even possible to level up?
|
||||
xp_req_to_level = SKILL_EXP_LIST[lvl_num+1] - SKILL_EXP_LIST[lvl_num]
|
||||
var/exp_percent = exp / SKILL_EXP_LIST[SKILL_LEVEL_LEGENDARY]
|
||||
.["skills"] += list(list("playername" = targetmind.current, "path" = type, "name" = S.name, "desc" = S.desc, "lvlnum" = lvl_num, "lvl" = lvl_name, "exp" = exp, "exp_prog" = xp_req_to_level - xp_prog_to_level, "exp_req" = xp_req_to_level, "exp_percent" = exp_percent, "max_exp" = SKILL_EXP_LIST[length(SKILL_EXP_LIST)]))
|
||||
|
||||
/datum/skill_panel/ui_act(action, params)
|
||||
. = ..()
|
||||
if(.)
|
||||
return
|
||||
switch (action)
|
||||
if ("adj_exp")
|
||||
var/skill = text2path(params["skill"])
|
||||
var/number = input("Please insert the amount of experience you'd like to add/subtract:") as num|null
|
||||
if (number)
|
||||
targetmind.adjust_experience(skill, number)
|
||||
if ("set_exp")
|
||||
var/skill = text2path(params["skill"])
|
||||
var/number = input("Please insert the number you want to set the player's exp to:") as num|null
|
||||
if (number)
|
||||
targetmind.set_experience(skill, number)
|
||||
if ("set_lvl")
|
||||
var/skill = text2path(params["skill"])
|
||||
var/max_skill = length(SKILL_EXP_LIST)
|
||||
var/number = input("Please insert a whole number between 1 (NONE) and [max_skill] (LEGENDARY) corresponding to the level you'd like to set the player to.") as num|null
|
||||
if (number > 0 && number <= max_skill )
|
||||
targetmind.set_level(skill, number)
|
||||
/datum/skill_panel
|
||||
var/datum/mind/targetmind
|
||||
var/client/holder //client of whoever is using this datum
|
||||
|
||||
/datum/skill_panel/New(user, datum/mind/mind)//H can either be a client or a mob due to byondcode(tm)
|
||||
targetmind = mind
|
||||
if (istype(user,/client))
|
||||
var/client/userClient = user
|
||||
holder = userClient //if its a client, assign it to holder
|
||||
else
|
||||
var/mob/userMob = user
|
||||
holder = userMob.client //if its a mob, assign the mob's client to holder
|
||||
|
||||
/datum/skill_panel/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, \
|
||||
force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.admin_state)//ui_interact is called when the client verb is called.
|
||||
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
|
||||
if(!ui)
|
||||
ui = new(user, src, ui_key, "SkillPanel", "Manage Skills", 600, 500, master_ui, state)
|
||||
ui.open()
|
||||
|
||||
/datum/skill_panel/ui_data(mob/user) //Sends info about the skills to UI
|
||||
. = list()
|
||||
for (var/type in GLOB.skill_types)
|
||||
var/datum/skill/S = GetSkillRef(type)
|
||||
var/lvl_num = targetmind.get_skill_level(type)
|
||||
var/lvl_name = uppertext(targetmind.get_skill_level_name(type))
|
||||
var/exp = targetmind.get_skill_exp(type)
|
||||
var/xp_prog_to_level = targetmind.exp_needed_to_level_up(type)
|
||||
var/xp_req_to_level = 0
|
||||
if (xp_prog_to_level)//is it even possible to level up?
|
||||
xp_req_to_level = SKILL_EXP_LIST[lvl_num+1] - SKILL_EXP_LIST[lvl_num]
|
||||
var/exp_percent = exp / SKILL_EXP_LIST[SKILL_LEVEL_LEGENDARY]
|
||||
.["skills"] += list(list("playername" = targetmind.current, "path" = type, "name" = S.name, "desc" = S.desc, "lvlnum" = lvl_num, "lvl" = lvl_name, "exp" = exp, "exp_prog" = xp_req_to_level - xp_prog_to_level, "exp_req" = xp_req_to_level, "exp_percent" = exp_percent, "max_exp" = SKILL_EXP_LIST[length(SKILL_EXP_LIST)]))
|
||||
|
||||
/datum/skill_panel/ui_act(action, params)
|
||||
. = ..()
|
||||
if(.)
|
||||
return
|
||||
switch (action)
|
||||
if ("adj_exp")
|
||||
var/skill = text2path(params["skill"])
|
||||
var/number = input("Please insert the amount of experience you'd like to add/subtract:") as num|null
|
||||
if (number)
|
||||
targetmind.adjust_experience(skill, number)
|
||||
if ("set_exp")
|
||||
var/skill = text2path(params["skill"])
|
||||
var/number = input("Please insert the number you want to set the player's exp to:") as num|null
|
||||
if (number)
|
||||
targetmind.set_experience(skill, number)
|
||||
if ("set_lvl")
|
||||
var/skill = text2path(params["skill"])
|
||||
var/max_skill = length(SKILL_EXP_LIST)
|
||||
var/number = input("Please insert a whole number between 1 (NONE) and [max_skill] (LEGENDARY) corresponding to the level you'd like to set the player to.") as num|null
|
||||
if (number > 0 && number <= max_skill )
|
||||
targetmind.set_level(skill, number)
|
||||
|
||||
+2291
-2291
File diff suppressed because it is too large
Load Diff
@@ -1,35 +1,35 @@
|
||||
/proc/getbrokeninhands()
|
||||
var/text
|
||||
for(var/A in typesof(/obj/item))
|
||||
var/obj/item/O = new A( locate(1,1,1) )
|
||||
if(!O)
|
||||
continue
|
||||
var/icon/IL = new(O.lefthand_file)
|
||||
var/list/Lstates = IL.IconStates()
|
||||
var/icon/IR = new(O.righthand_file)
|
||||
var/list/Rstates = IR.IconStates()
|
||||
var/icon/J = new(O.icon)
|
||||
var/list/istates = J.IconStates()
|
||||
if(!Lstates.Find(O.icon_state) && !Lstates.Find(O.inhand_icon_state))
|
||||
if(O.icon_state)
|
||||
text += "[O.type] WANTS IN LEFT HAND CALLED\n\"[O.icon_state]\".\n"
|
||||
if(!Rstates.Find(O.icon_state) && !Rstates.Find(O.inhand_icon_state))
|
||||
if(O.icon_state)
|
||||
text += "[O.type] WANTS IN RIGHT HAND CALLED\n\"[O.icon_state]\".\n"
|
||||
|
||||
|
||||
if(O.icon_state)
|
||||
if(!istates.Find(O.icon_state))
|
||||
text += "[O.type] MISSING NORMAL ICON CALLED\n\"[O.icon_state]\" IN \"[O.icon]\"\n"
|
||||
if(O.inhand_icon_state)
|
||||
if(!istates.Find(O.inhand_icon_state))
|
||||
text += "[O.type] MISSING NORMAL ICON CALLED\n\"[O.inhand_icon_state]\" IN \"[O.icon]\"\n"
|
||||
text+="\n"
|
||||
qdel(O)
|
||||
if(text)
|
||||
var/F = file("broken_icons.txt")
|
||||
fdel(F)
|
||||
WRITE_FILE(F, text)
|
||||
to_chat(world, "Completely successfully and written to [F]", confidential = TRUE)
|
||||
|
||||
|
||||
/proc/getbrokeninhands()
|
||||
var/text
|
||||
for(var/A in typesof(/obj/item))
|
||||
var/obj/item/O = new A( locate(1,1,1) )
|
||||
if(!O)
|
||||
continue
|
||||
var/icon/IL = new(O.lefthand_file)
|
||||
var/list/Lstates = IL.IconStates()
|
||||
var/icon/IR = new(O.righthand_file)
|
||||
var/list/Rstates = IR.IconStates()
|
||||
var/icon/J = new(O.icon)
|
||||
var/list/istates = J.IconStates()
|
||||
if(!Lstates.Find(O.icon_state) && !Lstates.Find(O.inhand_icon_state))
|
||||
if(O.icon_state)
|
||||
text += "[O.type] WANTS IN LEFT HAND CALLED\n\"[O.icon_state]\".\n"
|
||||
if(!Rstates.Find(O.icon_state) && !Rstates.Find(O.inhand_icon_state))
|
||||
if(O.icon_state)
|
||||
text += "[O.type] WANTS IN RIGHT HAND CALLED\n\"[O.icon_state]\".\n"
|
||||
|
||||
|
||||
if(O.icon_state)
|
||||
if(!istates.Find(O.icon_state))
|
||||
text += "[O.type] MISSING NORMAL ICON CALLED\n\"[O.icon_state]\" IN \"[O.icon]\"\n"
|
||||
if(O.inhand_icon_state)
|
||||
if(!istates.Find(O.inhand_icon_state))
|
||||
text += "[O.type] MISSING NORMAL ICON CALLED\n\"[O.inhand_icon_state]\" IN \"[O.icon]\"\n"
|
||||
text+="\n"
|
||||
qdel(O)
|
||||
if(text)
|
||||
var/F = file("broken_icons.txt")
|
||||
fdel(F)
|
||||
WRITE_FILE(F, text)
|
||||
to_chat(world, "Completely successfully and written to [F]", confidential = TRUE)
|
||||
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,160 +1,160 @@
|
||||
/client/proc/jumptoarea(area/A in GLOB.sortedAreas)
|
||||
set name = "Jump to Area"
|
||||
set desc = "Area to jump to"
|
||||
set category = "Admin - Game"
|
||||
if(!src.holder)
|
||||
to_chat(src, "Only administrators may use this command.", confidential = TRUE)
|
||||
return
|
||||
|
||||
if(!A)
|
||||
return
|
||||
|
||||
var/list/turfs = list()
|
||||
for(var/turf/T in A)
|
||||
if(T.density)
|
||||
continue
|
||||
turfs.Add(T)
|
||||
|
||||
if(length(turfs))
|
||||
var/turf/T = pick(turfs)
|
||||
usr.forceMove(T)
|
||||
log_admin("[key_name(usr)] jumped to [AREACOORD(T)]")
|
||||
message_admins("[key_name_admin(usr)] jumped to [AREACOORD(T)]")
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Jump To Area") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
else
|
||||
to_chat(src, "Nowhere to jump to!", confidential = TRUE)
|
||||
return
|
||||
|
||||
|
||||
/client/proc/jumptoturf(turf/T in world)
|
||||
set name = "Jump to Turf"
|
||||
set category = "Admin - Game"
|
||||
if(!src.holder)
|
||||
to_chat(src, "Only administrators may use this command.", confidential = TRUE)
|
||||
return
|
||||
|
||||
log_admin("[key_name(usr)] jumped to [AREACOORD(T)]")
|
||||
message_admins("[key_name_admin(usr)] jumped to [AREACOORD(T)]")
|
||||
usr.forceMove(T)
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Jump To Turf") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
return
|
||||
|
||||
/client/proc/jumptomob(mob/M in GLOB.mob_list)
|
||||
set category = "Admin - Game"
|
||||
set name = "Jump to Mob"
|
||||
|
||||
if(!src.holder)
|
||||
to_chat(src, "Only administrators may use this command.", confidential = TRUE)
|
||||
return
|
||||
|
||||
log_admin("[key_name(usr)] jumped to [key_name(M)]")
|
||||
message_admins("[key_name_admin(usr)] jumped to [ADMIN_LOOKUPFLW(M)] at [AREACOORD(M)]")
|
||||
if(src.mob)
|
||||
var/mob/A = src.mob
|
||||
var/turf/T = get_turf(M)
|
||||
if(T && isturf(T))
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Jump To Mob") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
A.forceMove(M.loc)
|
||||
else
|
||||
to_chat(A, "This mob is not located in the game world.", confidential = TRUE)
|
||||
|
||||
/client/proc/jumptocoord(tx as num, ty as num, tz as num)
|
||||
set category = "Admin - Game"
|
||||
set name = "Jump to Coordinate"
|
||||
|
||||
if (!holder)
|
||||
to_chat(src, "Only administrators may use this command.", confidential = TRUE)
|
||||
return
|
||||
|
||||
if(src.mob)
|
||||
var/mob/A = src.mob
|
||||
var/turf/T = locate(tx,ty,tz)
|
||||
A.forceMove(T)
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Jump To Coordiate") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
message_admins("[key_name_admin(usr)] jumped to coordinates [tx], [ty], [tz]")
|
||||
|
||||
/client/proc/jumptokey()
|
||||
set category = "Admin - Game"
|
||||
set name = "Jump to Key"
|
||||
|
||||
if(!src.holder)
|
||||
to_chat(src, "Only administrators may use this command.", confidential = TRUE)
|
||||
return
|
||||
|
||||
var/list/keys = list()
|
||||
for(var/mob/M in GLOB.player_list)
|
||||
keys += M.client
|
||||
var/client/selection = input("Please, select a player!", "Admin Jumping", null, null) as null|anything in sortKey(keys)
|
||||
if(!selection)
|
||||
to_chat(src, "No keys found.", confidential = TRUE)
|
||||
return
|
||||
var/mob/M = selection.mob
|
||||
log_admin("[key_name(usr)] jumped to [key_name(M)]")
|
||||
message_admins("[key_name_admin(usr)] jumped to [ADMIN_LOOKUPFLW(M)]")
|
||||
|
||||
usr.forceMove(M.loc)
|
||||
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Jump To Key") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
/client/proc/Getmob(mob/M in GLOB.mob_list - GLOB.dummy_mob_list)
|
||||
set category = "Admin - Game"
|
||||
set name = "Get Mob"
|
||||
set desc = "Mob to teleport"
|
||||
if(!src.holder)
|
||||
to_chat(src, "Only administrators may use this command.", confidential = TRUE)
|
||||
return
|
||||
|
||||
var/atom/loc = get_turf(usr)
|
||||
log_admin("[key_name(usr)] teleported [key_name(M)] to [AREACOORD(loc)]")
|
||||
var/msg = "[key_name_admin(usr)] teleported [ADMIN_LOOKUPFLW(M)] to [ADMIN_VERBOSEJMP(loc)]"
|
||||
message_admins(msg)
|
||||
admin_ticket_log(M, msg)
|
||||
M.forceMove(loc)
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Get Mob") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
/client/proc/Getkey()
|
||||
set category = "Admin - Game"
|
||||
set name = "Get Key"
|
||||
set desc = "Key to teleport"
|
||||
|
||||
if(!src.holder)
|
||||
to_chat(src, "Only administrators may use this command.", confidential = TRUE)
|
||||
return
|
||||
|
||||
var/list/keys = list()
|
||||
for(var/mob/M in GLOB.player_list)
|
||||
keys += M.client
|
||||
var/client/selection = input("Please, select a player!", "Admin Jumping", null, null) as null|anything in sortKey(keys)
|
||||
if(!selection)
|
||||
return
|
||||
var/mob/M = selection.mob
|
||||
|
||||
if(!M)
|
||||
return
|
||||
log_admin("[key_name(usr)] teleported [key_name(M)]")
|
||||
var/msg = "[key_name_admin(usr)] teleported [ADMIN_LOOKUPFLW(M)]"
|
||||
message_admins(msg)
|
||||
admin_ticket_log(M, msg)
|
||||
if(M)
|
||||
M.forceMove(get_turf(usr))
|
||||
usr.forceMove(M.loc)
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Get Key") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
/client/proc/sendmob(mob/M in sortmobs())
|
||||
set category = "Admin - Game"
|
||||
set name = "Send Mob"
|
||||
if(!src.holder)
|
||||
to_chat(src, "Only administrators may use this command.", confidential = TRUE)
|
||||
return
|
||||
var/area/A = input(usr, "Pick an area.", "Pick an area") in GLOB.sortedAreas|null
|
||||
if(A && istype(A))
|
||||
var/list/turfs = get_area_turfs(A)
|
||||
if(length(turfs) && M.forceMove(pick(turfs)))
|
||||
|
||||
log_admin("[key_name(usr)] teleported [key_name(M)] to [AREACOORD(M)]")
|
||||
var/msg = "[key_name_admin(usr)] teleported [ADMIN_LOOKUPFLW(M)] to [AREACOORD(M)]"
|
||||
message_admins(msg)
|
||||
admin_ticket_log(M, msg)
|
||||
else
|
||||
to_chat(src, "Failed to move mob to a valid location.", confidential = TRUE)
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Send Mob") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
/client/proc/jumptoarea(area/A in GLOB.sortedAreas)
|
||||
set name = "Jump to Area"
|
||||
set desc = "Area to jump to"
|
||||
set category = "Admin - Game"
|
||||
if(!src.holder)
|
||||
to_chat(src, "Only administrators may use this command.", confidential = TRUE)
|
||||
return
|
||||
|
||||
if(!A)
|
||||
return
|
||||
|
||||
var/list/turfs = list()
|
||||
for(var/turf/T in A)
|
||||
if(T.density)
|
||||
continue
|
||||
turfs.Add(T)
|
||||
|
||||
if(length(turfs))
|
||||
var/turf/T = pick(turfs)
|
||||
usr.forceMove(T)
|
||||
log_admin("[key_name(usr)] jumped to [AREACOORD(T)]")
|
||||
message_admins("[key_name_admin(usr)] jumped to [AREACOORD(T)]")
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Jump To Area") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
else
|
||||
to_chat(src, "Nowhere to jump to!", confidential = TRUE)
|
||||
return
|
||||
|
||||
|
||||
/client/proc/jumptoturf(turf/T in world)
|
||||
set name = "Jump to Turf"
|
||||
set category = "Admin - Game"
|
||||
if(!src.holder)
|
||||
to_chat(src, "Only administrators may use this command.", confidential = TRUE)
|
||||
return
|
||||
|
||||
log_admin("[key_name(usr)] jumped to [AREACOORD(T)]")
|
||||
message_admins("[key_name_admin(usr)] jumped to [AREACOORD(T)]")
|
||||
usr.forceMove(T)
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Jump To Turf") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
return
|
||||
|
||||
/client/proc/jumptomob(mob/M in GLOB.mob_list)
|
||||
set category = "Admin - Game"
|
||||
set name = "Jump to Mob"
|
||||
|
||||
if(!src.holder)
|
||||
to_chat(src, "Only administrators may use this command.", confidential = TRUE)
|
||||
return
|
||||
|
||||
log_admin("[key_name(usr)] jumped to [key_name(M)]")
|
||||
message_admins("[key_name_admin(usr)] jumped to [ADMIN_LOOKUPFLW(M)] at [AREACOORD(M)]")
|
||||
if(src.mob)
|
||||
var/mob/A = src.mob
|
||||
var/turf/T = get_turf(M)
|
||||
if(T && isturf(T))
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Jump To Mob") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
A.forceMove(M.loc)
|
||||
else
|
||||
to_chat(A, "This mob is not located in the game world.", confidential = TRUE)
|
||||
|
||||
/client/proc/jumptocoord(tx as num, ty as num, tz as num)
|
||||
set category = "Admin - Game"
|
||||
set name = "Jump to Coordinate"
|
||||
|
||||
if (!holder)
|
||||
to_chat(src, "Only administrators may use this command.", confidential = TRUE)
|
||||
return
|
||||
|
||||
if(src.mob)
|
||||
var/mob/A = src.mob
|
||||
var/turf/T = locate(tx,ty,tz)
|
||||
A.forceMove(T)
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Jump To Coordiate") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
message_admins("[key_name_admin(usr)] jumped to coordinates [tx], [ty], [tz]")
|
||||
|
||||
/client/proc/jumptokey()
|
||||
set category = "Admin - Game"
|
||||
set name = "Jump to Key"
|
||||
|
||||
if(!src.holder)
|
||||
to_chat(src, "Only administrators may use this command.", confidential = TRUE)
|
||||
return
|
||||
|
||||
var/list/keys = list()
|
||||
for(var/mob/M in GLOB.player_list)
|
||||
keys += M.client
|
||||
var/client/selection = input("Please, select a player!", "Admin Jumping", null, null) as null|anything in sortKey(keys)
|
||||
if(!selection)
|
||||
to_chat(src, "No keys found.", confidential = TRUE)
|
||||
return
|
||||
var/mob/M = selection.mob
|
||||
log_admin("[key_name(usr)] jumped to [key_name(M)]")
|
||||
message_admins("[key_name_admin(usr)] jumped to [ADMIN_LOOKUPFLW(M)]")
|
||||
|
||||
usr.forceMove(M.loc)
|
||||
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Jump To Key") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
/client/proc/Getmob(mob/M in GLOB.mob_list - GLOB.dummy_mob_list)
|
||||
set category = "Admin - Game"
|
||||
set name = "Get Mob"
|
||||
set desc = "Mob to teleport"
|
||||
if(!src.holder)
|
||||
to_chat(src, "Only administrators may use this command.", confidential = TRUE)
|
||||
return
|
||||
|
||||
var/atom/loc = get_turf(usr)
|
||||
log_admin("[key_name(usr)] teleported [key_name(M)] to [AREACOORD(loc)]")
|
||||
var/msg = "[key_name_admin(usr)] teleported [ADMIN_LOOKUPFLW(M)] to [ADMIN_VERBOSEJMP(loc)]"
|
||||
message_admins(msg)
|
||||
admin_ticket_log(M, msg)
|
||||
M.forceMove(loc)
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Get Mob") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
/client/proc/Getkey()
|
||||
set category = "Admin - Game"
|
||||
set name = "Get Key"
|
||||
set desc = "Key to teleport"
|
||||
|
||||
if(!src.holder)
|
||||
to_chat(src, "Only administrators may use this command.", confidential = TRUE)
|
||||
return
|
||||
|
||||
var/list/keys = list()
|
||||
for(var/mob/M in GLOB.player_list)
|
||||
keys += M.client
|
||||
var/client/selection = input("Please, select a player!", "Admin Jumping", null, null) as null|anything in sortKey(keys)
|
||||
if(!selection)
|
||||
return
|
||||
var/mob/M = selection.mob
|
||||
|
||||
if(!M)
|
||||
return
|
||||
log_admin("[key_name(usr)] teleported [key_name(M)]")
|
||||
var/msg = "[key_name_admin(usr)] teleported [ADMIN_LOOKUPFLW(M)]"
|
||||
message_admins(msg)
|
||||
admin_ticket_log(M, msg)
|
||||
if(M)
|
||||
M.forceMove(get_turf(usr))
|
||||
usr.forceMove(M.loc)
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Get Key") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
/client/proc/sendmob(mob/M in sortmobs())
|
||||
set category = "Admin - Game"
|
||||
set name = "Send Mob"
|
||||
if(!src.holder)
|
||||
to_chat(src, "Only administrators may use this command.", confidential = TRUE)
|
||||
return
|
||||
var/area/A = input(usr, "Pick an area.", "Pick an area") in GLOB.sortedAreas|null
|
||||
if(A && istype(A))
|
||||
var/list/turfs = get_area_turfs(A)
|
||||
if(length(turfs) && M.forceMove(pick(turfs)))
|
||||
|
||||
log_admin("[key_name(usr)] teleported [key_name(M)] to [AREACOORD(M)]")
|
||||
var/msg = "[key_name_admin(usr)] teleported [ADMIN_LOOKUPFLW(M)] to [AREACOORD(M)]"
|
||||
message_admins(msg)
|
||||
admin_ticket_log(M, msg)
|
||||
else
|
||||
to_chat(src, "Failed to move mob to a valid location.", confidential = TRUE)
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Send Mob") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
+331
-331
@@ -1,331 +1,331 @@
|
||||
#define EXTERNALREPLYCOUNT 2
|
||||
|
||||
//allows right clicking mobs to send an admin PM to their client, forwards the selected mob's client to cmd_admin_pm
|
||||
/client/proc/cmd_admin_pm_context(mob/M in GLOB.mob_list)
|
||||
set category = null
|
||||
set name = "Admin PM Mob"
|
||||
if(!holder)
|
||||
to_chat(src, "<span class='danger'>Error: Admin-PM-Context: Only administrators may use this command.</span>", confidential = TRUE)
|
||||
return
|
||||
if( !ismob(M) || !M.client )
|
||||
return
|
||||
cmd_admin_pm(M.client,null)
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Admin PM Mob") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
//shows a list of clients we could send PMs to, then forwards our choice to cmd_admin_pm
|
||||
/client/proc/cmd_admin_pm_panel()
|
||||
set category = "Admin"
|
||||
set name = "Admin PM"
|
||||
if(!holder)
|
||||
to_chat(src, "<span class='danger'>Error: Admin-PM-Panel: Only administrators may use this command.</span>", confidential = TRUE)
|
||||
return
|
||||
var/list/client/targets[0]
|
||||
for(var/client/T)
|
||||
if(T.mob)
|
||||
if(isnewplayer(T.mob))
|
||||
targets["(New Player) - [T]"] = T
|
||||
else if(isobserver(T.mob))
|
||||
targets["[T.mob.name](Ghost) - [T]"] = T
|
||||
else
|
||||
targets["[T.mob.real_name](as [T.mob.name]) - [T]"] = T
|
||||
else
|
||||
targets["(No Mob) - [T]"] = T
|
||||
var/target = input(src,"To whom shall we send a message?","Admin PM",null) as null|anything in sortList(targets)
|
||||
cmd_admin_pm(targets[target],null)
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Admin PM") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
/client/proc/cmd_ahelp_reply(whom)
|
||||
if(prefs.muted & MUTE_ADMINHELP)
|
||||
to_chat(src, "<span class='danger'>Error: Admin-PM: You are unable to use admin PM-s (muted).</span>", confidential = TRUE)
|
||||
return
|
||||
var/client/C
|
||||
if(istext(whom))
|
||||
if(whom[1] == "@")
|
||||
whom = findStealthKey(whom)
|
||||
C = GLOB.directory[whom]
|
||||
else if(istype(whom, /client))
|
||||
C = whom
|
||||
if(!C)
|
||||
if(holder)
|
||||
to_chat(src, "<span class='danger'>Error: Admin-PM: Client not found.</span>", confidential = TRUE)
|
||||
return
|
||||
|
||||
var/datum/admin_help/AH = C.current_ticket
|
||||
|
||||
if(AH)
|
||||
message_admins("[key_name_admin(src)] has started replying to [key_name_admin(C, 0, 0)]'s admin help.")
|
||||
var/msg = input(src,"Message:", "Private message to [C.holder?.fakekey ? "an Administrator" : key_name(C, 0, 0)].") as message|null
|
||||
if (!msg)
|
||||
message_admins("[key_name_admin(src)] has cancelled their reply to [key_name_admin(C, 0, 0)]'s admin help.")
|
||||
return
|
||||
cmd_admin_pm(whom, msg)
|
||||
|
||||
//takes input from cmd_admin_pm_context, cmd_admin_pm_panel or /client/Topic and sends them a PM.
|
||||
//Fetching a message if needed. src is the sender and C is the target client
|
||||
/client/proc/cmd_admin_pm(whom, msg)
|
||||
if(prefs.muted & MUTE_ADMINHELP)
|
||||
to_chat(src, "<span class='danger'>Error: Admin-PM: You are unable to use admin PM-s (muted).</span>", confidential = TRUE)
|
||||
return
|
||||
|
||||
if(!holder && !current_ticket) //no ticket? https://www.youtube.com/watch?v=iHSPf6x1Fdo
|
||||
to_chat(src, "<span class='danger'>You can no longer reply to this ticket, please open another one by using the Adminhelp verb if need be.</span>", confidential = TRUE)
|
||||
to_chat(src, "<span class='notice'>Message: [msg]</span>", confidential = TRUE)
|
||||
return
|
||||
|
||||
var/client/recipient
|
||||
var/external = 0
|
||||
if(istext(whom))
|
||||
if(whom[1] == "@")
|
||||
whom = findStealthKey(whom)
|
||||
if(whom == "IRCKEY")
|
||||
external = 1
|
||||
else
|
||||
recipient = GLOB.directory[whom]
|
||||
else if(istype(whom, /client))
|
||||
recipient = whom
|
||||
|
||||
|
||||
if(external)
|
||||
if(!externalreplyamount) //to prevent people from spamming irc/discord
|
||||
return
|
||||
if(!msg)
|
||||
msg = input(src,"Message:", "Private message to Administrator") as message|null
|
||||
|
||||
if(!msg)
|
||||
return
|
||||
if(holder)
|
||||
to_chat(src, "<span class='danger'>Error: Use the admin IRC/Discord channel, nerd.</span>", confidential = TRUE)
|
||||
return
|
||||
|
||||
|
||||
else
|
||||
if(!recipient)
|
||||
if(holder)
|
||||
to_chat(src, "<span class='danger'>Error: Admin-PM: Client not found.</span>", confidential = TRUE)
|
||||
if(msg)
|
||||
to_chat(src, msg, confidential = TRUE)
|
||||
return
|
||||
else if(msg) // you want to continue if there's no message instead of returning now
|
||||
current_ticket.MessageNoRecipient(msg)
|
||||
return
|
||||
|
||||
//get message text, limit it's length.and clean/escape html
|
||||
if(!msg)
|
||||
msg = input(src,"Message:", "Private message to [recipient.holder?.fakekey ? "an Administrator" : key_name(recipient, 0, 0)].") as message|null
|
||||
msg = trim(msg)
|
||||
if(!msg)
|
||||
return
|
||||
|
||||
if(prefs.muted & MUTE_ADMINHELP)
|
||||
to_chat(src, "<span class='danger'>Error: Admin-PM: You are unable to use admin PM-s (muted).</span>", confidential = TRUE)
|
||||
return
|
||||
|
||||
if(!recipient)
|
||||
if(holder)
|
||||
to_chat(src, "<span class='danger'>Error: Admin-PM: Client not found.</span>", confidential = TRUE)
|
||||
else
|
||||
current_ticket.MessageNoRecipient(msg)
|
||||
return
|
||||
|
||||
if (src.handle_spam_prevention(msg,MUTE_ADMINHELP))
|
||||
return
|
||||
|
||||
//clean the message if it's not sent by a high-rank admin
|
||||
if(!check_rights(R_SERVER|R_DEBUG,0)||external)//no sending html to the poor bots
|
||||
msg = sanitize(copytext_char(msg, 1, MAX_MESSAGE_LEN))
|
||||
if(!msg)
|
||||
return
|
||||
|
||||
var/rawmsg = msg
|
||||
|
||||
if(holder)
|
||||
msg = emoji_parse(msg)
|
||||
|
||||
var/keywordparsedmsg = keywords_lookup(msg)
|
||||
|
||||
if(external)
|
||||
to_chat(src, "<span class='notice'>PM to-<b>Admins</b>: <span class='linkify'>[rawmsg]</span></span>", confidential = TRUE)
|
||||
var/datum/admin_help/AH = admin_ticket_log(src, "<font color='red'>Reply PM from-<b>[key_name(src, TRUE, TRUE)]</b> to <i>External</i>: [keywordparsedmsg]</font>")
|
||||
externalreplyamount--
|
||||
send2adminchat("[AH ? "#[AH.id] " : ""]Reply: [ckey]", rawmsg)
|
||||
else
|
||||
if(recipient.holder)
|
||||
if(holder) //both are admins
|
||||
to_chat(recipient, "<span class='danger'>Admin PM from-<b>[key_name(src, recipient, 1)]</b>: <span class='linkify'>[keywordparsedmsg]</span></span>", confidential = TRUE)
|
||||
to_chat(src, "<span class='notice'>Admin PM to-<b>[key_name(recipient, src, 1)]</b>: <span class='linkify'>[keywordparsedmsg]</span></span>", confidential = TRUE)
|
||||
|
||||
//omg this is dumb, just fill in both their tickets
|
||||
var/interaction_message = "<font color='purple'>PM from-<b>[key_name(src, recipient, 1)]</b> to-<b>[key_name(recipient, src, 1)]</b>: [keywordparsedmsg]</font>"
|
||||
admin_ticket_log(src, interaction_message)
|
||||
if(recipient != src) //reeee
|
||||
admin_ticket_log(recipient, interaction_message)
|
||||
SSblackbox.LogAhelp(current_ticket.id, "Reply", msg, recipient.ckey, src.ckey)
|
||||
else //recipient is an admin but sender is not
|
||||
var/replymsg = "Reply PM from-<b>[key_name(src, recipient, 1)]</b>: <span class='linkify'>[keywordparsedmsg]</span>"
|
||||
admin_ticket_log(src, "<font color='red'>[replymsg]</font>")
|
||||
to_chat(recipient, "<span class='danger'>[replymsg]</span>", confidential = TRUE)
|
||||
to_chat(src, "<span class='notice'>PM to-<b>Admins</b>: <span class='linkify'>[msg]</span></span>", confidential = TRUE)
|
||||
SSblackbox.LogAhelp(current_ticket.id, "Reply", msg, recipient.ckey, src.ckey)
|
||||
|
||||
//play the receiving admin the adminhelp sound (if they have them enabled)
|
||||
if(recipient.prefs.toggles & SOUND_ADMINHELP)
|
||||
SEND_SOUND(recipient, sound('sound/effects/adminhelp.ogg'))
|
||||
|
||||
else
|
||||
if(holder) //sender is an admin but recipient is not. Do BIG RED TEXT
|
||||
var/already_logged = FALSE
|
||||
if(!recipient.current_ticket)
|
||||
new /datum/admin_help(msg, recipient, TRUE)
|
||||
already_logged = TRUE
|
||||
SSblackbox.LogAhelp(recipient.current_ticket.id, "Ticket Opened", msg, recipient.ckey, src.ckey)
|
||||
|
||||
to_chat(recipient, "<font color='red' size='4'><b>-- Administrator private message --</b></font>", confidential = TRUE)
|
||||
to_chat(recipient, "<span class='adminsay'>Admin PM from-<b>[key_name(src, recipient, 0)]</b>: <span class='linkify'>[msg]</span></span>", confidential = TRUE)
|
||||
to_chat(recipient, "<span class='adminsay'><i>Click on the administrator's name to reply.</i></span>", confidential = TRUE)
|
||||
to_chat(src, "<span class='notice'>Admin PM to-<b>[key_name(recipient, src, 1)]</b>: <span class='linkify'>[msg]</span></span>", confidential = TRUE)
|
||||
|
||||
admin_ticket_log(recipient, "<font color='purple'>PM From [key_name_admin(src)]: [keywordparsedmsg]</font>")
|
||||
|
||||
if(!already_logged) //Reply to an existing ticket
|
||||
SSblackbox.LogAhelp(recipient.current_ticket.id, "Reply", msg, recipient.ckey, src.ckey)
|
||||
|
||||
|
||||
//always play non-admin recipients the adminhelp sound
|
||||
SEND_SOUND(recipient, sound('sound/effects/adminhelp.ogg'))
|
||||
|
||||
//AdminPM popup for ApocStation and anybody else who wants to use it. Set it with POPUP_ADMIN_PM in config.txt ~Carn
|
||||
if(CONFIG_GET(flag/popup_admin_pm))
|
||||
INVOKE_ASYNC(src, .proc/popup_admin_pm, recipient, msg)
|
||||
|
||||
else //neither are admins
|
||||
to_chat(src, "<span class='danger'>Error: Admin-PM: Non-admin to non-admin PM communication is forbidden.</span>", confidential = TRUE)
|
||||
return
|
||||
|
||||
if(external)
|
||||
log_admin_private("PM: [key_name(src)]->External: [rawmsg]")
|
||||
for(var/client/X in GLOB.admins)
|
||||
to_chat(X, "<span class='notice'><B>PM: [key_name(src, X, 0)]->External:</B> [keywordparsedmsg]</span>", confidential = TRUE)
|
||||
else
|
||||
window_flash(recipient, ignorepref = TRUE)
|
||||
log_admin_private("PM: [key_name(src)]->[key_name(recipient)]: [rawmsg]")
|
||||
//we don't use message_admins here because the sender/receiver might get it too
|
||||
for(var/client/X in GLOB.admins)
|
||||
if(X.key!=key && X.key!=recipient.key) //check client/X is an admin and isn't the sender or recipient
|
||||
to_chat(X, "<span class='notice'><B>PM: [key_name(src, X, 0)]->[key_name(recipient, X, 0)]:</B> [keywordparsedmsg]</span>" , confidential = TRUE)
|
||||
|
||||
/client/proc/popup_admin_pm(client/recipient, msg)
|
||||
var/sender = src
|
||||
var/sendername = key
|
||||
var/reply = input(recipient, msg,"Admin PM from-[sendername]", "") as message|null //show message and await a reply
|
||||
if(recipient && reply)
|
||||
if(sender)
|
||||
recipient.cmd_admin_pm(sender,reply) //sender is still about, let's reply to them
|
||||
else
|
||||
adminhelp(reply) //sender has left, adminhelp instead
|
||||
|
||||
#define TGS_AHELP_USAGE "Usage: ticket <close|resolve|icissue|reject|reopen \[ticket #\]|list>"
|
||||
/proc/TgsPm(target,msg,sender)
|
||||
target = ckey(target)
|
||||
var/client/C = GLOB.directory[target]
|
||||
|
||||
var/datum/admin_help/ticket = C ? C.current_ticket : GLOB.ahelp_tickets.CKey2ActiveTicket(target)
|
||||
var/compliant_msg = trim(lowertext(msg))
|
||||
var/tgs_tagged = "[sender](TGS/External)"
|
||||
var/list/splits = splittext(compliant_msg, " ")
|
||||
if(splits.len && splits[1] == "ticket")
|
||||
if(splits.len < 2)
|
||||
return TGS_AHELP_USAGE
|
||||
switch(splits[2])
|
||||
if("close")
|
||||
if(ticket)
|
||||
ticket.Close(tgs_tagged)
|
||||
return "Ticket #[ticket.id] successfully closed"
|
||||
if("resolve")
|
||||
if(ticket)
|
||||
ticket.Resolve(tgs_tagged)
|
||||
return "Ticket #[ticket.id] successfully resolved"
|
||||
if("icissue")
|
||||
if(ticket)
|
||||
ticket.ICIssue(tgs_tagged)
|
||||
return "Ticket #[ticket.id] successfully marked as IC issue"
|
||||
if("reject")
|
||||
if(ticket)
|
||||
ticket.Reject(tgs_tagged)
|
||||
return "Ticket #[ticket.id] successfully rejected"
|
||||
if("reopen")
|
||||
if(ticket)
|
||||
return "Error: [target] already has ticket #[ticket.id] open"
|
||||
var/fail = splits.len < 3 ? null : -1
|
||||
if(!isnull(fail))
|
||||
fail = text2num(splits[3])
|
||||
if(isnull(fail))
|
||||
return "Error: No/Invalid ticket id specified. [TGS_AHELP_USAGE]"
|
||||
var/datum/admin_help/AH = GLOB.ahelp_tickets.TicketByID(fail)
|
||||
if(!AH)
|
||||
return "Error: Ticket #[fail] not found"
|
||||
if(AH.initiator_ckey != target)
|
||||
return "Error: Ticket #[fail] belongs to [AH.initiator_ckey]"
|
||||
AH.Reopen()
|
||||
return "Ticket #[ticket.id] successfully reopened"
|
||||
if("list")
|
||||
var/list/tickets = GLOB.ahelp_tickets.TicketsByCKey(target)
|
||||
if(!tickets.len)
|
||||
return "None"
|
||||
. = ""
|
||||
for(var/I in tickets)
|
||||
var/datum/admin_help/AH = I
|
||||
if(.)
|
||||
. += ", "
|
||||
if(AH == ticket)
|
||||
. += "Active: "
|
||||
. += "#[AH.id]"
|
||||
return
|
||||
else
|
||||
return TGS_AHELP_USAGE
|
||||
return "Error: Ticket could not be found"
|
||||
|
||||
var/static/stealthkey
|
||||
var/adminname = CONFIG_GET(flag/show_irc_name) ? tgs_tagged : "Administrator"
|
||||
|
||||
if(!C)
|
||||
return "Error: No client"
|
||||
|
||||
if(!stealthkey)
|
||||
stealthkey = GenTgsStealthKey()
|
||||
|
||||
msg = sanitize(copytext_char(msg, 1, MAX_MESSAGE_LEN))
|
||||
if(!msg)
|
||||
return "Error: No message"
|
||||
|
||||
message_admins("External message from [sender] to [key_name_admin(C)] : [msg]")
|
||||
log_admin_private("External PM: [sender] -> [key_name(C)] : [msg]")
|
||||
msg = emoji_parse(msg)
|
||||
|
||||
to_chat(C, "<font color='red' size='4'><b>-- Administrator private message --</b></font>", confidential = TRUE)
|
||||
to_chat(C, "<span class='adminsay'>Admin PM from-<b><a href='?priv_msg=[stealthkey]'>[adminname]</A></b>: [msg]</span>", confidential = TRUE)
|
||||
to_chat(C, "<span class='adminsay'><i>Click on the administrator's name to reply.</i></span>", confidential = TRUE)
|
||||
|
||||
admin_ticket_log(C, "<font color='purple'>PM From [tgs_tagged]: [msg]</font>")
|
||||
|
||||
window_flash(C, ignorepref = TRUE)
|
||||
//always play non-admin recipients the adminhelp sound
|
||||
SEND_SOUND(C, 'sound/effects/adminhelp.ogg')
|
||||
|
||||
C.externalreplyamount = EXTERNALREPLYCOUNT
|
||||
|
||||
return "Message Successful"
|
||||
|
||||
/proc/GenTgsStealthKey()
|
||||
var/num = (rand(0,1000))
|
||||
var/i = 0
|
||||
while(i == 0)
|
||||
i = 1
|
||||
for(var/P in GLOB.stealthminID)
|
||||
if(num == GLOB.stealthminID[P])
|
||||
num++
|
||||
i = 0
|
||||
var/stealth = "@[num2text(num)]"
|
||||
GLOB.stealthminID["IRCKEY"] = stealth
|
||||
return stealth
|
||||
|
||||
#undef EXTERNALREPLYCOUNT
|
||||
#define EXTERNALREPLYCOUNT 2
|
||||
|
||||
//allows right clicking mobs to send an admin PM to their client, forwards the selected mob's client to cmd_admin_pm
|
||||
/client/proc/cmd_admin_pm_context(mob/M in GLOB.mob_list)
|
||||
set category = null
|
||||
set name = "Admin PM Mob"
|
||||
if(!holder)
|
||||
to_chat(src, "<span class='danger'>Error: Admin-PM-Context: Only administrators may use this command.</span>", confidential = TRUE)
|
||||
return
|
||||
if( !ismob(M) || !M.client )
|
||||
return
|
||||
cmd_admin_pm(M.client,null)
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Admin PM Mob") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
//shows a list of clients we could send PMs to, then forwards our choice to cmd_admin_pm
|
||||
/client/proc/cmd_admin_pm_panel()
|
||||
set category = "Admin"
|
||||
set name = "Admin PM"
|
||||
if(!holder)
|
||||
to_chat(src, "<span class='danger'>Error: Admin-PM-Panel: Only administrators may use this command.</span>", confidential = TRUE)
|
||||
return
|
||||
var/list/client/targets[0]
|
||||
for(var/client/T)
|
||||
if(T.mob)
|
||||
if(isnewplayer(T.mob))
|
||||
targets["(New Player) - [T]"] = T
|
||||
else if(isobserver(T.mob))
|
||||
targets["[T.mob.name](Ghost) - [T]"] = T
|
||||
else
|
||||
targets["[T.mob.real_name](as [T.mob.name]) - [T]"] = T
|
||||
else
|
||||
targets["(No Mob) - [T]"] = T
|
||||
var/target = input(src,"To whom shall we send a message?","Admin PM",null) as null|anything in sortList(targets)
|
||||
cmd_admin_pm(targets[target],null)
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Admin PM") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
/client/proc/cmd_ahelp_reply(whom)
|
||||
if(prefs.muted & MUTE_ADMINHELP)
|
||||
to_chat(src, "<span class='danger'>Error: Admin-PM: You are unable to use admin PM-s (muted).</span>", confidential = TRUE)
|
||||
return
|
||||
var/client/C
|
||||
if(istext(whom))
|
||||
if(whom[1] == "@")
|
||||
whom = findStealthKey(whom)
|
||||
C = GLOB.directory[whom]
|
||||
else if(istype(whom, /client))
|
||||
C = whom
|
||||
if(!C)
|
||||
if(holder)
|
||||
to_chat(src, "<span class='danger'>Error: Admin-PM: Client not found.</span>", confidential = TRUE)
|
||||
return
|
||||
|
||||
var/datum/admin_help/AH = C.current_ticket
|
||||
|
||||
if(AH)
|
||||
message_admins("[key_name_admin(src)] has started replying to [key_name_admin(C, 0, 0)]'s admin help.")
|
||||
var/msg = input(src,"Message:", "Private message to [C.holder?.fakekey ? "an Administrator" : key_name(C, 0, 0)].") as message|null
|
||||
if (!msg)
|
||||
message_admins("[key_name_admin(src)] has cancelled their reply to [key_name_admin(C, 0, 0)]'s admin help.")
|
||||
return
|
||||
cmd_admin_pm(whom, msg)
|
||||
|
||||
//takes input from cmd_admin_pm_context, cmd_admin_pm_panel or /client/Topic and sends them a PM.
|
||||
//Fetching a message if needed. src is the sender and C is the target client
|
||||
/client/proc/cmd_admin_pm(whom, msg)
|
||||
if(prefs.muted & MUTE_ADMINHELP)
|
||||
to_chat(src, "<span class='danger'>Error: Admin-PM: You are unable to use admin PM-s (muted).</span>", confidential = TRUE)
|
||||
return
|
||||
|
||||
if(!holder && !current_ticket) //no ticket? https://www.youtube.com/watch?v=iHSPf6x1Fdo
|
||||
to_chat(src, "<span class='danger'>You can no longer reply to this ticket, please open another one by using the Adminhelp verb if need be.</span>", confidential = TRUE)
|
||||
to_chat(src, "<span class='notice'>Message: [msg]</span>", confidential = TRUE)
|
||||
return
|
||||
|
||||
var/client/recipient
|
||||
var/external = 0
|
||||
if(istext(whom))
|
||||
if(whom[1] == "@")
|
||||
whom = findStealthKey(whom)
|
||||
if(whom == "IRCKEY")
|
||||
external = 1
|
||||
else
|
||||
recipient = GLOB.directory[whom]
|
||||
else if(istype(whom, /client))
|
||||
recipient = whom
|
||||
|
||||
|
||||
if(external)
|
||||
if(!externalreplyamount) //to prevent people from spamming irc/discord
|
||||
return
|
||||
if(!msg)
|
||||
msg = input(src,"Message:", "Private message to Administrator") as message|null
|
||||
|
||||
if(!msg)
|
||||
return
|
||||
if(holder)
|
||||
to_chat(src, "<span class='danger'>Error: Use the admin IRC/Discord channel, nerd.</span>", confidential = TRUE)
|
||||
return
|
||||
|
||||
|
||||
else
|
||||
if(!recipient)
|
||||
if(holder)
|
||||
to_chat(src, "<span class='danger'>Error: Admin-PM: Client not found.</span>", confidential = TRUE)
|
||||
if(msg)
|
||||
to_chat(src, msg, confidential = TRUE)
|
||||
return
|
||||
else if(msg) // you want to continue if there's no message instead of returning now
|
||||
current_ticket.MessageNoRecipient(msg)
|
||||
return
|
||||
|
||||
//get message text, limit it's length.and clean/escape html
|
||||
if(!msg)
|
||||
msg = input(src,"Message:", "Private message to [recipient.holder?.fakekey ? "an Administrator" : key_name(recipient, 0, 0)].") as message|null
|
||||
msg = trim(msg)
|
||||
if(!msg)
|
||||
return
|
||||
|
||||
if(prefs.muted & MUTE_ADMINHELP)
|
||||
to_chat(src, "<span class='danger'>Error: Admin-PM: You are unable to use admin PM-s (muted).</span>", confidential = TRUE)
|
||||
return
|
||||
|
||||
if(!recipient)
|
||||
if(holder)
|
||||
to_chat(src, "<span class='danger'>Error: Admin-PM: Client not found.</span>", confidential = TRUE)
|
||||
else
|
||||
current_ticket.MessageNoRecipient(msg)
|
||||
return
|
||||
|
||||
if (src.handle_spam_prevention(msg,MUTE_ADMINHELP))
|
||||
return
|
||||
|
||||
//clean the message if it's not sent by a high-rank admin
|
||||
if(!check_rights(R_SERVER|R_DEBUG,0)||external)//no sending html to the poor bots
|
||||
msg = sanitize(copytext_char(msg, 1, MAX_MESSAGE_LEN))
|
||||
if(!msg)
|
||||
return
|
||||
|
||||
var/rawmsg = msg
|
||||
|
||||
if(holder)
|
||||
msg = emoji_parse(msg)
|
||||
|
||||
var/keywordparsedmsg = keywords_lookup(msg)
|
||||
|
||||
if(external)
|
||||
to_chat(src, "<span class='notice'>PM to-<b>Admins</b>: <span class='linkify'>[rawmsg]</span></span>", confidential = TRUE)
|
||||
var/datum/admin_help/AH = admin_ticket_log(src, "<font color='red'>Reply PM from-<b>[key_name(src, TRUE, TRUE)]</b> to <i>External</i>: [keywordparsedmsg]</font>")
|
||||
externalreplyamount--
|
||||
send2adminchat("[AH ? "#[AH.id] " : ""]Reply: [ckey]", rawmsg)
|
||||
else
|
||||
if(recipient.holder)
|
||||
if(holder) //both are admins
|
||||
to_chat(recipient, "<span class='danger'>Admin PM from-<b>[key_name(src, recipient, 1)]</b>: <span class='linkify'>[keywordparsedmsg]</span></span>", confidential = TRUE)
|
||||
to_chat(src, "<span class='notice'>Admin PM to-<b>[key_name(recipient, src, 1)]</b>: <span class='linkify'>[keywordparsedmsg]</span></span>", confidential = TRUE)
|
||||
|
||||
//omg this is dumb, just fill in both their tickets
|
||||
var/interaction_message = "<font color='purple'>PM from-<b>[key_name(src, recipient, 1)]</b> to-<b>[key_name(recipient, src, 1)]</b>: [keywordparsedmsg]</font>"
|
||||
admin_ticket_log(src, interaction_message)
|
||||
if(recipient != src) //reeee
|
||||
admin_ticket_log(recipient, interaction_message)
|
||||
SSblackbox.LogAhelp(current_ticket.id, "Reply", msg, recipient.ckey, src.ckey)
|
||||
else //recipient is an admin but sender is not
|
||||
var/replymsg = "Reply PM from-<b>[key_name(src, recipient, 1)]</b>: <span class='linkify'>[keywordparsedmsg]</span>"
|
||||
admin_ticket_log(src, "<font color='red'>[replymsg]</font>")
|
||||
to_chat(recipient, "<span class='danger'>[replymsg]</span>", confidential = TRUE)
|
||||
to_chat(src, "<span class='notice'>PM to-<b>Admins</b>: <span class='linkify'>[msg]</span></span>", confidential = TRUE)
|
||||
SSblackbox.LogAhelp(current_ticket.id, "Reply", msg, recipient.ckey, src.ckey)
|
||||
|
||||
//play the receiving admin the adminhelp sound (if they have them enabled)
|
||||
if(recipient.prefs.toggles & SOUND_ADMINHELP)
|
||||
SEND_SOUND(recipient, sound('sound/effects/adminhelp.ogg'))
|
||||
|
||||
else
|
||||
if(holder) //sender is an admin but recipient is not. Do BIG RED TEXT
|
||||
var/already_logged = FALSE
|
||||
if(!recipient.current_ticket)
|
||||
new /datum/admin_help(msg, recipient, TRUE)
|
||||
already_logged = TRUE
|
||||
SSblackbox.LogAhelp(recipient.current_ticket.id, "Ticket Opened", msg, recipient.ckey, src.ckey)
|
||||
|
||||
to_chat(recipient, "<font color='red' size='4'><b>-- Administrator private message --</b></font>", confidential = TRUE)
|
||||
to_chat(recipient, "<span class='adminsay'>Admin PM from-<b>[key_name(src, recipient, 0)]</b>: <span class='linkify'>[msg]</span></span>", confidential = TRUE)
|
||||
to_chat(recipient, "<span class='adminsay'><i>Click on the administrator's name to reply.</i></span>", confidential = TRUE)
|
||||
to_chat(src, "<span class='notice'>Admin PM to-<b>[key_name(recipient, src, 1)]</b>: <span class='linkify'>[msg]</span></span>", confidential = TRUE)
|
||||
|
||||
admin_ticket_log(recipient, "<font color='purple'>PM From [key_name_admin(src)]: [keywordparsedmsg]</font>")
|
||||
|
||||
if(!already_logged) //Reply to an existing ticket
|
||||
SSblackbox.LogAhelp(recipient.current_ticket.id, "Reply", msg, recipient.ckey, src.ckey)
|
||||
|
||||
|
||||
//always play non-admin recipients the adminhelp sound
|
||||
SEND_SOUND(recipient, sound('sound/effects/adminhelp.ogg'))
|
||||
|
||||
//AdminPM popup for ApocStation and anybody else who wants to use it. Set it with POPUP_ADMIN_PM in config.txt ~Carn
|
||||
if(CONFIG_GET(flag/popup_admin_pm))
|
||||
INVOKE_ASYNC(src, .proc/popup_admin_pm, recipient, msg)
|
||||
|
||||
else //neither are admins
|
||||
to_chat(src, "<span class='danger'>Error: Admin-PM: Non-admin to non-admin PM communication is forbidden.</span>", confidential = TRUE)
|
||||
return
|
||||
|
||||
if(external)
|
||||
log_admin_private("PM: [key_name(src)]->External: [rawmsg]")
|
||||
for(var/client/X in GLOB.admins)
|
||||
to_chat(X, "<span class='notice'><B>PM: [key_name(src, X, 0)]->External:</B> [keywordparsedmsg]</span>", confidential = TRUE)
|
||||
else
|
||||
window_flash(recipient, ignorepref = TRUE)
|
||||
log_admin_private("PM: [key_name(src)]->[key_name(recipient)]: [rawmsg]")
|
||||
//we don't use message_admins here because the sender/receiver might get it too
|
||||
for(var/client/X in GLOB.admins)
|
||||
if(X.key!=key && X.key!=recipient.key) //check client/X is an admin and isn't the sender or recipient
|
||||
to_chat(X, "<span class='notice'><B>PM: [key_name(src, X, 0)]->[key_name(recipient, X, 0)]:</B> [keywordparsedmsg]</span>" , confidential = TRUE)
|
||||
|
||||
/client/proc/popup_admin_pm(client/recipient, msg)
|
||||
var/sender = src
|
||||
var/sendername = key
|
||||
var/reply = input(recipient, msg,"Admin PM from-[sendername]", "") as message|null //show message and await a reply
|
||||
if(recipient && reply)
|
||||
if(sender)
|
||||
recipient.cmd_admin_pm(sender,reply) //sender is still about, let's reply to them
|
||||
else
|
||||
adminhelp(reply) //sender has left, adminhelp instead
|
||||
|
||||
#define TGS_AHELP_USAGE "Usage: ticket <close|resolve|icissue|reject|reopen \[ticket #\]|list>"
|
||||
/proc/TgsPm(target,msg,sender)
|
||||
target = ckey(target)
|
||||
var/client/C = GLOB.directory[target]
|
||||
|
||||
var/datum/admin_help/ticket = C ? C.current_ticket : GLOB.ahelp_tickets.CKey2ActiveTicket(target)
|
||||
var/compliant_msg = trim(lowertext(msg))
|
||||
var/tgs_tagged = "[sender](TGS/External)"
|
||||
var/list/splits = splittext(compliant_msg, " ")
|
||||
if(splits.len && splits[1] == "ticket")
|
||||
if(splits.len < 2)
|
||||
return TGS_AHELP_USAGE
|
||||
switch(splits[2])
|
||||
if("close")
|
||||
if(ticket)
|
||||
ticket.Close(tgs_tagged)
|
||||
return "Ticket #[ticket.id] successfully closed"
|
||||
if("resolve")
|
||||
if(ticket)
|
||||
ticket.Resolve(tgs_tagged)
|
||||
return "Ticket #[ticket.id] successfully resolved"
|
||||
if("icissue")
|
||||
if(ticket)
|
||||
ticket.ICIssue(tgs_tagged)
|
||||
return "Ticket #[ticket.id] successfully marked as IC issue"
|
||||
if("reject")
|
||||
if(ticket)
|
||||
ticket.Reject(tgs_tagged)
|
||||
return "Ticket #[ticket.id] successfully rejected"
|
||||
if("reopen")
|
||||
if(ticket)
|
||||
return "Error: [target] already has ticket #[ticket.id] open"
|
||||
var/fail = splits.len < 3 ? null : -1
|
||||
if(!isnull(fail))
|
||||
fail = text2num(splits[3])
|
||||
if(isnull(fail))
|
||||
return "Error: No/Invalid ticket id specified. [TGS_AHELP_USAGE]"
|
||||
var/datum/admin_help/AH = GLOB.ahelp_tickets.TicketByID(fail)
|
||||
if(!AH)
|
||||
return "Error: Ticket #[fail] not found"
|
||||
if(AH.initiator_ckey != target)
|
||||
return "Error: Ticket #[fail] belongs to [AH.initiator_ckey]"
|
||||
AH.Reopen()
|
||||
return "Ticket #[ticket.id] successfully reopened"
|
||||
if("list")
|
||||
var/list/tickets = GLOB.ahelp_tickets.TicketsByCKey(target)
|
||||
if(!tickets.len)
|
||||
return "None"
|
||||
. = ""
|
||||
for(var/I in tickets)
|
||||
var/datum/admin_help/AH = I
|
||||
if(.)
|
||||
. += ", "
|
||||
if(AH == ticket)
|
||||
. += "Active: "
|
||||
. += "#[AH.id]"
|
||||
return
|
||||
else
|
||||
return TGS_AHELP_USAGE
|
||||
return "Error: Ticket could not be found"
|
||||
|
||||
var/static/stealthkey
|
||||
var/adminname = CONFIG_GET(flag/show_irc_name) ? tgs_tagged : "Administrator"
|
||||
|
||||
if(!C)
|
||||
return "Error: No client"
|
||||
|
||||
if(!stealthkey)
|
||||
stealthkey = GenTgsStealthKey()
|
||||
|
||||
msg = sanitize(copytext_char(msg, 1, MAX_MESSAGE_LEN))
|
||||
if(!msg)
|
||||
return "Error: No message"
|
||||
|
||||
message_admins("External message from [sender] to [key_name_admin(C)] : [msg]")
|
||||
log_admin_private("External PM: [sender] -> [key_name(C)] : [msg]")
|
||||
msg = emoji_parse(msg)
|
||||
|
||||
to_chat(C, "<font color='red' size='4'><b>-- Administrator private message --</b></font>", confidential = TRUE)
|
||||
to_chat(C, "<span class='adminsay'>Admin PM from-<b><a href='?priv_msg=[stealthkey]'>[adminname]</A></b>: [msg]</span>", confidential = TRUE)
|
||||
to_chat(C, "<span class='adminsay'><i>Click on the administrator's name to reply.</i></span>", confidential = TRUE)
|
||||
|
||||
admin_ticket_log(C, "<font color='purple'>PM From [tgs_tagged]: [msg]</font>")
|
||||
|
||||
window_flash(C, ignorepref = TRUE)
|
||||
//always play non-admin recipients the adminhelp sound
|
||||
SEND_SOUND(C, 'sound/effects/adminhelp.ogg')
|
||||
|
||||
C.externalreplyamount = EXTERNALREPLYCOUNT
|
||||
|
||||
return "Message Successful"
|
||||
|
||||
/proc/GenTgsStealthKey()
|
||||
var/num = (rand(0,1000))
|
||||
var/i = 0
|
||||
while(i == 0)
|
||||
i = 1
|
||||
for(var/P in GLOB.stealthminID)
|
||||
if(num == GLOB.stealthminID[P])
|
||||
num++
|
||||
i = 0
|
||||
var/stealth = "@[num2text(num)]"
|
||||
GLOB.stealthminID["IRCKEY"] = stealth
|
||||
return stealth
|
||||
|
||||
#undef EXTERNALREPLYCOUNT
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
/client/proc/cmd_admin_say(msg as text)
|
||||
set category = "Admin"
|
||||
set name = "Asay" //Gave this shit a shorter name so you only have to time out "asay" rather than "admin say" to use it --NeoFite
|
||||
set hidden = 1
|
||||
if(!check_rights(0))
|
||||
return
|
||||
|
||||
msg = emoji_parse(copytext_char(sanitize(msg), 1, MAX_MESSAGE_LEN))
|
||||
if(!msg)
|
||||
return
|
||||
|
||||
mob.log_talk(msg, LOG_ASAY)
|
||||
msg = keywords_lookup(msg)
|
||||
var/custom_asay_color = (CONFIG_GET(flag/allow_admin_asaycolor) && prefs.asaycolor) ? "<font color=[prefs.asaycolor]>" : "<font color='#FF4500'>"
|
||||
msg = "<span class='adminsay'><span class='prefix'>ADMIN:</span> <EM>[key_name(usr, 1)]</EM> [ADMIN_FLW(mob)]: [custom_asay_color]<span class='message linkify'>[msg]</span></span>[custom_asay_color ? "</font>":null]"
|
||||
to_chat(GLOB.admins, msg, confidential = TRUE)
|
||||
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Asay") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
/client/proc/get_admin_say()
|
||||
var/msg = input(src, null, "asay \"text\"") as text|null
|
||||
cmd_admin_say(msg)
|
||||
/client/proc/cmd_admin_say(msg as text)
|
||||
set category = "Admin"
|
||||
set name = "Asay" //Gave this shit a shorter name so you only have to time out "asay" rather than "admin say" to use it --NeoFite
|
||||
set hidden = 1
|
||||
if(!check_rights(0))
|
||||
return
|
||||
|
||||
msg = emoji_parse(copytext_char(sanitize(msg), 1, MAX_MESSAGE_LEN))
|
||||
if(!msg)
|
||||
return
|
||||
|
||||
mob.log_talk(msg, LOG_ASAY)
|
||||
msg = keywords_lookup(msg)
|
||||
var/custom_asay_color = (CONFIG_GET(flag/allow_admin_asaycolor) && prefs.asaycolor) ? "<font color=[prefs.asaycolor]>" : "<font color='#FF4500'>"
|
||||
msg = "<span class='adminsay'><span class='prefix'>ADMIN:</span> <EM>[key_name(usr, 1)]</EM> [ADMIN_FLW(mob)]: [custom_asay_color]<span class='message linkify'>[msg]</span></span>[custom_asay_color ? "</font>":null]"
|
||||
to_chat(GLOB.admins, msg, confidential = TRUE)
|
||||
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Asay") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
/client/proc/get_admin_say()
|
||||
var/msg = input(src, null, "asay \"text\"") as text|null
|
||||
cmd_admin_say(msg)
|
||||
|
||||
@@ -1,56 +1,56 @@
|
||||
/client/proc/atmosscan()
|
||||
set category = "Mapping"
|
||||
set name = "Check Plumbing"
|
||||
if(!src.holder)
|
||||
to_chat(src, "Only administrators may use this command.", confidential = TRUE)
|
||||
return
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Check Plumbing") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
//all plumbing - yes, some things might get stated twice, doesn't matter.
|
||||
for(var/obj/machinery/atmospherics/components/pipe in GLOB.machines)
|
||||
if(pipe.z && (!pipe.nodes || !pipe.nodes.len || (null in pipe.nodes)))
|
||||
to_chat(usr, "Unconnected [pipe.name] located at [ADMIN_VERBOSEJMP(pipe)]", confidential = TRUE)
|
||||
|
||||
//Manifolds
|
||||
for(var/obj/machinery/atmospherics/pipe/manifold/pipe in GLOB.machines)
|
||||
if(pipe.z && (!pipe.nodes || !pipe.nodes.len || (null in pipe.nodes)))
|
||||
to_chat(usr, "Unconnected [pipe.name] located at [ADMIN_VERBOSEJMP(pipe)]", confidential = TRUE)
|
||||
|
||||
//Pipes
|
||||
for(var/obj/machinery/atmospherics/pipe/simple/pipe in GLOB.machines)
|
||||
if(pipe.z && (!pipe.nodes || !pipe.nodes.len || (null in pipe.nodes)))
|
||||
to_chat(usr, "Unconnected [pipe.name] located at [ADMIN_VERBOSEJMP(pipe)]", confidential = TRUE)
|
||||
|
||||
/client/proc/powerdebug()
|
||||
set category = "Mapping"
|
||||
set name = "Check Power"
|
||||
if(!src.holder)
|
||||
to_chat(src, "Only administrators may use this command.", confidential = TRUE)
|
||||
return
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Check Power") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
var/list/results = list()
|
||||
|
||||
for (var/datum/powernet/PN in GLOB.powernets)
|
||||
if (!PN.nodes || !PN.nodes.len)
|
||||
if(PN.cables && (PN.cables.len > 1))
|
||||
var/obj/structure/cable/C = PN.cables[1]
|
||||
results += "Powernet with no nodes! (number [PN.number]) - example cable at [ADMIN_VERBOSEJMP(C)]"
|
||||
|
||||
if (!PN.cables || (PN.cables.len < 10))
|
||||
if(PN.cables && (PN.cables.len > 1))
|
||||
var/obj/structure/cable/C = PN.cables[1]
|
||||
results += "Powernet with fewer than 10 cables! (number [PN.number]) - example cable at [ADMIN_VERBOSEJMP(C)]"
|
||||
|
||||
for(var/turf/T in world.contents)
|
||||
var/found_one = FALSE
|
||||
for(var/obj/structure/cable/C in T.contents)
|
||||
if(found_one)
|
||||
results += "Doubled wire at [ADMIN_VERBOSEJMP(C)]"
|
||||
else
|
||||
found_one = TRUE
|
||||
var/obj/machinery/power/terminal/term = locate(/obj/machinery/power/terminal) in T.contents
|
||||
if(term)
|
||||
var/obj/structure/cable/C = locate(/obj/structure/cable) in T.contents
|
||||
if(!C)
|
||||
results += "Unwired terminal at [ADMIN_VERBOSEJMP(term)]"
|
||||
to_chat(usr, "[results.Join("\n")]", confidential = TRUE)
|
||||
/client/proc/atmosscan()
|
||||
set category = "Mapping"
|
||||
set name = "Check Plumbing"
|
||||
if(!src.holder)
|
||||
to_chat(src, "Only administrators may use this command.", confidential = TRUE)
|
||||
return
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Check Plumbing") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
//all plumbing - yes, some things might get stated twice, doesn't matter.
|
||||
for(var/obj/machinery/atmospherics/components/pipe in GLOB.machines)
|
||||
if(pipe.z && (!pipe.nodes || !pipe.nodes.len || (null in pipe.nodes)))
|
||||
to_chat(usr, "Unconnected [pipe.name] located at [ADMIN_VERBOSEJMP(pipe)]", confidential = TRUE)
|
||||
|
||||
//Manifolds
|
||||
for(var/obj/machinery/atmospherics/pipe/manifold/pipe in GLOB.machines)
|
||||
if(pipe.z && (!pipe.nodes || !pipe.nodes.len || (null in pipe.nodes)))
|
||||
to_chat(usr, "Unconnected [pipe.name] located at [ADMIN_VERBOSEJMP(pipe)]", confidential = TRUE)
|
||||
|
||||
//Pipes
|
||||
for(var/obj/machinery/atmospherics/pipe/simple/pipe in GLOB.machines)
|
||||
if(pipe.z && (!pipe.nodes || !pipe.nodes.len || (null in pipe.nodes)))
|
||||
to_chat(usr, "Unconnected [pipe.name] located at [ADMIN_VERBOSEJMP(pipe)]", confidential = TRUE)
|
||||
|
||||
/client/proc/powerdebug()
|
||||
set category = "Mapping"
|
||||
set name = "Check Power"
|
||||
if(!src.holder)
|
||||
to_chat(src, "Only administrators may use this command.", confidential = TRUE)
|
||||
return
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Check Power") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
var/list/results = list()
|
||||
|
||||
for (var/datum/powernet/PN in GLOB.powernets)
|
||||
if (!PN.nodes || !PN.nodes.len)
|
||||
if(PN.cables && (PN.cables.len > 1))
|
||||
var/obj/structure/cable/C = PN.cables[1]
|
||||
results += "Powernet with no nodes! (number [PN.number]) - example cable at [ADMIN_VERBOSEJMP(C)]"
|
||||
|
||||
if (!PN.cables || (PN.cables.len < 10))
|
||||
if(PN.cables && (PN.cables.len > 1))
|
||||
var/obj/structure/cable/C = PN.cables[1]
|
||||
results += "Powernet with fewer than 10 cables! (number [PN.number]) - example cable at [ADMIN_VERBOSEJMP(C)]"
|
||||
|
||||
for(var/turf/T in world.contents)
|
||||
var/found_one = FALSE
|
||||
for(var/obj/structure/cable/C in T.contents)
|
||||
if(found_one)
|
||||
results += "Doubled wire at [ADMIN_VERBOSEJMP(C)]"
|
||||
else
|
||||
found_one = TRUE
|
||||
var/obj/machinery/power/terminal/term = locate(/obj/machinery/power/terminal) in T.contents
|
||||
if(term)
|
||||
var/obj/structure/cable/C = locate(/obj/structure/cable) in T.contents
|
||||
if(!C)
|
||||
results += "Unwired terminal at [ADMIN_VERBOSEJMP(term)]"
|
||||
to_chat(usr, "[results.Join("\n")]", confidential = TRUE)
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
/client/proc/cinematic()
|
||||
set name = "cinematic"
|
||||
set category = "Fun"
|
||||
set desc = "Shows a cinematic." // Intended for testing but I thought it might be nice for events on the rare occasion Feel free to comment it out if it's not wanted.
|
||||
set hidden = 1
|
||||
if(!SSticker)
|
||||
return
|
||||
|
||||
var/datum/cinematic/choice = input(src,"Cinematic","Choose",null) as anything in sortList(subtypesof(/datum/cinematic), /proc/cmp_typepaths_asc)
|
||||
if(choice)
|
||||
Cinematic(initial(choice.id),world,null)
|
||||
/client/proc/cinematic()
|
||||
set name = "cinematic"
|
||||
set category = "Fun"
|
||||
set desc = "Shows a cinematic." // Intended for testing but I thought it might be nice for events on the rare occasion Feel free to comment it out if it's not wanted.
|
||||
set hidden = 1
|
||||
if(!SSticker)
|
||||
return
|
||||
|
||||
var/datum/cinematic/choice = input(src,"Cinematic","Choose",null) as anything in sortList(subtypesof(/datum/cinematic), /proc/cmp_typepaths_asc)
|
||||
if(choice)
|
||||
Cinematic(initial(choice.id),world,null)
|
||||
|
||||
@@ -1,43 +1,43 @@
|
||||
/client/proc/dsay(msg as text)
|
||||
set category = "Admin - Game"
|
||||
set name = "Dsay"
|
||||
set hidden = 1
|
||||
if(!holder)
|
||||
to_chat(src, "Only administrators may use this command.", confidential = TRUE)
|
||||
return
|
||||
if(!mob)
|
||||
return
|
||||
if(prefs.muted & MUTE_DEADCHAT)
|
||||
to_chat(src, "<span class='danger'>You cannot send DSAY messages (muted).</span>", confidential = TRUE)
|
||||
return
|
||||
|
||||
if (handle_spam_prevention(msg,MUTE_DEADCHAT))
|
||||
return
|
||||
|
||||
msg = copytext_char(sanitize(msg), 1, MAX_MESSAGE_LEN)
|
||||
mob.log_talk(msg, LOG_DSAY)
|
||||
|
||||
if (!msg)
|
||||
return
|
||||
var/rank_name = holder.rank
|
||||
var/admin_name = key
|
||||
if(holder.fakekey)
|
||||
rank_name = pick(strings("admin_nicknames.json", "ranks", "config"))
|
||||
admin_name = pick(strings("admin_nicknames.json", "names", "config"))
|
||||
var/rendered = "<span class='game deadsay'><span class='prefix'>DEAD:</span> <span class='name'>[rank_name]([admin_name])</span> says, <span class='message'>\"[emoji_parse(msg)]\"</span></span>"
|
||||
|
||||
for (var/mob/M in GLOB.player_list)
|
||||
if(isnewplayer(M))
|
||||
continue
|
||||
if (M.stat == DEAD || (M.client.holder && (M.client.prefs.chat_toggles & CHAT_DEAD))) //admins can toggle deadchat on and off. This is a proc in admin.dm and is only give to Administrators and above
|
||||
to_chat(M, rendered, confidential = TRUE)
|
||||
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Dsay") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
/client/proc/get_dead_say()
|
||||
var/msg = input(src, null, "dsay \"text\"") as text|null
|
||||
|
||||
if (isnull(msg))
|
||||
return
|
||||
|
||||
dsay(msg)
|
||||
/client/proc/dsay(msg as text)
|
||||
set category = "Admin - Game"
|
||||
set name = "Dsay"
|
||||
set hidden = 1
|
||||
if(!holder)
|
||||
to_chat(src, "Only administrators may use this command.", confidential = TRUE)
|
||||
return
|
||||
if(!mob)
|
||||
return
|
||||
if(prefs.muted & MUTE_DEADCHAT)
|
||||
to_chat(src, "<span class='danger'>You cannot send DSAY messages (muted).</span>", confidential = TRUE)
|
||||
return
|
||||
|
||||
if (handle_spam_prevention(msg,MUTE_DEADCHAT))
|
||||
return
|
||||
|
||||
msg = copytext_char(sanitize(msg), 1, MAX_MESSAGE_LEN)
|
||||
mob.log_talk(msg, LOG_DSAY)
|
||||
|
||||
if (!msg)
|
||||
return
|
||||
var/rank_name = holder.rank
|
||||
var/admin_name = key
|
||||
if(holder.fakekey)
|
||||
rank_name = pick(strings("admin_nicknames.json", "ranks", "config"))
|
||||
admin_name = pick(strings("admin_nicknames.json", "names", "config"))
|
||||
var/rendered = "<span class='game deadsay'><span class='prefix'>DEAD:</span> <span class='name'>[rank_name]([admin_name])</span> says, <span class='message'>\"[emoji_parse(msg)]\"</span></span>"
|
||||
|
||||
for (var/mob/M in GLOB.player_list)
|
||||
if(isnewplayer(M))
|
||||
continue
|
||||
if (M.stat == DEAD || (M.client.holder && (M.client.prefs.chat_toggles & CHAT_DEAD))) //admins can toggle deadchat on and off. This is a proc in admin.dm and is only give to Administrators and above
|
||||
to_chat(M, rendered, confidential = TRUE)
|
||||
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Dsay") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
/client/proc/get_dead_say()
|
||||
var/msg = input(src, null, "dsay \"text\"") as text|null
|
||||
|
||||
if (isnull(msg))
|
||||
return
|
||||
|
||||
dsay(msg)
|
||||
|
||||
@@ -1,26 +1,26 @@
|
||||
//replaces the old Ticklag verb, fps is easier to understand
|
||||
/client/proc/set_server_fps()
|
||||
set category = "Debug"
|
||||
set name = "Set Server FPS"
|
||||
set desc = "Sets game speed in frames-per-second. Can potentially break the game"
|
||||
|
||||
if(!check_rights(R_DEBUG))
|
||||
return
|
||||
|
||||
var/cfg_fps = CONFIG_GET(number/fps)
|
||||
var/new_fps = round(input("Sets game frames-per-second. Can potentially break the game (default: [cfg_fps])","FPS", world.fps) as num|null)
|
||||
|
||||
if(new_fps <= 0)
|
||||
to_chat(src, "<span class='danger'>Error: set_server_fps(): Invalid world.fps value. No changes made.</span>", confidential = TRUE)
|
||||
return
|
||||
if(new_fps > cfg_fps * 1.5)
|
||||
if(alert(src, "You are setting fps to a high value:\n\t[new_fps] frames-per-second\n\tconfig.fps = [cfg_fps]","Warning!","Confirm","ABORT-ABORT-ABORT") != "Confirm")
|
||||
return
|
||||
|
||||
var/msg = "[key_name(src)] has modified world.fps to [new_fps]"
|
||||
log_admin(msg, 0)
|
||||
message_admins(msg, 0)
|
||||
SSblackbox.record_feedback("nested tally", "admin_toggle", 1, list("Set Server FPS", "[new_fps]")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
CONFIG_SET(number/fps, new_fps)
|
||||
world.change_fps(new_fps)
|
||||
//replaces the old Ticklag verb, fps is easier to understand
|
||||
/client/proc/set_server_fps()
|
||||
set category = "Debug"
|
||||
set name = "Set Server FPS"
|
||||
set desc = "Sets game speed in frames-per-second. Can potentially break the game"
|
||||
|
||||
if(!check_rights(R_DEBUG))
|
||||
return
|
||||
|
||||
var/cfg_fps = CONFIG_GET(number/fps)
|
||||
var/new_fps = round(input("Sets game frames-per-second. Can potentially break the game (default: [cfg_fps])","FPS", world.fps) as num|null)
|
||||
|
||||
if(new_fps <= 0)
|
||||
to_chat(src, "<span class='danger'>Error: set_server_fps(): Invalid world.fps value. No changes made.</span>", confidential = TRUE)
|
||||
return
|
||||
if(new_fps > cfg_fps * 1.5)
|
||||
if(alert(src, "You are setting fps to a high value:\n\t[new_fps] frames-per-second\n\tconfig.fps = [cfg_fps]","Warning!","Confirm","ABORT-ABORT-ABORT") != "Confirm")
|
||||
return
|
||||
|
||||
var/msg = "[key_name(src)] has modified world.fps to [new_fps]"
|
||||
log_admin(msg, 0)
|
||||
message_admins(msg, 0)
|
||||
SSblackbox.record_feedback("nested tally", "admin_toggle", 1, list("Set Server FPS", "[new_fps]")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
CONFIG_SET(number/fps, new_fps)
|
||||
world.change_fps(new_fps)
|
||||
|
||||
@@ -1,35 +1,35 @@
|
||||
//This proc allows download of past server logs saved within the data/logs/ folder.
|
||||
/client/proc/getserverlogs()
|
||||
set name = "Get Server Logs"
|
||||
set desc = "View/retrieve logfiles."
|
||||
set category = "Admin"
|
||||
|
||||
browseserverlogs()
|
||||
|
||||
/client/proc/getcurrentlogs()
|
||||
set name = "Get Current Logs"
|
||||
set desc = "View/retrieve logfiles for the current round."
|
||||
set category = "Admin"
|
||||
|
||||
browseserverlogs(current=TRUE)
|
||||
|
||||
/client/proc/browseserverlogs(current=FALSE)
|
||||
var/path = browse_files(current ? BROWSE_ROOT_CURRENT_LOGS : BROWSE_ROOT_ALL_LOGS)
|
||||
if(!path)
|
||||
return
|
||||
|
||||
if(file_spam_check())
|
||||
return
|
||||
|
||||
message_admins("[key_name_admin(src)] accessed file: [path]")
|
||||
switch(alert("View (in game), Open (in your system's text editor), or Download?", path, "View", "Open", "Download"))
|
||||
if ("View")
|
||||
src << browse("<pre style='word-wrap: break-word;'>[html_encode(file2text(file(path)))]</pre>", list2params(list("window" = "viewfile.[path]")))
|
||||
if ("Open")
|
||||
src << run(file(path))
|
||||
if ("Download")
|
||||
src << ftp(file(path))
|
||||
else
|
||||
return
|
||||
to_chat(src, "Attempting to send [path], this may take a fair few minutes if the file is very large.", confidential = TRUE)
|
||||
return
|
||||
//This proc allows download of past server logs saved within the data/logs/ folder.
|
||||
/client/proc/getserverlogs()
|
||||
set name = "Get Server Logs"
|
||||
set desc = "View/retrieve logfiles."
|
||||
set category = "Admin"
|
||||
|
||||
browseserverlogs()
|
||||
|
||||
/client/proc/getcurrentlogs()
|
||||
set name = "Get Current Logs"
|
||||
set desc = "View/retrieve logfiles for the current round."
|
||||
set category = "Admin"
|
||||
|
||||
browseserverlogs(current=TRUE)
|
||||
|
||||
/client/proc/browseserverlogs(current=FALSE)
|
||||
var/path = browse_files(current ? BROWSE_ROOT_CURRENT_LOGS : BROWSE_ROOT_ALL_LOGS)
|
||||
if(!path)
|
||||
return
|
||||
|
||||
if(file_spam_check())
|
||||
return
|
||||
|
||||
message_admins("[key_name_admin(src)] accessed file: [path]")
|
||||
switch(alert("View (in game), Open (in your system's text editor), or Download?", path, "View", "Open", "Download"))
|
||||
if ("View")
|
||||
src << browse("<pre style='word-wrap: break-word;'>[html_encode(file2text(file(path)))]</pre>", list2params(list("window" = "viewfile.[path]")))
|
||||
if ("Open")
|
||||
src << run(file(path))
|
||||
if ("Download")
|
||||
src << ftp(file(path))
|
||||
else
|
||||
return
|
||||
to_chat(src, "Attempting to send [path], this may take a fair few minutes if the file is very large.", confidential = TRUE)
|
||||
return
|
||||
|
||||
+377
-377
@@ -1,377 +1,377 @@
|
||||
//- Are all the floors with or without air, as they should be? (regular or airless)
|
||||
//- Does the area have an APC?
|
||||
//- Does the area have an Air Alarm?
|
||||
//- Does the area have a Request Console?
|
||||
//- Does the area have lights?
|
||||
//- Does the area have a light switch?
|
||||
//- Does the area have enough intercoms?
|
||||
//- Does the area have enough security cameras? (Use the 'Camera Range Display' verb under Debug)
|
||||
//- Is the area connected to the scrubbers air loop?
|
||||
//- Is the area connected to the vent air loop? (vent pumps)
|
||||
//- Is everything wired properly?
|
||||
//- Does the area have a fire alarm and firedoors?
|
||||
//- Do all pod doors work properly?
|
||||
//- Are accesses set properly on doors, pod buttons, etc.
|
||||
//- Are all items placed properly? (not below vents, scrubbers, tables)
|
||||
//- Does the disposal system work properly from all the disposal units in this room and all the units, the pipes of which pass through this room?
|
||||
//- Check for any misplaced or stacked piece of pipe (air and disposal)
|
||||
//- Check for any misplaced or stacked piece of wire
|
||||
//- Identify how hard it is to break into the area and where the weak points are
|
||||
//- Check if the area has too much empty space. If so, make it smaller and replace the rest with maintenance tunnels.
|
||||
|
||||
GLOBAL_LIST_INIT(admin_verbs_debug_mapping, list(
|
||||
/client/proc/camera_view, //-errorage
|
||||
/client/proc/sec_camera_report, //-errorage
|
||||
/client/proc/intercom_view, //-errorage
|
||||
/client/proc/air_status, //Air things
|
||||
/client/proc/Cell, //More air things
|
||||
/client/proc/atmosscan, //check plumbing
|
||||
/client/proc/powerdebug, //check power
|
||||
/client/proc/count_objects_on_z_level,
|
||||
/client/proc/count_objects_all,
|
||||
/client/proc/cmd_assume_direct_control, //-errorage
|
||||
/client/proc/cmd_give_direct_control,
|
||||
/client/proc/startSinglo,
|
||||
/client/proc/set_server_fps, //allows you to set the ticklag.
|
||||
/client/proc/cmd_admin_grantfullaccess,
|
||||
/client/proc/cmd_admin_areatest_all,
|
||||
/client/proc/cmd_admin_areatest_station,
|
||||
#ifdef TESTING
|
||||
/client/proc/see_dirty_varedits,
|
||||
#endif
|
||||
/client/proc/cmd_admin_test_atmos_controllers,
|
||||
/client/proc/cmd_admin_rejuvenate,
|
||||
/datum/admins/proc/show_traitor_panel,
|
||||
/client/proc/disable_communication,
|
||||
/client/proc/cmd_show_at_list,
|
||||
/client/proc/cmd_show_at_markers,
|
||||
/client/proc/manipulate_organs,
|
||||
/client/proc/start_line_profiling,
|
||||
/client/proc/stop_line_profiling,
|
||||
/client/proc/show_line_profiling,
|
||||
/client/proc/create_mapping_job_icons,
|
||||
/client/proc/debug_z_levels,
|
||||
/client/proc/place_ruin
|
||||
))
|
||||
GLOBAL_PROTECT(admin_verbs_debug_mapping)
|
||||
|
||||
/obj/effect/debugging/mapfix_marker
|
||||
name = "map fix marker"
|
||||
icon = 'icons/mob/screen_gen.dmi'
|
||||
icon_state = "mapfixmarker"
|
||||
desc = "I am a mappers mistake."
|
||||
|
||||
/obj/effect/debugging/marker
|
||||
icon = 'icons/turf/areas.dmi'
|
||||
icon_state = "yellow"
|
||||
|
||||
/obj/effect/debugging/marker/Move()
|
||||
return 0
|
||||
|
||||
/client/proc/camera_view()
|
||||
set category = "Mapping"
|
||||
set name = "Camera Range Display"
|
||||
|
||||
var/on = FALSE
|
||||
for(var/turf/T in world)
|
||||
if(T.maptext)
|
||||
on = TRUE
|
||||
T.maptext = null
|
||||
|
||||
if(!on)
|
||||
var/list/seen = list()
|
||||
for(var/obj/machinery/camera/C in GLOB.cameranet.cameras)
|
||||
for(var/turf/T in C.can_see())
|
||||
seen[T]++
|
||||
for(var/turf/T in seen)
|
||||
T.maptext = "[seen[T]]"
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Show Camera Range") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Show Camera Range")
|
||||
|
||||
#ifdef TESTING
|
||||
GLOBAL_LIST_EMPTY(dirty_vars)
|
||||
|
||||
/client/proc/see_dirty_varedits()
|
||||
set category = "Mapping"
|
||||
set name = "Dirty Varedits"
|
||||
|
||||
var/list/dat = list()
|
||||
dat += "<h3>Abandon all hope ye who enter here</h3><br><br>"
|
||||
for(var/thing in GLOB.dirty_vars)
|
||||
dat += "[thing]<br>"
|
||||
CHECK_TICK
|
||||
var/datum/browser/popup = new(usr, "dirty_vars", "Dirty Varedits", 900, 750)
|
||||
popup.set_content(dat.Join())
|
||||
popup.open()
|
||||
#endif
|
||||
|
||||
/client/proc/sec_camera_report()
|
||||
set category = "Mapping"
|
||||
set name = "Camera Report"
|
||||
|
||||
if(!Master)
|
||||
alert(usr,"Master_controller not found.","Sec Camera Report")
|
||||
return 0
|
||||
|
||||
var/list/obj/machinery/camera/CL = list()
|
||||
|
||||
for(var/obj/machinery/camera/C in GLOB.cameranet.cameras)
|
||||
CL += C
|
||||
|
||||
var/output = {"<B>Camera Abnormalities Report</B><HR>
|
||||
<B>The following abnormalities have been detected. The ones in red need immediate attention: Some of those in black may be intentional.</B><BR><ul>"}
|
||||
|
||||
for(var/obj/machinery/camera/C1 in CL)
|
||||
for(var/obj/machinery/camera/C2 in CL)
|
||||
if(C1 != C2)
|
||||
if(C1.c_tag == C2.c_tag)
|
||||
output += "<li><font color='red'>c_tag match for cameras at [ADMIN_VERBOSEJMP(C1)] and [ADMIN_VERBOSEJMP(C2)] - c_tag is [C1.c_tag]</font></li>"
|
||||
if(C1.loc == C2.loc && C1.dir == C2.dir && C1.pixel_x == C2.pixel_x && C1.pixel_y == C2.pixel_y)
|
||||
output += "<li><font color='red'>FULLY overlapping cameras at [ADMIN_VERBOSEJMP(C1)] Networks: [json_encode(C1.network)] and [json_encode(C2.network)]</font></li>"
|
||||
if(C1.loc == C2.loc)
|
||||
output += "<li>Overlapping cameras at [ADMIN_VERBOSEJMP(C1)] Networks: [json_encode(C1.network)] and [json_encode(C2.network)]</li>"
|
||||
var/turf/T = get_step(C1,turn(C1.dir,180))
|
||||
if(!T || !isturf(T) || !T.density )
|
||||
if(!(locate(/obj/structure/grille) in T))
|
||||
var/window_check = 0
|
||||
for(var/obj/structure/window/W in T)
|
||||
if (W.dir == turn(C1.dir,180) || (W.dir in list(NORTHEAST,SOUTHEAST,NORTHWEST,SOUTHWEST)) )
|
||||
window_check = 1
|
||||
break
|
||||
if(!window_check)
|
||||
output += "<li><font color='red'>Camera not connected to wall at [ADMIN_VERBOSEJMP(C1)] Network: [json_encode(C1.network)]</font></li>"
|
||||
|
||||
output += "</ul>"
|
||||
usr << browse(output,"window=airreport;size=1000x500")
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Show Camera Report") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
/client/proc/intercom_view()
|
||||
set category = "Mapping"
|
||||
set name = "Intercom Range Display"
|
||||
|
||||
var/static/intercom_range_display_status = FALSE
|
||||
intercom_range_display_status = !intercom_range_display_status //blame cyberboss if this breaks something
|
||||
|
||||
for(var/obj/effect/debugging/marker/M in world)
|
||||
qdel(M)
|
||||
|
||||
if(intercom_range_display_status)
|
||||
for(var/obj/item/radio/intercom/I in world)
|
||||
for(var/turf/T in orange(7,I))
|
||||
var/obj/effect/debugging/marker/F = new/obj/effect/debugging/marker(T)
|
||||
if (!(F in view(7,I.loc)))
|
||||
qdel(F)
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Show Intercom Range") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
/client/proc/cmd_show_at_list()
|
||||
set category = "Mapping"
|
||||
set name = "Show roundstart AT list"
|
||||
set desc = "Displays a list of active turfs coordinates at roundstart"
|
||||
|
||||
var/dat = {"<b>Coordinate list of Active Turfs at Roundstart</b>
|
||||
<br>Real-time Active Turfs list you can see in Air Subsystem at active_turfs var<br>"}
|
||||
|
||||
for(var/t in GLOB.active_turfs_startlist)
|
||||
var/turf/T = t
|
||||
dat += "[ADMIN_VERBOSEJMP(T)]\n"
|
||||
dat += "<br>"
|
||||
|
||||
usr << browse(dat, "window=at_list")
|
||||
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Show Roundstart Active Turfs") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
/client/proc/cmd_show_at_markers()
|
||||
set category = "Mapping"
|
||||
set name = "Show roundstart AT markers"
|
||||
set desc = "Places a marker on all active-at-roundstart turfs"
|
||||
|
||||
var/count = 0
|
||||
for(var/obj/effect/abstract/marker/at/AT in GLOB.all_abstract_markers)
|
||||
qdel(AT)
|
||||
count++
|
||||
|
||||
if(count)
|
||||
to_chat(usr, "[count] AT markers removed.", confidential = TRUE)
|
||||
else
|
||||
for(var/t in GLOB.active_turfs_startlist)
|
||||
new /obj/effect/abstract/marker/at(t)
|
||||
count++
|
||||
to_chat(usr, "[count] AT markers placed.", confidential = TRUE)
|
||||
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Show Roundstart Active Turf Markers")
|
||||
|
||||
/client/proc/enable_debug_verbs()
|
||||
set category = "Debug"
|
||||
set name = "Debug verbs - Enable"
|
||||
if(!check_rights(R_DEBUG))
|
||||
return
|
||||
verbs -= /client/proc/enable_debug_verbs
|
||||
verbs.Add(/client/proc/disable_debug_verbs, GLOB.admin_verbs_debug_mapping)
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Enable Debug Verbs") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
/client/proc/disable_debug_verbs()
|
||||
set category = "Debug"
|
||||
set name = "Debug verbs - Disable"
|
||||
verbs.Remove(/client/proc/disable_debug_verbs, GLOB.admin_verbs_debug_mapping)
|
||||
verbs += /client/proc/enable_debug_verbs
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Disable Debug Verbs") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
/client/proc/count_objects_on_z_level()
|
||||
set category = "Mapping"
|
||||
set name = "Count Objects On Level"
|
||||
var/level = input("Which z-level?","Level?") as text|null
|
||||
if(!level)
|
||||
return
|
||||
var/num_level = text2num(level)
|
||||
if(!num_level)
|
||||
return
|
||||
if(!isnum(num_level))
|
||||
return
|
||||
|
||||
var/type_text = input("Which type path?","Path?") as text|null
|
||||
if(!type_text)
|
||||
return
|
||||
var/type_path = text2path(type_text)
|
||||
if(!type_path)
|
||||
return
|
||||
|
||||
var/count = 0
|
||||
|
||||
var/list/atom/atom_list = list()
|
||||
|
||||
for(var/atom/A in world)
|
||||
if(istype(A,type_path))
|
||||
var/atom/B = A
|
||||
while(!(isturf(B.loc)))
|
||||
if(B && B.loc)
|
||||
B = B.loc
|
||||
else
|
||||
break
|
||||
if(B)
|
||||
if(B.z == num_level)
|
||||
count++
|
||||
atom_list += A
|
||||
|
||||
to_chat(world, "There are [count] objects of type [type_path] on z-level [num_level]", confidential = TRUE)
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Count Objects Zlevel") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
/client/proc/count_objects_all()
|
||||
set category = "Mapping"
|
||||
set name = "Count Objects All"
|
||||
|
||||
var/type_text = input("Which type path?","") as text|null
|
||||
if(!type_text)
|
||||
return
|
||||
var/type_path = text2path(type_text)
|
||||
if(!type_path)
|
||||
return
|
||||
|
||||
var/count = 0
|
||||
|
||||
for(var/atom/A in world)
|
||||
if(istype(A,type_path))
|
||||
count++
|
||||
|
||||
to_chat(world, "There are [count] objects of type [type_path] in the game world", confidential = TRUE)
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Count Objects All") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
|
||||
//This proc is intended to detect lag problems relating to communication procs
|
||||
GLOBAL_VAR_INIT(say_disabled, FALSE)
|
||||
/client/proc/disable_communication()
|
||||
set category = "Mapping"
|
||||
set name = "Disable all communication verbs"
|
||||
|
||||
GLOB.say_disabled = !GLOB.say_disabled
|
||||
if(GLOB.say_disabled)
|
||||
message_admins("[key] used 'Disable all communication verbs', killing all communication methods.")
|
||||
else
|
||||
message_admins("[key] used 'Disable all communication verbs', restoring all communication methods.")
|
||||
|
||||
//This generates the icon states for job starting location landmarks.
|
||||
/client/proc/create_mapping_job_icons()
|
||||
set name = "Generate job landmarks icons"
|
||||
set category = "Mapping"
|
||||
var/icon/final = icon()
|
||||
var/mob/living/carbon/human/dummy/D = new(locate(1,1,1)) //spawn on 1,1,1 so we don't have runtimes when items are deleted
|
||||
D.setDir(SOUTH)
|
||||
for(var/job in subtypesof(/datum/job))
|
||||
var/datum/job/JB = new job
|
||||
switch(JB.title)
|
||||
if("AI")
|
||||
final.Insert(icon('icons/mob/ai.dmi', "ai", SOUTH, 1), "AI")
|
||||
if("Cyborg")
|
||||
final.Insert(icon('icons/mob/robots.dmi', "robot", SOUTH, 1), "Cyborg")
|
||||
else
|
||||
for(var/obj/item/I in D)
|
||||
qdel(I)
|
||||
randomize_human(D)
|
||||
JB.equip(D, TRUE, FALSE)
|
||||
COMPILE_OVERLAYS(D)
|
||||
var/icon/I = icon(getFlatIcon(D), frame = 1)
|
||||
final.Insert(I, JB.title)
|
||||
qdel(D)
|
||||
//Also add the x
|
||||
for(var/x_number in 1 to 4)
|
||||
final.Insert(icon('icons/mob/screen_gen.dmi', "x[x_number == 1 ? "" : x_number]"), "x[x_number == 1 ? "" : x_number]")
|
||||
fcopy(final, "icons/mob/landmarks.dmi")
|
||||
|
||||
/client/proc/debug_z_levels()
|
||||
set name = "Debug Z-Levels"
|
||||
set category = "Mapping"
|
||||
|
||||
var/list/z_list = SSmapping.z_list
|
||||
var/list/messages = list()
|
||||
messages += "<b>World</b>: [world.maxx] x [world.maxy] x [world.maxz]<br>"
|
||||
|
||||
var/list/linked_levels = list()
|
||||
var/min_x = INFINITY
|
||||
var/min_y = INFINITY
|
||||
var/max_x = -INFINITY
|
||||
var/max_y = -INFINITY
|
||||
|
||||
for(var/z in 1 to max(world.maxz, z_list.len))
|
||||
if (z > z_list.len)
|
||||
messages += "<b>[z]</b>: Unmanaged (out of bounds)<br>"
|
||||
continue
|
||||
var/datum/space_level/S = z_list[z]
|
||||
if (!S)
|
||||
messages += "<b>[z]</b>: Unmanaged (null)<br>"
|
||||
continue
|
||||
var/linkage
|
||||
switch (S.linkage)
|
||||
if (UNAFFECTED)
|
||||
linkage = "no linkage"
|
||||
if (SELFLOOPING)
|
||||
linkage = "self-looping"
|
||||
if (CROSSLINKED)
|
||||
linkage = "linked at ([S.xi], [S.yi])"
|
||||
linked_levels += S
|
||||
min_x = min(min_x, S.xi)
|
||||
min_y = min(min_y, S.yi)
|
||||
max_x = max(max_x, S.xi)
|
||||
max_y = max(max_y, S.yi)
|
||||
else
|
||||
linkage = "unknown linkage '[S.linkage]'"
|
||||
|
||||
messages += "<b>[z]</b>: [S.name], [linkage], traits: [json_encode(S.traits)]<br>"
|
||||
if (S.z_value != z)
|
||||
messages += "-- z_value is [S.z_value], should be [z]<br>"
|
||||
if (S.name == initial(S.name))
|
||||
messages += "-- name not set<br>"
|
||||
if (z > world.maxz)
|
||||
messages += "-- exceeds max z"
|
||||
|
||||
var/grid[max_x - min_x + 1][max_y - min_y + 1]
|
||||
for(var/datum/space_level/S in linked_levels)
|
||||
grid[S.xi - min_x + 1][S.yi - min_y + 1] = S.z_value
|
||||
|
||||
messages += "<table border='1'>"
|
||||
for(var/y in max_y to min_y step -1)
|
||||
var/list/part = list()
|
||||
for(var/x in min_x to max_x)
|
||||
part += "[grid[x - min_x + 1][y - min_y + 1]]"
|
||||
messages += "<tr><td>[part.Join("</td><td>")]</td></tr>"
|
||||
messages += "</table>"
|
||||
|
||||
to_chat(src, messages.Join(""), confidential = TRUE)
|
||||
//- Are all the floors with or without air, as they should be? (regular or airless)
|
||||
//- Does the area have an APC?
|
||||
//- Does the area have an Air Alarm?
|
||||
//- Does the area have a Request Console?
|
||||
//- Does the area have lights?
|
||||
//- Does the area have a light switch?
|
||||
//- Does the area have enough intercoms?
|
||||
//- Does the area have enough security cameras? (Use the 'Camera Range Display' verb under Debug)
|
||||
//- Is the area connected to the scrubbers air loop?
|
||||
//- Is the area connected to the vent air loop? (vent pumps)
|
||||
//- Is everything wired properly?
|
||||
//- Does the area have a fire alarm and firedoors?
|
||||
//- Do all pod doors work properly?
|
||||
//- Are accesses set properly on doors, pod buttons, etc.
|
||||
//- Are all items placed properly? (not below vents, scrubbers, tables)
|
||||
//- Does the disposal system work properly from all the disposal units in this room and all the units, the pipes of which pass through this room?
|
||||
//- Check for any misplaced or stacked piece of pipe (air and disposal)
|
||||
//- Check for any misplaced or stacked piece of wire
|
||||
//- Identify how hard it is to break into the area and where the weak points are
|
||||
//- Check if the area has too much empty space. If so, make it smaller and replace the rest with maintenance tunnels.
|
||||
|
||||
GLOBAL_LIST_INIT(admin_verbs_debug_mapping, list(
|
||||
/client/proc/camera_view, //-errorage
|
||||
/client/proc/sec_camera_report, //-errorage
|
||||
/client/proc/intercom_view, //-errorage
|
||||
/client/proc/air_status, //Air things
|
||||
/client/proc/Cell, //More air things
|
||||
/client/proc/atmosscan, //check plumbing
|
||||
/client/proc/powerdebug, //check power
|
||||
/client/proc/count_objects_on_z_level,
|
||||
/client/proc/count_objects_all,
|
||||
/client/proc/cmd_assume_direct_control, //-errorage
|
||||
/client/proc/cmd_give_direct_control,
|
||||
/client/proc/startSinglo,
|
||||
/client/proc/set_server_fps, //allows you to set the ticklag.
|
||||
/client/proc/cmd_admin_grantfullaccess,
|
||||
/client/proc/cmd_admin_areatest_all,
|
||||
/client/proc/cmd_admin_areatest_station,
|
||||
#ifdef TESTING
|
||||
/client/proc/see_dirty_varedits,
|
||||
#endif
|
||||
/client/proc/cmd_admin_test_atmos_controllers,
|
||||
/client/proc/cmd_admin_rejuvenate,
|
||||
/datum/admins/proc/show_traitor_panel,
|
||||
/client/proc/disable_communication,
|
||||
/client/proc/cmd_show_at_list,
|
||||
/client/proc/cmd_show_at_markers,
|
||||
/client/proc/manipulate_organs,
|
||||
/client/proc/start_line_profiling,
|
||||
/client/proc/stop_line_profiling,
|
||||
/client/proc/show_line_profiling,
|
||||
/client/proc/create_mapping_job_icons,
|
||||
/client/proc/debug_z_levels,
|
||||
/client/proc/place_ruin
|
||||
))
|
||||
GLOBAL_PROTECT(admin_verbs_debug_mapping)
|
||||
|
||||
/obj/effect/debugging/mapfix_marker
|
||||
name = "map fix marker"
|
||||
icon = 'icons/mob/screen_gen.dmi'
|
||||
icon_state = "mapfixmarker"
|
||||
desc = "I am a mappers mistake."
|
||||
|
||||
/obj/effect/debugging/marker
|
||||
icon = 'icons/turf/areas.dmi'
|
||||
icon_state = "yellow"
|
||||
|
||||
/obj/effect/debugging/marker/Move()
|
||||
return 0
|
||||
|
||||
/client/proc/camera_view()
|
||||
set category = "Mapping"
|
||||
set name = "Camera Range Display"
|
||||
|
||||
var/on = FALSE
|
||||
for(var/turf/T in world)
|
||||
if(T.maptext)
|
||||
on = TRUE
|
||||
T.maptext = null
|
||||
|
||||
if(!on)
|
||||
var/list/seen = list()
|
||||
for(var/obj/machinery/camera/C in GLOB.cameranet.cameras)
|
||||
for(var/turf/T in C.can_see())
|
||||
seen[T]++
|
||||
for(var/turf/T in seen)
|
||||
T.maptext = "[seen[T]]"
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Show Camera Range") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Show Camera Range")
|
||||
|
||||
#ifdef TESTING
|
||||
GLOBAL_LIST_EMPTY(dirty_vars)
|
||||
|
||||
/client/proc/see_dirty_varedits()
|
||||
set category = "Mapping"
|
||||
set name = "Dirty Varedits"
|
||||
|
||||
var/list/dat = list()
|
||||
dat += "<h3>Abandon all hope ye who enter here</h3><br><br>"
|
||||
for(var/thing in GLOB.dirty_vars)
|
||||
dat += "[thing]<br>"
|
||||
CHECK_TICK
|
||||
var/datum/browser/popup = new(usr, "dirty_vars", "Dirty Varedits", 900, 750)
|
||||
popup.set_content(dat.Join())
|
||||
popup.open()
|
||||
#endif
|
||||
|
||||
/client/proc/sec_camera_report()
|
||||
set category = "Mapping"
|
||||
set name = "Camera Report"
|
||||
|
||||
if(!Master)
|
||||
alert(usr,"Master_controller not found.","Sec Camera Report")
|
||||
return 0
|
||||
|
||||
var/list/obj/machinery/camera/CL = list()
|
||||
|
||||
for(var/obj/machinery/camera/C in GLOB.cameranet.cameras)
|
||||
CL += C
|
||||
|
||||
var/output = {"<B>Camera Abnormalities Report</B><HR>
|
||||
<B>The following abnormalities have been detected. The ones in red need immediate attention: Some of those in black may be intentional.</B><BR><ul>"}
|
||||
|
||||
for(var/obj/machinery/camera/C1 in CL)
|
||||
for(var/obj/machinery/camera/C2 in CL)
|
||||
if(C1 != C2)
|
||||
if(C1.c_tag == C2.c_tag)
|
||||
output += "<li><font color='red'>c_tag match for cameras at [ADMIN_VERBOSEJMP(C1)] and [ADMIN_VERBOSEJMP(C2)] - c_tag is [C1.c_tag]</font></li>"
|
||||
if(C1.loc == C2.loc && C1.dir == C2.dir && C1.pixel_x == C2.pixel_x && C1.pixel_y == C2.pixel_y)
|
||||
output += "<li><font color='red'>FULLY overlapping cameras at [ADMIN_VERBOSEJMP(C1)] Networks: [json_encode(C1.network)] and [json_encode(C2.network)]</font></li>"
|
||||
if(C1.loc == C2.loc)
|
||||
output += "<li>Overlapping cameras at [ADMIN_VERBOSEJMP(C1)] Networks: [json_encode(C1.network)] and [json_encode(C2.network)]</li>"
|
||||
var/turf/T = get_step(C1,turn(C1.dir,180))
|
||||
if(!T || !isturf(T) || !T.density )
|
||||
if(!(locate(/obj/structure/grille) in T))
|
||||
var/window_check = 0
|
||||
for(var/obj/structure/window/W in T)
|
||||
if (W.dir == turn(C1.dir,180) || (W.dir in list(NORTHEAST,SOUTHEAST,NORTHWEST,SOUTHWEST)) )
|
||||
window_check = 1
|
||||
break
|
||||
if(!window_check)
|
||||
output += "<li><font color='red'>Camera not connected to wall at [ADMIN_VERBOSEJMP(C1)] Network: [json_encode(C1.network)]</font></li>"
|
||||
|
||||
output += "</ul>"
|
||||
usr << browse(output,"window=airreport;size=1000x500")
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Show Camera Report") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
/client/proc/intercom_view()
|
||||
set category = "Mapping"
|
||||
set name = "Intercom Range Display"
|
||||
|
||||
var/static/intercom_range_display_status = FALSE
|
||||
intercom_range_display_status = !intercom_range_display_status //blame cyberboss if this breaks something
|
||||
|
||||
for(var/obj/effect/debugging/marker/M in world)
|
||||
qdel(M)
|
||||
|
||||
if(intercom_range_display_status)
|
||||
for(var/obj/item/radio/intercom/I in world)
|
||||
for(var/turf/T in orange(7,I))
|
||||
var/obj/effect/debugging/marker/F = new/obj/effect/debugging/marker(T)
|
||||
if (!(F in view(7,I.loc)))
|
||||
qdel(F)
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Show Intercom Range") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
/client/proc/cmd_show_at_list()
|
||||
set category = "Mapping"
|
||||
set name = "Show roundstart AT list"
|
||||
set desc = "Displays a list of active turfs coordinates at roundstart"
|
||||
|
||||
var/dat = {"<b>Coordinate list of Active Turfs at Roundstart</b>
|
||||
<br>Real-time Active Turfs list you can see in Air Subsystem at active_turfs var<br>"}
|
||||
|
||||
for(var/t in GLOB.active_turfs_startlist)
|
||||
var/turf/T = t
|
||||
dat += "[ADMIN_VERBOSEJMP(T)]\n"
|
||||
dat += "<br>"
|
||||
|
||||
usr << browse(dat, "window=at_list")
|
||||
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Show Roundstart Active Turfs") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
/client/proc/cmd_show_at_markers()
|
||||
set category = "Mapping"
|
||||
set name = "Show roundstart AT markers"
|
||||
set desc = "Places a marker on all active-at-roundstart turfs"
|
||||
|
||||
var/count = 0
|
||||
for(var/obj/effect/abstract/marker/at/AT in GLOB.all_abstract_markers)
|
||||
qdel(AT)
|
||||
count++
|
||||
|
||||
if(count)
|
||||
to_chat(usr, "[count] AT markers removed.", confidential = TRUE)
|
||||
else
|
||||
for(var/t in GLOB.active_turfs_startlist)
|
||||
new /obj/effect/abstract/marker/at(t)
|
||||
count++
|
||||
to_chat(usr, "[count] AT markers placed.", confidential = TRUE)
|
||||
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Show Roundstart Active Turf Markers")
|
||||
|
||||
/client/proc/enable_debug_verbs()
|
||||
set category = "Debug"
|
||||
set name = "Debug verbs - Enable"
|
||||
if(!check_rights(R_DEBUG))
|
||||
return
|
||||
verbs -= /client/proc/enable_debug_verbs
|
||||
verbs.Add(/client/proc/disable_debug_verbs, GLOB.admin_verbs_debug_mapping)
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Enable Debug Verbs") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
/client/proc/disable_debug_verbs()
|
||||
set category = "Debug"
|
||||
set name = "Debug verbs - Disable"
|
||||
verbs.Remove(/client/proc/disable_debug_verbs, GLOB.admin_verbs_debug_mapping)
|
||||
verbs += /client/proc/enable_debug_verbs
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Disable Debug Verbs") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
/client/proc/count_objects_on_z_level()
|
||||
set category = "Mapping"
|
||||
set name = "Count Objects On Level"
|
||||
var/level = input("Which z-level?","Level?") as text|null
|
||||
if(!level)
|
||||
return
|
||||
var/num_level = text2num(level)
|
||||
if(!num_level)
|
||||
return
|
||||
if(!isnum(num_level))
|
||||
return
|
||||
|
||||
var/type_text = input("Which type path?","Path?") as text|null
|
||||
if(!type_text)
|
||||
return
|
||||
var/type_path = text2path(type_text)
|
||||
if(!type_path)
|
||||
return
|
||||
|
||||
var/count = 0
|
||||
|
||||
var/list/atom/atom_list = list()
|
||||
|
||||
for(var/atom/A in world)
|
||||
if(istype(A,type_path))
|
||||
var/atom/B = A
|
||||
while(!(isturf(B.loc)))
|
||||
if(B && B.loc)
|
||||
B = B.loc
|
||||
else
|
||||
break
|
||||
if(B)
|
||||
if(B.z == num_level)
|
||||
count++
|
||||
atom_list += A
|
||||
|
||||
to_chat(world, "There are [count] objects of type [type_path] on z-level [num_level]", confidential = TRUE)
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Count Objects Zlevel") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
/client/proc/count_objects_all()
|
||||
set category = "Mapping"
|
||||
set name = "Count Objects All"
|
||||
|
||||
var/type_text = input("Which type path?","") as text|null
|
||||
if(!type_text)
|
||||
return
|
||||
var/type_path = text2path(type_text)
|
||||
if(!type_path)
|
||||
return
|
||||
|
||||
var/count = 0
|
||||
|
||||
for(var/atom/A in world)
|
||||
if(istype(A,type_path))
|
||||
count++
|
||||
|
||||
to_chat(world, "There are [count] objects of type [type_path] in the game world", confidential = TRUE)
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Count Objects All") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
|
||||
//This proc is intended to detect lag problems relating to communication procs
|
||||
GLOBAL_VAR_INIT(say_disabled, FALSE)
|
||||
/client/proc/disable_communication()
|
||||
set category = "Mapping"
|
||||
set name = "Disable all communication verbs"
|
||||
|
||||
GLOB.say_disabled = !GLOB.say_disabled
|
||||
if(GLOB.say_disabled)
|
||||
message_admins("[key] used 'Disable all communication verbs', killing all communication methods.")
|
||||
else
|
||||
message_admins("[key] used 'Disable all communication verbs', restoring all communication methods.")
|
||||
|
||||
//This generates the icon states for job starting location landmarks.
|
||||
/client/proc/create_mapping_job_icons()
|
||||
set name = "Generate job landmarks icons"
|
||||
set category = "Mapping"
|
||||
var/icon/final = icon()
|
||||
var/mob/living/carbon/human/dummy/D = new(locate(1,1,1)) //spawn on 1,1,1 so we don't have runtimes when items are deleted
|
||||
D.setDir(SOUTH)
|
||||
for(var/job in subtypesof(/datum/job))
|
||||
var/datum/job/JB = new job
|
||||
switch(JB.title)
|
||||
if("AI")
|
||||
final.Insert(icon('icons/mob/ai.dmi', "ai", SOUTH, 1), "AI")
|
||||
if("Cyborg")
|
||||
final.Insert(icon('icons/mob/robots.dmi', "robot", SOUTH, 1), "Cyborg")
|
||||
else
|
||||
for(var/obj/item/I in D)
|
||||
qdel(I)
|
||||
randomize_human(D)
|
||||
JB.equip(D, TRUE, FALSE)
|
||||
COMPILE_OVERLAYS(D)
|
||||
var/icon/I = icon(getFlatIcon(D), frame = 1)
|
||||
final.Insert(I, JB.title)
|
||||
qdel(D)
|
||||
//Also add the x
|
||||
for(var/x_number in 1 to 4)
|
||||
final.Insert(icon('icons/mob/screen_gen.dmi', "x[x_number == 1 ? "" : x_number]"), "x[x_number == 1 ? "" : x_number]")
|
||||
fcopy(final, "icons/mob/landmarks.dmi")
|
||||
|
||||
/client/proc/debug_z_levels()
|
||||
set name = "Debug Z-Levels"
|
||||
set category = "Mapping"
|
||||
|
||||
var/list/z_list = SSmapping.z_list
|
||||
var/list/messages = list()
|
||||
messages += "<b>World</b>: [world.maxx] x [world.maxy] x [world.maxz]<br>"
|
||||
|
||||
var/list/linked_levels = list()
|
||||
var/min_x = INFINITY
|
||||
var/min_y = INFINITY
|
||||
var/max_x = -INFINITY
|
||||
var/max_y = -INFINITY
|
||||
|
||||
for(var/z in 1 to max(world.maxz, z_list.len))
|
||||
if (z > z_list.len)
|
||||
messages += "<b>[z]</b>: Unmanaged (out of bounds)<br>"
|
||||
continue
|
||||
var/datum/space_level/S = z_list[z]
|
||||
if (!S)
|
||||
messages += "<b>[z]</b>: Unmanaged (null)<br>"
|
||||
continue
|
||||
var/linkage
|
||||
switch (S.linkage)
|
||||
if (UNAFFECTED)
|
||||
linkage = "no linkage"
|
||||
if (SELFLOOPING)
|
||||
linkage = "self-looping"
|
||||
if (CROSSLINKED)
|
||||
linkage = "linked at ([S.xi], [S.yi])"
|
||||
linked_levels += S
|
||||
min_x = min(min_x, S.xi)
|
||||
min_y = min(min_y, S.yi)
|
||||
max_x = max(max_x, S.xi)
|
||||
max_y = max(max_y, S.yi)
|
||||
else
|
||||
linkage = "unknown linkage '[S.linkage]'"
|
||||
|
||||
messages += "<b>[z]</b>: [S.name], [linkage], traits: [json_encode(S.traits)]<br>"
|
||||
if (S.z_value != z)
|
||||
messages += "-- z_value is [S.z_value], should be [z]<br>"
|
||||
if (S.name == initial(S.name))
|
||||
messages += "-- name not set<br>"
|
||||
if (z > world.maxz)
|
||||
messages += "-- exceeds max z"
|
||||
|
||||
var/grid[max_x - min_x + 1][max_y - min_y + 1]
|
||||
for(var/datum/space_level/S in linked_levels)
|
||||
grid[S.xi - min_x + 1][S.yi - min_y + 1] = S.z_value
|
||||
|
||||
messages += "<table border='1'>"
|
||||
for(var/y in max_y to min_y step -1)
|
||||
var/list/part = list()
|
||||
for(var/x in min_x to max_x)
|
||||
part += "[grid[x - min_x + 1][y - min_y + 1]]"
|
||||
messages += "<tr><td>[part.Join("</td><td>")]</td></tr>"
|
||||
messages += "</table>"
|
||||
|
||||
to_chat(src, messages.Join(""), confidential = TRUE)
|
||||
|
||||
@@ -1,31 +1,31 @@
|
||||
GLOBAL_VAR_INIT(highlander, FALSE)
|
||||
/client/proc/only_one() //Gives everyone kilts, berets, claymores, and pinpointers, with the objective to hijack the emergency shuttle.
|
||||
if(!SSticker.HasRoundStarted())
|
||||
alert("The game hasn't started yet!")
|
||||
return
|
||||
GLOB.highlander = TRUE
|
||||
|
||||
send_to_playing_players("<span class='boldannounce'><font size=6>THERE CAN BE ONLY ONE</font></span>")
|
||||
|
||||
for(var/obj/item/disk/nuclear/N in GLOB.poi_list)
|
||||
var/datum/component/stationloving/component = N.GetComponent(/datum/component/stationloving)
|
||||
if (component)
|
||||
component.relocate() //Gets it out of bags and such
|
||||
|
||||
for(var/mob/living/carbon/human/H in GLOB.player_list)
|
||||
if(H.stat == DEAD)
|
||||
continue
|
||||
H.make_scottish()
|
||||
|
||||
message_admins("<span class='adminnotice'>[key_name_admin(usr)] used THERE CAN BE ONLY ONE!</span>")
|
||||
log_admin("[key_name(usr)] used THERE CAN BE ONLY ONE.")
|
||||
addtimer(CALLBACK(SSshuttle.emergency, /obj/docking_port/mobile/emergency.proc/request, null, 1), 50)
|
||||
|
||||
/client/proc/only_one_delayed()
|
||||
send_to_playing_players("<span class='userdanger'>Bagpipes begin to blare. You feel Scottish pride coming over you.</span>")
|
||||
message_admins("<span class='adminnotice'>[key_name_admin(usr)] used (delayed) THERE CAN BE ONLY ONE!</span>")
|
||||
log_admin("[key_name(usr)] used delayed THERE CAN BE ONLY ONE.")
|
||||
addtimer(CALLBACK(src, .proc/only_one), 420)
|
||||
|
||||
/mob/living/carbon/human/proc/make_scottish()
|
||||
mind.add_antag_datum(/datum/antagonist/highlander)
|
||||
GLOBAL_VAR_INIT(highlander, FALSE)
|
||||
/client/proc/only_one() //Gives everyone kilts, berets, claymores, and pinpointers, with the objective to hijack the emergency shuttle.
|
||||
if(!SSticker.HasRoundStarted())
|
||||
alert("The game hasn't started yet!")
|
||||
return
|
||||
GLOB.highlander = TRUE
|
||||
|
||||
send_to_playing_players("<span class='boldannounce'><font size=6>THERE CAN BE ONLY ONE</font></span>")
|
||||
|
||||
for(var/obj/item/disk/nuclear/N in GLOB.poi_list)
|
||||
var/datum/component/stationloving/component = N.GetComponent(/datum/component/stationloving)
|
||||
if (component)
|
||||
component.relocate() //Gets it out of bags and such
|
||||
|
||||
for(var/mob/living/carbon/human/H in GLOB.player_list)
|
||||
if(H.stat == DEAD)
|
||||
continue
|
||||
H.make_scottish()
|
||||
|
||||
message_admins("<span class='adminnotice'>[key_name_admin(usr)] used THERE CAN BE ONLY ONE!</span>")
|
||||
log_admin("[key_name(usr)] used THERE CAN BE ONLY ONE.")
|
||||
addtimer(CALLBACK(SSshuttle.emergency, /obj/docking_port/mobile/emergency.proc/request, null, 1), 50)
|
||||
|
||||
/client/proc/only_one_delayed()
|
||||
send_to_playing_players("<span class='userdanger'>Bagpipes begin to blare. You feel Scottish pride coming over you.</span>")
|
||||
message_admins("<span class='adminnotice'>[key_name_admin(usr)] used (delayed) THERE CAN BE ONLY ONE!</span>")
|
||||
log_admin("[key_name(usr)] used delayed THERE CAN BE ONLY ONE.")
|
||||
addtimer(CALLBACK(src, .proc/only_one), 420)
|
||||
|
||||
/mob/living/carbon/human/proc/make_scottish()
|
||||
mind.add_antag_datum(/datum/antagonist/highlander)
|
||||
|
||||
@@ -1,177 +1,177 @@
|
||||
/client/proc/play_sound(S as sound)
|
||||
set category = "Fun"
|
||||
set name = "Play Global Sound"
|
||||
if(!check_rights(R_SOUND))
|
||||
return
|
||||
|
||||
var/freq = 1
|
||||
var/vol = input(usr, "What volume would you like the sound to play at?",, 100) as null|num
|
||||
if(!vol)
|
||||
return
|
||||
vol = clamp(vol, 1, 100)
|
||||
|
||||
var/sound/admin_sound = new()
|
||||
admin_sound.file = S
|
||||
admin_sound.priority = 250
|
||||
admin_sound.channel = CHANNEL_ADMIN
|
||||
admin_sound.frequency = freq
|
||||
admin_sound.wait = 1
|
||||
admin_sound.repeat = 0
|
||||
admin_sound.status = SOUND_STREAM
|
||||
admin_sound.volume = vol
|
||||
|
||||
var/res = alert(usr, "Show the title of this song to the players?",, "Yes","No", "Cancel")
|
||||
switch(res)
|
||||
if("Yes")
|
||||
to_chat(world, "<span class='boldannounce'>An admin played: [S]</span>", confidential = TRUE)
|
||||
if("Cancel")
|
||||
return
|
||||
|
||||
log_admin("[key_name(src)] played sound [S]")
|
||||
message_admins("[key_name_admin(src)] played sound [S]")
|
||||
|
||||
for(var/mob/M in GLOB.player_list)
|
||||
if(M.client.prefs.toggles & SOUND_MIDI)
|
||||
var/user_vol = M.client.chatOutput.adminMusicVolume
|
||||
if(user_vol)
|
||||
admin_sound.volume = vol * (user_vol / 100)
|
||||
SEND_SOUND(M, admin_sound)
|
||||
admin_sound.volume = vol
|
||||
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Play Global Sound") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
|
||||
/client/proc/play_local_sound(S as sound)
|
||||
set category = "Fun"
|
||||
set name = "Play Local Sound"
|
||||
if(!check_rights(R_SOUND))
|
||||
return
|
||||
|
||||
log_admin("[key_name(src)] played a local sound [S]")
|
||||
message_admins("[key_name_admin(src)] played a local sound [S]")
|
||||
playsound(get_turf(src.mob), S, 50, FALSE, FALSE)
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Play Local Sound") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
/client/proc/play_direct_mob_sound(S as sound, mob/M)
|
||||
set category = "Fun"
|
||||
set name = "Play Direct Mob Sound"
|
||||
if(!check_rights(R_SOUND))
|
||||
return
|
||||
|
||||
if(!M)
|
||||
M = input(usr, "Choose a mob to play the sound to. Only they will hear it.", "Play Mob Sound") as null|anything in sortNames(GLOB.player_list)
|
||||
if(!M || QDELETED(M))
|
||||
return
|
||||
log_admin("[key_name(src)] played a direct mob sound [S] to [M].")
|
||||
message_admins("[key_name_admin(src)] played a direct mob sound [S] to [ADMIN_LOOKUPFLW(M)].")
|
||||
SEND_SOUND(M, S)
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Play Direct Mob Sound") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
/client/proc/play_web_sound()
|
||||
set category = "Fun"
|
||||
set name = "Play Internet Sound"
|
||||
if(!check_rights(R_SOUND))
|
||||
return
|
||||
|
||||
var/ytdl = CONFIG_GET(string/invoke_youtubedl)
|
||||
if(!ytdl)
|
||||
to_chat(src, "<span class='boldwarning'>Youtube-dl was not configured, action unavailable</span>", confidential = TRUE) //Check config.txt for the INVOKE_YOUTUBEDL value
|
||||
return
|
||||
|
||||
var/web_sound_input = input("Enter content URL (supported sites only, leave blank to stop playing)", "Play Internet Sound via youtube-dl") as text|null
|
||||
if(istext(web_sound_input))
|
||||
var/web_sound_url = ""
|
||||
var/stop_web_sounds = FALSE
|
||||
var/list/music_extra_data = list()
|
||||
if(length(web_sound_input))
|
||||
|
||||
web_sound_input = trim(web_sound_input)
|
||||
if(findtext(web_sound_input, ":") && !findtext(web_sound_input, GLOB.is_http_protocol))
|
||||
to_chat(src, "<span class='boldwarning'>Non-http(s) URIs are not allowed.</span>", confidential = TRUE)
|
||||
to_chat(src, "<span class='warning'>For youtube-dl shortcuts like ytsearch: please use the appropriate full url from the website.</span>", confidential = TRUE)
|
||||
return
|
||||
var/shell_scrubbed_input = shell_url_scrub(web_sound_input)
|
||||
var/list/output = world.shelleo("[ytdl] --geo-bypass --format \"bestaudio\[ext=mp3]/best\[ext=mp4]\[height<=360]/bestaudio\[ext=m4a]/bestaudio\[ext=aac]\" --dump-single-json --no-playlist -- \"[shell_scrubbed_input]\"")
|
||||
var/errorlevel = output[SHELLEO_ERRORLEVEL]
|
||||
var/stdout = output[SHELLEO_STDOUT]
|
||||
var/stderr = output[SHELLEO_STDERR]
|
||||
if(!errorlevel)
|
||||
var/list/data
|
||||
try
|
||||
data = json_decode(stdout)
|
||||
catch(var/exception/e)
|
||||
to_chat(src, "<span class='boldwarning'>Youtube-dl JSON parsing FAILED:</span>", confidential = TRUE)
|
||||
to_chat(src, "<span class='warning'>[e]: [stdout]</span>", confidential = TRUE)
|
||||
return
|
||||
|
||||
if (data["url"])
|
||||
web_sound_url = data["url"]
|
||||
var/title = "[data["title"]]"
|
||||
var/webpage_url = title
|
||||
if (data["webpage_url"])
|
||||
webpage_url = "<a href=\"[data["webpage_url"]]\">[title]</a>"
|
||||
music_extra_data["start"] = data["start_time"]
|
||||
music_extra_data["end"] = data["end_time"]
|
||||
|
||||
var/res = alert(usr, "Show the title of and link to this song to the players?\n[title]",, "No", "Yes", "Cancel")
|
||||
switch(res)
|
||||
if("Yes")
|
||||
to_chat(world, "<span class='boldannounce'>An admin played: [webpage_url]</span>", confidential = TRUE)
|
||||
if("Cancel")
|
||||
return
|
||||
|
||||
SSblackbox.record_feedback("nested tally", "played_url", 1, list("[ckey]", "[web_sound_input]"))
|
||||
log_admin("[key_name(src)] played web sound: [web_sound_input]")
|
||||
message_admins("[key_name(src)] played web sound: [web_sound_input]")
|
||||
else
|
||||
to_chat(src, "<span class='boldwarning'>Youtube-dl URL retrieval FAILED:</span>", confidential = TRUE)
|
||||
to_chat(src, "<span class='warning'>[stderr]</span>", confidential = TRUE)
|
||||
|
||||
else //pressed ok with blank
|
||||
log_admin("[key_name(src)] stopped web sound")
|
||||
message_admins("[key_name(src)] stopped web sound")
|
||||
web_sound_url = null
|
||||
stop_web_sounds = TRUE
|
||||
|
||||
if(web_sound_url && !findtext(web_sound_url, GLOB.is_http_protocol))
|
||||
to_chat(src, "<span class='boldwarning'>BLOCKED: Content URL not using http(s) protocol</span>", confidential = TRUE)
|
||||
to_chat(src, "<span class='warning'>The media provider returned a content URL that isn't using the HTTP or HTTPS protocol</span>", confidential = TRUE)
|
||||
return
|
||||
if(web_sound_url || stop_web_sounds)
|
||||
for(var/m in GLOB.player_list)
|
||||
var/mob/M = m
|
||||
var/client/C = M.client
|
||||
if((C.prefs.toggles & SOUND_MIDI) && C.chatOutput && !C.chatOutput.broken && C.chatOutput.loaded)
|
||||
if(!stop_web_sounds)
|
||||
C.chatOutput.sendMusic(web_sound_url, music_extra_data)
|
||||
else
|
||||
C.chatOutput.stopMusic()
|
||||
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Play Internet Sound")
|
||||
|
||||
/client/proc/set_round_end_sound(S as sound)
|
||||
set category = "Fun"
|
||||
set name = "Set Round End Sound"
|
||||
if(!check_rights(R_SOUND))
|
||||
return
|
||||
|
||||
SSticker.SetRoundEndSound(S)
|
||||
|
||||
log_admin("[key_name(src)] set the round end sound to [S]")
|
||||
message_admins("[key_name_admin(src)] set the round end sound to [S]")
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Set Round End Sound") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
/client/proc/stop_sounds()
|
||||
set category = "Debug"
|
||||
set name = "Stop All Playing Sounds"
|
||||
if(!src.holder)
|
||||
return
|
||||
|
||||
log_admin("[key_name(src)] stopped all currently playing sounds.")
|
||||
message_admins("[key_name_admin(src)] stopped all currently playing sounds.")
|
||||
for(var/mob/M in GLOB.player_list)
|
||||
SEND_SOUND(M, sound(null))
|
||||
var/client/C = M.client
|
||||
if(C && C.chatOutput && !C.chatOutput.broken && C.chatOutput.loaded)
|
||||
C.chatOutput.stopMusic()
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Stop All Playing Sounds") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
/client/proc/play_sound(S as sound)
|
||||
set category = "Fun"
|
||||
set name = "Play Global Sound"
|
||||
if(!check_rights(R_SOUND))
|
||||
return
|
||||
|
||||
var/freq = 1
|
||||
var/vol = input(usr, "What volume would you like the sound to play at?",, 100) as null|num
|
||||
if(!vol)
|
||||
return
|
||||
vol = clamp(vol, 1, 100)
|
||||
|
||||
var/sound/admin_sound = new()
|
||||
admin_sound.file = S
|
||||
admin_sound.priority = 250
|
||||
admin_sound.channel = CHANNEL_ADMIN
|
||||
admin_sound.frequency = freq
|
||||
admin_sound.wait = 1
|
||||
admin_sound.repeat = 0
|
||||
admin_sound.status = SOUND_STREAM
|
||||
admin_sound.volume = vol
|
||||
|
||||
var/res = alert(usr, "Show the title of this song to the players?",, "Yes","No", "Cancel")
|
||||
switch(res)
|
||||
if("Yes")
|
||||
to_chat(world, "<span class='boldannounce'>An admin played: [S]</span>", confidential = TRUE)
|
||||
if("Cancel")
|
||||
return
|
||||
|
||||
log_admin("[key_name(src)] played sound [S]")
|
||||
message_admins("[key_name_admin(src)] played sound [S]")
|
||||
|
||||
for(var/mob/M in GLOB.player_list)
|
||||
if(M.client.prefs.toggles & SOUND_MIDI)
|
||||
var/user_vol = M.client.chatOutput.adminMusicVolume
|
||||
if(user_vol)
|
||||
admin_sound.volume = vol * (user_vol / 100)
|
||||
SEND_SOUND(M, admin_sound)
|
||||
admin_sound.volume = vol
|
||||
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Play Global Sound") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
|
||||
/client/proc/play_local_sound(S as sound)
|
||||
set category = "Fun"
|
||||
set name = "Play Local Sound"
|
||||
if(!check_rights(R_SOUND))
|
||||
return
|
||||
|
||||
log_admin("[key_name(src)] played a local sound [S]")
|
||||
message_admins("[key_name_admin(src)] played a local sound [S]")
|
||||
playsound(get_turf(src.mob), S, 50, FALSE, FALSE)
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Play Local Sound") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
/client/proc/play_direct_mob_sound(S as sound, mob/M)
|
||||
set category = "Fun"
|
||||
set name = "Play Direct Mob Sound"
|
||||
if(!check_rights(R_SOUND))
|
||||
return
|
||||
|
||||
if(!M)
|
||||
M = input(usr, "Choose a mob to play the sound to. Only they will hear it.", "Play Mob Sound") as null|anything in sortNames(GLOB.player_list)
|
||||
if(!M || QDELETED(M))
|
||||
return
|
||||
log_admin("[key_name(src)] played a direct mob sound [S] to [M].")
|
||||
message_admins("[key_name_admin(src)] played a direct mob sound [S] to [ADMIN_LOOKUPFLW(M)].")
|
||||
SEND_SOUND(M, S)
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Play Direct Mob Sound") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
/client/proc/play_web_sound()
|
||||
set category = "Fun"
|
||||
set name = "Play Internet Sound"
|
||||
if(!check_rights(R_SOUND))
|
||||
return
|
||||
|
||||
var/ytdl = CONFIG_GET(string/invoke_youtubedl)
|
||||
if(!ytdl)
|
||||
to_chat(src, "<span class='boldwarning'>Youtube-dl was not configured, action unavailable</span>", confidential = TRUE) //Check config.txt for the INVOKE_YOUTUBEDL value
|
||||
return
|
||||
|
||||
var/web_sound_input = input("Enter content URL (supported sites only, leave blank to stop playing)", "Play Internet Sound via youtube-dl") as text|null
|
||||
if(istext(web_sound_input))
|
||||
var/web_sound_url = ""
|
||||
var/stop_web_sounds = FALSE
|
||||
var/list/music_extra_data = list()
|
||||
if(length(web_sound_input))
|
||||
|
||||
web_sound_input = trim(web_sound_input)
|
||||
if(findtext(web_sound_input, ":") && !findtext(web_sound_input, GLOB.is_http_protocol))
|
||||
to_chat(src, "<span class='boldwarning'>Non-http(s) URIs are not allowed.</span>", confidential = TRUE)
|
||||
to_chat(src, "<span class='warning'>For youtube-dl shortcuts like ytsearch: please use the appropriate full url from the website.</span>", confidential = TRUE)
|
||||
return
|
||||
var/shell_scrubbed_input = shell_url_scrub(web_sound_input)
|
||||
var/list/output = world.shelleo("[ytdl] --geo-bypass --format \"bestaudio\[ext=mp3]/best\[ext=mp4]\[height<=360]/bestaudio\[ext=m4a]/bestaudio\[ext=aac]\" --dump-single-json --no-playlist -- \"[shell_scrubbed_input]\"")
|
||||
var/errorlevel = output[SHELLEO_ERRORLEVEL]
|
||||
var/stdout = output[SHELLEO_STDOUT]
|
||||
var/stderr = output[SHELLEO_STDERR]
|
||||
if(!errorlevel)
|
||||
var/list/data
|
||||
try
|
||||
data = json_decode(stdout)
|
||||
catch(var/exception/e)
|
||||
to_chat(src, "<span class='boldwarning'>Youtube-dl JSON parsing FAILED:</span>", confidential = TRUE)
|
||||
to_chat(src, "<span class='warning'>[e]: [stdout]</span>", confidential = TRUE)
|
||||
return
|
||||
|
||||
if (data["url"])
|
||||
web_sound_url = data["url"]
|
||||
var/title = "[data["title"]]"
|
||||
var/webpage_url = title
|
||||
if (data["webpage_url"])
|
||||
webpage_url = "<a href=\"[data["webpage_url"]]\">[title]</a>"
|
||||
music_extra_data["start"] = data["start_time"]
|
||||
music_extra_data["end"] = data["end_time"]
|
||||
|
||||
var/res = alert(usr, "Show the title of and link to this song to the players?\n[title]",, "No", "Yes", "Cancel")
|
||||
switch(res)
|
||||
if("Yes")
|
||||
to_chat(world, "<span class='boldannounce'>An admin played: [webpage_url]</span>", confidential = TRUE)
|
||||
if("Cancel")
|
||||
return
|
||||
|
||||
SSblackbox.record_feedback("nested tally", "played_url", 1, list("[ckey]", "[web_sound_input]"))
|
||||
log_admin("[key_name(src)] played web sound: [web_sound_input]")
|
||||
message_admins("[key_name(src)] played web sound: [web_sound_input]")
|
||||
else
|
||||
to_chat(src, "<span class='boldwarning'>Youtube-dl URL retrieval FAILED:</span>", confidential = TRUE)
|
||||
to_chat(src, "<span class='warning'>[stderr]</span>", confidential = TRUE)
|
||||
|
||||
else //pressed ok with blank
|
||||
log_admin("[key_name(src)] stopped web sound")
|
||||
message_admins("[key_name(src)] stopped web sound")
|
||||
web_sound_url = null
|
||||
stop_web_sounds = TRUE
|
||||
|
||||
if(web_sound_url && !findtext(web_sound_url, GLOB.is_http_protocol))
|
||||
to_chat(src, "<span class='boldwarning'>BLOCKED: Content URL not using http(s) protocol</span>", confidential = TRUE)
|
||||
to_chat(src, "<span class='warning'>The media provider returned a content URL that isn't using the HTTP or HTTPS protocol</span>", confidential = TRUE)
|
||||
return
|
||||
if(web_sound_url || stop_web_sounds)
|
||||
for(var/m in GLOB.player_list)
|
||||
var/mob/M = m
|
||||
var/client/C = M.client
|
||||
if((C.prefs.toggles & SOUND_MIDI) && C.chatOutput && !C.chatOutput.broken && C.chatOutput.loaded)
|
||||
if(!stop_web_sounds)
|
||||
C.chatOutput.sendMusic(web_sound_url, music_extra_data)
|
||||
else
|
||||
C.chatOutput.stopMusic()
|
||||
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Play Internet Sound")
|
||||
|
||||
/client/proc/set_round_end_sound(S as sound)
|
||||
set category = "Fun"
|
||||
set name = "Set Round End Sound"
|
||||
if(!check_rights(R_SOUND))
|
||||
return
|
||||
|
||||
SSticker.SetRoundEndSound(S)
|
||||
|
||||
log_admin("[key_name(src)] set the round end sound to [S]")
|
||||
message_admins("[key_name_admin(src)] set the round end sound to [S]")
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Set Round End Sound") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
/client/proc/stop_sounds()
|
||||
set category = "Debug"
|
||||
set name = "Stop All Playing Sounds"
|
||||
if(!src.holder)
|
||||
return
|
||||
|
||||
log_admin("[key_name(src)] stopped all currently playing sounds.")
|
||||
message_admins("[key_name_admin(src)] stopped all currently playing sounds.")
|
||||
for(var/mob/M in GLOB.player_list)
|
||||
SEND_SOUND(M, sound(null))
|
||||
var/client/C = M.client
|
||||
if(C && C.chatOutput && !C.chatOutput.broken && C.chatOutput.loaded)
|
||||
C.chatOutput.stopMusic()
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Stop All Playing Sounds") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
@@ -1,53 +1,53 @@
|
||||
/proc/possess(obj/O in world)
|
||||
set name = "Possess Obj"
|
||||
set category = "Object"
|
||||
|
||||
if((O.obj_flags & DANGEROUS_POSSESSION) && CONFIG_GET(flag/forbid_singulo_possession))
|
||||
to_chat(usr, "[O] is too powerful for you to possess.", confidential = TRUE)
|
||||
return
|
||||
|
||||
var/turf/T = get_turf(O)
|
||||
|
||||
if(T)
|
||||
log_admin("[key_name(usr)] has possessed [O] ([O.type]) at [AREACOORD(T)]")
|
||||
message_admins("[key_name(usr)] has possessed [O] ([O.type]) at [AREACOORD(T)]")
|
||||
else
|
||||
log_admin("[key_name(usr)] has possessed [O] ([O.type]) at an unknown location")
|
||||
message_admins("[key_name(usr)] has possessed [O] ([O.type]) at an unknown location")
|
||||
|
||||
if(!usr.control_object) //If you're not already possessing something...
|
||||
usr.name_archive = usr.real_name
|
||||
|
||||
usr.loc = O
|
||||
usr.real_name = O.name
|
||||
usr.name = O.name
|
||||
usr.reset_perspective(O)
|
||||
usr.control_object = O
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Possess Object") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
/proc/release()
|
||||
set name = "Release Obj"
|
||||
set category = "Object"
|
||||
//usr.loc = get_turf(usr)
|
||||
|
||||
if(usr.control_object && usr.name_archive) //if you have a name archived and if you are actually relassing an object
|
||||
usr.real_name = usr.name_archive
|
||||
usr.name_archive = ""
|
||||
usr.name = usr.real_name
|
||||
if(ishuman(usr))
|
||||
var/mob/living/carbon/human/H = usr
|
||||
H.name = H.get_visible_name()
|
||||
|
||||
|
||||
usr.loc = get_turf(usr.control_object)
|
||||
usr.reset_perspective()
|
||||
usr.control_object = null
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Release Object") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
/proc/givetestverbs(mob/M in GLOB.mob_list)
|
||||
set desc = "Give this guy possess/release verbs"
|
||||
set category = "Debug"
|
||||
set name = "Give Possessing Verbs"
|
||||
M.verbs += /proc/possess
|
||||
M.verbs += /proc/release
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Give Possessing Verbs") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
/proc/possess(obj/O in world)
|
||||
set name = "Possess Obj"
|
||||
set category = "Object"
|
||||
|
||||
if((O.obj_flags & DANGEROUS_POSSESSION) && CONFIG_GET(flag/forbid_singulo_possession))
|
||||
to_chat(usr, "[O] is too powerful for you to possess.", confidential = TRUE)
|
||||
return
|
||||
|
||||
var/turf/T = get_turf(O)
|
||||
|
||||
if(T)
|
||||
log_admin("[key_name(usr)] has possessed [O] ([O.type]) at [AREACOORD(T)]")
|
||||
message_admins("[key_name(usr)] has possessed [O] ([O.type]) at [AREACOORD(T)]")
|
||||
else
|
||||
log_admin("[key_name(usr)] has possessed [O] ([O.type]) at an unknown location")
|
||||
message_admins("[key_name(usr)] has possessed [O] ([O.type]) at an unknown location")
|
||||
|
||||
if(!usr.control_object) //If you're not already possessing something...
|
||||
usr.name_archive = usr.real_name
|
||||
|
||||
usr.loc = O
|
||||
usr.real_name = O.name
|
||||
usr.name = O.name
|
||||
usr.reset_perspective(O)
|
||||
usr.control_object = O
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Possess Object") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
/proc/release()
|
||||
set name = "Release Obj"
|
||||
set category = "Object"
|
||||
//usr.loc = get_turf(usr)
|
||||
|
||||
if(usr.control_object && usr.name_archive) //if you have a name archived and if you are actually relassing an object
|
||||
usr.real_name = usr.name_archive
|
||||
usr.name_archive = ""
|
||||
usr.name = usr.real_name
|
||||
if(ishuman(usr))
|
||||
var/mob/living/carbon/human/H = usr
|
||||
H.name = H.get_visible_name()
|
||||
|
||||
|
||||
usr.loc = get_turf(usr.control_object)
|
||||
usr.reset_perspective()
|
||||
usr.control_object = null
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Release Object") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
/proc/givetestverbs(mob/M in GLOB.mob_list)
|
||||
set desc = "Give this guy possess/release verbs"
|
||||
set category = "Debug"
|
||||
set name = "Give Possessing Verbs"
|
||||
M.verbs += /proc/possess
|
||||
M.verbs += /proc/release
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Give Possessing Verbs") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
@@ -1,75 +1,75 @@
|
||||
/mob/verb/pray(msg as text)
|
||||
set category = "IC"
|
||||
set name = "Pray"
|
||||
|
||||
if(GLOB.say_disabled) //This is here to try to identify lag problems
|
||||
to_chat(usr, "<span class='danger'>Speech is currently admin-disabled.</span>", confidential = TRUE)
|
||||
return
|
||||
|
||||
msg = copytext_char(sanitize(msg), 1, MAX_MESSAGE_LEN)
|
||||
if(!msg)
|
||||
return
|
||||
log_prayer("[src.key]/([src.name]): [msg]")
|
||||
if(usr.client)
|
||||
if(usr.client.prefs.muted & MUTE_PRAY)
|
||||
to_chat(usr, "<span class='danger'>You cannot pray (muted).</span>", confidential = TRUE)
|
||||
return
|
||||
if(src.client.handle_spam_prevention(msg,MUTE_PRAY))
|
||||
return
|
||||
|
||||
var/mutable_appearance/cross = mutable_appearance('icons/obj/storage.dmi', "bible")
|
||||
var/font_color = "purple"
|
||||
var/prayer_type = "PRAYER"
|
||||
var/deity
|
||||
if(usr.job == "Chaplain")
|
||||
cross.icon_state = "kingyellow"
|
||||
font_color = "blue"
|
||||
prayer_type = "CHAPLAIN PRAYER"
|
||||
if(GLOB.deity)
|
||||
deity = GLOB.deity
|
||||
else if(iscultist(usr))
|
||||
cross.icon_state = "tome"
|
||||
font_color = "red"
|
||||
prayer_type = "CULTIST PRAYER"
|
||||
deity = "Nar'Sie"
|
||||
else if(isliving(usr))
|
||||
var/mob/living/L = usr
|
||||
if(HAS_TRAIT(L, TRAIT_SPIRITUAL))
|
||||
cross.icon_state = "holylight"
|
||||
font_color = "blue"
|
||||
prayer_type = "SPIRITUAL PRAYER"
|
||||
|
||||
var/msg_tmp = msg
|
||||
msg = "<span class='adminnotice'>[icon2html(cross, GLOB.admins)]<b><font color=[font_color]>[prayer_type][deity ? " (to [deity])" : ""]: </font>[ADMIN_FULLMONTY(src)] [ADMIN_SC(src)]:</b> <span class='linkify'>[msg]</span></span>"
|
||||
|
||||
for(var/client/C in GLOB.admins)
|
||||
if(C.prefs.chat_toggles & CHAT_PRAYER)
|
||||
to_chat(C, msg, confidential = TRUE)
|
||||
if(C.prefs.toggles & SOUND_PRAYERS)
|
||||
if(usr.job == "Chaplain")
|
||||
SEND_SOUND(C, sound('sound/effects/pray.ogg'))
|
||||
to_chat(usr, "<span class='info'>You pray to the gods: \"[msg_tmp]\"</span>", confidential = TRUE)
|
||||
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Prayer") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
//log_admin("HELP: [key_name(src)]: [msg]")
|
||||
|
||||
/proc/CentCom_announce(text , mob/Sender)
|
||||
var/msg = copytext_char(sanitize(text), 1, MAX_MESSAGE_LEN)
|
||||
msg = "<span class='adminnotice'><b><font color=orange>CENTCOM:</font>[ADMIN_FULLMONTY(Sender)] [ADMIN_CENTCOM_REPLY(Sender)]:</b> [msg]</span>"
|
||||
to_chat(GLOB.admins, msg, confidential = TRUE)
|
||||
for(var/obj/machinery/computer/communications/C in GLOB.machines)
|
||||
C.overrideCooldown()
|
||||
|
||||
/proc/Syndicate_announce(text , mob/Sender)
|
||||
var/msg = copytext_char(sanitize(text), 1, MAX_MESSAGE_LEN)
|
||||
msg = "<span class='adminnotice'><b><font color=crimson>SYNDICATE:</font>[ADMIN_FULLMONTY(Sender)] [ADMIN_SYNDICATE_REPLY(Sender)]:</b> [msg]</span>"
|
||||
to_chat(GLOB.admins, msg, confidential = TRUE)
|
||||
for(var/obj/machinery/computer/communications/C in GLOB.machines)
|
||||
C.overrideCooldown()
|
||||
|
||||
/proc/Nuke_request(text , mob/Sender)
|
||||
var/msg = copytext_char(sanitize(text), 1, MAX_MESSAGE_LEN)
|
||||
msg = "<span class='adminnotice'><b><font color=orange>NUKE CODE REQUEST:</font>[ADMIN_FULLMONTY(Sender)] [ADMIN_CENTCOM_REPLY(Sender)] [ADMIN_SET_SD_CODE]:</b> [msg]</span>"
|
||||
to_chat(GLOB.admins, msg, confidential = TRUE)
|
||||
for(var/obj/machinery/computer/communications/C in GLOB.machines)
|
||||
C.overrideCooldown()
|
||||
/mob/verb/pray(msg as text)
|
||||
set category = "IC"
|
||||
set name = "Pray"
|
||||
|
||||
if(GLOB.say_disabled) //This is here to try to identify lag problems
|
||||
to_chat(usr, "<span class='danger'>Speech is currently admin-disabled.</span>", confidential = TRUE)
|
||||
return
|
||||
|
||||
msg = copytext_char(sanitize(msg), 1, MAX_MESSAGE_LEN)
|
||||
if(!msg)
|
||||
return
|
||||
log_prayer("[src.key]/([src.name]): [msg]")
|
||||
if(usr.client)
|
||||
if(usr.client.prefs.muted & MUTE_PRAY)
|
||||
to_chat(usr, "<span class='danger'>You cannot pray (muted).</span>", confidential = TRUE)
|
||||
return
|
||||
if(src.client.handle_spam_prevention(msg,MUTE_PRAY))
|
||||
return
|
||||
|
||||
var/mutable_appearance/cross = mutable_appearance('icons/obj/storage.dmi', "bible")
|
||||
var/font_color = "purple"
|
||||
var/prayer_type = "PRAYER"
|
||||
var/deity
|
||||
if(usr.job == "Chaplain")
|
||||
cross.icon_state = "kingyellow"
|
||||
font_color = "blue"
|
||||
prayer_type = "CHAPLAIN PRAYER"
|
||||
if(GLOB.deity)
|
||||
deity = GLOB.deity
|
||||
else if(iscultist(usr))
|
||||
cross.icon_state = "tome"
|
||||
font_color = "red"
|
||||
prayer_type = "CULTIST PRAYER"
|
||||
deity = "Nar'Sie"
|
||||
else if(isliving(usr))
|
||||
var/mob/living/L = usr
|
||||
if(HAS_TRAIT(L, TRAIT_SPIRITUAL))
|
||||
cross.icon_state = "holylight"
|
||||
font_color = "blue"
|
||||
prayer_type = "SPIRITUAL PRAYER"
|
||||
|
||||
var/msg_tmp = msg
|
||||
msg = "<span class='adminnotice'>[icon2html(cross, GLOB.admins)]<b><font color=[font_color]>[prayer_type][deity ? " (to [deity])" : ""]: </font>[ADMIN_FULLMONTY(src)] [ADMIN_SC(src)]:</b> <span class='linkify'>[msg]</span></span>"
|
||||
|
||||
for(var/client/C in GLOB.admins)
|
||||
if(C.prefs.chat_toggles & CHAT_PRAYER)
|
||||
to_chat(C, msg, confidential = TRUE)
|
||||
if(C.prefs.toggles & SOUND_PRAYERS)
|
||||
if(usr.job == "Chaplain")
|
||||
SEND_SOUND(C, sound('sound/effects/pray.ogg'))
|
||||
to_chat(usr, "<span class='info'>You pray to the gods: \"[msg_tmp]\"</span>", confidential = TRUE)
|
||||
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Prayer") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
//log_admin("HELP: [key_name(src)]: [msg]")
|
||||
|
||||
/proc/CentCom_announce(text , mob/Sender)
|
||||
var/msg = copytext_char(sanitize(text), 1, MAX_MESSAGE_LEN)
|
||||
msg = "<span class='adminnotice'><b><font color=orange>CENTCOM:</font>[ADMIN_FULLMONTY(Sender)] [ADMIN_CENTCOM_REPLY(Sender)]:</b> [msg]</span>"
|
||||
to_chat(GLOB.admins, msg, confidential = TRUE)
|
||||
for(var/obj/machinery/computer/communications/C in GLOB.machines)
|
||||
C.overrideCooldown()
|
||||
|
||||
/proc/Syndicate_announce(text , mob/Sender)
|
||||
var/msg = copytext_char(sanitize(text), 1, MAX_MESSAGE_LEN)
|
||||
msg = "<span class='adminnotice'><b><font color=crimson>SYNDICATE:</font>[ADMIN_FULLMONTY(Sender)] [ADMIN_SYNDICATE_REPLY(Sender)]:</b> [msg]</span>"
|
||||
to_chat(GLOB.admins, msg, confidential = TRUE)
|
||||
for(var/obj/machinery/computer/communications/C in GLOB.machines)
|
||||
C.overrideCooldown()
|
||||
|
||||
/proc/Nuke_request(text , mob/Sender)
|
||||
var/msg = copytext_char(sanitize(text), 1, MAX_MESSAGE_LEN)
|
||||
msg = "<span class='adminnotice'><b><font color=orange>NUKE CODE REQUEST:</font>[ADMIN_FULLMONTY(Sender)] [ADMIN_CENTCOM_REPLY(Sender)] [ADMIN_SET_SD_CODE]:</b> [msg]</span>"
|
||||
to_chat(GLOB.admins, msg, confidential = TRUE)
|
||||
for(var/obj/machinery/computer/communications/C in GLOB.machines)
|
||||
C.overrideCooldown()
|
||||
|
||||
+1314
-1314
File diff suppressed because it is too large
Load Diff
@@ -1,76 +1,76 @@
|
||||
/datum/admins/proc/open_shuttlepanel()
|
||||
set category = "Admin - Events"
|
||||
set name = "Shuttle Manipulator"
|
||||
set desc = "Opens the shuttle manipulator UI."
|
||||
|
||||
if(!check_rights(R_ADMIN))
|
||||
return
|
||||
|
||||
SSshuttle.ui_interact(usr)
|
||||
|
||||
|
||||
/obj/docking_port/mobile/proc/admin_fly_shuttle(mob/user)
|
||||
var/list/options = list()
|
||||
|
||||
for(var/port in SSshuttle.stationary)
|
||||
if (istype(port, /obj/docking_port/stationary/transit))
|
||||
continue // please don't do this
|
||||
var/obj/docking_port/stationary/S = port
|
||||
if (canDock(S) == SHUTTLE_CAN_DOCK)
|
||||
options[S.name || S.id] = S
|
||||
|
||||
options += "--------"
|
||||
options += "Infinite Transit"
|
||||
options += "Delete Shuttle"
|
||||
options += "Into The Sunset (delete & greentext 'escape')"
|
||||
|
||||
var/selection = input(user, "Select where to fly [name || id]:", "Fly Shuttle") as null|anything in options
|
||||
if(!selection)
|
||||
return
|
||||
|
||||
switch(selection)
|
||||
if("Infinite Transit")
|
||||
destination = null
|
||||
mode = SHUTTLE_IGNITING
|
||||
setTimer(ignitionTime)
|
||||
|
||||
if("Delete Shuttle")
|
||||
if(alert(user, "Really delete [name || id]?", "Delete Shuttle", "Cancel", "Really!") != "Really!")
|
||||
return
|
||||
jumpToNullSpace()
|
||||
|
||||
if("Into The Sunset (delete & greentext 'escape')")
|
||||
if(alert(user, "Really delete [name || id] and greentext escape objectives?", "Delete Shuttle", "Cancel", "Really!") != "Really!")
|
||||
return
|
||||
intoTheSunset()
|
||||
|
||||
else
|
||||
if(options[selection])
|
||||
request(options[selection])
|
||||
|
||||
/obj/docking_port/mobile/emergency/admin_fly_shuttle(mob/user)
|
||||
return // use the existing verbs for this
|
||||
|
||||
/obj/docking_port/mobile/arrivals/admin_fly_shuttle(mob/user)
|
||||
switch(alert(user, "Would you like to fly the arrivals shuttle once or change its destination?", "Fly Shuttle", "Fly", "Retarget", "Cancel"))
|
||||
if("Cancel")
|
||||
return
|
||||
if("Fly")
|
||||
return ..()
|
||||
|
||||
var/list/options = list()
|
||||
|
||||
for(var/port in SSshuttle.stationary)
|
||||
if (istype(port, /obj/docking_port/stationary/transit))
|
||||
continue // please don't do this
|
||||
var/obj/docking_port/stationary/S = port
|
||||
if (canDock(S) == SHUTTLE_CAN_DOCK)
|
||||
options[S.name || S.id] = S
|
||||
|
||||
var/selection = input(user, "Select the new arrivals destination:", "Fly Shuttle") as null|anything in options
|
||||
if(!selection)
|
||||
return
|
||||
target_dock = options[selection]
|
||||
if(!QDELETED(target_dock))
|
||||
destination = target_dock
|
||||
|
||||
/datum/admins/proc/open_shuttlepanel()
|
||||
set category = "Admin - Events"
|
||||
set name = "Shuttle Manipulator"
|
||||
set desc = "Opens the shuttle manipulator UI."
|
||||
|
||||
if(!check_rights(R_ADMIN))
|
||||
return
|
||||
|
||||
SSshuttle.ui_interact(usr)
|
||||
|
||||
|
||||
/obj/docking_port/mobile/proc/admin_fly_shuttle(mob/user)
|
||||
var/list/options = list()
|
||||
|
||||
for(var/port in SSshuttle.stationary)
|
||||
if (istype(port, /obj/docking_port/stationary/transit))
|
||||
continue // please don't do this
|
||||
var/obj/docking_port/stationary/S = port
|
||||
if (canDock(S) == SHUTTLE_CAN_DOCK)
|
||||
options[S.name || S.id] = S
|
||||
|
||||
options += "--------"
|
||||
options += "Infinite Transit"
|
||||
options += "Delete Shuttle"
|
||||
options += "Into The Sunset (delete & greentext 'escape')"
|
||||
|
||||
var/selection = input(user, "Select where to fly [name || id]:", "Fly Shuttle") as null|anything in options
|
||||
if(!selection)
|
||||
return
|
||||
|
||||
switch(selection)
|
||||
if("Infinite Transit")
|
||||
destination = null
|
||||
mode = SHUTTLE_IGNITING
|
||||
setTimer(ignitionTime)
|
||||
|
||||
if("Delete Shuttle")
|
||||
if(alert(user, "Really delete [name || id]?", "Delete Shuttle", "Cancel", "Really!") != "Really!")
|
||||
return
|
||||
jumpToNullSpace()
|
||||
|
||||
if("Into The Sunset (delete & greentext 'escape')")
|
||||
if(alert(user, "Really delete [name || id] and greentext escape objectives?", "Delete Shuttle", "Cancel", "Really!") != "Really!")
|
||||
return
|
||||
intoTheSunset()
|
||||
|
||||
else
|
||||
if(options[selection])
|
||||
request(options[selection])
|
||||
|
||||
/obj/docking_port/mobile/emergency/admin_fly_shuttle(mob/user)
|
||||
return // use the existing verbs for this
|
||||
|
||||
/obj/docking_port/mobile/arrivals/admin_fly_shuttle(mob/user)
|
||||
switch(alert(user, "Would you like to fly the arrivals shuttle once or change its destination?", "Fly Shuttle", "Fly", "Retarget", "Cancel"))
|
||||
if("Cancel")
|
||||
return
|
||||
if("Fly")
|
||||
return ..()
|
||||
|
||||
var/list/options = list()
|
||||
|
||||
for(var/port in SSshuttle.stationary)
|
||||
if (istype(port, /obj/docking_port/stationary/transit))
|
||||
continue // please don't do this
|
||||
var/obj/docking_port/stationary/S = port
|
||||
if (canDock(S) == SHUTTLE_CAN_DOCK)
|
||||
options[S.name || S.id] = S
|
||||
|
||||
var/selection = input(user, "Select the new arrivals destination:", "Fly Shuttle") as null|anything in options
|
||||
if(!selection)
|
||||
return
|
||||
target_dock = options[selection]
|
||||
if(!QDELETED(target_dock))
|
||||
destination = target_dock
|
||||
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
#define WHITELISTFILE "[global.config.directory]/whitelist.txt"
|
||||
|
||||
GLOBAL_LIST(whitelist)
|
||||
GLOBAL_PROTECT(whitelist)
|
||||
|
||||
/proc/load_whitelist()
|
||||
GLOB.whitelist = list()
|
||||
for(var/line in world.file2list(WHITELISTFILE))
|
||||
if(!line)
|
||||
continue
|
||||
if(findtextEx(line,"#",1,2))
|
||||
continue
|
||||
GLOB.whitelist += ckey(line)
|
||||
|
||||
if(!GLOB.whitelist.len)
|
||||
GLOB.whitelist = null
|
||||
|
||||
/proc/check_whitelist(var/ckey)
|
||||
if(!GLOB.whitelist)
|
||||
return FALSE
|
||||
. = (ckey in GLOB.whitelist)
|
||||
|
||||
#undef WHITELISTFILE
|
||||
#define WHITELISTFILE "[global.config.directory]/whitelist.txt"
|
||||
|
||||
GLOBAL_LIST(whitelist)
|
||||
GLOBAL_PROTECT(whitelist)
|
||||
|
||||
/proc/load_whitelist()
|
||||
GLOB.whitelist = list()
|
||||
for(var/line in world.file2list(WHITELISTFILE))
|
||||
if(!line)
|
||||
continue
|
||||
if(findtextEx(line,"#",1,2))
|
||||
continue
|
||||
GLOB.whitelist += ckey(line)
|
||||
|
||||
if(!GLOB.whitelist.len)
|
||||
GLOB.whitelist = null
|
||||
|
||||
/proc/check_whitelist(var/ckey)
|
||||
if(!GLOB.whitelist)
|
||||
return FALSE
|
||||
. = (ckey in GLOB.whitelist)
|
||||
|
||||
#undef WHITELISTFILE
|
||||
|
||||
Reference in New Issue
Block a user