From 625b35b8cbc034e9d9cb11544eab724ec7c89320 Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Tue, 12 Nov 2019 23:59:31 -0700 Subject: [PATCH] Merge pull request #9755 from Ghommie/Ghommie-cit331 lentext to length, yet another 513 requirement. --- code/__HELPERS/text.dm | 1592 ++--- code/game/machinery/status_display.dm | 736 +-- code/game/objects/structures/musician.dm | 762 +-- code/modules/admin/topic.dm | 5982 +++++++++---------- code/modules/admin/verbs/modifyvariables.dm | 1286 ++-- tools/Redirector/textprocs.dm | 304 +- 6 files changed, 5331 insertions(+), 5331 deletions(-) diff --git a/code/__HELPERS/text.dm b/code/__HELPERS/text.dm index cb19779f..cd0d8678 100644 --- a/code/__HELPERS/text.dm +++ b/code/__HELPERS/text.dm @@ -1,797 +1,797 @@ -/* - * Holds procs designed to help with filtering text - * Contains groups: - * SQL sanitization/formating - * Text sanitization - * Text searches - * Text modification - * Misc - */ - - -/* - * SQL sanitization - */ - -// Run all strings to be used in an SQL query through this proc first to properly escape out injection attempts. -/proc/sanitizeSQL(t) - return SSdbcore.Quote("[t]") - -/proc/format_table_name(table as text) - return CONFIG_GET(string/feedback_tableprefix) + table - -/* - * Text sanitization - */ - -//Simply removes < and > and limits the length of the message -/proc/strip_html_simple(t,limit=MAX_MESSAGE_LEN) - var/list/strip_chars = list("<",">") - t = copytext(t,1,limit) - for(var/char in strip_chars) - var/index = findtext(t, char) - while(index) - t = copytext(t, 1, index) + copytext(t, index+1) - index = findtext(t, char) - return t - -//Removes a few problematic characters -/proc/sanitize_simple(t,list/repl_chars = list("\n"="#","\t"="#")) - for(var/char in repl_chars) - var/index = findtext(t, char) - while(index) - t = copytext(t, 1, index) + repl_chars[char] + copytext(t, index+1) - index = findtext(t, char, index+1) - return t - -/proc/sanitize_filename(t) - return sanitize_simple(t, list("\n"="", "\t"="", "/"="", "\\"="", "?"="", "%"="", "*"="", ":"="", "|"="", "\""="", "<"="", ">"="")) - -//Runs byond's sanitization proc along-side sanitize_simple -/proc/sanitize(t,list/repl_chars = null) - return html_encode(sanitize_simple(t,repl_chars)) - -//Runs sanitize and strip_html_simple -//I believe strip_html_simple() is required to run first to prevent '<' from displaying as '<' after sanitize() calls byond's html_encode() -/proc/strip_html(t,limit=MAX_MESSAGE_LEN) - return copytext((sanitize(strip_html_simple(t))),1,limit) - -//Runs byond's sanitization proc along-side strip_html_simple -//I believe strip_html_simple() is required to run first to prevent '<' from displaying as '<' that html_encode() would cause -/proc/adminscrub(t,limit=MAX_MESSAGE_LEN) - return copytext((html_encode(strip_html_simple(t))),1,limit) - - -//Returns null if there is any bad text in the string -/proc/reject_bad_text(text, max_length=512) - if(length(text) > max_length) - return //message too long - var/non_whitespace = 0 - for(var/i=1, i<=length(text), i++) - switch(text2ascii(text,i)) - if(62,60,92,47) - return //rejects the text if it contains these bad characters: <, >, \ or / - if(127 to 255) - return //rejects weird letters like � - if(0 to 31) - return //more weird stuff - if(32) - continue //whitespace - else - non_whitespace = 1 - if(non_whitespace) - return text //only accepts the text if it has some non-spaces - -// Used to get a properly sanitized input, of max_length -// no_trim is self explanatory but it prevents the input from being trimed if you intend to parse newlines or whitespace. -/proc/stripped_input(mob/user, message = "", title = "", default = "", max_length=MAX_MESSAGE_LEN, no_trim=FALSE) - var/name = input(user, message, title, default) as text|null - if(no_trim) - return copytext(html_encode(name), 1, max_length) - else - return trim(html_encode(name), max_length) //trim is "outside" because html_encode can expand single symbols into multiple symbols (such as turning < into <) - -// Used to get a properly sanitized multiline input, of max_length -/proc/stripped_multiline_input(mob/user, message = "", title = "", default = "", max_length=MAX_MESSAGE_LEN, no_trim=FALSE) - var/name = input(user, message, title, default) as message|null - if(isnull(name)) // Return null if canceled. - return null - if(no_trim) - return copytext(html_encode(name), 1, max_length) - else - return trim(html_encode(name), max_length) - - -//Filters out undesirable characters from names -/proc/reject_bad_name(t_in, allow_numbers=1, max_length=MAX_NAME_LEN) - if(!t_in || length(t_in) > max_length) - return //Rejects the input if it is null or if it is longer then the max length allowed - - var/number_of_alphanumeric = 0 - var/last_char_group = 0 - var/t_out = "" - - for(var/i=1, i<=length(t_in), i++) - var/ascii_char = text2ascii(t_in,i) - switch(ascii_char) - // A .. Z - if(65 to 90) //Uppercase Letters - t_out += ascii2text(ascii_char) - number_of_alphanumeric++ - last_char_group = 4 - - // a .. z - if(97 to 122) //Lowercase Letters - if(last_char_group<2) - t_out += ascii2text(ascii_char-32) //Force uppercase first character - else - t_out += ascii2text(ascii_char) - number_of_alphanumeric++ - last_char_group = 4 - - // 0 .. 9 - if(48 to 57) //Numbers - if(!last_char_group) - continue //suppress at start of string - if(!allow_numbers) - continue - t_out += ascii2text(ascii_char) - number_of_alphanumeric++ - last_char_group = 3 - - // ' - . - if(39,45,46) //Common name punctuation - if(!last_char_group) - continue - t_out += ascii2text(ascii_char) - last_char_group = 2 - - // ~ | @ : # $ % & * + - if(126,124,64,58,35,36,37,38,42,43) //Other symbols that we'll allow (mainly for AI) - if(!last_char_group) - continue //suppress at start of string - if(!allow_numbers) - continue - t_out += ascii2text(ascii_char) - last_char_group = 2 - - //Space - if(32) - if(last_char_group <= 1) - continue //suppress double-spaces and spaces at start of string - t_out += ascii2text(ascii_char) - last_char_group = 1 - else - return - - if(number_of_alphanumeric < 2) - return //protects against tiny names like "A" and also names like "' ' ' ' ' ' ' '" - - if(last_char_group == 1) - t_out = copytext(t_out,1,length(t_out)) //removes the last character (in this case a space) - - for(var/bad_name in list("space","floor","wall","r-wall","monkey","unknown","inactive ai")) //prevents these common metagamey names - if(cmptext(t_out,bad_name)) - return //(not case sensitive) - - return t_out - -//html_encode helper proc that returns the smallest non null of two numbers -//or 0 if they're both null (needed because of findtext returning 0 when a value is not present) -/proc/non_zero_min(a, b) - if(!a) - return b - if(!b) - return a - return (a < b ? a : b) - -/* - * Text searches - */ - -//Checks the beginning of a string for a specified sub-string -//Returns the position of the substring or 0 if it was not found -/proc/dd_hasprefix(text, prefix) - var/start = 1 - var/end = length(prefix) + 1 - return findtext(text, prefix, start, end) - -//Checks the beginning of a string for a specified sub-string. This proc is case sensitive -//Returns the position of the substring or 0 if it was not found -/proc/dd_hasprefix_case(text, prefix) - var/start = 1 - var/end = length(prefix) + 1 - return findtextEx(text, prefix, start, end) - -//Checks the end of a string for a specified substring. -//Returns the position of the substring or 0 if it was not found -/proc/dd_hassuffix(text, suffix) - var/start = length(text) - length(suffix) - if(start) - return findtext(text, suffix, start, null) - return - -//Checks the end of a string for a specified substring. This proc is case sensitive -//Returns the position of the substring or 0 if it was not found -/proc/dd_hassuffix_case(text, suffix) - var/start = length(text) - length(suffix) - if(start) - return findtextEx(text, suffix, start, null) - -//Checks if any of a given list of needles is in the haystack -/proc/text_in_list(haystack, list/needle_list, start=1, end=0) - for(var/needle in needle_list) - if(findtext(haystack, needle, start, end)) - return 1 - return 0 - -//Like above, but case sensitive -/proc/text_in_list_case(haystack, list/needle_list, start=1, end=0) - for(var/needle in needle_list) - if(findtextEx(haystack, needle, start, end)) - return 1 - return 0 - -//Adds 'u' number of zeros ahead of the text 't' -/proc/add_zero(t, u) - while (length(t) < u) - t = "0[t]" - return t - -//Adds 'u' number of spaces ahead of the text 't' -/proc/add_lspace(t, u) - while(length(t) < u) - t = " [t]" - return t - -//Adds 'u' number of spaces behind the text 't' -/proc/add_tspace(t, u) - while(length(t) < u) - t = "[t] " - return t - -//Returns a string with reserved characters and spaces before the first letter removed -/proc/trim_left(text) - for (var/i = 1 to length(text)) - if (text2ascii(text, i) > 32) - return copytext(text, i) - return "" - -//Returns a string with reserved characters and spaces after the last letter removed -/proc/trim_right(text) - for (var/i = length(text), i > 0, i--) - if (text2ascii(text, i) > 32) - return copytext(text, 1, i + 1) - - return "" - -//Returns a string with reserved characters and spaces before the first word and after the last word removed. -/proc/trim(text, max_length) - if(max_length) - text = copytext(text, 1, max_length) - return trim_left(trim_right(text)) - -//Returns a string with the first element of the string capitalized. -/proc/capitalize(t as text) - return uppertext(copytext(t, 1, 2)) + copytext(t, 2) - -//Centers text by adding spaces to either side of the string. -/proc/dd_centertext(message, length) - var/new_message = message - var/size = length(message) - var/delta = length - size - if(size == length) - return new_message - if(size > length) - return copytext(new_message, 1, length + 1) - if(delta == 1) - return new_message + " " - if(delta % 2) - new_message = " " + new_message - delta-- - var/spaces = add_lspace("",delta/2-1) - return spaces + new_message + spaces - -//Limits the length of the text. Note: MAX_MESSAGE_LEN and MAX_NAME_LEN are widely used for this purpose -/proc/dd_limittext(message, length) - var/size = length(message) - if(size <= length) - return message - return copytext(message, 1, length + 1) - - -/proc/stringmerge(text,compare,replace = "*") -//This proc fills in all spaces with the "replace" var (* by default) with whatever -//is in the other string at the same spot (assuming it is not a replace char). -//This is used for fingerprints - var/newtext = text - if(length(text) != length(compare)) - return 0 - for(var/i = 1, i < length(text), i++) - var/a = copytext(text,i,i+1) - var/b = copytext(compare,i,i+1) -//if it isn't both the same letter, or if they are both the replacement character -//(no way to know what it was supposed to be) - if(a != b) - if(a == replace) //if A is the replacement char - newtext = copytext(newtext,1,i) + b + copytext(newtext, i+1) - else if(b == replace) //if B is the replacement char - newtext = copytext(newtext,1,i) + a + copytext(newtext, i+1) - else //The lists disagree, Uh-oh! - return 0 - return newtext - -/proc/stringpercent(text,character = "*") -//This proc returns the number of chars of the string that is the character -//This is used for detective work to determine fingerprint completion. - if(!text || !character) - return 0 - var/count = 0 - for(var/i = 1, i <= length(text), i++) - var/a = copytext(text,i,i+1) - if(a == character) - count++ - return count - -/proc/reverse_text(text = "") - var/new_text = "" - for(var/i = length(text); i > 0; i--) - new_text += copytext(text, i, i+1) - return new_text - -GLOBAL_LIST_INIT(zero_character_only, list("0")) -GLOBAL_LIST_INIT(hex_characters, list("0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f")) -GLOBAL_LIST_INIT(alphabet, list("a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z")) -GLOBAL_LIST_INIT(binary, list("0","1")) -/proc/random_string(length, list/characters) - . = "" - for(var/i=1, i<=length, i++) - . += pick(characters) - -/proc/repeat_string(times, string="") - . = "" - for(var/i=1, i<=times, i++) - . += string - -/proc/random_short_color() - return random_string(3, GLOB.hex_characters) - -/proc/random_color() - return random_string(6, GLOB.hex_characters) - -/proc/add_zero2(t, u) - var/temp1 - while (length(t) < u) - t = "0[t]" - temp1 = t - if (length(t) > u) - temp1 = copytext(t,2,u+1) - return temp1 - -//merges non-null characters (3rd argument) from "from" into "into". Returns result -//e.g. into = "Hello World" -// from = "Seeya______" -// returns"Seeya World" -//The returned text is always the same length as into -//This was coded to handle DNA gene-splicing. -/proc/merge_text(into, from, null_char="_") - . = "" - if(!istext(into)) - into = "" - if(!istext(from)) - from = "" - var/null_ascii = istext(null_char) ? text2ascii(null_char,1) : null_char - - var/previous = 0 - var/start = 1 - var/end = length(into) + 1 - - for(var/i=1, i") - t = replacetext(t, "))", "") - t = replacetext(t, regex("(-){3,}", "gm"), "
") - t = replacetext(t, regex("^\\((-){3,}\\)$", "gm"), "$1") - - // Parse lists - - var/list/tlist = splittext(t, "\n") - var/tlistlen = tlist.len - var/listlevel = -1 - var/singlespace = -1 // if 0, double spaces are used before asterisks, if 1, single are - for(var/i = 1, i <= tlistlen, i++) - var/line = tlist[i] - var/count_asterisk = length(replacetext(line, regex("\[^\\*\]+", "g"), "")) - if(count_asterisk % 2 == 1 && findtext(line, regex("^\\s*\\*", "g"))) // there is an extra asterisk in the beggining - - var/count_w = length(replacetext(line, regex("^( *)\\*.*$", "g"), "$1")) // whitespace before asterisk - line = replacetext(line, regex("^ *(\\*.*)$", "g"), "$1") - - if(singlespace == -1 && count_w == 2) - if(listlevel == 0) - singlespace = 0 - else - singlespace = 1 - - if(singlespace == 0) - count_w = count_w % 2 ? round(count_w / 2 + 0.25) : count_w / 2 - - line = replacetext(line, regex("\\*", ""), "
  • ") - while(listlevel < count_w) - line = "" + line - listlevel-- - - else while(listlevel >= 0) - line = "" + line - listlevel-- - - tlist[i] = line - // end for - - t = tlist[1] - for(var/i = 2, i <= tlistlen, i++) - t += "\n" + tlist[i] - - while(listlevel >= 0) - t += "" - listlevel-- - - else - t = replacetext(t, "((", "") - t = replacetext(t, "))", "") - - // Parse headers - - t = replacetext(t, regex("^#(?!#) ?(.+)$", "gm"), "

    $1

    ") - t = replacetext(t, regex("^##(?!#) ?(.+)$", "gm"), "

    $1

    ") - t = replacetext(t, regex("^###(?!#) ?(.+)$", "gm"), "

    $1

    ") - t = replacetext(t, regex("^#### ?(.+)$", "gm"), "
    $1
    ") - - // Parse most rules - - t = replacetext(t, regex("\\*(\[^\\*\]*)\\*", "g"), "$1") - t = replacetext(t, regex("_(\[^_\]*)_", "g"), "$1") - t = replacetext(t, "", "!") - t = replacetext(t, "", "!") - t = replacetext(t, regex("\\!(\[^\\!\]+)\\!", "g"), "$1") - t = replacetext(t, regex("\\^(\[^\\^\]+)\\^", "g"), "$1") - t = replacetext(t, regex("\\|(\[^\\|\]+)\\|", "g"), "
    $1
    ") - t = replacetext(t, "!", "
    ") - - return t - -/proc/parsemarkdown_basic_step2(t) - if(length(t) <= 0) - return - - // Restore the single characters used - - t = replacetext(t, "$a", "!") - - // Redo the escaping - - t = replacetext(t, "$1", "\\") - t = replacetext(t, "$2", "**") - t = replacetext(t, "$3", "*") - t = replacetext(t, "$4", "__") - t = replacetext(t, "$5", "_") - t = replacetext(t, "$6", "^") - t = replacetext(t, "$7", "((") - t = replacetext(t, "$8", "))") - t = replacetext(t, "$9", "|") - t = replacetext(t, "$0", "%") - t = replacetext(t, "$-", "$") - - return t - -/proc/parsemarkdown_basic(t, limited=FALSE) - t = parsemarkdown_basic_step1(t, limited) - t = parsemarkdown_basic_step2(t) - return t - -/proc/parsemarkdown(t, mob/user=null, limited=FALSE) - if(length(t) <= 0) - return - - // Premanage whitespace - - t = replacetext(t, regex("\[^\\S\\r\\n \]", "g"), " ") - - t = parsemarkdown_basic_step1(t) - - t = replacetext(t, regex("%s(?:ign)?(?=\\s|$)", "igm"), user ? "[user.real_name]" : "") - t = replacetext(t, regex("%f(?:ield)?(?=\\s|$)", "igm"), "") - - t = parsemarkdown_basic_step2(t) - - // Manage whitespace - - t = replacetext(t, regex("(?:\\r\\n?|\\n)", "g"), "
    ") - - t = replacetext(t, " ", "  ") - - // Done - - return t - -#define string2charlist(string) (splittext(string, regex("(.)")) - splittext(string, "")) - -/proc/rot13(text = "") - var/list/textlist = string2charlist(text) - var/list/result = list() - for(var/c in textlist) - var/ca = text2ascii(c) - if(ca >= text2ascii("a") && ca <= text2ascii("m")) - ca += 13 - else if(ca >= text2ascii("n") && ca <= text2ascii("z")) - ca -= 13 - else if(ca >= text2ascii("A") && ca <= text2ascii("M")) - ca += 13 - else if(ca >= text2ascii("N") && ca <= text2ascii("Z")) - ca -= 13 - result += ascii2text(ca) - return jointext(result, "") - -//Takes a list of values, sanitizes it down for readability and character count, -//then exports it as a json file at data/npc_saves/[filename].json. -//As far as SS13 is concerned this is write only data. You can't change something -//in the json file and have it be reflected in the in game item/mob it came from. -//(That's what things like savefiles are for) Note that this list is not shuffled. -/proc/twitterize(list/proposed, filename, cullshort = 1, storemax = 1000) - if(!islist(proposed) || !filename || !CONFIG_GET(flag/log_twitter)) - return - - //Regular expressions are, as usual, absolute magic - var/regex/all_invalid_symbols = new("\[^ -~]+") - - var/list/accepted = list() - for(var/string in proposed) - if(findtext(string,GLOB.is_website) || findtext(string,GLOB.is_email) || findtext(string,all_invalid_symbols) || !findtext(string,GLOB.is_alphanumeric)) - continue - var/buffer = "" - var/early_culling = TRUE - for(var/pos = 1, pos <= length(string), pos++) - var/let = copytext(string, pos, (pos + 1) % length(string)) - if(early_culling && !findtext(let,GLOB.is_alphanumeric)) - continue - early_culling = FALSE - buffer += let - if(!findtext(buffer,GLOB.is_alphanumeric)) - continue - var/punctbuffer = "" - var/cutoff = length(buffer) - for(var/pos = length(buffer), pos >= 0, pos--) - var/let = copytext(buffer, pos, (pos + 1) % length(buffer)) - if(findtext(let,GLOB.is_alphanumeric)) - break - if(findtext(let,GLOB.is_punctuation)) - punctbuffer = let + punctbuffer //Note this isn't the same thing as using += - cutoff = pos - if(punctbuffer) //We clip down excessive punctuation to get the letter count lower and reduce repeats. It's not perfect but it helps. - var/exclaim = FALSE - var/question = FALSE - var/periods = 0 - for(var/pos = length(punctbuffer), pos >= 0, pos--) - var/punct = copytext(punctbuffer, pos, (pos + 1) % length(punctbuffer)) - if(!exclaim && findtext(punct,"!")) - exclaim = TRUE - if(!question && findtext(punct,"?")) - question = TRUE - if(!exclaim && !question && findtext(punct,".")) - periods += 1 - if(exclaim) - if(question) - punctbuffer = "?!" - else - punctbuffer = "!" - else if(question) - punctbuffer = "?" - else if(periods) - if(periods > 1) - punctbuffer = "..." - else - punctbuffer = "" //Grammer nazis be damned - buffer = copytext(buffer, 1, cutoff) + punctbuffer - if(!findtext(buffer,GLOB.is_alphanumeric)) - continue - if(!buffer || length(buffer) > 280 || length(buffer) <= cullshort || buffer in accepted) - continue - - accepted += buffer - - var/log = file("data/npc_saves/[filename].json") //If this line ever shows up as changed in a PR be very careful you aren't being memed on - var/list/oldjson = list() - var/list/oldentries = list() - if(fexists(log)) - oldjson = json_decode(file2text(log)) - oldentries = oldjson["data"] - if(!isemptylist(oldentries)) - for(var/string in accepted) - for(var/old in oldentries) - if(string == old) - oldentries.Remove(old) //Line's position in line is "refreshed" until it falls off the in game radar - break - - var/list/finalized = list() - finalized = accepted.Copy() + oldentries.Copy() //we keep old and unreferenced phrases near the bottom for culling - listclearnulls(finalized) - if(!isemptylist(finalized) && length(finalized) > storemax) - finalized.Cut(storemax + 1) - fdel(log) - - var/list/tosend = list() - tosend["data"] = finalized - WRITE_FILE(log, json_encode(tosend)) - -//Used for applying byonds text macros to strings that are loaded at runtime -/proc/apply_text_macros(string) - var/next_backslash = findtext(string, "\\") - if(!next_backslash) - return string - - var/leng = length(string) - - var/next_space = findtext(string, " ", next_backslash + 1) - if(!next_space) - next_space = leng - next_backslash - - if(!next_space) //trailing bs - return string - - var/base = next_backslash == 1 ? "" : copytext(string, 1, next_backslash) - var/macro = lowertext(copytext(string, next_backslash + 1, next_space)) - var/rest = next_backslash > leng ? "" : copytext(string, next_space + 1) - - //See https://secure.byond.com/docs/ref/info.html#/DM/text/macros - switch(macro) - //prefixes/agnostic - if("the") - rest = text("\the []", rest) - if("a") - rest = text("\a []", rest) - if("an") - rest = text("\an []", rest) - if("proper") - rest = text("\proper []", rest) - if("improper") - rest = text("\improper []", rest) - if("roman") - rest = text("\roman []", rest) - //postfixes - if("th") - base = text("[]\th", rest) - if("s") - base = text("[]\s", rest) - if("he") - base = text("[]\he", rest) - if("she") - base = text("[]\she", rest) - if("his") - base = text("[]\his", rest) - if("himself") - base = text("[]\himself", rest) - if("herself") - base = text("[]\herself", rest) - if("hers") - base = text("[]\hers", rest) - - . = base - if(rest) - . += .(rest) - -//Replacement for the \th macro when you want the whole word output as text (first instead of 1st) -/proc/thtotext(number) - if(!isnum(number)) - return - switch(number) - if(1) - return "first" - if(2) - return "second" - if(3) - return "third" - if(4) - return "fourth" - if(5) - return "fifth" - if(6) - return "sixth" - if(7) - return "seventh" - if(8) - return "eighth" - if(9) - return "ninth" - if(10) - return "tenth" - if(11) - return "eleventh" - if(12) - return "twelfth" - else - return "[number]\th" - -/proc/unintelligize(message) - var/prefix=copytext(message,1,2) - if(prefix == ";") - message = copytext(message,2) - else if(prefix in list(":","#")) - prefix += copytext(message,2,3) - message = copytext(message,3) - else - prefix="" - - var/list/words = splittext(message," ") - var/list/rearranged = list() - for(var/i=1;i<=words.len;i++) - var/cword = pick(words) - words.Remove(cword) - var/suffix = copytext(cword,length(cword)-1,length(cword)) - while(length(cword)>0 && suffix in list(".",",",";","!",":","?")) - cword = copytext(cword,1 ,length(cword)-1) - suffix = copytext(cword,length(cword)-1,length(cword) ) - if(length(cword)) - rearranged += cword - message = "[prefix][jointext(rearranged," ")]" - . = message - -#define is_alpha(X) ((text2ascii(X) <= 122) && (text2ascii(X) >= 97)) +/* + * Holds procs designed to help with filtering text + * Contains groups: + * SQL sanitization/formating + * Text sanitization + * Text searches + * Text modification + * Misc + */ + + +/* + * SQL sanitization + */ + +// Run all strings to be used in an SQL query through this proc first to properly escape out injection attempts. +/proc/sanitizeSQL(t) + return SSdbcore.Quote("[t]") + +/proc/format_table_name(table as text) + return CONFIG_GET(string/feedback_tableprefix) + table + +/* + * Text sanitization + */ + +//Simply removes < and > and limits the length of the message +/proc/strip_html_simple(t,limit=MAX_MESSAGE_LEN) + var/list/strip_chars = list("<",">") + t = copytext(t,1,limit) + for(var/char in strip_chars) + var/index = findtext(t, char) + while(index) + t = copytext(t, 1, index) + copytext(t, index+1) + index = findtext(t, char) + return t + +//Removes a few problematic characters +/proc/sanitize_simple(t,list/repl_chars = list("\n"="#","\t"="#")) + for(var/char in repl_chars) + var/index = findtext(t, char) + while(index) + t = copytext(t, 1, index) + repl_chars[char] + copytext(t, index+1) + index = findtext(t, char, index+1) + return t + +/proc/sanitize_filename(t) + return sanitize_simple(t, list("\n"="", "\t"="", "/"="", "\\"="", "?"="", "%"="", "*"="", ":"="", "|"="", "\""="", "<"="", ">"="")) + +//Runs byond's sanitization proc along-side sanitize_simple +/proc/sanitize(t,list/repl_chars = null) + return html_encode(sanitize_simple(t,repl_chars)) + +//Runs sanitize and strip_html_simple +//I believe strip_html_simple() is required to run first to prevent '<' from displaying as '<' after sanitize() calls byond's html_encode() +/proc/strip_html(t,limit=MAX_MESSAGE_LEN) + return copytext((sanitize(strip_html_simple(t))),1,limit) + +//Runs byond's sanitization proc along-side strip_html_simple +//I believe strip_html_simple() is required to run first to prevent '<' from displaying as '<' that html_encode() would cause +/proc/adminscrub(t,limit=MAX_MESSAGE_LEN) + return copytext((html_encode(strip_html_simple(t))),1,limit) + + +//Returns null if there is any bad text in the string +/proc/reject_bad_text(text, max_length=512) + if(length(text) > max_length) + return //message too long + var/non_whitespace = 0 + for(var/i=1, i<=length(text), i++) + switch(text2ascii(text,i)) + if(62,60,92,47) + return //rejects the text if it contains these bad characters: <, >, \ or / + if(127 to 255) + return //rejects weird letters like � + if(0 to 31) + return //more weird stuff + if(32) + continue //whitespace + else + non_whitespace = 1 + if(non_whitespace) + return text //only accepts the text if it has some non-spaces + +// Used to get a properly sanitized input, of max_length +// no_trim is self explanatory but it prevents the input from being trimed if you intend to parse newlines or whitespace. +/proc/stripped_input(mob/user, message = "", title = "", default = "", max_length=MAX_MESSAGE_LEN, no_trim=FALSE) + var/name = input(user, message, title, default) as text|null + if(no_trim) + return copytext(html_encode(name), 1, max_length) + else + return trim(html_encode(name), max_length) //trim is "outside" because html_encode can expand single symbols into multiple symbols (such as turning < into <) + +// Used to get a properly sanitized multiline input, of max_length +/proc/stripped_multiline_input(mob/user, message = "", title = "", default = "", max_length=MAX_MESSAGE_LEN, no_trim=FALSE) + var/name = input(user, message, title, default) as message|null + if(isnull(name)) // Return null if canceled. + return null + if(no_trim) + return copytext(html_encode(name), 1, max_length) + else + return trim(html_encode(name), max_length) + + +//Filters out undesirable characters from names +/proc/reject_bad_name(t_in, allow_numbers=1, max_length=MAX_NAME_LEN) + if(!t_in || length(t_in) > max_length) + return //Rejects the input if it is null or if it is longer then the max length allowed + + var/number_of_alphanumeric = 0 + var/last_char_group = 0 + var/t_out = "" + + for(var/i=1, i<=length(t_in), i++) + var/ascii_char = text2ascii(t_in,i) + switch(ascii_char) + // A .. Z + if(65 to 90) //Uppercase Letters + t_out += ascii2text(ascii_char) + number_of_alphanumeric++ + last_char_group = 4 + + // a .. z + if(97 to 122) //Lowercase Letters + if(last_char_group<2) + t_out += ascii2text(ascii_char-32) //Force uppercase first character + else + t_out += ascii2text(ascii_char) + number_of_alphanumeric++ + last_char_group = 4 + + // 0 .. 9 + if(48 to 57) //Numbers + if(!last_char_group) + continue //suppress at start of string + if(!allow_numbers) + continue + t_out += ascii2text(ascii_char) + number_of_alphanumeric++ + last_char_group = 3 + + // ' - . + if(39,45,46) //Common name punctuation + if(!last_char_group) + continue + t_out += ascii2text(ascii_char) + last_char_group = 2 + + // ~ | @ : # $ % & * + + if(126,124,64,58,35,36,37,38,42,43) //Other symbols that we'll allow (mainly for AI) + if(!last_char_group) + continue //suppress at start of string + if(!allow_numbers) + continue + t_out += ascii2text(ascii_char) + last_char_group = 2 + + //Space + if(32) + if(last_char_group <= 1) + continue //suppress double-spaces and spaces at start of string + t_out += ascii2text(ascii_char) + last_char_group = 1 + else + return + + if(number_of_alphanumeric < 2) + return //protects against tiny names like "A" and also names like "' ' ' ' ' ' ' '" + + if(last_char_group == 1) + t_out = copytext(t_out,1,length(t_out)) //removes the last character (in this case a space) + + for(var/bad_name in list("space","floor","wall","r-wall","monkey","unknown","inactive ai")) //prevents these common metagamey names + if(cmptext(t_out,bad_name)) + return //(not case sensitive) + + return t_out + +//html_encode helper proc that returns the smallest non null of two numbers +//or 0 if they're both null (needed because of findtext returning 0 when a value is not present) +/proc/non_zero_min(a, b) + if(!a) + return b + if(!b) + return a + return (a < b ? a : b) + +/* + * Text searches + */ + +//Checks the beginning of a string for a specified sub-string +//Returns the position of the substring or 0 if it was not found +/proc/dd_hasprefix(text, prefix) + var/start = 1 + var/end = length(prefix) + 1 + return findtext(text, prefix, start, end) + +//Checks the beginning of a string for a specified sub-string. This proc is case sensitive +//Returns the position of the substring or 0 if it was not found +/proc/dd_hasprefix_case(text, prefix) + var/start = 1 + var/end = length(prefix) + 1 + return findtextEx(text, prefix, start, end) + +//Checks the end of a string for a specified substring. +//Returns the position of the substring or 0 if it was not found +/proc/dd_hassuffix(text, suffix) + var/start = length(text) - length(suffix) + if(start) + return findtext(text, suffix, start, null) + return + +//Checks the end of a string for a specified substring. This proc is case sensitive +//Returns the position of the substring or 0 if it was not found +/proc/dd_hassuffix_case(text, suffix) + var/start = length(text) - length(suffix) + if(start) + return findtextEx(text, suffix, start, null) + +//Checks if any of a given list of needles is in the haystack +/proc/text_in_list(haystack, list/needle_list, start=1, end=0) + for(var/needle in needle_list) + if(findtext(haystack, needle, start, end)) + return 1 + return 0 + +//Like above, but case sensitive +/proc/text_in_list_case(haystack, list/needle_list, start=1, end=0) + for(var/needle in needle_list) + if(findtextEx(haystack, needle, start, end)) + return 1 + return 0 + +//Adds 'u' number of zeros ahead of the text 't' +/proc/add_zero(t, u) + while (length(t) < u) + t = "0[t]" + return t + +//Adds 'u' number of spaces ahead of the text 't' +/proc/add_lspace(t, u) + while(length(t) < u) + t = " [t]" + return t + +//Adds 'u' number of spaces behind the text 't' +/proc/add_tspace(t, u) + while(length(t) < u) + t = "[t] " + return t + +//Returns a string with reserved characters and spaces before the first letter removed +/proc/trim_left(text) + for (var/i = 1 to length(text)) + if (text2ascii(text, i) > 32) + return copytext(text, i) + return "" + +//Returns a string with reserved characters and spaces after the last letter removed +/proc/trim_right(text) + for (var/i = length(text), i > 0, i--) + if (text2ascii(text, i) > 32) + return copytext(text, 1, i + 1) + + return "" + +//Returns a string with reserved characters and spaces before the first word and after the last word removed. +/proc/trim(text, max_length) + if(max_length) + text = copytext(text, 1, max_length) + return trim_left(trim_right(text)) + +//Returns a string with the first element of the string capitalized. +/proc/capitalize(t as text) + return uppertext(copytext(t, 1, 2)) + copytext(t, 2) + +//Centers text by adding spaces to either side of the string. +/proc/dd_centertext(message, length) + var/new_message = message + var/size = length(message) + var/delta = length - size + if(size == length) + return new_message + if(size > length) + return copytext(new_message, 1, length + 1) + if(delta == 1) + return new_message + " " + if(delta % 2) + new_message = " " + new_message + delta-- + var/spaces = add_lspace("",delta/2-1) + return spaces + new_message + spaces + +//Limits the length of the text. Note: MAX_MESSAGE_LEN and MAX_NAME_LEN are widely used for this purpose +/proc/dd_limittext(message, length) + var/size = length(message) + if(size <= length) + return message + return copytext(message, 1, length + 1) + + +/proc/stringmerge(text,compare,replace = "*") +//This proc fills in all spaces with the "replace" var (* by default) with whatever +//is in the other string at the same spot (assuming it is not a replace char). +//This is used for fingerprints + var/newtext = text + if(length(text) != length(compare)) + return 0 + for(var/i = 1, i < length(text), i++) + var/a = copytext(text,i,i+1) + var/b = copytext(compare,i,i+1) +//if it isn't both the same letter, or if they are both the replacement character +//(no way to know what it was supposed to be) + if(a != b) + if(a == replace) //if A is the replacement char + newtext = copytext(newtext,1,i) + b + copytext(newtext, i+1) + else if(b == replace) //if B is the replacement char + newtext = copytext(newtext,1,i) + a + copytext(newtext, i+1) + else //The lists disagree, Uh-oh! + return 0 + return newtext + +/proc/stringpercent(text,character = "*") +//This proc returns the number of chars of the string that is the character +//This is used for detective work to determine fingerprint completion. + if(!text || !character) + return 0 + var/count = 0 + for(var/i = 1, i <= length(text), i++) + var/a = copytext(text,i,i+1) + if(a == character) + count++ + return count + +/proc/reverse_text(text = "") + var/new_text = "" + for(var/i = length(text); i > 0; i--) + new_text += copytext(text, i, i+1) + return new_text + +GLOBAL_LIST_INIT(zero_character_only, list("0")) +GLOBAL_LIST_INIT(hex_characters, list("0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f")) +GLOBAL_LIST_INIT(alphabet, list("a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z")) +GLOBAL_LIST_INIT(binary, list("0","1")) +/proc/random_string(length, list/characters) + . = "" + for(var/i=1, i<=length, i++) + . += pick(characters) + +/proc/repeat_string(times, string="") + . = "" + for(var/i=1, i<=times, i++) + . += string + +/proc/random_short_color() + return random_string(3, GLOB.hex_characters) + +/proc/random_color() + return random_string(6, GLOB.hex_characters) + +/proc/add_zero2(t, u) + var/temp1 + while (length(t) < u) + t = "0[t]" + temp1 = t + if (length(t) > u) + temp1 = copytext(t,2,u+1) + return temp1 + +//merges non-null characters (3rd argument) from "from" into "into". Returns result +//e.g. into = "Hello World" +// from = "Seeya______" +// returns"Seeya World" +//The returned text is always the same length as into +//This was coded to handle DNA gene-splicing. +/proc/merge_text(into, from, null_char="_") + . = "" + if(!istext(into)) + into = "" + if(!istext(from)) + from = "" + var/null_ascii = istext(null_char) ? text2ascii(null_char,1) : null_char + + var/previous = 0 + var/start = 1 + var/end = length(into) + 1 + + for(var/i=1, i") + t = replacetext(t, "))", "") + t = replacetext(t, regex("(-){3,}", "gm"), "
    ") + t = replacetext(t, regex("^\\((-){3,}\\)$", "gm"), "$1") + + // Parse lists + + var/list/tlist = splittext(t, "\n") + var/tlistlen = tlist.len + var/listlevel = -1 + var/singlespace = -1 // if 0, double spaces are used before asterisks, if 1, single are + for(var/i = 1, i <= tlistlen, i++) + var/line = tlist[i] + var/count_asterisk = length(replacetext(line, regex("\[^\\*\]+", "g"), "")) + if(count_asterisk % 2 == 1 && findtext(line, regex("^\\s*\\*", "g"))) // there is an extra asterisk in the beggining + + var/count_w = length(replacetext(line, regex("^( *)\\*.*$", "g"), "$1")) // whitespace before asterisk + line = replacetext(line, regex("^ *(\\*.*)$", "g"), "$1") + + if(singlespace == -1 && count_w == 2) + if(listlevel == 0) + singlespace = 0 + else + singlespace = 1 + + if(singlespace == 0) + count_w = count_w % 2 ? round(count_w / 2 + 0.25) : count_w / 2 + + line = replacetext(line, regex("\\*", ""), "
  • ") + while(listlevel < count_w) + line = "
      " + line + listlevel++ + while(listlevel > count_w) + line = "
    " + line + listlevel-- + + else while(listlevel >= 0) + line = "" + line + listlevel-- + + tlist[i] = line + // end for + + t = tlist[1] + for(var/i = 2, i <= tlistlen, i++) + t += "\n" + tlist[i] + + while(listlevel >= 0) + t += "" + listlevel-- + + else + t = replacetext(t, "((", "") + t = replacetext(t, "))", "") + + // Parse headers + + t = replacetext(t, regex("^#(?!#) ?(.+)$", "gm"), "

    $1

    ") + t = replacetext(t, regex("^##(?!#) ?(.+)$", "gm"), "

    $1

    ") + t = replacetext(t, regex("^###(?!#) ?(.+)$", "gm"), "

    $1

    ") + t = replacetext(t, regex("^#### ?(.+)$", "gm"), "
    $1
    ") + + // Parse most rules + + t = replacetext(t, regex("\\*(\[^\\*\]*)\\*", "g"), "$1") + t = replacetext(t, regex("_(\[^_\]*)_", "g"), "$1") + t = replacetext(t, "", "!") + t = replacetext(t, "
    ", "!") + t = replacetext(t, regex("\\!(\[^\\!\]+)\\!", "g"), "$1") + t = replacetext(t, regex("\\^(\[^\\^\]+)\\^", "g"), "$1") + t = replacetext(t, regex("\\|(\[^\\|\]+)\\|", "g"), "
    $1
    ") + t = replacetext(t, "!", "
    ") + + return t + +/proc/parsemarkdown_basic_step2(t) + if(length(t) <= 0) + return + + // Restore the single characters used + + t = replacetext(t, "$a", "!") + + // Redo the escaping + + t = replacetext(t, "$1", "\\") + t = replacetext(t, "$2", "**") + t = replacetext(t, "$3", "*") + t = replacetext(t, "$4", "__") + t = replacetext(t, "$5", "_") + t = replacetext(t, "$6", "^") + t = replacetext(t, "$7", "((") + t = replacetext(t, "$8", "))") + t = replacetext(t, "$9", "|") + t = replacetext(t, "$0", "%") + t = replacetext(t, "$-", "$") + + return t + +/proc/parsemarkdown_basic(t, limited=FALSE) + t = parsemarkdown_basic_step1(t, limited) + t = parsemarkdown_basic_step2(t) + return t + +/proc/parsemarkdown(t, mob/user=null, limited=FALSE) + if(length(t) <= 0) + return + + // Premanage whitespace + + t = replacetext(t, regex("\[^\\S\\r\\n \]", "g"), " ") + + t = parsemarkdown_basic_step1(t) + + t = replacetext(t, regex("%s(?:ign)?(?=\\s|$)", "igm"), user ? "[user.real_name]" : "") + t = replacetext(t, regex("%f(?:ield)?(?=\\s|$)", "igm"), "") + + t = parsemarkdown_basic_step2(t) + + // Manage whitespace + + t = replacetext(t, regex("(?:\\r\\n?|\\n)", "g"), "
    ") + + t = replacetext(t, " ", "  ") + + // Done + + return t + +#define string2charlist(string) (splittext(string, regex("(.)")) - splittext(string, "")) + +/proc/rot13(text = "") + var/list/textlist = string2charlist(text) + var/list/result = list() + for(var/c in textlist) + var/ca = text2ascii(c) + if(ca >= text2ascii("a") && ca <= text2ascii("m")) + ca += 13 + else if(ca >= text2ascii("n") && ca <= text2ascii("z")) + ca -= 13 + else if(ca >= text2ascii("A") && ca <= text2ascii("M")) + ca += 13 + else if(ca >= text2ascii("N") && ca <= text2ascii("Z")) + ca -= 13 + result += ascii2text(ca) + return jointext(result, "") + +//Takes a list of values, sanitizes it down for readability and character count, +//then exports it as a json file at data/npc_saves/[filename].json. +//As far as SS13 is concerned this is write only data. You can't change something +//in the json file and have it be reflected in the in game item/mob it came from. +//(That's what things like savefiles are for) Note that this list is not shuffled. +/proc/twitterize(list/proposed, filename, cullshort = 1, storemax = 1000) + if(!islist(proposed) || !filename || !CONFIG_GET(flag/log_twitter)) + return + + //Regular expressions are, as usual, absolute magic + var/regex/all_invalid_symbols = new("\[^ -~]+") + + var/list/accepted = list() + for(var/string in proposed) + if(findtext(string,GLOB.is_website) || findtext(string,GLOB.is_email) || findtext(string,all_invalid_symbols) || !findtext(string,GLOB.is_alphanumeric)) + continue + var/buffer = "" + var/early_culling = TRUE + for(var/pos = 1, pos <= length(string), pos++) + var/let = copytext(string, pos, (pos + 1) % length(string)) + if(early_culling && !findtext(let,GLOB.is_alphanumeric)) + continue + early_culling = FALSE + buffer += let + if(!findtext(buffer,GLOB.is_alphanumeric)) + continue + var/punctbuffer = "" + var/cutoff = length(buffer) + for(var/pos = length(buffer), pos >= 0, pos--) + var/let = copytext(buffer, pos, (pos + 1) % length(buffer)) + if(findtext(let,GLOB.is_alphanumeric)) + break + if(findtext(let,GLOB.is_punctuation)) + punctbuffer = let + punctbuffer //Note this isn't the same thing as using += + cutoff = pos + if(punctbuffer) //We clip down excessive punctuation to get the letter count lower and reduce repeats. It's not perfect but it helps. + var/exclaim = FALSE + var/question = FALSE + var/periods = 0 + for(var/pos = length(punctbuffer), pos >= 0, pos--) + var/punct = copytext(punctbuffer, pos, (pos + 1) % length(punctbuffer)) + if(!exclaim && findtext(punct,"!")) + exclaim = TRUE + if(!question && findtext(punct,"?")) + question = TRUE + if(!exclaim && !question && findtext(punct,".")) + periods += 1 + if(exclaim) + if(question) + punctbuffer = "?!" + else + punctbuffer = "!" + else if(question) + punctbuffer = "?" + else if(periods) + if(periods > 1) + punctbuffer = "..." + else + punctbuffer = "" //Grammer nazis be damned + buffer = copytext(buffer, 1, cutoff) + punctbuffer + if(!findtext(buffer,GLOB.is_alphanumeric)) + continue + if(!buffer || length(buffer) > 280 || length(buffer) <= cullshort || buffer in accepted) + continue + + accepted += buffer + + var/log = file("data/npc_saves/[filename].json") //If this line ever shows up as changed in a PR be very careful you aren't being memed on + var/list/oldjson = list() + var/list/oldentries = list() + if(fexists(log)) + oldjson = json_decode(file2text(log)) + oldentries = oldjson["data"] + if(!isemptylist(oldentries)) + for(var/string in accepted) + for(var/old in oldentries) + if(string == old) + oldentries.Remove(old) //Line's position in line is "refreshed" until it falls off the in game radar + break + + var/list/finalized = list() + finalized = accepted.Copy() + oldentries.Copy() //we keep old and unreferenced phrases near the bottom for culling + listclearnulls(finalized) + if(!isemptylist(finalized) && length(finalized) > storemax) + finalized.Cut(storemax + 1) + fdel(log) + + var/list/tosend = list() + tosend["data"] = finalized + WRITE_FILE(log, json_encode(tosend)) + +//Used for applying byonds text macros to strings that are loaded at runtime +/proc/apply_text_macros(string) + var/next_backslash = findtext(string, "\\") + if(!next_backslash) + return string + + var/leng = length(string) + + var/next_space = findtext(string, " ", next_backslash + 1) + if(!next_space) + next_space = leng - next_backslash + + if(!next_space) //trailing bs + return string + + var/base = next_backslash == 1 ? "" : copytext(string, 1, next_backslash) + var/macro = lowertext(copytext(string, next_backslash + 1, next_space)) + var/rest = next_backslash > leng ? "" : copytext(string, next_space + 1) + + //See https://secure.byond.com/docs/ref/info.html#/DM/text/macros + switch(macro) + //prefixes/agnostic + if("the") + rest = text("\the []", rest) + if("a") + rest = text("\a []", rest) + if("an") + rest = text("\an []", rest) + if("proper") + rest = text("\proper []", rest) + if("improper") + rest = text("\improper []", rest) + if("roman") + rest = text("\roman []", rest) + //postfixes + if("th") + base = text("[]\th", rest) + if("s") + base = text("[]\s", rest) + if("he") + base = text("[]\he", rest) + if("she") + base = text("[]\she", rest) + if("his") + base = text("[]\his", rest) + if("himself") + base = text("[]\himself", rest) + if("herself") + base = text("[]\herself", rest) + if("hers") + base = text("[]\hers", rest) + + . = base + if(rest) + . += .(rest) + +//Replacement for the \th macro when you want the whole word output as text (first instead of 1st) +/proc/thtotext(number) + if(!isnum(number)) + return + switch(number) + if(1) + return "first" + if(2) + return "second" + if(3) + return "third" + if(4) + return "fourth" + if(5) + return "fifth" + if(6) + return "sixth" + if(7) + return "seventh" + if(8) + return "eighth" + if(9) + return "ninth" + if(10) + return "tenth" + if(11) + return "eleventh" + if(12) + return "twelfth" + else + return "[number]\th" + +/proc/unintelligize(message) + var/prefix=copytext(message,1,2) + if(prefix == ";") + message = copytext(message,2) + else if(prefix in list(":","#")) + prefix += copytext(message,2,3) + message = copytext(message,3) + else + prefix="" + + var/list/words = splittext(message," ") + var/list/rearranged = list() + for(var/i=1;i<=words.len;i++) + var/cword = pick(words) + words.Remove(cword) + var/suffix = copytext(cword,length(cword)-1,length(cword)) + while(length(cword)>0 && suffix in list(".",",",";","!",":","?")) + cword = copytext(cword,1 ,length(cword)-1) + suffix = copytext(cword,length(cword)-1,length(cword) ) + if(length(cword)) + rearranged += cword + message = "[prefix][jointext(rearranged," ")]" + . = message + +#define is_alpha(X) ((text2ascii(X) <= 122) && (text2ascii(X) >= 97)) #define is_digit(X) ((length(X) == 1) && (length(text2num(X)) == 1)) \ No newline at end of file diff --git a/code/game/machinery/status_display.dm b/code/game/machinery/status_display.dm index a722fcaf..af5f50f2 100644 --- a/code/game/machinery/status_display.dm +++ b/code/game/machinery/status_display.dm @@ -1,368 +1,368 @@ -// Status display -// (formerly Countdown timer display) - -#define CHARS_PER_LINE 5 -#define FONT_SIZE "5pt" -#define FONT_COLOR "#09f" -#define FONT_STYLE "Arial Black" -#define SCROLL_SPEED 2 - -#define SD_BLANK 0 // 0 = Blank -#define SD_EMERGENCY 1 // 1 = Emergency Shuttle timer -#define SD_MESSAGE 2 // 2 = Arbitrary message(s) -#define SD_PICTURE 3 // 3 = alert picture - -#define SD_AI_EMOTE 1 // 1 = AI emoticon -#define SD_AI_BSOD 2 // 2 = Blue screen of death - -/// Status display which can show images and scrolling text. -/obj/machinery/status_display - name = "status display" - desc = null - icon = 'icons/obj/status_display.dmi' - icon_state = "frame" - density = FALSE - use_power = IDLE_POWER_USE - idle_power_usage = 10 - - maptext_height = 26 - maptext_width = 32 - - var/message1 = "" // message line 1 - var/message2 = "" // message line 2 - var/index1 // display index for scrolling messages or 0 if non-scrolling - var/index2 - -/// Immediately blank the display. -/obj/machinery/status_display/proc/remove_display() - cut_overlays() - if(maptext) - maptext = "" - -/// Immediately change the display to the given picture. -/obj/machinery/status_display/proc/set_picture(state) - remove_display() - add_overlay(state) - -/// Immediately change the display to the given two lines. -/obj/machinery/status_display/proc/update_display(line1, line2) - var/new_text = {"
    [line1]
    [line2]
    "} - if(maptext != new_text) - maptext = new_text - -/// Prepare the display to marquee the given two lines. -/// -/// Call with no arguments to disable. -/obj/machinery/status_display/proc/set_message(m1, m2) - if(m1) - index1 = (length(m1) > CHARS_PER_LINE) - message1 = m1 - else - message1 = "" - index1 = 0 - - if(m2) - index2 = (length(m2) > CHARS_PER_LINE) - message2 = m2 - else - message2 = "" - index2 = 0 - -// Timed process - performs default marquee action if so needed. -/obj/machinery/status_display/process() - if(stat & NOPOWER) - // No power, no processing. - remove_display() - return PROCESS_KILL - - var/line1 = message1 - if(index1) - line1 = copytext("[message1]|[message1]", index1, index1+CHARS_PER_LINE) - var/message1_len = length(message1) - index1 += SCROLL_SPEED - if(index1 > message1_len) - index1 -= message1_len - - var/line2 = message2 - if(index2) - line2 = copytext("[message2]|[message2]", index2, index2+CHARS_PER_LINE) - var/message2_len = length(message2) - index2 += SCROLL_SPEED - if(index2 > message2_len) - index2 -= message2_len - - update_display(line1, line2) - if (!index1 && !index2) - // No marquee, no processing. - return PROCESS_KILL - -/// Update the display and, if necessary, re-enable processing. -/obj/machinery/status_display/proc/update() - if (process() != PROCESS_KILL) - START_PROCESSING(SSmachines, src) - -/obj/machinery/status_display/power_change() - . = ..() - update() - -/obj/machinery/status_display/emp_act(severity) - . = ..() - if(stat & (NOPOWER|BROKEN) || . & EMP_PROTECT_SELF) - return - set_picture("ai_bsod") - -/obj/machinery/status_display/examine(mob/user) - . = ..() - if (message1 || message2) - . += "The display says:" - if (message1) - . += "\t[html_encode(message1)]" - if (message2) - . += "\t[html_encode(message2)]" - -// Helper procs for child display types. -/obj/machinery/status_display/proc/display_shuttle_status(obj/docking_port/mobile/shuttle) - if(!shuttle) - // the shuttle is missing - no processing - update_display("shutl?","") - return PROCESS_KILL - else if(shuttle.timer) - var/line1 = "-[shuttle.getModeStr()]-" - var/line2 = shuttle.getTimerStr() - - if(length(line2) > CHARS_PER_LINE) - line2 = "error" - update_display(line1, line2) - else - // don't kill processing, the timer might turn back on - remove_display() - -/obj/machinery/status_display/proc/examine_shuttle(mob/user, obj/docking_port/mobile/shuttle) - if (shuttle) - var/modestr = shuttle.getModeStr() - if (modestr) - if (shuttle.timer) - modestr = "
    \t[modestr]: [shuttle.getTimerStr()]" - else - modestr = "
    \t[modestr]" - return "The display says:
    \t[shuttle.name][modestr]" - else - return "The display says:
    \tShuttle missing!" - - -/// Evac display which shows shuttle timer or message set by Command. -/obj/machinery/status_display/evac - var/frequency = FREQ_STATUS_DISPLAYS - var/mode = SD_EMERGENCY - var/friendc = FALSE // track if Friend Computer mode - var/last_picture // For when Friend Computer mode is undone - -/obj/machinery/status_display/evac/Initialize() - . = ..() - // register for radio system - SSradio.add_object(src, frequency) - -/obj/machinery/status_display/evac/Destroy() - SSradio.remove_object(src,frequency) - return ..() - -/obj/machinery/status_display/evac/process() - if(stat & NOPOWER) - // No power, no processing. - remove_display() - return PROCESS_KILL - - if(friendc) //Makes all status displays except supply shuttle timer display the eye -- Urist - set_picture("ai_friend") - return PROCESS_KILL - - switch(mode) - if(SD_BLANK) - remove_display() - return PROCESS_KILL - - if(SD_EMERGENCY) - return display_shuttle_status(SSshuttle.emergency) - - if(SD_MESSAGE) - return ..() - - if(SD_PICTURE) - set_picture(last_picture) - return PROCESS_KILL - -/obj/machinery/status_display/evac/examine(mob/user) - . = ..() - if(mode == SD_EMERGENCY) - . += examine_shuttle(user, SSshuttle.emergency) - else if(!message1 && !message2) - . += "The display is blank." - -/obj/machinery/status_display/evac/receive_signal(datum/signal/signal) - switch(signal.data["command"]) - if("blank") - mode = SD_BLANK - set_message(null, null) - if("shuttle") - mode = SD_EMERGENCY - set_message(null, null) - if("message") - mode = SD_MESSAGE - set_message(signal.data["msg1"], signal.data["msg2"]) - if("alert") - mode = SD_PICTURE - last_picture = signal.data["picture_state"] - set_picture(last_picture) - if("friendcomputer") - friendc = !friendc - update() - - -/// Supply display which shows the status of the supply shuttle. -/obj/machinery/status_display/supply - name = "supply display" - -/obj/machinery/status_display/supply/process() - if(stat & NOPOWER) - // No power, no processing. - remove_display() - return PROCESS_KILL - - var/line1 - var/line2 - if(!SSshuttle.supply) - // Might be missing in our first update on initialize before shuttles - // have loaded. Cross our fingers that it will soon return. - line1 = "CARGO" - line2 = "shutl?" - else if(SSshuttle.supply.mode == SHUTTLE_IDLE) - if(is_station_level(SSshuttle.supply.z)) - line1 = "CARGO" - line2 = "Docked" - else - line1 = "CARGO" - line2 = SSshuttle.supply.getTimerStr() - if(length(line2) > CHARS_PER_LINE) - line2 = "Error" - update_display(line1, line2) - -/obj/machinery/status_display/supply/examine(mob/user) - . = ..() - var/obj/docking_port/mobile/shuttle = SSshuttle.supply - var/shuttleMsg = null - if (shuttle.mode == SHUTTLE_IDLE) - if (is_station_level(shuttle.z)) - shuttleMsg = "Docked" - else - shuttleMsg = "[shuttle.getModeStr()]: [shuttle.getTimerStr()]" - if (shuttleMsg) - . += "The display says:
    \t[shuttleMsg]" - else - . += "The display is blank." - - -/// General-purpose shuttle status display. -/obj/machinery/status_display/shuttle - name = "shuttle display" - var/shuttle_id - -/obj/machinery/status_display/shuttle/process() - if(!shuttle_id || (stat & NOPOWER)) - // No power, no processing. - remove_display() - return PROCESS_KILL - - return display_shuttle_status(SSshuttle.getShuttle(shuttle_id)) - -/obj/machinery/status_display/shuttle/examine(mob/user) - . = ..() - if(shuttle_id) - . += examine_shuttle(user, SSshuttle.getShuttle(shuttle_id)) - else - . += "The display is blank." - -/obj/machinery/status_display/shuttle/vv_edit_var(var_name, var_value) - . = ..() - if(!.) - return - switch(var_name) - if("shuttle_id") - update() - -/obj/machinery/status_display/shuttle/proc/connect_to_shuttle(obj/docking_port/mobile/port, obj/docking_port/stationary/dock, idnum, override) - if (port && (shuttle_id == initial(shuttle_id) || override)) - shuttle_id = port.id - update() - - -/// Pictograph display which the AI can use to emote. -/obj/machinery/status_display/ai - name = "\improper AI display" - desc = "A small screen which the AI can use to present itself." - - var/mode = SD_BLANK - var/emotion = "Neutral" - -/obj/machinery/status_display/ai/Initialize() - . = ..() - GLOB.ai_status_displays.Add(src) - -/obj/machinery/status_display/ai/Destroy() - GLOB.ai_status_displays.Remove(src) - . = ..() - -/obj/machinery/status_display/ai/attack_ai(mob/living/silicon/ai/user) - if(isAI(user)) - user.ai_statuschange() - -/obj/machinery/status_display/ai/process() - if(mode == SD_BLANK || (stat & NOPOWER)) - remove_display() - return PROCESS_KILL - - if(mode == SD_AI_EMOTE) - switch(emotion) - if("Very Happy") - set_picture("ai_veryhappy") - if("Happy") - set_picture("ai_happy") - if("Neutral") - set_picture("ai_neutral") - if("Unsure") - set_picture("ai_unsure") - if("Confused") - set_picture("ai_confused") - if("Sad") - set_picture("ai_sad") - if("BSOD") - set_picture("ai_bsod") - if("Blank") - set_picture("ai_off") - if("Problems?") - set_picture("ai_trollface") - if("Awesome") - set_picture("ai_awesome") - if("Dorfy") - set_picture("ai_urist") - if("Thinking") - set_picture("ai_thinking") - if("Facepalm") - set_picture("ai_facepalm") - if("Friend Computer") - set_picture("ai_friend") - if("Blue Glow") - set_picture("ai_sal") - if("Red Glow") - set_picture("ai_hal") - return PROCESS_KILL - - if(mode == SD_AI_BSOD) - set_picture("ai_bsod") - return PROCESS_KILL - - -#undef CHARS_PER_LINE -#undef FONT_SIZE -#undef FONT_COLOR -#undef FONT_STYLE -#undef SCROLL_SPEED +// Status display +// (formerly Countdown timer display) + +#define CHARS_PER_LINE 5 +#define FONT_SIZE "5pt" +#define FONT_COLOR "#09f" +#define FONT_STYLE "Arial Black" +#define SCROLL_SPEED 2 + +#define SD_BLANK 0 // 0 = Blank +#define SD_EMERGENCY 1 // 1 = Emergency Shuttle timer +#define SD_MESSAGE 2 // 2 = Arbitrary message(s) +#define SD_PICTURE 3 // 3 = alert picture + +#define SD_AI_EMOTE 1 // 1 = AI emoticon +#define SD_AI_BSOD 2 // 2 = Blue screen of death + +/// Status display which can show images and scrolling text. +/obj/machinery/status_display + name = "status display" + desc = null + icon = 'icons/obj/status_display.dmi' + icon_state = "frame" + density = FALSE + use_power = IDLE_POWER_USE + idle_power_usage = 10 + + maptext_height = 26 + maptext_width = 32 + + var/message1 = "" // message line 1 + var/message2 = "" // message line 2 + var/index1 // display index for scrolling messages or 0 if non-scrolling + var/index2 + +/// Immediately blank the display. +/obj/machinery/status_display/proc/remove_display() + cut_overlays() + if(maptext) + maptext = "" + +/// Immediately change the display to the given picture. +/obj/machinery/status_display/proc/set_picture(state) + remove_display() + add_overlay(state) + +/// Immediately change the display to the given two lines. +/obj/machinery/status_display/proc/update_display(line1, line2) + var/new_text = {"
    [line1]
    [line2]
    "} + if(maptext != new_text) + maptext = new_text + +/// Prepare the display to marquee the given two lines. +/// +/// Call with no arguments to disable. +/obj/machinery/status_display/proc/set_message(m1, m2) + if(m1) + index1 = (length(m1) > CHARS_PER_LINE) + message1 = m1 + else + message1 = "" + index1 = 0 + + if(m2) + index2 = (length(m2) > CHARS_PER_LINE) + message2 = m2 + else + message2 = "" + index2 = 0 + +// Timed process - performs default marquee action if so needed. +/obj/machinery/status_display/process() + if(stat & NOPOWER) + // No power, no processing. + remove_display() + return PROCESS_KILL + + var/line1 = message1 + if(index1) + line1 = copytext("[message1]|[message1]", index1, index1+CHARS_PER_LINE) + var/message1_len = length(message1) + index1 += SCROLL_SPEED + if(index1 > message1_len) + index1 -= message1_len + + var/line2 = message2 + if(index2) + line2 = copytext("[message2]|[message2]", index2, index2+CHARS_PER_LINE) + var/message2_len = length(message2) + index2 += SCROLL_SPEED + if(index2 > message2_len) + index2 -= message2_len + + update_display(line1, line2) + if (!index1 && !index2) + // No marquee, no processing. + return PROCESS_KILL + +/// Update the display and, if necessary, re-enable processing. +/obj/machinery/status_display/proc/update() + if (process() != PROCESS_KILL) + START_PROCESSING(SSmachines, src) + +/obj/machinery/status_display/power_change() + . = ..() + update() + +/obj/machinery/status_display/emp_act(severity) + . = ..() + if(stat & (NOPOWER|BROKEN) || . & EMP_PROTECT_SELF) + return + set_picture("ai_bsod") + +/obj/machinery/status_display/examine(mob/user) + . = ..() + if (message1 || message2) + . += "The display says:" + if (message1) + . += "\t[html_encode(message1)]" + if (message2) + . += "\t[html_encode(message2)]" + +// Helper procs for child display types. +/obj/machinery/status_display/proc/display_shuttle_status(obj/docking_port/mobile/shuttle) + if(!shuttle) + // the shuttle is missing - no processing + update_display("shutl?","") + return PROCESS_KILL + else if(shuttle.timer) + var/line1 = "-[shuttle.getModeStr()]-" + var/line2 = shuttle.getTimerStr() + + if(length(line2) > CHARS_PER_LINE) + line2 = "error" + update_display(line1, line2) + else + // don't kill processing, the timer might turn back on + remove_display() + +/obj/machinery/status_display/proc/examine_shuttle(mob/user, obj/docking_port/mobile/shuttle) + if (shuttle) + var/modestr = shuttle.getModeStr() + if (modestr) + if (shuttle.timer) + modestr = "
    \t[modestr]: [shuttle.getTimerStr()]" + else + modestr = "
    \t[modestr]" + return "The display says:
    \t[shuttle.name][modestr]" + else + return "The display says:
    \tShuttle missing!" + + +/// Evac display which shows shuttle timer or message set by Command. +/obj/machinery/status_display/evac + var/frequency = FREQ_STATUS_DISPLAYS + var/mode = SD_EMERGENCY + var/friendc = FALSE // track if Friend Computer mode + var/last_picture // For when Friend Computer mode is undone + +/obj/machinery/status_display/evac/Initialize() + . = ..() + // register for radio system + SSradio.add_object(src, frequency) + +/obj/machinery/status_display/evac/Destroy() + SSradio.remove_object(src,frequency) + return ..() + +/obj/machinery/status_display/evac/process() + if(stat & NOPOWER) + // No power, no processing. + remove_display() + return PROCESS_KILL + + if(friendc) //Makes all status displays except supply shuttle timer display the eye -- Urist + set_picture("ai_friend") + return PROCESS_KILL + + switch(mode) + if(SD_BLANK) + remove_display() + return PROCESS_KILL + + if(SD_EMERGENCY) + return display_shuttle_status(SSshuttle.emergency) + + if(SD_MESSAGE) + return ..() + + if(SD_PICTURE) + set_picture(last_picture) + return PROCESS_KILL + +/obj/machinery/status_display/evac/examine(mob/user) + . = ..() + if(mode == SD_EMERGENCY) + . += examine_shuttle(user, SSshuttle.emergency) + else if(!message1 && !message2) + . += "The display is blank." + +/obj/machinery/status_display/evac/receive_signal(datum/signal/signal) + switch(signal.data["command"]) + if("blank") + mode = SD_BLANK + set_message(null, null) + if("shuttle") + mode = SD_EMERGENCY + set_message(null, null) + if("message") + mode = SD_MESSAGE + set_message(signal.data["msg1"], signal.data["msg2"]) + if("alert") + mode = SD_PICTURE + last_picture = signal.data["picture_state"] + set_picture(last_picture) + if("friendcomputer") + friendc = !friendc + update() + + +/// Supply display which shows the status of the supply shuttle. +/obj/machinery/status_display/supply + name = "supply display" + +/obj/machinery/status_display/supply/process() + if(stat & NOPOWER) + // No power, no processing. + remove_display() + return PROCESS_KILL + + var/line1 + var/line2 + if(!SSshuttle.supply) + // Might be missing in our first update on initialize before shuttles + // have loaded. Cross our fingers that it will soon return. + line1 = "CARGO" + line2 = "shutl?" + else if(SSshuttle.supply.mode == SHUTTLE_IDLE) + if(is_station_level(SSshuttle.supply.z)) + line1 = "CARGO" + line2 = "Docked" + else + line1 = "CARGO" + line2 = SSshuttle.supply.getTimerStr() + if(length(line2) > CHARS_PER_LINE) + line2 = "Error" + update_display(line1, line2) + +/obj/machinery/status_display/supply/examine(mob/user) + . = ..() + var/obj/docking_port/mobile/shuttle = SSshuttle.supply + var/shuttleMsg = null + if (shuttle.mode == SHUTTLE_IDLE) + if (is_station_level(shuttle.z)) + shuttleMsg = "Docked" + else + shuttleMsg = "[shuttle.getModeStr()]: [shuttle.getTimerStr()]" + if (shuttleMsg) + . += "The display says:
    \t[shuttleMsg]" + else + . += "The display is blank." + + +/// General-purpose shuttle status display. +/obj/machinery/status_display/shuttle + name = "shuttle display" + var/shuttle_id + +/obj/machinery/status_display/shuttle/process() + if(!shuttle_id || (stat & NOPOWER)) + // No power, no processing. + remove_display() + return PROCESS_KILL + + return display_shuttle_status(SSshuttle.getShuttle(shuttle_id)) + +/obj/machinery/status_display/shuttle/examine(mob/user) + . = ..() + if(shuttle_id) + . += examine_shuttle(user, SSshuttle.getShuttle(shuttle_id)) + else + . += "The display is blank." + +/obj/machinery/status_display/shuttle/vv_edit_var(var_name, var_value) + . = ..() + if(!.) + return + switch(var_name) + if("shuttle_id") + update() + +/obj/machinery/status_display/shuttle/proc/connect_to_shuttle(obj/docking_port/mobile/port, obj/docking_port/stationary/dock, idnum, override) + if (port && (shuttle_id == initial(shuttle_id) || override)) + shuttle_id = port.id + update() + + +/// Pictograph display which the AI can use to emote. +/obj/machinery/status_display/ai + name = "\improper AI display" + desc = "A small screen which the AI can use to present itself." + + var/mode = SD_BLANK + var/emotion = "Neutral" + +/obj/machinery/status_display/ai/Initialize() + . = ..() + GLOB.ai_status_displays.Add(src) + +/obj/machinery/status_display/ai/Destroy() + GLOB.ai_status_displays.Remove(src) + . = ..() + +/obj/machinery/status_display/ai/attack_ai(mob/living/silicon/ai/user) + if(isAI(user)) + user.ai_statuschange() + +/obj/machinery/status_display/ai/process() + if(mode == SD_BLANK || (stat & NOPOWER)) + remove_display() + return PROCESS_KILL + + if(mode == SD_AI_EMOTE) + switch(emotion) + if("Very Happy") + set_picture("ai_veryhappy") + if("Happy") + set_picture("ai_happy") + if("Neutral") + set_picture("ai_neutral") + if("Unsure") + set_picture("ai_unsure") + if("Confused") + set_picture("ai_confused") + if("Sad") + set_picture("ai_sad") + if("BSOD") + set_picture("ai_bsod") + if("Blank") + set_picture("ai_off") + if("Problems?") + set_picture("ai_trollface") + if("Awesome") + set_picture("ai_awesome") + if("Dorfy") + set_picture("ai_urist") + if("Thinking") + set_picture("ai_thinking") + if("Facepalm") + set_picture("ai_facepalm") + if("Friend Computer") + set_picture("ai_friend") + if("Blue Glow") + set_picture("ai_sal") + if("Red Glow") + set_picture("ai_hal") + return PROCESS_KILL + + if(mode == SD_AI_BSOD) + set_picture("ai_bsod") + return PROCESS_KILL + + +#undef CHARS_PER_LINE +#undef FONT_SIZE +#undef FONT_COLOR +#undef FONT_STYLE +#undef SCROLL_SPEED diff --git a/code/game/objects/structures/musician.dm b/code/game/objects/structures/musician.dm index 4c85e746..36dbcc1e 100644 --- a/code/game/objects/structures/musician.dm +++ b/code/game/objects/structures/musician.dm @@ -1,381 +1,381 @@ - -#define MUSICIAN_HEARCHECK_MINDELAY 4 -#define MUSIC_MAXLINES 600 -#define MUSIC_MAXLINECHARS 50 - -/datum/song - var/name = "Untitled" - var/list/lines = new() - var/tempo = 5 // delay between notes - - var/playing = 0 // if we're playing - var/help = 0 // if help is open - var/edit = 1 // if we're in editing mode - var/repeat = 0 // number of times remaining to repeat - var/max_repeats = 10 // maximum times we can repeat - - var/instrumentDir = "piano" // the folder with the sounds - var/instrumentExt = "ogg" // the file extension - var/obj/instrumentObj = null // the associated obj playing the sound - var/last_hearcheck = 0 - var/list/hearing_mobs - -/datum/song/New(dir, obj, ext = "ogg") - tempo = sanitize_tempo(tempo) - instrumentDir = dir - instrumentObj = obj - instrumentExt = ext - -/datum/song/Destroy() - instrumentObj = null - return ..() - -// note is a number from 1-7 for A-G -// acc is either "b", "n", or "#" -// oct is 1-8 (or 9 for C) -/datum/song/proc/playnote(note, acc as text, oct) - // handle accidental -> B<>C of E<>F - if(acc == "b" && (note == 3 || note == 6)) // C or F - if(note == 3) - oct-- - note-- - acc = "n" - else if(acc == "#" && (note == 2 || note == 5)) // B or E - if(note == 2) - oct++ - note++ - acc = "n" - else if(acc == "#" && (note == 7)) //G# - note = 1 - acc = "b" - else if(acc == "#") // mass convert all sharps to flats, octave jump already handled - acc = "b" - note++ - - // check octave, C is allowed to go to 9 - if(oct < 1 || (note == 3 ? oct > 9 : oct > 8)) - return - - // now generate name - var/soundfile = "sound/instruments/[instrumentDir]/[ascii2text(note+64)][acc][oct].[instrumentExt]" - soundfile = file(soundfile) - // make sure the note exists - if(!fexists(soundfile)) - return - // and play - var/turf/source = get_turf(instrumentObj) - if((world.time - MUSICIAN_HEARCHECK_MINDELAY) > last_hearcheck) - LAZYCLEARLIST(hearing_mobs) - for(var/mob/M in get_hearers_in_view(15, source)) - if(!M.client || !(M.client.prefs.toggles & SOUND_INSTRUMENTS)) - continue - LAZYADD(hearing_mobs, M) - last_hearcheck = world.time - - var/sound/music_played = sound(soundfile) - for(var/i in hearing_mobs) - var/mob/M = i - M.playsound_local(source, null, 100, falloff = 5, S = music_played) - -/datum/song/proc/updateDialog(mob/user) - instrumentObj.updateDialog() // assumes it's an object in world, override if otherwise - -/datum/song/proc/shouldStopPlaying(mob/user) - if(instrumentObj) - if(!user.canUseTopic(instrumentObj)) - return TRUE - return !instrumentObj.anchored // add special cases to stop in subclasses - else - return TRUE - -/datum/song/proc/playsong(mob/user) - while(repeat >= 0) - var/cur_oct[7] - var/cur_acc[7] - for(var/i = 1 to 7) - cur_oct[i] = 3 - cur_acc[i] = "n" - - for(var/line in lines) - for(var/beat in splittext(lowertext(line), ",")) - var/list/notes = splittext(beat, "/") - for(var/note in splittext(notes[1], "-")) - if(!playing || shouldStopPlaying(user))//If the instrument is playing, or special case - playing = FALSE - hearing_mobs = null - return - if(!length(note)) - continue - var/cur_note = text2ascii(note) - 96 - if(cur_note < 1 || cur_note > 7) - continue - for(var/i=2 to length(note)) - var/ni = copytext(note,i,i+1) - if(!text2num(ni)) - if(ni == "#" || ni == "b" || ni == "n") - cur_acc[cur_note] = ni - else if(ni == "s") - cur_acc[cur_note] = "#" // so shift is never required - else - cur_oct[cur_note] = text2num(ni) - if(user.dizziness > 0 && prob(user.dizziness / 2)) - cur_note = CLAMP(cur_note + rand(round(-user.dizziness / 10), round(user.dizziness / 10)), 1, 7) - if(user.dizziness > 0 && prob(user.dizziness / 5)) - if(prob(30)) - cur_acc[cur_note] = "#" - else if(prob(42)) - cur_acc[cur_note] = "b" - else if(prob(75)) - cur_acc[cur_note] = "n" - playnote(cur_note, cur_acc[cur_note], cur_oct[cur_note]) - if(notes.len >= 2 && text2num(notes[2])) - sleep(sanitize_tempo(tempo / text2num(notes[2]))) - else - sleep(tempo) - repeat-- - hearing_mobs = null - playing = FALSE - repeat = 0 - updateDialog(user) - -/datum/song/proc/interact(mob/user) - var/dat = "" - - if(lines.len > 0) - dat += "

    Playback

    " - if(!playing) - dat += "Play Stop

    " - dat += "Repeat Song: " - dat += repeat > 0 ? "--" : "--" - dat += " [repeat] times " - dat += repeat < max_repeats ? "++" : "++" - dat += "
    " - else - dat += "Play Stop
    " - dat += "Repeats left: [repeat]
    " - if(!edit) - dat += "
    Show Editor
    " - else - dat += "

    Editing

    " - dat += "Hide Editor" - dat += " Start a New Song" - dat += " Import a Song

    " - var/bpm = round(600 / tempo) - dat += "Tempo: - [bpm] BPM +

    " - var/linecount = 0 - for(var/line in lines) - linecount += 1 - dat += "Line [linecount]: Edit X [line]
    " - dat += "Add Line

    " - if(help) - dat += "Hide Help
    " - dat += {" - Lines are a series of chords, separated by commas (,), each with notes separated by hyphens (-).
    - Every note in a chord will play together, with chord timed by the tempo.
    -
    - Notes are played by the names of the note, and optionally, the accidental, and/or the octave number.
    - By default, every note is natural and in octave 3. Defining otherwise is remembered for each note.
    - Example: C,D,E,F,G,A,B will play a C major scale.
    - After a note has an accidental placed, it will be remembered: C,C4,C,C3 is C3,C4,C4,C3
    - Chords can be played simply by seperating each note with a hyphon: A-C#,Cn-E,E-G#,Gn-B
    - A pause may be denoted by an empty chord: C,E,,C,G
    - To make a chord be a different time, end it with /x, where the chord length will be length
    - defined by tempo / x: C,G/2,E/4
    - Combined, an example is: E-E4/4,F#/2,G#/8,B/8,E3-E4/4 -
    - Lines may be up to [MUSIC_MAXLINECHARS] characters.
    - A song may only contain up to [MUSIC_MAXLINES] lines.
    - "} - else - dat += "Show Help
    " - - var/datum/browser/popup = new(user, "instrument", instrumentObj.name, 700, 500) - popup.set_content(dat) - popup.set_title_image(user.browse_rsc_icon(instrumentObj.icon, instrumentObj.icon_state)) - popup.open() - -/datum/song/proc/ParseSong(text) - set waitfor = FALSE - //split into lines - lines = splittext(text, "\n") - if(lines.len) - if(copytext(lines[1],1,6) == "BPM: ") - tempo = sanitize_tempo(600 / text2num(copytext(lines[1],6))) - lines.Cut(1,2) - else - tempo = sanitize_tempo(5) // default 120 BPM - if(lines.len > MUSIC_MAXLINES) - to_chat(usr, "Too many lines!") - lines.Cut(MUSIC_MAXLINES + 1) - var/linenum = 1 - for(var/l in lines) - if(length(l) > MUSIC_MAXLINECHARS) - to_chat(usr, "Line [linenum] too long!") - lines.Remove(l) - else - linenum++ - updateDialog(usr) // make sure updates when complete - -/datum/song/Topic(href, href_list) - if(!usr.canUseTopic(instrumentObj)) - usr << browse(null, "window=instrument") - usr.unset_machine() - return - - instrumentObj.add_fingerprint(usr) - - if(href_list["newsong"]) - lines = new() - tempo = sanitize_tempo(5) // default 120 BPM - name = "" - - else if(href_list["import"]) - var/t = "" - do - t = html_encode(input(usr, "Please paste the entire song, formatted:", text("[]", name), t) as message) - if(!in_range(instrumentObj, usr)) - return - - if(length(t) >= MUSIC_MAXLINES * MUSIC_MAXLINECHARS) - var/cont = input(usr, "Your message is too long! Would you like to continue editing it?", "", "yes") in list("yes", "no") - if(cont == "no") - break - while(length(t) > MUSIC_MAXLINES * MUSIC_MAXLINECHARS) - ParseSong(t) - - else if(href_list["help"]) - help = text2num(href_list["help"]) - 1 - - else if(href_list["edit"]) - edit = text2num(href_list["edit"]) - 1 - - if(href_list["repeat"]) //Changing this from a toggle to a number of repeats to avoid infinite loops. - if(playing) - return //So that people cant keep adding to repeat. If the do it intentionally, it could result in the server crashing. - repeat += round(text2num(href_list["repeat"])) - if(repeat < 0) - repeat = 0 - if(repeat > max_repeats) - repeat = max_repeats - - else if(href_list["tempo"]) - tempo = sanitize_tempo(tempo + text2num(href_list["tempo"])) - - else if(href_list["play"]) - playing = TRUE - spawn() - playsong(usr) - - else if(href_list["newline"]) - var/newline = html_encode(input("Enter your line: ", instrumentObj.name) as text|null) - if(!newline || !in_range(instrumentObj, usr)) - return - if(lines.len > MUSIC_MAXLINES) - return - if(length(newline) > MUSIC_MAXLINECHARS) - newline = copytext(newline, 1, MUSIC_MAXLINECHARS) - lines.Add(newline) - - else if(href_list["deleteline"]) - var/num = round(text2num(href_list["deleteline"])) - if(num > lines.len || num < 1) - return - lines.Cut(num, num+1) - - else if(href_list["modifyline"]) - var/num = round(text2num(href_list["modifyline"]),1) - var/content = html_encode(input("Enter your line: ", instrumentObj.name, lines[num]) as text|null) - if(!content || !in_range(instrumentObj, usr)) - return - if(length(content) > MUSIC_MAXLINECHARS) - content = copytext(content, 1, MUSIC_MAXLINECHARS) - if(num > lines.len || num < 1) - return - lines[num] = content - - else if(href_list["stop"]) - playing = FALSE - hearing_mobs = null - - updateDialog(usr) - return - -/datum/song/proc/sanitize_tempo(new_tempo) - new_tempo = abs(new_tempo) - return max(round(new_tempo, world.tick_lag), world.tick_lag) - -// subclass for handheld instruments, like violin -/datum/song/handheld - -/datum/song/handheld/updateDialog(mob/user) - instrumentObj.interact(user) - -/datum/song/handheld/shouldStopPlaying() - if(instrumentObj) - return !isliving(instrumentObj.loc) - else - return TRUE - - -////////////////////////////////////////////////////////////////////////// - - -/obj/structure/piano - name = "space minimoog" - icon = 'icons/obj/musician.dmi' - icon_state = "minimoog" - anchored = TRUE - density = TRUE - var/datum/song/song - -/obj/structure/piano/unanchored - anchored = FALSE - -/obj/structure/piano/New() - ..() - song = new("piano", src) - - if(prob(50) && icon_state == initial(icon_state)) - name = "space minimoog" - desc = "This is a minimoog, like a space piano, but more spacey!" - icon_state = "minimoog" - else - name = "space piano" - desc = "This is a space piano, like a regular piano, but always in tune! Even if the musician isn't." - icon_state = "piano" - -/obj/structure/piano/Destroy() - qdel(song) - song = null - return ..() - -/obj/structure/piano/Initialize(mapload) - . = ..() - if(mapload) - song.tempo = song.sanitize_tempo(song.tempo) // tick_lag isn't set when the map is loaded - -/obj/structure/piano/attack_hand(mob/user) - . = ..() - if(.) - return - interact(user) - -/obj/structure/piano/attack_paw(mob/user) - return attack_hand(user) - -/obj/structure/piano/interact(mob/user) - ui_interact(user) - -/obj/structure/piano/ui_interact(mob/user) - if(!user || !anchored) - return - - if(!user.IsAdvancedToolUser()) - to_chat(user, "You don't have the dexterity to do this!") - return 1 - user.set_machine(src) - song.interact(user) - -/obj/structure/piano/wrench_act(mob/living/user, obj/item/I) - default_unfasten_wrench(user, I, 40) - return TRUE + +#define MUSICIAN_HEARCHECK_MINDELAY 4 +#define MUSIC_MAXLINES 600 +#define MUSIC_MAXLINECHARS 50 + +/datum/song + var/name = "Untitled" + var/list/lines = new() + var/tempo = 5 // delay between notes + + var/playing = 0 // if we're playing + var/help = 0 // if help is open + var/edit = 1 // if we're in editing mode + var/repeat = 0 // number of times remaining to repeat + var/max_repeats = 10 // maximum times we can repeat + + var/instrumentDir = "piano" // the folder with the sounds + var/instrumentExt = "ogg" // the file extension + var/obj/instrumentObj = null // the associated obj playing the sound + var/last_hearcheck = 0 + var/list/hearing_mobs + +/datum/song/New(dir, obj, ext = "ogg") + tempo = sanitize_tempo(tempo) + instrumentDir = dir + instrumentObj = obj + instrumentExt = ext + +/datum/song/Destroy() + instrumentObj = null + return ..() + +// note is a number from 1-7 for A-G +// acc is either "b", "n", or "#" +// oct is 1-8 (or 9 for C) +/datum/song/proc/playnote(note, acc as text, oct) + // handle accidental -> B<>C of E<>F + if(acc == "b" && (note == 3 || note == 6)) // C or F + if(note == 3) + oct-- + note-- + acc = "n" + else if(acc == "#" && (note == 2 || note == 5)) // B or E + if(note == 2) + oct++ + note++ + acc = "n" + else if(acc == "#" && (note == 7)) //G# + note = 1 + acc = "b" + else if(acc == "#") // mass convert all sharps to flats, octave jump already handled + acc = "b" + note++ + + // check octave, C is allowed to go to 9 + if(oct < 1 || (note == 3 ? oct > 9 : oct > 8)) + return + + // now generate name + var/soundfile = "sound/instruments/[instrumentDir]/[ascii2text(note+64)][acc][oct].[instrumentExt]" + soundfile = file(soundfile) + // make sure the note exists + if(!fexists(soundfile)) + return + // and play + var/turf/source = get_turf(instrumentObj) + if((world.time - MUSICIAN_HEARCHECK_MINDELAY) > last_hearcheck) + LAZYCLEARLIST(hearing_mobs) + for(var/mob/M in get_hearers_in_view(15, source)) + if(!M.client || !(M.client.prefs.toggles & SOUND_INSTRUMENTS)) + continue + LAZYADD(hearing_mobs, M) + last_hearcheck = world.time + + var/sound/music_played = sound(soundfile) + for(var/i in hearing_mobs) + var/mob/M = i + M.playsound_local(source, null, 100, falloff = 5, S = music_played) + +/datum/song/proc/updateDialog(mob/user) + instrumentObj.updateDialog() // assumes it's an object in world, override if otherwise + +/datum/song/proc/shouldStopPlaying(mob/user) + if(instrumentObj) + if(!user.canUseTopic(instrumentObj)) + return TRUE + return !instrumentObj.anchored // add special cases to stop in subclasses + else + return TRUE + +/datum/song/proc/playsong(mob/user) + while(repeat >= 0) + var/cur_oct[7] + var/cur_acc[7] + for(var/i = 1 to 7) + cur_oct[i] = 3 + cur_acc[i] = "n" + + for(var/line in lines) + for(var/beat in splittext(lowertext(line), ",")) + var/list/notes = splittext(beat, "/") + for(var/note in splittext(notes[1], "-")) + if(!playing || shouldStopPlaying(user))//If the instrument is playing, or special case + playing = FALSE + hearing_mobs = null + return + if(!length(note)) + continue + var/cur_note = text2ascii(note) - 96 + if(cur_note < 1 || cur_note > 7) + continue + for(var/i=2 to length(note)) + var/ni = copytext(note,i,i+1) + if(!text2num(ni)) + if(ni == "#" || ni == "b" || ni == "n") + cur_acc[cur_note] = ni + else if(ni == "s") + cur_acc[cur_note] = "#" // so shift is never required + else + cur_oct[cur_note] = text2num(ni) + if(user.dizziness > 0 && prob(user.dizziness / 2)) + cur_note = CLAMP(cur_note + rand(round(-user.dizziness / 10), round(user.dizziness / 10)), 1, 7) + if(user.dizziness > 0 && prob(user.dizziness / 5)) + if(prob(30)) + cur_acc[cur_note] = "#" + else if(prob(42)) + cur_acc[cur_note] = "b" + else if(prob(75)) + cur_acc[cur_note] = "n" + playnote(cur_note, cur_acc[cur_note], cur_oct[cur_note]) + if(notes.len >= 2 && text2num(notes[2])) + sleep(sanitize_tempo(tempo / text2num(notes[2]))) + else + sleep(tempo) + repeat-- + hearing_mobs = null + playing = FALSE + repeat = 0 + updateDialog(user) + +/datum/song/proc/interact(mob/user) + var/dat = "" + + if(lines.len > 0) + dat += "

    Playback

    " + if(!playing) + dat += "Play Stop

    " + dat += "Repeat Song: " + dat += repeat > 0 ? "--" : "--" + dat += " [repeat] times " + dat += repeat < max_repeats ? "++" : "++" + dat += "
    " + else + dat += "Play Stop
    " + dat += "Repeats left: [repeat]
    " + if(!edit) + dat += "
    Show Editor
    " + else + dat += "

    Editing

    " + dat += "Hide Editor" + dat += " Start a New Song" + dat += " Import a Song

    " + var/bpm = round(600 / tempo) + dat += "Tempo: - [bpm] BPM +

    " + var/linecount = 0 + for(var/line in lines) + linecount += 1 + dat += "Line [linecount]: Edit X [line]
    " + dat += "Add Line

    " + if(help) + dat += "Hide Help
    " + dat += {" + Lines are a series of chords, separated by commas (,), each with notes separated by hyphens (-).
    + Every note in a chord will play together, with chord timed by the tempo.
    +
    + Notes are played by the names of the note, and optionally, the accidental, and/or the octave number.
    + By default, every note is natural and in octave 3. Defining otherwise is remembered for each note.
    + Example: C,D,E,F,G,A,B will play a C major scale.
    + After a note has an accidental placed, it will be remembered: C,C4,C,C3 is C3,C4,C4,C3
    + Chords can be played simply by seperating each note with a hyphon: A-C#,Cn-E,E-G#,Gn-B
    + A pause may be denoted by an empty chord: C,E,,C,G
    + To make a chord be a different time, end it with /x, where the chord length will be length
    + defined by tempo / x: C,G/2,E/4
    + Combined, an example is: E-E4/4,F#/2,G#/8,B/8,E3-E4/4 +
    + Lines may be up to [MUSIC_MAXLINECHARS] characters.
    + A song may only contain up to [MUSIC_MAXLINES] lines.
    + "} + else + dat += "Show Help
    " + + var/datum/browser/popup = new(user, "instrument", instrumentObj.name, 700, 500) + popup.set_content(dat) + popup.set_title_image(user.browse_rsc_icon(instrumentObj.icon, instrumentObj.icon_state)) + popup.open() + +/datum/song/proc/ParseSong(text) + set waitfor = FALSE + //split into lines + lines = splittext(text, "\n") + if(lines.len) + if(copytext(lines[1],1,6) == "BPM: ") + tempo = sanitize_tempo(600 / text2num(copytext(lines[1],6))) + lines.Cut(1,2) + else + tempo = sanitize_tempo(5) // default 120 BPM + if(lines.len > MUSIC_MAXLINES) + to_chat(usr, "Too many lines!") + lines.Cut(MUSIC_MAXLINES + 1) + var/linenum = 1 + for(var/l in lines) + if(length(l) > MUSIC_MAXLINECHARS) + to_chat(usr, "Line [linenum] too long!") + lines.Remove(l) + else + linenum++ + updateDialog(usr) // make sure updates when complete + +/datum/song/Topic(href, href_list) + if(!usr.canUseTopic(instrumentObj)) + usr << browse(null, "window=instrument") + usr.unset_machine() + return + + instrumentObj.add_fingerprint(usr) + + if(href_list["newsong"]) + lines = new() + tempo = sanitize_tempo(5) // default 120 BPM + name = "" + + else if(href_list["import"]) + var/t = "" + do + t = html_encode(input(usr, "Please paste the entire song, formatted:", text("[]", name), t) as message) + if(!in_range(instrumentObj, usr)) + return + + if(length(t) >= MUSIC_MAXLINES * MUSIC_MAXLINECHARS) + var/cont = input(usr, "Your message is too long! Would you like to continue editing it?", "", "yes") in list("yes", "no") + if(cont == "no") + break + while(length(t) > MUSIC_MAXLINES * MUSIC_MAXLINECHARS) + ParseSong(t) + + else if(href_list["help"]) + help = text2num(href_list["help"]) - 1 + + else if(href_list["edit"]) + edit = text2num(href_list["edit"]) - 1 + + if(href_list["repeat"]) //Changing this from a toggle to a number of repeats to avoid infinite loops. + if(playing) + return //So that people cant keep adding to repeat. If the do it intentionally, it could result in the server crashing. + repeat += round(text2num(href_list["repeat"])) + if(repeat < 0) + repeat = 0 + if(repeat > max_repeats) + repeat = max_repeats + + else if(href_list["tempo"]) + tempo = sanitize_tempo(tempo + text2num(href_list["tempo"])) + + else if(href_list["play"]) + playing = TRUE + spawn() + playsong(usr) + + else if(href_list["newline"]) + var/newline = html_encode(input("Enter your line: ", instrumentObj.name) as text|null) + if(!newline || !in_range(instrumentObj, usr)) + return + if(lines.len > MUSIC_MAXLINES) + return + if(length(newline) > MUSIC_MAXLINECHARS) + newline = copytext(newline, 1, MUSIC_MAXLINECHARS) + lines.Add(newline) + + else if(href_list["deleteline"]) + var/num = round(text2num(href_list["deleteline"])) + if(num > lines.len || num < 1) + return + lines.Cut(num, num+1) + + else if(href_list["modifyline"]) + var/num = round(text2num(href_list["modifyline"]),1) + var/content = html_encode(input("Enter your line: ", instrumentObj.name, lines[num]) as text|null) + if(!content || !in_range(instrumentObj, usr)) + return + if(length(content) > MUSIC_MAXLINECHARS) + content = copytext(content, 1, MUSIC_MAXLINECHARS) + if(num > lines.len || num < 1) + return + lines[num] = content + + else if(href_list["stop"]) + playing = FALSE + hearing_mobs = null + + updateDialog(usr) + return + +/datum/song/proc/sanitize_tempo(new_tempo) + new_tempo = abs(new_tempo) + return max(round(new_tempo, world.tick_lag), world.tick_lag) + +// subclass for handheld instruments, like violin +/datum/song/handheld + +/datum/song/handheld/updateDialog(mob/user) + instrumentObj.interact(user) + +/datum/song/handheld/shouldStopPlaying() + if(instrumentObj) + return !isliving(instrumentObj.loc) + else + return TRUE + + +////////////////////////////////////////////////////////////////////////// + + +/obj/structure/piano + name = "space minimoog" + icon = 'icons/obj/musician.dmi' + icon_state = "minimoog" + anchored = TRUE + density = TRUE + var/datum/song/song + +/obj/structure/piano/unanchored + anchored = FALSE + +/obj/structure/piano/New() + ..() + song = new("piano", src) + + if(prob(50) && icon_state == initial(icon_state)) + name = "space minimoog" + desc = "This is a minimoog, like a space piano, but more spacey!" + icon_state = "minimoog" + else + name = "space piano" + desc = "This is a space piano, like a regular piano, but always in tune! Even if the musician isn't." + icon_state = "piano" + +/obj/structure/piano/Destroy() + qdel(song) + song = null + return ..() + +/obj/structure/piano/Initialize(mapload) + . = ..() + if(mapload) + song.tempo = song.sanitize_tempo(song.tempo) // tick_lag isn't set when the map is loaded + +/obj/structure/piano/attack_hand(mob/user) + . = ..() + if(.) + return + interact(user) + +/obj/structure/piano/attack_paw(mob/user) + return attack_hand(user) + +/obj/structure/piano/interact(mob/user) + ui_interact(user) + +/obj/structure/piano/ui_interact(mob/user) + if(!user || !anchored) + return + + if(!user.IsAdvancedToolUser()) + to_chat(user, "You don't have the dexterity to do this!") + return 1 + user.set_machine(src) + song.interact(user) + +/obj/structure/piano/wrench_act(mob/living/user, obj/item/I) + default_unfasten_wrench(user, I, 40) + return TRUE diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm index 8b45bb47..09cfb2d1 100644 --- a/code/modules/admin/topic.dm +++ b/code/modules/admin/topic.dm @@ -1,2991 +1,2991 @@ -/datum/admins/proc/CheckAdminHref(href, href_list) - var/auth = href_list["admin_token"] - . = auth && (auth == href_token || auth == GLOB.href_token) - if(.) - return - var/msg = !auth ? "no" : "a bad" - message_admins("[key_name_admin(usr)] clicked an href with [msg] authorization key!") - if(CONFIG_GET(flag/debug_admin_hrefs)) - message_admins("Debug mode enabled, call not blocked. Please ask your coders to review this round's logs.") - log_world("UAH: [href]") - return TRUE - log_admin_private("[key_name(usr)] clicked an href with [msg] authorization key! [href]") - -/datum/admins/Topic(href, href_list) - ..() - - if(usr.client != src.owner || !check_rights(0)) - message_admins("[usr.key] tried to use the admin panel without authorization.") - log_admin("[key_name(usr)] tried to use the admin panel without authorization.") - return - - if(!CheckAdminHref(href, href_list)) - return - - if(href_list["makementor"]) - makeMentor(href_list["makementor"]) - else if(href_list["removementor"]) - removeMentor(href_list["removementor"]) - - if(href_list["ahelp"]) - if(!check_rights(R_ADMIN, TRUE)) - return - - var/ahelp_ref = href_list["ahelp"] - var/datum/admin_help/AH = locate(ahelp_ref) - if(AH) - AH.Action(href_list["ahelp_action"]) - else - to_chat(usr, "Ticket [ahelp_ref] has been deleted!") - - else if(href_list["ahelp_tickets"]) - GLOB.ahelp_tickets.BrowseTickets(text2num(href_list["ahelp_tickets"])) - - else if(href_list["stickyban"]) - stickyban(href_list["stickyban"],href_list) - - else if(href_list["getplaytimewindow"]) - if(!check_rights(R_ADMIN)) - return - var/mob/M = locate(href_list["getplaytimewindow"]) in GLOB.mob_list - if(!M) - to_chat(usr, "ERROR: Mob not found.") - return - cmd_show_exp_panel(M.client) - - else if(href_list["toggleexempt"]) - if(!check_rights(R_ADMIN)) - return - var/client/C = locate(href_list["toggleexempt"]) in GLOB.clients - if(!C) - to_chat(usr, "ERROR: Client not found.") - return - toggle_exempt_status(C) - - else if(href_list["makeAntag"]) - if(!check_rights(R_ADMIN)) - return - if (!SSticker.mode) - to_chat(usr, "Not until the round starts!") - return - switch(href_list["makeAntag"]) - if("traitors") - if(src.makeTraitors()) - message_admins("[key_name_admin(usr)] created traitors.") - log_admin("[key_name(usr)] created traitors.") - else - message_admins("[key_name_admin(usr)] tried to create traitors. Unfortunately, there were no candidates available.") - log_admin("[key_name(usr)] failed to create traitors.") - if("changelings") - if(src.makeChangelings()) - message_admins("[key_name(usr)] created changelings.") - log_admin("[key_name(usr)] created changelings.") - else - message_admins("[key_name_admin(usr)] tried to create changelings. Unfortunately, there were no candidates available.") - log_admin("[key_name(usr)] failed to create changelings.") - if("revs") - if(src.makeRevs()) - message_admins("[key_name(usr)] started a revolution.") - log_admin("[key_name(usr)] started a revolution.") - else - message_admins("[key_name_admin(usr)] tried to start a revolution. Unfortunately, there were no candidates available.") - log_admin("[key_name(usr)] failed to start a revolution.") - if("cult") - if(src.makeCult()) - message_admins("[key_name(usr)] started a cult.") - log_admin("[key_name(usr)] started a cult.") - else - message_admins("[key_name_admin(usr)] tried to start a cult. Unfortunately, there were no candidates available.") - log_admin("[key_name(usr)] failed to start a cult.") - if("wizard") - message_admins("[key_name(usr)] is creating a wizard...") - if(src.makeWizard()) - message_admins("[key_name(usr)] created a wizard.") - log_admin("[key_name(usr)] created a wizard.") - else - message_admins("[key_name_admin(usr)] tried to create a wizard. Unfortunately, there were no candidates available.") - log_admin("[key_name(usr)] failed to create a wizard.") - if("nukeops") - message_admins("[key_name(usr)] is creating a nuke team...") - if(src.makeNukeTeam()) - message_admins("[key_name(usr)] created a nuke team.") - log_admin("[key_name(usr)] created a nuke team.") - else - message_admins("[key_name_admin(usr)] tried to create a nuke team. Unfortunately, there were not enough candidates available.") - log_admin("[key_name(usr)] failed to create a nuke team.") - if("ninja") - message_admins("[key_name(usr)] spawned a ninja.") - log_admin("[key_name(usr)] spawned a ninja.") - src.makeSpaceNinja() - if("aliens") - message_admins("[key_name(usr)] started an alien infestation.") - log_admin("[key_name(usr)] started an alien infestation.") - src.makeAliens() - if("deathsquad") - message_admins("[key_name(usr)] is creating a death squad...") - if(src.makeDeathsquad()) - message_admins("[key_name(usr)] created a death squad.") - log_admin("[key_name(usr)] created a death squad.") - else - message_admins("[key_name_admin(usr)] tried to create a death squad. Unfortunately, there were not enough candidates available.") - log_admin("[key_name(usr)] failed to create a death squad.") - if("blob") - var/strength = input("Set Blob Resource Gain Rate","Set Resource Rate",1) as num|null - if(!strength) - return - message_admins("[key_name(usr)] spawned a blob with base resource gain [strength].") - log_admin("[key_name(usr)] spawned a blob with base resource gain [strength].") - new/datum/round_event/ghost_role/blob(TRUE, strength) - if("centcom") - message_admins("[key_name(usr)] is creating a CentCom response team...") - if(src.makeEmergencyresponseteam()) - message_admins("[key_name(usr)] created a CentCom response team.") - log_admin("[key_name(usr)] created a CentCom response team.") - else - message_admins("[key_name_admin(usr)] tried to create a CentCom response team. Unfortunately, there were not enough candidates available.") - log_admin("[key_name(usr)] failed to create a CentCom response team.") - if("abductors") - message_admins("[key_name(usr)] is creating an abductor team...") - if(src.makeAbductorTeam()) - message_admins("[key_name(usr)] created an abductor team.") - log_admin("[key_name(usr)] created an abductor team.") - else - message_admins("[key_name_admin(usr)] tried to create an abductor team. Unfortunatly there were not enough candidates available.") - log_admin("[key_name(usr)] failed to create an abductor team.") - if("clockcult") - if(src.makeClockCult()) - message_admins("[key_name(usr)] started a clockwork cult.") - log_admin("[key_name(usr)] started a clockwork cult.") - else - message_admins("[key_name_admin(usr)] tried to start a clockwork cult. Unfortunately, there were no candidates available.") - log_admin("[key_name(usr)] failed to start a clockwork cult.") - if("revenant") - if(src.makeRevenant()) - message_admins("[key_name(usr)] created a revenant.") - log_admin("[key_name(usr)] created a revenant.") - else - message_admins("[key_name_admin(usr)] tried to create a revenant. Unfortunately, there were no candidates available.") - log_admin("[key_name(usr)] failed to create a revenant.") - //Hyper - if("lewdtraitors") - if(src.makeLewdtraitors()) - message_admins("[key_name(usr)] created a lewd traitor.") - log_admin("[key_name(usr)] created a lewd traitor.") - else - message_admins("[key_name_admin(usr)] tried to create a lewd traitor. Unfortunately, there were no candidates available.") - log_admin("[key_name(usr)] failed to create a lewd traitor.") - - else if(href_list["forceevent"]) - if(!check_rights(R_FUN)) - return - var/datum/round_event_control/E = locate(href_list["forceevent"]) in SSevents.control - if(E) - E.admin_setup(usr) - var/datum/round_event/event = E.runEvent() - if(event.announceWhen>0) - event.processing = FALSE - var/prompt = alert(usr, "Would you like to alert the crew?", "Alert", "Yes", "No", "Cancel") - switch(prompt) - if("Cancel") - event.kill() - return - if("No") - event.announceWhen = -1 - event.processing = TRUE - message_admins("[key_name_admin(usr)] has triggered an event. ([E.name])") - log_admin("[key_name(usr)] has triggered an event. ([E.name])") - return - - else if(href_list["dbsearchckey"] || href_list["dbsearchadmin"] || href_list["dbsearchip"] || href_list["dbsearchcid"]) - var/adminckey = href_list["dbsearchadmin"] - var/playerckey = href_list["dbsearchckey"] - var/ip = href_list["dbsearchip"] - var/cid = href_list["dbsearchcid"] - var/page = href_list["dbsearchpage"] - - DB_ban_panel(playerckey, adminckey, ip, cid, page) - return - - else if(href_list["dbbanedit"]) - var/banedit = href_list["dbbanedit"] - var/banid = text2num(href_list["dbbanid"]) - if(!banedit || !banid) - return - - DB_ban_edit(banid, banedit) - return - - else if(href_list["dbbanaddtype"]) - if(!check_rights(R_BAN)) - return - var/bantype = text2num(href_list["dbbanaddtype"]) - var/bankey = href_list["dbbanaddkey"] - var/banckey = ckey(bankey) - var/banip = href_list["dbbanaddip"] - var/bancid = href_list["dbbanaddcid"] - var/banduration = text2num(href_list["dbbaddduration"]) - var/banjob = href_list["dbbanaddjob"] - var/banreason = href_list["dbbanreason"] - var/banseverity = href_list["dbbanaddseverity"] - - switch(bantype) - if(BANTYPE_PERMA) - if(!banckey || !banreason || !banseverity) - to_chat(usr, "Not enough parameters (Requires ckey, severity, and reason).") - return - banduration = null - banjob = null - if(BANTYPE_TEMP) - if(!banckey || !banreason || !banduration || !banseverity) - to_chat(usr, "Not enough parameters (Requires ckey, reason, severity and duration).") - return - banjob = null - if(BANTYPE_JOB_PERMA) - if(!banckey || !banreason || !banjob || !banseverity) - to_chat(usr, "Not enough parameters (Requires ckey, severity, reason and job).") - return - banduration = null - if(BANTYPE_JOB_TEMP) - if(!banckey || !banreason || !banjob || !banduration || !banseverity) - to_chat(usr, "Not enough parameters (Requires ckey, severity, reason and job).") - return - if(BANTYPE_ADMIN_PERMA) - if(!banckey || !banreason || !banseverity) - to_chat(usr, "Not enough parameters (Requires ckey, severity and reason).") - return - banduration = null - banjob = null - if(BANTYPE_ADMIN_TEMP) - if(!banckey || !banreason || !banduration || !banseverity) - to_chat(usr, "Not enough parameters (Requires ckey, severity, reason and duration).") - return - banjob = null - - var/mob/playermob - - for(var/mob/M in GLOB.player_list) - if(M.ckey == banckey) - playermob = M - break - - - banreason = "(MANUAL BAN) "+banreason - - if(!playermob) - if(banip) - banreason = "[banreason] (CUSTOM IP)" - if(bancid) - banreason = "[banreason] (CUSTOM CID)" - else - message_admins("Ban process: A mob matching [playermob.key] was found at location [playermob.x], [playermob.y], [playermob.z]. Custom ip and computer id fields replaced with the ip and computer id from the located mob.") - - if(!DB_ban_record(bantype, playermob, banduration, banreason, banjob, bankey, banip, bancid )) - to_chat(usr, "Failed to apply ban.") - return - create_message("note", bankey, null, banreason, null, null, 0, 0, null, 0, banseverity) - - else if(href_list["editrightsbrowser"]) - edit_admin_permissions(0) - - else if(href_list["editrightsbrowserlog"]) - edit_admin_permissions(1, href_list["editrightstarget"], href_list["editrightsoperation"], href_list["editrightspage"]) - - if(href_list["editrightsbrowsermanage"]) - if(href_list["editrightschange"]) - change_admin_rank(ckey(href_list["editrightschange"]), href_list["editrightschange"], TRUE) - else if(href_list["editrightsremove"]) - remove_admin(ckey(href_list["editrightsremove"]), href_list["editrightsremove"], TRUE) - else if(href_list["editrightsremoverank"]) - remove_rank(href_list["editrightsremoverank"]) - edit_admin_permissions(2) - - else if(href_list["editrights"]) - edit_rights_topic(href_list) - - else if(href_list["gamemode_panel"]) - if(!check_rights(R_ADMIN)) - return - SSticker.mode.admin_panel() - - - else if(href_list["call_shuttle"]) - if(!check_rights(R_ADMIN)) - return - - - switch(href_list["call_shuttle"]) - if("1") - if(EMERGENCY_AT_LEAST_DOCKED) - return - SSshuttle.emergency.request() - log_admin("[key_name(usr)] called the Emergency Shuttle.") - message_admins("[key_name_admin(usr)] called the Emergency Shuttle to the station.") - - if("2") - if(EMERGENCY_AT_LEAST_DOCKED) - return - switch(SSshuttle.emergency.mode) - if(SHUTTLE_CALL) - SSshuttle.emergency.cancel() - log_admin("[key_name(usr)] sent the Emergency Shuttle back.") - message_admins("[key_name_admin(usr)] sent the Emergency Shuttle back.") - else - SSshuttle.emergency.cancel() - log_admin("[key_name(usr)] called the Emergency Shuttle.") - message_admins("[key_name_admin(usr)] called the Emergency Shuttle to the station.") - - - href_list["secrets"] = "check_antagonist" - - else if(href_list["edit_shuttle_time"]) - if(!check_rights(R_SERVER)) - return - - var/timer = input("Enter new shuttle duration (seconds):","Edit Shuttle Timeleft", SSshuttle.emergency.timeLeft() ) as num|null - if(!timer) - return - SSshuttle.emergency.setTimer(timer*10) - log_admin("[key_name(usr)] edited the Emergency Shuttle's timeleft to [timer] seconds.") - minor_announce("The emergency shuttle will reach its destination in [round(SSshuttle.emergency.timeLeft(600))] minutes.") - message_admins("[key_name_admin(usr)] edited the Emergency Shuttle's timeleft to [timer] seconds.") - href_list["secrets"] = "check_antagonist" - else if(href_list["trigger_centcom_recall"]) - if(!check_rights(R_ADMIN)) - return - - usr.client.trigger_centcom_recall() - - else if(href_list["toggle_continuous"]) - if(!check_rights(R_ADMIN)) - return - var/list/continuous = CONFIG_GET(keyed_list/continuous) - if(!continuous[SSticker.mode.config_tag]) - continuous[SSticker.mode.config_tag] = TRUE - else - continuous[SSticker.mode.config_tag] = FALSE - - message_admins("[key_name_admin(usr)] toggled the round to [continuous[SSticker.mode.config_tag] ? "continue if all antagonists die" : "end with the antagonists"].") - check_antagonists() - - else if(href_list["toggle_midround_antag"]) - if(!check_rights(R_ADMIN)) - return - - var/list/midround_antag = CONFIG_GET(keyed_list/midround_antag) - if(!midround_antag[SSticker.mode.config_tag]) - midround_antag[SSticker.mode.config_tag] = TRUE - else - midround_antag[SSticker.mode.config_tag] = FALSE - - message_admins("[key_name_admin(usr)] toggled the round to [midround_antag[SSticker.mode.config_tag] ? "use" : "skip"] the midround antag system.") - check_antagonists() - - else if(href_list["alter_midround_time_limit"]) - if(!check_rights(R_ADMIN)) - return - - var/timer = input("Enter new maximum time",, CONFIG_GET(number/midround_antag_time_check)) as num|null - if(!timer) - return - CONFIG_SET(number/midround_antag_time_check, timer) - message_admins("[key_name_admin(usr)] edited the maximum midround antagonist time to [timer] minutes.") - check_antagonists() - - else if(href_list["alter_midround_life_limit"]) - if(!check_rights(R_ADMIN)) - return - - var/ratio = input("Enter new life ratio",, CONFIG_GET(number/midround_antag_life_check) * 100) as num - if(!ratio) - return - CONFIG_SET(number/midround_antag_life_check, ratio / 100) - - message_admins("[key_name_admin(usr)] edited the midround antagonist living crew ratio to [ratio]% alive.") - check_antagonists() - - else if(href_list["toggle_noncontinuous_behavior"]) - if(!check_rights(R_ADMIN)) - return - - if(!SSticker.mode.round_ends_with_antag_death) - SSticker.mode.round_ends_with_antag_death = 1 - else - SSticker.mode.round_ends_with_antag_death = 0 - - message_admins("[key_name_admin(usr)] edited the midround antagonist system to [SSticker.mode.round_ends_with_antag_death ? "end the round" : "continue as extended"] upon failure.") - check_antagonists() - - else if(href_list["delay_round_end"]) - if(!check_rights(R_SERVER)) - return - if(!SSticker.delay_end) - SSticker.admin_delay_notice = input(usr, "Enter a reason for delaying the round end", "Round Delay Reason") as null|text - if(isnull(SSticker.admin_delay_notice)) - return - else - SSticker.admin_delay_notice = null - SSticker.delay_end = !SSticker.delay_end - var/reason = SSticker.delay_end ? "for reason: [SSticker.admin_delay_notice]" : "."//laziness - var/msg = "[SSticker.delay_end ? "delayed" : "undelayed"] the round end [reason]" - log_admin("[key_name(usr)] [msg]") - message_admins("[key_name_admin(usr)] [msg]") - href_list["secrets"] = "check_antagonist" - if(SSticker.ready_for_reboot && !SSticker.delay_end) //we undelayed after standard reboot would occur - SSticker.standard_reboot() - - else if(href_list["end_round"]) - if(!check_rights(R_ADMIN)) - return - - message_admins("[key_name_admin(usr)] is considering ending the round.") - if(alert(usr, "This will end the round, are you SURE you want to do this?", "Confirmation", "Yes", "No") == "Yes") - if(alert(usr, "Final Confirmation: End the round NOW?", "Confirmation", "Yes", "No") == "Yes") - message_admins("[key_name_admin(usr)] has ended the round.") - SSticker.force_ending = 1 //Yeah there we go APC destroyed mission accomplished - return - else - message_admins("[key_name_admin(usr)] decided against ending the round.") - else - message_admins("[key_name_admin(usr)] decided against ending the round.") - - else if(href_list["simplemake"]) - if(!check_rights(R_SPAWN)) - return - - var/mob/M = locate(href_list["mob"]) - if(!ismob(M)) - to_chat(usr, "This can only be used on instances of type /mob.") - return - - var/delmob = 0 - switch(alert("Delete old mob?","Message","Yes","No","Cancel")) - if("Cancel") - return - if("Yes") - delmob = 1 - - log_admin("[key_name(usr)] has used rudimentary transformation on [key_name(M)]. Transforming to [href_list["simplemake"]].; deletemob=[delmob]") - message_admins("[key_name_admin(usr)] has used rudimentary transformation on [key_name_admin(M)]. Transforming to [href_list["simplemake"]].; deletemob=[delmob]") - switch(href_list["simplemake"]) - if("observer") - M.change_mob_type( /mob/dead/observer , null, null, delmob ) - if("drone") - M.change_mob_type( /mob/living/carbon/alien/humanoid/drone , null, null, delmob ) - if("hunter") - M.change_mob_type( /mob/living/carbon/alien/humanoid/hunter , null, null, delmob ) - if("queen") - M.change_mob_type( /mob/living/carbon/alien/humanoid/royal/queen , null, null, delmob ) - if("praetorian") - M.change_mob_type( /mob/living/carbon/alien/humanoid/royal/praetorian , null, null, delmob ) - if("sentinel") - M.change_mob_type( /mob/living/carbon/alien/humanoid/sentinel , null, null, delmob ) - if("larva") - M.change_mob_type( /mob/living/carbon/alien/larva , null, null, delmob ) - if("human") - var/posttransformoutfit = usr.client.robust_dress_shop() - var/mob/living/carbon/human/newmob = M.change_mob_type( /mob/living/carbon/human , null, null, delmob ) - if(posttransformoutfit && istype(newmob)) - newmob.equipOutfit(posttransformoutfit) - if("slime") - M.change_mob_type( /mob/living/simple_animal/slime , null, null, delmob ) - if("monkey") - M.change_mob_type( /mob/living/carbon/monkey , null, null, delmob ) - if("robot") - M.change_mob_type( /mob/living/silicon/robot , null, null, delmob ) - if("cat") - M.change_mob_type( /mob/living/simple_animal/pet/cat , null, null, delmob ) - if("runtime") - M.change_mob_type( /mob/living/simple_animal/pet/cat/Runtime , null, null, delmob ) - if("corgi") - M.change_mob_type( /mob/living/simple_animal/pet/dog/corgi , null, null, delmob ) - if("ian") - M.change_mob_type( /mob/living/simple_animal/pet/dog/corgi/Ian , null, null, delmob ) - if("pug") - M.change_mob_type( /mob/living/simple_animal/pet/dog/pug , null, null, delmob ) - if("crab") - M.change_mob_type( /mob/living/simple_animal/crab , null, null, delmob ) - if("coffee") - M.change_mob_type( /mob/living/simple_animal/crab/Coffee , null, null, delmob ) - if("parrot") - M.change_mob_type( /mob/living/simple_animal/parrot , null, null, delmob ) - if("polyparrot") - M.change_mob_type( /mob/living/simple_animal/parrot/Poly , null, null, delmob ) - if("constructarmored") - M.change_mob_type( /mob/living/simple_animal/hostile/construct/armored , null, null, delmob ) - if("constructbuilder") - M.change_mob_type( /mob/living/simple_animal/hostile/construct/builder , null, null, delmob ) - if("constructwraith") - M.change_mob_type( /mob/living/simple_animal/hostile/construct/wraith , null, null, delmob ) - if("shade") - M.change_mob_type( /mob/living/simple_animal/shade , null, null, delmob ) - - - /////////////////////////////////////new ban stuff - else if(href_list["unbanf"]) - if(!check_rights(R_BAN)) - return - - var/banfolder = href_list["unbanf"] - GLOB.Banlist.cd = "/base/[banfolder]" - var/key = GLOB.Banlist["key"] - if(alert(usr, "Are you sure you want to unban [key]?", "Confirmation", "Yes", "No") == "Yes") - if(RemoveBan(banfolder)) - unbanpanel() - else - alert(usr, "This ban has already been lifted / does not exist.", "Error", "Ok") - unbanpanel() - - else if(href_list["unbane"]) - if(!check_rights(R_BAN)) - return - - UpdateTime() - var/reason - - var/banfolder = href_list["unbane"] - GLOB.Banlist.cd = "/base/[banfolder]" - var/reason2 = GLOB.Banlist["reason"] - var/temp = GLOB.Banlist["temp"] - - var/minutes = GLOB.Banlist["minutes"] - - var/banned_key = GLOB.Banlist["key"] - GLOB.Banlist.cd = "/base" - - var/duration - - switch(alert("Temporary Ban for [banned_key]?",,"Yes","No")) - if("Yes") - temp = 1 - var/mins = 0 - if(minutes > GLOB.CMinutes) - mins = minutes - GLOB.CMinutes - mins = input(usr,"How long (in minutes)? (Default: 1440)","Ban time",mins ? mins : 1440) as num|null - if(mins <= 0) - to_chat(usr, "[mins] is not a valid duration.") - return - minutes = GLOB.CMinutes + mins - duration = GetExp(minutes) - reason = input(usr,"Please State Reason For Banning [banned_key].","Reason",reason2) as message|null - if(!reason) - return - if("No") - temp = 0 - duration = "Perma" - reason = input(usr,"Please State Reason For Banning [banned_key].","Reason",reason2) as message|null - if(!reason) - return - - log_admin_private("[key_name(usr)] edited [banned_key]'s ban. Reason: [reason] Duration: [duration]") - ban_unban_log_save("[key_name(usr)] edited [banned_key]'s ban. Reason: [reason] Duration: [duration]") - message_admins("[key_name_admin(usr)] edited [banned_key]'s ban. Reason: [reason] Duration: [duration]") - GLOB.Banlist.cd = "/base/[banfolder]" - WRITE_FILE(GLOB.Banlist["reason"], reason) - WRITE_FILE(GLOB.Banlist["temp"], temp) - WRITE_FILE(GLOB.Banlist["minutes"], minutes) - WRITE_FILE(GLOB.Banlist["bannedby"], usr.ckey) - GLOB.Banlist.cd = "/base" - unbanpanel() - - /////////////////////////////////////new ban stuff - - else if(href_list["appearanceban"]) - if(!check_rights(R_BAN)) - return - var/mob/M = locate(href_list["appearanceban"]) - if(!ismob(M)) - to_chat(usr, "This can only be used on instances of type /mob") - return - if(!M.ckey) //sanity - to_chat(usr, "This mob has no ckey") - return - - - if(jobban_isbanned(M, "appearance")) - switch(alert("Remove appearance ban?","Please Confirm","Yes","No")) - if("Yes") - ban_unban_log_save("[key_name(usr)] removed [key_name(M)]'s appearance ban.") - log_admin_private("[key_name(usr)] removed [key_name(M)]'s appearance ban.") - DB_ban_unban(M.ckey, BANTYPE_ANY_JOB, "appearance") - if(M.client) - jobban_buildcache(M.client) - message_admins("[key_name_admin(usr)] removed [key_name_admin(M)]'s appearance ban.") - to_chat(M, "[usr.client.key] has removed your appearance ban.") - - else switch(alert("Appearance ban [M.key]?",,"Yes","No", "Cancel")) - if("Yes") - var/reason = input(usr,"Please State Reason.","Reason") as message|null - if(!reason) - return - var/severity = input("Set the severity of the note/ban.", "Severity", null, null) as null|anything in list("High", "Medium", "Minor", "None") - if(!severity) - return - if(!DB_ban_record(BANTYPE_JOB_PERMA, M, -1, reason, "appearance")) - to_chat(usr, "Failed to apply ban.") - return - if(M.client) - jobban_buildcache(M.client) - ban_unban_log_save("[key_name(usr)] appearance banned [key_name(M)]. reason: [reason]") - log_admin_private("[key_name(usr)] appearance banned [key_name(M)]. \nReason: [reason]") - create_message("note", M.key, null, "Appearance banned - [reason]", null, null, 0, 0, null, 0, severity) - message_admins("[key_name_admin(usr)] appearance banned [key_name_admin(M)].") - to_chat(M, "You have been appearance banned by [usr.client.key].") - to_chat(M, "The reason is: [reason]") - to_chat(M, "Appearance ban can be lifted only upon request.") - var/bran = CONFIG_GET(string/banappeals) - if(bran) - to_chat(M, "To try to resolve this matter head to [bran]") - else - to_chat(M, "No ban appeals URL has been set.") - if("No") - return - - else if(href_list["jobban2"]) - if(!check_rights(R_BAN)) - return - var/mob/M = locate(href_list["jobban2"]) - if(!ismob(M)) - to_chat(usr, "This can only be used on instances of type /mob.") - return - - if(!M.ckey) //sanity - to_chat(usr, "This mob has no ckey.") - return - - var/dat = "Job-Ban Panel: [key_name(M)]" - - /***********************************WARNING!************************************ - The jobban stuff looks mangled and disgusting - But it looks beautiful in-game - -Nodrak - ************************************WARNING!***********************************/ - var/counter = 0 -//Regular jobs - //Command (Blue) - dat += "" - dat += "" - for(var/jobPos in GLOB.command_positions) - if(!jobPos) - continue - if(jobban_isbanned(M, jobPos)) - dat += "" - counter++ - else - dat += "" - counter++ - - if(counter >= 6) //So things dont get squiiiiished! - dat += "" - counter = 0 - dat += "
    Command Positions
    [jobPos][jobPos]
    " - - //Security (Red) - counter = 0 - dat += "" - dat += "" - for(var/jobPos in GLOB.security_positions) - if(!jobPos) - continue - if(jobban_isbanned(M, jobPos)) - dat += "" - counter++ - else - dat += "" - counter++ - - if(counter >= 5) //So things dont get squiiiiished! - dat += "" - counter = 0 - dat += "
    Security Positions
    [jobPos][jobPos]
    " - - //Engineering (Yellow) - counter = 0 - dat += "" - dat += "" - for(var/jobPos in GLOB.engineering_positions) - if(!jobPos) - continue - if(jobban_isbanned(M, jobPos)) - dat += "" - counter++ - else - dat += "" - counter++ - - if(counter >= 5) //So things dont get squiiiiished! - dat += "" - counter = 0 - dat += "
    Engineering Positions
    [jobPos][jobPos]
    " - - //Medical (White) - counter = 0 - dat += "" - dat += "" - for(var/jobPos in GLOB.medical_positions) - if(!jobPos) - continue - if(jobban_isbanned(M, jobPos)) - dat += "" - counter++ - else - dat += "" - counter++ - - if(counter >= 5) //So things dont get squiiiiished! - dat += "" - counter = 0 - dat += "
    Medical Positions
    [jobPos][jobPos]
    " - - //Science (Purple) - counter = 0 - dat += "" - dat += "" - for(var/jobPos in GLOB.science_positions) - if(!jobPos) - continue - if(jobban_isbanned(M, jobPos)) - dat += "" - counter++ - else - dat += "" - counter++ - - if(counter >= 5) //So things dont get squiiiiished! - dat += "" - counter = 0 - dat += "
    Science Positions
    [jobPos][jobPos]
    " - - //Supply (Brown) - counter = 0 - dat += "" - dat += "" - for(var/jobPos in GLOB.supply_positions) - if(!jobPos) - continue - if(jobban_isbanned(M, jobPos)) - dat += "" - counter++ - else - dat += "" - counter++ - - if(counter >= 5) //So things dont get COPYPASTE! - dat += "" - counter = 0 - dat += "
    Supply Positions
    [jobPos][jobPos]
    " - - //Civilian (Grey) - counter = 0 - dat += "" - dat += "" - for(var/jobPos in GLOB.civilian_positions) - if(!jobPos) - continue - if(jobban_isbanned(M, jobPos)) - dat += "" - counter++ - else - dat += "" - counter++ - - if(counter >= 5) //So things dont get squiiiiished! - dat += "" - counter = 0 - dat += "
    Civilian Positions
    [jobPos][jobPos]
    " - - //Non-Human (Green) - counter = 0 - dat += "" - dat += "" - for(var/jobPos in GLOB.nonhuman_positions) - if(!jobPos) - continue - if(jobban_isbanned(M, jobPos)) - dat += "" - counter++ - else - dat += "" - counter++ - - if(counter >= 5) //So things dont get squiiiiished! - dat += "" - counter = 0 - - dat += "
    Non-human Positions
    [jobPos][jobPos]
    " - - //Ghost Roles (light light gray) - dat += "" - dat += "" - - //pAI - if(jobban_isbanned(M, ROLE_PAI)) - dat += "" - else - dat += "" - - - //Drones - if(jobban_isbanned(M, ROLE_DRONE)) - dat += "" - else - dat += "" - - - //Positronic Brains - if(jobban_isbanned(M, ROLE_POSIBRAIN)) - dat += "" - else - dat += "" - - //Sentience Potion Spawn - if(jobban_isbanned(M, ROLE_SENTIENCE)) - dat += "" - else - dat += "" - - //Deathsquad - if(jobban_isbanned(M, ROLE_DEATHSQUAD)) - dat += "" - else - dat += "" - - //Lavaland roles - if(jobban_isbanned(M, ROLE_LAVALAND)) - dat += "" - else - dat += "" - - dat += "
    Ghost Roles
    pAIpAIDroneDronePosibrainPosibrainSentience Potion SpawnSentience Potion SpawnDeathsquadDeathsquadLavalandLavaland
    " - - //Antagonist (Orange) - var/isbanned_dept = jobban_isbanned(M, ROLE_SYNDICATE) - dat += "" - dat += "" - - //Traitor - if(jobban_isbanned(M, ROLE_TRAITOR) || isbanned_dept) - dat += "" - else - dat += "" - - //Changeling - if(jobban_isbanned(M, ROLE_CHANGELING) || isbanned_dept) - dat += "" - else - dat += "" - - //Nuke Operative - if(jobban_isbanned(M, ROLE_OPERATIVE) || isbanned_dept) - dat += "" - else - dat += "" - - //Revolutionary - if(jobban_isbanned(M, ROLE_REV) || isbanned_dept) - dat += "" - else - dat += "" - - //Cultist - if(jobban_isbanned(M, ROLE_CULTIST) || isbanned_dept) - dat += "" - else - dat += "" - - dat += "" //So things dont get squished. - - //Servant of Ratvar - if(jobban_isbanned(M, ROLE_SERVANT_OF_RATVAR) || isbanned_dept) - dat += "" - else - dat += "" - - //Wizard - if(jobban_isbanned(M, ROLE_WIZARD) || isbanned_dept) - dat += "" - else - dat += "" - - //Abductor - if(jobban_isbanned(M, ROLE_ABDUCTOR) || isbanned_dept) - dat += "" - else - dat += "" - - //Alien - if(jobban_isbanned(M, ROLE_ALIEN) || isbanned_dept) - dat += "" - else - dat += "" - - //Gang - if(jobban_isbanned(M, ROLE_GANG) || isbanned_dept) - dat += "" - else - dat += "" - - - //Other Roles (black) - dat += "
    Antagonist Positions | " - dat += "Team Antagonists | " - dat += "Conversion Antagonists
    TraitorTraitorChangelingChangelingNuke OperativeNuke OperativeRevolutionaryRevolutionaryCultistCultist
    ServantServantWizardWizardAbductorAbductorAlienAlienGangGang
    " - dat += "" - - //Mind Transfer Potion - if(jobban_isbanned(M, ROLE_MIND_TRANSFER)) - dat += "" - else - dat += "" - - dat += "
    Other Roles
    Mind Transfer PotionMind Transfer Potion
    " - usr << browse(dat, "window=jobban2;size=800x450") - return - - //JOBBAN'S INNARDS - else if(href_list["jobban3"]) - if(!check_rights(R_BAN)) - return - var/mob/M = locate(href_list["jobban4"]) - if(!ismob(M)) - to_chat(usr, "This can only be used on instances of type /mob") - return - if(!SSjob) - to_chat(usr, "Jobs subsystem not initialized yet!") - return - //get jobs for department if specified, otherwise just return the one job in a list. - var/list/joblist = list() - switch(href_list["jobban3"]) - if("commanddept") - for(var/jobPos in GLOB.command_positions) - if(!jobPos) - continue - joblist += jobPos - if("securitydept") - for(var/jobPos in GLOB.security_positions) - if(!jobPos) - continue - joblist += jobPos - if("engineeringdept") - for(var/jobPos in GLOB.engineering_positions) - if(!jobPos) - continue - joblist += jobPos - if("medicaldept") - for(var/jobPos in GLOB.medical_positions) - if(!jobPos) - continue - joblist += jobPos - if("sciencedept") - for(var/jobPos in GLOB.science_positions) - if(!jobPos) - continue - joblist += jobPos - if("supplydept") - for(var/jobPos in GLOB.supply_positions) - if(!jobPos) - continue - joblist += jobPos - if("civiliandept") - for(var/jobPos in GLOB.civilian_positions) - if(!jobPos) - continue - joblist += jobPos - if("nonhumandept") - for(var/jobPos in GLOB.nonhuman_positions) - if(!jobPos) - continue - joblist += jobPos - if("ghostroles") - joblist += list(ROLE_PAI, ROLE_POSIBRAIN, ROLE_DRONE , ROLE_DEATHSQUAD, ROLE_LAVALAND, ROLE_SENTIENCE) - if("teamantags") - joblist += list(ROLE_OPERATIVE, ROLE_REV, ROLE_CULTIST, ROLE_SERVANT_OF_RATVAR, ROLE_ABDUCTOR, ROLE_ALIEN, ROLE_GANG) - if("convertantags") - joblist += list(ROLE_REV, ROLE_CULTIST, ROLE_SERVANT_OF_RATVAR, ROLE_ALIEN) - if("otherroles") - joblist += list(ROLE_MIND_TRANSFER) - else - joblist += href_list["jobban3"] - - //Create a list of unbanned jobs within joblist - var/list/notbannedlist = list() - for(var/job in joblist) - if(!jobban_isbanned(M, job)) - notbannedlist += job - - //Banning comes first - if(notbannedlist.len) //at least 1 unbanned job exists in joblist so we have stuff to ban. - var/severity = null - switch(alert("Temporary Ban for [M.key]?",,"Yes","No", "Cancel")) - if("Yes") - var/mins = input(usr,"How long (in minutes)?","Ban time",1440) as num|null - if(mins <= 0) - to_chat(usr, "[mins] is not a valid duration.") - return - var/reason = input(usr,"Please State Reason For Banning [M.key].","Reason") as message|null - if(!reason) - return - severity = input("Set the severity of the note/ban.", "Severity", null, null) as null|anything in list("High", "Medium", "Minor", "None") - if(!severity) - return - var/msg - for(var/job in notbannedlist) - if(!DB_ban_record(BANTYPE_JOB_TEMP, M, mins, reason, job)) - to_chat(usr, "Failed to apply ban.") - return - if(M.client) - jobban_buildcache(M.client) - ban_unban_log_save("[key_name(usr)] temp-jobbanned [key_name(M)] from [job] for [mins] minutes. reason: [reason]") - log_admin_private("[key_name(usr)] temp-jobbanned [key_name(M)] from [job] for [mins] minutes.") - if(!msg) - msg = job - else - msg += ", [job]" - create_message("note", M.key, null, "Banned from [msg] - [reason]", null, null, 0, 0, null, 0, severity) - message_admins("[key_name_admin(usr)] banned [key_name_admin(M)] from [msg] for [mins] minutes.") - to_chat(M, "You have been [(msg == ("ooc" || "appearance")) ? "banned" : "jobbanned"] by [usr.client.key] from: [msg].") - to_chat(M, "The reason is: [reason]") - to_chat(M, "This jobban will be lifted in [mins] minutes.") - href_list["jobban2"] = 1 // lets it fall through and refresh - return 1 - if("No") - var/reason = input(usr,"Please State Reason For Banning [M.key].","Reason") as message|null - severity = input("Set the severity of the note/ban.", "Severity", null, null) as null|anything in list("High", "Medium", "Minor", "None") - if(!severity) - return - if(reason) - var/msg - for(var/job in notbannedlist) - if(!DB_ban_record(BANTYPE_JOB_PERMA, M, -1, reason, job)) - to_chat(usr, "Failed to apply ban.") - return - if(M.client) - jobban_buildcache(M.client) - ban_unban_log_save("[key_name(usr)] perma-jobbanned [key_name(M)] from [job]. reason: [reason]") - log_admin_private("[key_name(usr)] perma-banned [key_name(M)] from [job]") - if(!msg) - msg = job - else - msg += ", [job]" - create_message("note", M.key, null, "Banned from [msg] - [reason]", null, null, 0, 0, null, 0, severity) - message_admins("[key_name_admin(usr)] banned [key_name_admin(M)] from [msg].") - to_chat(M, "You have been [(msg == ("ooc" || "appearance")) ? "banned" : "jobbanned"] by [usr.client.key] from: [msg].") - to_chat(M, "The reason is: [reason]") - to_chat(M, "Jobban can be lifted only upon request.") - href_list["jobban2"] = 1 // lets it fall through and refresh - return 1 - if("Cancel") - return - - //Unbanning joblist - //all jobs in joblist are banned already OR we didn't give a reason (implying they shouldn't be banned) - if(joblist.len) //at least 1 banned job exists in joblist so we have stuff to unban. - var/msg - for(var/job in joblist) - var/reason = jobban_isbanned(M, job) - if(!reason) - continue //skip if it isn't jobbanned anyway - switch(alert("Job: '[job]' Reason: '[reason]' Un-jobban?","Please Confirm","Yes","No")) - if("Yes") - ban_unban_log_save("[key_name(usr)] unjobbanned [key_name(M)] from [job]") - log_admin_private("[key_name(usr)] unbanned [key_name(M)] from [job]") - DB_ban_unban(M.ckey, BANTYPE_ANY_JOB, job) - if(M.client) - jobban_buildcache(M.client) - if(!msg) - msg = job - else - msg += ", [job]" - else - continue - if(msg) - message_admins("[key_name_admin(usr)] unbanned [key_name_admin(M)] from [msg].") - to_chat(M, "You have been un-jobbanned by [usr.client.key] from [msg].") - href_list["jobban2"] = 1 // lets it fall through and refresh - return 1 - return 0 //we didn't do anything! - - else if(href_list["boot2"]) - if(!check_rights(R_ADMIN)) - return - var/mob/M = locate(href_list["boot2"]) - if(ismob(M)) - if(!check_if_greater_rights_than(M.client)) - to_chat(usr, "Error: They have more rights than you do.") - return - if(alert(usr, "Kick [key_name(M)]?", "Confirm", "Yes", "No") != "Yes") - return - if(!M) - to_chat(usr, "Error: [M] no longer exists!") - return - if(!M.client) - to_chat(usr, "Error: [M] no longer has a client!") - return - to_chat(M, "You have been kicked from the server by [usr.client.holder.fakekey ? "an Administrator" : "[usr.client.key]"].") - log_admin("[key_name(usr)] kicked [key_name(M)].") - message_admins("[key_name_admin(usr)] kicked [key_name_admin(M)].") - qdel(M.client) - - else if(href_list["addmessage"]) - if(!check_rights(R_ADMIN)) - return - var/target_key = href_list["addmessage"] - create_message("message", target_key, secret = 0) - - else if(href_list["addnote"]) - if(!check_rights(R_ADMIN)) - return - var/target_key = href_list["addnote"] - create_message("note", target_key) - - else if(href_list["addwatch"]) - if(!check_rights(R_ADMIN)) - return - var/target_key = href_list["addwatch"] - create_message("watchlist entry", target_key, secret = 1) - - else if(href_list["addmemo"]) - if(!check_rights(R_ADMIN)) - return - create_message("memo", secret = 0, browse = 1) - - else if(href_list["addmessageempty"]) - if(!check_rights(R_ADMIN)) - return - create_message("message", secret = 0) - - else if(href_list["addnoteempty"]) - if(!check_rights(R_ADMIN)) - return - create_message("note") - - else if(href_list["addwatchempty"]) - if(!check_rights(R_ADMIN)) - return - create_message("watchlist entry", secret = 1) - - else if(href_list["deletemessage"]) - if(!check_rights(R_ADMIN)) - return - var/safety = alert("Delete message/note?",,"Yes","No"); - if (safety == "Yes") - var/message_id = href_list["deletemessage"] - delete_message(message_id) - - else if(href_list["deletemessageempty"]) - if(!check_rights(R_ADMIN)) - return - var/safety = alert("Delete message/note?",,"Yes","No"); - if (safety == "Yes") - var/message_id = href_list["deletemessageempty"] - delete_message(message_id, browse = TRUE) - - else if(href_list["editmessage"]) - if(!check_rights(R_ADMIN)) - return - var/message_id = href_list["editmessage"] - edit_message(message_id) - - else if(href_list["editmessageempty"]) - if(!check_rights(R_ADMIN)) - return - var/message_id = href_list["editmessageempty"] - edit_message(message_id, browse = 1) - - else if(href_list["editmessageexpiry"]) - if(!check_rights(R_ADMIN)) - return - var/message_id = href_list["editmessageexpiry"] - edit_message_expiry(message_id) - - else if(href_list["editmessageexpiryempty"]) - if(!check_rights(R_ADMIN)) - return - var/message_id = href_list["editmessageexpiryempty"] - edit_message_expiry(message_id, browse = 1) - - else if(href_list["editmessageseverity"]) - if(!check_rights(R_ADMIN)) - return - var/message_id = href_list["editmessageseverity"] - edit_message_severity(message_id) - - else if(href_list["secretmessage"]) - if(!check_rights(R_ADMIN)) - return - var/message_id = href_list["secretmessage"] - toggle_message_secrecy(message_id) - - else if(href_list["searchmessages"]) - if(!check_rights(R_ADMIN)) - return - var/target = href_list["searchmessages"] - browse_messages(index = target) - - else if(href_list["nonalpha"]) - if(!check_rights(R_ADMIN)) - return - var/target = href_list["nonalpha"] - target = text2num(target) - browse_messages(index = target) - - else if(href_list["showmessages"]) - if(!check_rights(R_ADMIN)) - return - var/target = href_list["showmessages"] - browse_messages(index = target) - - else if(href_list["showmemo"]) - if(!check_rights(R_ADMIN)) - return - browse_messages("memo") - - else if(href_list["showwatch"]) - if(!check_rights(R_ADMIN)) - return - browse_messages("watchlist entry") - - else if(href_list["showwatchfilter"]) - if(!check_rights(R_ADMIN)) - return - browse_messages("watchlist entry", filter = 1) - - else if(href_list["showmessageckey"]) - if(!check_rights(R_ADMIN)) - return - var/target = href_list["showmessageckey"] - var/agegate = TRUE - if (href_list["showall"]) - agegate = FALSE - browse_messages(target_ckey = target, agegate = agegate) - - else if(href_list["showmessageckeylinkless"]) - var/target = href_list["showmessageckeylinkless"] - browse_messages(target_ckey = target, linkless = 1) - - else if(href_list["messageedits"]) - if(!check_rights(R_ADMIN)) - return - var/message_id = sanitizeSQL("[href_list["messageedits"]]") - var/datum/DBQuery/query_get_message_edits = SSdbcore.NewQuery("SELECT edits FROM [format_table_name("messages")] WHERE id = '[message_id]'") - if(!query_get_message_edits.warn_execute()) - qdel(query_get_message_edits) - return - if(query_get_message_edits.NextRow()) - var/edit_log = query_get_message_edits.item[1] - if(!QDELETED(usr)) - var/datum/browser/browser = new(usr, "Note edits", "Note edits") - browser.set_content(jointext(edit_log, "")) - browser.open() - qdel(query_get_message_edits) - - else if(href_list["newban"]) - if(!check_rights(R_BAN)) - return - - var/mob/M = locate(href_list["newban"]) - if(!ismob(M)) - return - - if(M.client && M.client.holder) - return //admins cannot be banned. Even if they could, the ban doesn't affect them anyway - - switch(alert("Temporary Ban for [M.key]?",,"Yes","No", "Cancel")) - if("Yes") - var/mins = input(usr,"How long (in minutes)?","Ban time",1440) as num|null - if(mins <= 0) - to_chat(usr, "[mins] is not a valid duration.") - return - var/reason = input(usr,"Please State Reason For Banning [M.key].","Reason") as message|null - if(!reason) - return - if(!DB_ban_record(BANTYPE_TEMP, M, mins, reason)) - to_chat(usr, "Failed to apply ban.") - return - AddBan(M.ckey, M.computer_id, reason, usr.ckey, 1, mins) - ban_unban_log_save("[key_name(usr)] has banned [key_name(M)]. - Reason: [reason] - This will be removed in [mins] minutes.") - to_chat(M, "You have been banned by [usr.client.key].\nReason: [reason]") - to_chat(M, "This is a temporary ban, it will be removed in [mins] minutes. The round ID is [GLOB.round_id].") - var/bran = CONFIG_GET(string/banappeals) - if(bran) - to_chat(M, "To try to resolve this matter head to [bran]") - else - to_chat(M, "No ban appeals URL has been set.") - log_admin_private("[key_name(usr)] has banned [key_name(M)].\nReason: [key_name(M)]\nThis will be removed in [mins] minutes.") - var/msg = "[key_name_admin(usr)] has banned [key_name_admin(M)].\nReason: [reason]\nThis will be removed in [mins] minutes." - message_admins(msg) - var/datum/admin_help/AH = M.client ? M.client.current_ticket : null - if(AH) - AH.Resolve() - qdel(M.client) - if("No") - var/reason = input(usr,"Please State Reason For Banning [M.key].","Reason") as message|null - if(!reason) - return - switch(alert(usr,"IP ban?",,"Yes","No","Cancel")) - if("Cancel") - return - if("Yes") - AddBan(M.ckey, M.computer_id, reason, usr.ckey, 0, 0, M.lastKnownIP) - if("No") - AddBan(M.ckey, M.computer_id, reason, usr.ckey, 0, 0) - to_chat(M, "You have been banned by [usr.client.key].\nReason: [reason]") - to_chat(M, "This is a permanent ban. The round ID is [GLOB.round_id].") - var/bran = CONFIG_GET(string/banappeals) - if(bran) - to_chat(M, "To try to resolve this matter head to [bran]") - else - to_chat(M, "No ban appeals URL has been set.") - if(!DB_ban_record(BANTYPE_PERMA, M, -1, reason)) - to_chat(usr, "Failed to apply ban.") - return - ban_unban_log_save("[key_name(usr)] has permabanned [key_name(M)]. - Reason: [reason] - This is a permanent ban.") - log_admin_private("[key_name(usr)] has banned [key_name(M)].\nReason: [reason]\nThis is a permanent ban.") - var/msg = "[key_name_admin(usr)] has banned [key_name_admin(M)].\nReason: [reason]\nThis is a permanent ban." - message_admins(msg) - var/datum/admin_help/AH = M.client ? M.client.current_ticket : null - if(AH) - AH.Resolve() - qdel(M.client) - if("Cancel") - return - - else if(href_list["mute"]) - if(!check_rights(R_ADMIN)) - return - cmd_admin_mute(href_list["mute"], text2num(href_list["mute_type"])) - - else if(href_list["c_mode"]) - return HandleCMode() - - else if(href_list["f_secret"]) - return HandleFSecret() - -//Dynamic mode - else if(href_list["f_dynamic_roundstart"]) - if(!check_rights(R_ADMIN)) - return - if(SSticker && SSticker.mode) - return alert(usr, "The game has already started.", null, null, null, null) - if(GLOB.master_mode != "dynamic") - return alert(usr, "The game mode has to be dynamic mode.", null, null, null, null) - var/roundstart_rules = list() - for (var/rule in subtypesof(/datum/dynamic_ruleset/roundstart)) - var/datum/dynamic_ruleset/roundstart/newrule = new rule() - roundstart_rules[newrule.name] = newrule - var/added_rule = input(usr,"What ruleset do you want to force? This will bypass threat level and population restrictions.", "Rigging Roundstart", null) as null|anything in roundstart_rules - if (added_rule) - GLOB.dynamic_forced_roundstart_ruleset += roundstart_rules[added_rule] - log_admin("[key_name(usr)] set [added_rule] to be a forced roundstart ruleset.") - message_admins("[key_name(usr)] set [added_rule] to be a forced roundstart ruleset.", 1) - Game() - - else if(href_list["f_dynamic_roundstart_clear"]) - if(!check_rights(R_ADMIN)) - return - GLOB.dynamic_forced_roundstart_ruleset = list() - Game() - log_admin("[key_name(usr)] cleared the rigged roundstart rulesets. The mode will pick them as normal.") - message_admins("[key_name(usr)] cleared the rigged roundstart rulesets. The mode will pick them as normal.", 1) - - else if(href_list["f_dynamic_roundstart_remove"]) - if(!check_rights(R_ADMIN)) - return - var/datum/dynamic_ruleset/roundstart/rule = locate(href_list["f_dynamic_roundstart_remove"]) - GLOB.dynamic_forced_roundstart_ruleset -= rule - Game() - log_admin("[key_name(usr)] removed [rule] from the forced roundstart rulesets.") - message_admins("[key_name(usr)] removed [rule] from the forced roundstart rulesets.", 1) - - else if(href_list["f_dynamic_latejoin"]) - if(!check_rights(R_ADMIN)) - return - if(!SSticker || !SSticker.mode) - return alert(usr, "The game must start first.", null, null, null, null) - if(GLOB.master_mode != "dynamic") - return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null) - var/latejoin_rules = list() - for (var/rule in subtypesof(/datum/dynamic_ruleset/latejoin)) - var/datum/dynamic_ruleset/latejoin/newrule = new rule() - latejoin_rules[newrule.name] = newrule - var/added_rule = input(usr,"What ruleset do you want to force upon the next latejoiner? This will bypass threat level and population restrictions.", "Rigging Latejoin", null) as null|anything in latejoin_rules - if (added_rule) - var/datum/game_mode/dynamic/mode = SSticker.mode - mode.forced_latejoin_rule = latejoin_rules[added_rule] - log_admin("[key_name(usr)] set [added_rule] to proc on the next latejoin.") - message_admins("[key_name(usr)] set [added_rule] to proc on the next latejoin.", 1) - Game() - - else if(href_list["f_dynamic_latejoin_clear"]) - if(!check_rights(R_ADMIN)) - return - if (SSticker && SSticker.mode && istype(SSticker.mode,/datum/game_mode/dynamic)) - var/datum/game_mode/dynamic/mode = SSticker.mode - mode.forced_latejoin_rule = null - Game() - log_admin("[key_name(usr)] cleared the forced latejoin ruleset.") - message_admins("[key_name(usr)] cleared the forced latejoin ruleset.", 1) - - else if(href_list["f_dynamic_midround"]) - if(!check_rights(R_ADMIN)) - return - if(!SSticker || !SSticker.mode) - return alert(usr, "The game must start first.", null, null, null, null) - if(GLOB.master_mode != "dynamic") - return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null) - var/midround_rules = list() - for (var/rule in subtypesof(/datum/dynamic_ruleset/midround)) - var/datum/dynamic_ruleset/midround/newrule = new rule() - midround_rules[newrule.name] = rule - var/added_rule = input(usr,"What ruleset do you want to force right now? This will bypass threat level and population restrictions.", "Execute Ruleset", null) as null|anything in midround_rules - if (added_rule) - var/datum/game_mode/dynamic/mode = SSticker.mode - log_admin("[key_name(usr)] executed the [added_rule] ruleset.") - message_admins("[key_name(usr)] executed the [added_rule] ruleset.", 1) - mode.picking_specific_rule(midround_rules[added_rule],1) - - else if (href_list["f_dynamic_options"]) - if(!check_rights(R_ADMIN)) - return - - if(SSticker && SSticker.mode) - return alert(usr, "The game has already started.", null, null, null, null) - if(GLOB.master_mode != "dynamic") - return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null) - - dynamic_mode_options(usr) - - else if(href_list["f_dynamic_roundstart_centre"]) - if(!check_rights(R_ADMIN)) - return - if(SSticker && SSticker.mode) - return alert(usr, "The game has already started.", null, null, null, null) - if(GLOB.master_mode != "dynamic") - return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null) - - var/new_centre = input(usr,"Change the centre of the dynamic mode threat curve. A negative value will give a more peaceful round ; a positive value, a round with higher threat. Any number between -5 and +5 is allowed.", "Change curve centre", null) as num - if (new_centre < -5 || new_centre > 5) - return alert(usr, "Only values between -5 and +5 are allowed.", null, null, null, null) - - log_admin("[key_name(usr)] changed the distribution curve center to [new_centre].") - message_admins("[key_name(usr)] changed the distribution curve center to [new_centre]", 1) - GLOB.dynamic_curve_centre = new_centre - dynamic_mode_options(usr) - - else if(href_list["f_dynamic_roundstart_width"]) - if(!check_rights(R_ADMIN)) - return - if(SSticker && SSticker.mode) - return alert(usr, "The game has already started.", null, null, null, null) - if(GLOB.master_mode != "dynamic") - return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null) - - var/new_width = input(usr,"Change the width of the dynamic mode threat curve. A higher value will favour extreme rounds ; a lower value, a round closer to the average. Any Number between 0.5 and 4 are allowed.", "Change curve width", null) as num - if (new_width < 0.5 || new_width > 4) - return alert(usr, "Only values between 0.5 and +2.5 are allowed.", null, null, null, null) - - log_admin("[key_name(usr)] changed the distribution curve width to [new_width].") - message_admins("[key_name(usr)] changed the distribution curve width to [new_width]", 1) - GLOB.dynamic_curve_width = new_width - dynamic_mode_options(usr) - - else if(href_list["f_dynamic_roundstart_latejoin_min"]) - if(!check_rights(R_ADMIN)) - return - if(SSticker && SSticker.mode) - return alert(usr, "The game has already started.", null, null, null, null) - if(GLOB.master_mode != "dynamic") - return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null) - var/new_min = input(usr,"Change the minimum delay of latejoin injection in minutes.", "Change latejoin injection delay minimum", null) as num - if(new_min <= 0) - return alert(usr, "The minimum can't be zero or lower.", null, null, null, null) - if((new_min MINUTES) > GLOB.dynamic_latejoin_delay_max) - return alert(usr, "The minimum must be lower than the maximum.", null, null, null, null) - - log_admin("[key_name(usr)] changed the latejoin injection minimum delay to [new_min] minutes.") - message_admins("[key_name(usr)] changed the latejoin injection minimum delay to [new_min] minutes", 1) - GLOB.dynamic_latejoin_delay_min = (new_min MINUTES) - dynamic_mode_options(usr) - - else if(href_list["f_dynamic_roundstart_latejoin_max"]) - if(!check_rights(R_ADMIN)) - return - if(SSticker && SSticker.mode) - return alert(usr, "The game has already started.", null, null, null, null) - if(GLOB.master_mode != "dynamic") - return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null) - var/new_max = input(usr,"Change the maximum delay of latejoin injection in minutes.", "Change latejoin injection delay maximum", null) as num - if(new_max <= 0) - return alert(usr, "The maximum can't be zero or lower.", null, null, null, null) - if((new_max MINUTES) < GLOB.dynamic_latejoin_delay_min) - return alert(usr, "The maximum must be higher than the minimum.", null, null, null, null) - - log_admin("[key_name(usr)] changed the latejoin injection maximum delay to [new_max] minutes.") - message_admins("[key_name(usr)] changed the latejoin injection maximum delay to [new_max] minutes", 1) - GLOB.dynamic_latejoin_delay_max = (new_max MINUTES) - dynamic_mode_options(usr) - - else if(href_list["f_dynamic_roundstart_midround_min"]) - if(!check_rights(R_ADMIN)) - return - if(SSticker && SSticker.mode) - return alert(usr, "The game has already started.", null, null, null, null) - if(GLOB.master_mode != "dynamic") - return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null) - var/new_min = input(usr,"Change the minimum delay of midround injection in minutes.", "Change midround injection delay minimum", null) as num - if(new_min <= 0) - return alert(usr, "The minimum can't be zero or lower.", null, null, null, null) - if((new_min MINUTES) > GLOB.dynamic_midround_delay_max) - return alert(usr, "The minimum must be lower than the maximum.", null, null, null, null) - - log_admin("[key_name(usr)] changed the midround injection minimum delay to [new_min] minutes.") - message_admins("[key_name(usr)] changed the midround injection minimum delay to [new_min] minutes", 1) - GLOB.dynamic_midround_delay_min = (new_min MINUTES) - dynamic_mode_options(usr) - - else if(href_list["f_dynamic_roundstart_midround_max"]) - if(!check_rights(R_ADMIN)) - return - if(SSticker && SSticker.mode) - return alert(usr, "The game has already started.", null, null, null, null) - if(GLOB.master_mode != "dynamic") - return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null) - var/new_max = input(usr,"Change the maximum delay of midround injection in minutes.", "Change midround injection delay maximum", null) as num - if(new_max <= 0) - return alert(usr, "The maximum can't be zero or lower.", null, null, null, null) - if((new_max MINUTES) > GLOB.dynamic_midround_delay_max) - return alert(usr, "The maximum must be higher than the minimum.", null, null, null, null) - - log_admin("[key_name(usr)] changed the midround injection maximum delay to [new_max] minutes.") - message_admins("[key_name(usr)] changed the midround injection maximum delay to [new_max] minutes", 1) - GLOB.dynamic_midround_delay_max = (new_max MINUTES) - dynamic_mode_options(usr) - - else if(href_list["f_dynamic_force_extended"]) - if(!check_rights(R_ADMIN)) - return - - if(GLOB.master_mode != "dynamic") - return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null) - - GLOB.dynamic_forced_extended = !GLOB.dynamic_forced_extended - log_admin("[key_name(usr)] set 'forced_extended' to [GLOB.dynamic_forced_extended].") - message_admins("[key_name(usr)] set 'forced_extended' to [GLOB.dynamic_forced_extended].") - dynamic_mode_options(usr) - - else if(href_list["f_dynamic_no_stacking"]) - if(!check_rights(R_ADMIN)) - return - - if(GLOB.master_mode != "dynamic") - return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null) - - GLOB.dynamic_no_stacking = !GLOB.dynamic_no_stacking - log_admin("[key_name(usr)] set 'no_stacking' to [GLOB.dynamic_no_stacking].") - message_admins("[key_name(usr)] set 'no_stacking' to [GLOB.dynamic_no_stacking].") - dynamic_mode_options(usr) - - else if(href_list["f_dynamic_classic_secret"]) - if(!check_rights(R_ADMIN)) - return - - if(GLOB.master_mode != "dynamic") - return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null) - - GLOB.dynamic_classic_secret = !GLOB.dynamic_classic_secret - log_admin("[key_name(usr)] set 'classic_secret' to [GLOB.dynamic_classic_secret].") - message_admins("[key_name(usr)] set 'classic_secret' to [GLOB.dynamic_classic_secret].") - dynamic_mode_options(usr) - - else if(href_list["f_dynamic_stacking_limit"]) - if(!check_rights(R_ADMIN)) - return - - if(GLOB.master_mode != "dynamic") - return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null) - - GLOB.dynamic_stacking_limit = input(usr,"Change the threat limit at which round-endings rulesets will start to stack.", "Change stacking limit", null) as num - log_admin("[key_name(usr)] set 'stacking_limit' to [GLOB.dynamic_stacking_limit].") - message_admins("[key_name(usr)] set 'stacking_limit' to [GLOB.dynamic_stacking_limit].") - dynamic_mode_options(usr) - - else if(href_list["f_dynamic_high_pop_limit"]) - if(!check_rights(R_ADMIN)) - return - - if(SSticker && SSticker.mode) - return alert(usr, "The game has already started.", null, null, null, null) - - if(GLOB.master_mode != "dynamic") - return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null) - - var/new_value = input(usr, "Enter the high-pop override threshold for dynamic mode.", "High pop override") as num - if (new_value < 0) - return alert(usr, "Only positive values allowed!", null, null, null, null) - GLOB.dynamic_high_pop_limit = new_value - - log_admin("[key_name(usr)] set 'high_pop_limit' to [GLOB.dynamic_high_pop_limit].") - message_admins("[key_name(usr)] set 'high_pop_limit' to [GLOB.dynamic_high_pop_limit].") - dynamic_mode_options(usr) - - else if(href_list["f_dynamic_forced_threat"]) - if(!check_rights(R_ADMIN)) - return - - if(SSticker && SSticker.mode) - return alert(usr, "The game has already started.", null, null, null, null) - - if(GLOB.master_mode != "dynamic") - return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null) - - var/new_value = input(usr, "Enter the forced threat level for dynamic mode.", "Forced threat level") as num - if (new_value > 100) - return alert(usr, "The value must be be under 100.", null, null, null, null) - GLOB.dynamic_forced_threat_level = new_value - - log_admin("[key_name(usr)] set 'forced_threat_level' to [GLOB.dynamic_forced_threat_level].") - message_admins("[key_name(usr)] set 'forced_threat_level' to [GLOB.dynamic_forced_threat_level].") - dynamic_mode_options(usr) - - else if(href_list["f_dynamic_chaos_level"]) - if(!check_rights(R_ADMIN)) - return - - if(SSticker && SSticker.mode) - return alert(usr, "The game has already started.", null, null, null, null) - - if(GLOB.master_mode != "dynamic") - return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null) - - var/new_value = input(usr, "Enter the chaos level for dynamic mode.", "Chaos level") as num - if (new_value > 5 || new_value < 0) - return alert(usr, "The value must be between 0 and 5.", null, null, null, null) - GLOB.dynamic_chaos_level = new_value - - log_admin("[key_name(usr)] set 'dynamic_chaos_level' to [GLOB.dynamic_chaos_level].") - message_admins("[key_name(usr)] set 'dynamic_chaos_level' to [GLOB.dynamic_chaos_level].") - dynamic_mode_options(usr) -//End Dynamic mode - - else if(href_list["c_mode2"]) - if(!check_rights(R_ADMIN|R_SERVER)) - return - - if (SSticker.HasRoundStarted()) - return alert(usr, "The game has already started.", null, null, null, null) - GLOB.master_mode = href_list["c_mode2"] - log_admin("[key_name(usr)] set the mode as [GLOB.master_mode].") - message_admins("[key_name_admin(usr)] set the mode as [GLOB.master_mode].") - to_chat(world, "The mode is now: [GLOB.master_mode]") - Game() // updates the main game menu - SSticker.save_mode(GLOB.master_mode) - HandleCMode() - - else if(href_list["f_secret2"]) - if(!check_rights(R_ADMIN|R_SERVER)) - return - - if(SSticker.HasRoundStarted()) - return alert(usr, "The game has already started.", null, null, null, null) - if(GLOB.master_mode != "secret") - return alert(usr, "The game mode has to be secret!", null, null, null, null) - GLOB.secret_force_mode = href_list["f_secret2"] - log_admin("[key_name(usr)] set the forced secret mode as [GLOB.secret_force_mode].") - message_admins("[key_name_admin(usr)] set the forced secret mode as [GLOB.secret_force_mode].") - Game() // updates the main game menu - HandleFSecret() - - else if(href_list["monkeyone"]) - if(!check_rights(R_SPAWN)) - return - - var/mob/living/carbon/human/H = locate(href_list["monkeyone"]) - if(!istype(H)) - to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human.") - return - - log_admin("[key_name(usr)] attempting to monkeyize [key_name(H)].") - message_admins("[key_name_admin(usr)] attempting to monkeyize [key_name_admin(H)].") - H.monkeyize() - - else if(href_list["humanone"]) - if(!check_rights(R_SPAWN)) - return - - var/mob/living/carbon/monkey/Mo = locate(href_list["humanone"]) - if(!istype(Mo)) - to_chat(usr, "This can only be used on instances of type /mob/living/carbon/monkey.") - return - - log_admin("[key_name(usr)] attempting to humanize [key_name(Mo)].") - message_admins("[key_name_admin(usr)] attempting to humanize [key_name_admin(Mo)].") - Mo.humanize() - - else if(href_list["corgione"]) - if(!check_rights(R_SPAWN)) - return - - var/mob/living/carbon/human/H = locate(href_list["corgione"]) - if(!istype(H)) - to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human.") - return - - log_admin("[key_name(usr)] attempting to corgize [key_name(H)].") - message_admins("[key_name_admin(usr)] attempting to corgize [key_name_admin(H)].") - H.corgize() - - - else if(href_list["forcespeech"]) - if(!check_rights(R_FUN)) - return - - var/mob/M = locate(href_list["forcespeech"]) - if(!ismob(M)) - to_chat(usr, "this can only be used on instances of type /mob.") - - var/speech = input("What will [key_name(M)] say?", "Force speech", "")// Don't need to sanitize, since it does that in say(), we also trust our admins. - if(!speech) - return - M.say(speech, forced = "admin speech") - speech = sanitize(speech) // Nah, we don't trust them - log_admin("[key_name(usr)] forced [key_name(M)] to say: [speech]") - message_admins("[key_name_admin(usr)] forced [key_name_admin(M)] to say: [speech]") - - else if(href_list["makeeligible"]) - if(!check_rights(R_ADMIN)) - return - var/mob/M = locate(href_list["makeeligible"]) - if(!ismob(M)) - to_chat(usr, "this can only be used on instances of type /mob.") - if(M.ckey in GLOB.client_ghost_timeouts) - GLOB.client_ghost_timeouts -= M.ckey - - else if(href_list["sendtoprison"]) - if(!check_rights(R_ADMIN)) - return - - var/mob/M = locate(href_list["sendtoprison"]) - if(!ismob(M)) - to_chat(usr, "This can only be used on instances of type /mob.") - return - if(isAI(M)) - to_chat(usr, "This cannot be used on instances of type /mob/living/silicon/ai.") - return - - if(alert(usr, "Send [key_name(M)] to Prison?", "Message", "Yes", "No") != "Yes") - return - - M.forceMove(pick(GLOB.prisonwarp)) - to_chat(M, "You have been sent to Prison!") - - log_admin("[key_name(usr)] has sent [key_name(M)] to Prison!") - message_admins("[key_name_admin(usr)] has sent [key_name_admin(M)] to Prison!") - - else if(href_list["sendbacktolobby"]) - if(!check_rights(R_ADMIN)) - return - - var/mob/M = locate(href_list["sendbacktolobby"]) - - if(!isobserver(M)) - to_chat(usr, "You can only send ghost players back to the Lobby.") - return - - if(!M.client) - to_chat(usr, "[M] doesn't seem to have an active client.") - return - - if(alert(usr, "Send [key_name(M)] back to Lobby?", "Message", "Yes", "No") != "Yes") - return - - log_admin("[key_name(usr)] has sent [key_name(M)] back to the Lobby.") - message_admins("[key_name(usr)] has sent [key_name(M)] back to the Lobby.") - - var/mob/dead/new_player/NP = new() - NP.ckey = M.ckey - qdel(M) - - else if(href_list["tdome1"]) - if(!check_rights(R_FUN)) - return - - if(alert(usr, "Confirm?", "Message", "Yes", "No") != "Yes") - return - - var/mob/M = locate(href_list["tdome1"]) - if(!isliving(M)) - to_chat(usr, "This can only be used on instances of type /mob/living.") - return - if(isAI(M)) - to_chat(usr, "This cannot be used on instances of type /mob/living/silicon/ai.") - return - var/mob/living/L = M - - for(var/obj/item/I in L) - L.dropItemToGround(I, TRUE) - - L.Unconscious(100) - sleep(5) - L.forceMove(pick(GLOB.tdome1)) - spawn(50) - to_chat(L, "You have been sent to the Thunderdome.") - log_admin("[key_name(usr)] has sent [key_name(L)] to the thunderdome. (Team 1)") - message_admins("[key_name_admin(usr)] has sent [key_name_admin(L)] to the thunderdome. (Team 1)") - - else if(href_list["tdome2"]) - if(!check_rights(R_FUN)) - return - - if(alert(usr, "Confirm?", "Message", "Yes", "No") != "Yes") - return - - var/mob/M = locate(href_list["tdome2"]) - if(!isliving(M)) - to_chat(usr, "This can only be used on instances of type /mob/living.") - return - if(isAI(M)) - to_chat(usr, "This cannot be used on instances of type /mob/living/silicon/ai.") - return - var/mob/living/L = M - - for(var/obj/item/I in L) - L.dropItemToGround(I, TRUE) - - L.Unconscious(100) - sleep(5) - L.forceMove(pick(GLOB.tdome2)) - spawn(50) - to_chat(L, "You have been sent to the Thunderdome.") - log_admin("[key_name(usr)] has sent [key_name(L)] to the thunderdome. (Team 2)") - message_admins("[key_name_admin(usr)] has sent [key_name_admin(L)] to the thunderdome. (Team 2)") - - else if(href_list["tdomeadmin"]) - if(!check_rights(R_FUN)) - return - - if(alert(usr, "Confirm?", "Message", "Yes", "No") != "Yes") - return - - var/mob/M = locate(href_list["tdomeadmin"]) - if(!isliving(M)) - to_chat(usr, "This can only be used on instances of type /mob/living.") - return - if(isAI(M)) - to_chat(usr, "This cannot be used on instances of type /mob/living/silicon/ai.") - return - var/mob/living/L = M - - L.Unconscious(100) - sleep(5) - L.forceMove(pick(GLOB.tdomeadmin)) - spawn(50) - to_chat(L, "You have been sent to the Thunderdome.") - log_admin("[key_name(usr)] has sent [key_name(L)] to the thunderdome. (Admin.)") - message_admins("[key_name_admin(usr)] has sent [key_name_admin(L)] to the thunderdome. (Admin.)") - - else if(href_list["tdomeobserve"]) - if(!check_rights(R_FUN)) - return - - if(alert(usr, "Confirm?", "Message", "Yes", "No") != "Yes") - return - - var/mob/M = locate(href_list["tdomeobserve"]) - if(!isliving(M)) - to_chat(usr, "This can only be used on instances of type /mob/living.") - return - if(isAI(M)) - to_chat(usr, "This cannot be used on instances of type /mob/living/silicon/ai.") - return - var/mob/living/L = M - - for(var/obj/item/I in L) - L.dropItemToGround(I, TRUE) - - if(ishuman(L)) - var/mob/living/carbon/human/observer = L - observer.equip_to_slot_or_del(new /obj/item/clothing/under/suit_jacket(observer), SLOT_W_UNIFORM) - observer.equip_to_slot_or_del(new /obj/item/clothing/shoes/sneakers/black(observer), SLOT_SHOES) - L.Unconscious(100) - sleep(5) - L.forceMove(pick(GLOB.tdomeobserve)) - spawn(50) - to_chat(L, "You have been sent to the Thunderdome.") - log_admin("[key_name(usr)] has sent [key_name(L)] to the thunderdome. (Observer.)") - message_admins("[key_name_admin(usr)] has sent [key_name_admin(L)] to the thunderdome. (Observer.)") - - else if(href_list["revive"]) - if(!check_rights(R_ADMIN)) - return - - var/mob/living/L = locate(href_list["revive"]) - if(!istype(L)) - to_chat(usr, "This can only be used on instances of type /mob/living.") - return - - L.revive(full_heal = 1, admin_revive = 1) - message_admins("Admin [key_name_admin(usr)] healed / revived [key_name_admin(L)]!") - log_admin("[key_name(usr)] healed / Revived [key_name(L)].") - - else if(href_list["makeai"]) - if(!check_rights(R_SPAWN)) - return - - var/mob/living/carbon/human/H = locate(href_list["makeai"]) - if(!istype(H)) - to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human.") - return - - message_admins("Admin [key_name_admin(usr)] AIized [key_name_admin(H)]!") - log_admin("[key_name(usr)] AIized [key_name(H)].") - H.AIize() - - else if(href_list["makealien"]) - if(!check_rights(R_SPAWN)) - return - - var/mob/living/carbon/human/H = locate(href_list["makealien"]) - if(!istype(H)) - to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human.") - return - - usr.client.cmd_admin_alienize(H) - - else if(href_list["makeslime"]) - if(!check_rights(R_SPAWN)) - return - - var/mob/living/carbon/human/H = locate(href_list["makeslime"]) - if(!istype(H)) - to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human.") - return - - usr.client.cmd_admin_slimeize(H) - - else if(href_list["makeblob"]) - if(!check_rights(R_SPAWN)) - return - - var/mob/living/carbon/human/H = locate(href_list["makeblob"]) - if(!istype(H)) - to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human.") - return - - usr.client.cmd_admin_blobize(H) - - - else if(href_list["makerobot"]) - if(!check_rights(R_SPAWN)) - return - - var/mob/living/carbon/human/H = locate(href_list["makerobot"]) - if(!istype(H)) - to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human.") - return - - usr.client.cmd_admin_robotize(H) - - else if(href_list["makeanimal"]) - if(!check_rights(R_SPAWN)) - return - - var/mob/M = locate(href_list["makeanimal"]) - if(isnewplayer(M)) - to_chat(usr, "This cannot be used on instances of type /mob/dead/new_player.") - return - - usr.client.cmd_admin_animalize(M) - - else if(href_list["adminplayeropts"]) - var/mob/M = locate(href_list["adminplayeropts"]) - show_player_panel(M) - - else if(href_list["adminplayerobservefollow"]) - if(!isobserver(usr) && !check_rights(R_ADMIN)) - return - - var/atom/movable/AM = locate(href_list["adminplayerobservefollow"]) - - var/client/C = usr.client - if(!isobserver(usr)) - C.admin_ghost() - var/mob/dead/observer/A = C.mob - A.ManualFollow(AM) - - else if(href_list["admingetmovable"]) - if(!check_rights(R_ADMIN)) - return - - var/atom/movable/AM = locate(href_list["admingetmovable"]) - if(QDELETED(AM)) - return - AM.forceMove(get_turf(usr)) - - else if(href_list["adminplayerobservecoodjump"]) - if(!isobserver(usr) && !check_rights(R_ADMIN)) - return - - var/x = text2num(href_list["X"]) - var/y = text2num(href_list["Y"]) - var/z = text2num(href_list["Z"]) - - var/client/C = usr.client - if(!isobserver(usr)) - C.admin_ghost() - sleep(2) - C.jumptocoord(x,y,z) - - else if(href_list["adminchecklaws"]) - if(!check_rights(R_ADMIN)) - return - output_ai_laws() - - else if(href_list["admincheckdevilinfo"]) - if(!check_rights(R_ADMIN)) - return - var/mob/M = locate(href_list["admincheckdevilinfo"]) - output_devil_info(M) - - else if(href_list["adminmoreinfo"]) - var/mob/M = locate(href_list["adminmoreinfo"]) in GLOB.mob_list - if(!ismob(M)) - to_chat(usr, "This can only be used on instances of type /mob.") - return - - var/location_description = "" - var/special_role_description = "" - var/health_description = "" - var/gender_description = "" - var/turf/T = get_turf(M) - - //Location - if(isturf(T)) - if(isarea(T.loc)) - location_description = "([M.loc == T ? "at coordinates " : "in [M.loc] at coordinates "] [T.x], [T.y], [T.z] in area [T.loc])" - else - location_description = "([M.loc == T ? "at coordinates " : "in [M.loc] at coordinates "] [T.x], [T.y], [T.z])" - - //Job + antagonist - if(M.mind) - special_role_description = "Role: [M.mind.assigned_role]; Antagonist: [M.mind.special_role]" - else - special_role_description = "Role: Mind datum missing Antagonist: Mind datum missing" - - //Health - if(isliving(M)) - var/mob/living/L = M - var/status - switch (M.stat) - if(CONSCIOUS) - status = "Alive" - if(SOFT_CRIT) - status = "Dying" - if(UNCONSCIOUS) - status = "[L.InCritical() ? "Unconscious and Dying" : "Unconscious"]" - if(DEAD) - status = "Dead" - health_description = "Status = [status]" - health_description += "
    Oxy: [L.getOxyLoss()] - Tox: [L.getToxLoss()] - Fire: [L.getFireLoss()] - Brute: [L.getBruteLoss()] - Clone: [L.getCloneLoss()] - Brain: [L.getOrganLoss(ORGAN_SLOT_BRAIN)] - Stamina: [L.getStaminaLoss()]" - else - health_description = "This mob type has no health to speak of." - - //Gender - switch(M.gender) - if(MALE,FEMALE) - gender_description = "[M.gender]" - else - gender_description = "[M.gender]" - - to_chat(src.owner, "Info about [M.name]: ") - to_chat(src.owner, "Mob type = [M.type]; Gender = [gender_description] Damage = [health_description]") - to_chat(src.owner, "Name = [M.name]; Real_name = [M.real_name]; Mind_name = [M.mind?"[M.mind.name]":""]; Key = [M.key];") - to_chat(src.owner, "Location = [location_description];") - to_chat(src.owner, "[special_role_description]") - to_chat(src.owner, ADMIN_FULLMONTY_NONAME(M)) - - else if(href_list["addjobslot"]) - if(!check_rights(R_ADMIN)) - return - - var/Add = href_list["addjobslot"] - - for(var/datum/job/job in SSjob.occupations) - if(job.title == Add) - job.total_positions += 1 - break - - src.manage_free_slots() - - - else if(href_list["customjobslot"]) - if(!check_rights(R_ADMIN)) - return - - var/Add = href_list["customjobslot"] - - for(var/datum/job/job in SSjob.occupations) - if(job.title == Add) - var/newtime = null - newtime = input(usr, "How many jebs do you want?", "Add wanted posters", "[newtime]") as num|null - if(!newtime) - to_chat(src.owner, "Setting to amount of positions filled for the job") - job.total_positions = job.current_positions - break - job.total_positions = newtime - - src.manage_free_slots() - - else if(href_list["removejobslot"]) - if(!check_rights(R_ADMIN)) - return - - var/Remove = href_list["removejobslot"] - - for(var/datum/job/job in SSjob.occupations) - if(job.title == Remove && job.total_positions - job.current_positions > 0) - job.total_positions -= 1 - break - - src.manage_free_slots() - - else if(href_list["unlimitjobslot"]) - if(!check_rights(R_ADMIN)) - return - - var/Unlimit = href_list["unlimitjobslot"] - - for(var/datum/job/job in SSjob.occupations) - if(job.title == Unlimit) - job.total_positions = -1 - break - - src.manage_free_slots() - - else if(href_list["limitjobslot"]) - if(!check_rights(R_ADMIN)) - return - - var/Limit = href_list["limitjobslot"] - - for(var/datum/job/job in SSjob.occupations) - if(job.title == Limit) - job.total_positions = job.current_positions - break - - src.manage_free_slots() - - - else if(href_list["adminspawncookie"]) - if(!check_rights(R_ADMIN|R_FUN)) - return - - var/mob/living/carbon/human/H = locate(href_list["adminspawncookie"]) - if(!ishuman(H)) - to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human.") - return - - var/obj/item/reagent_containers/food/snacks/cookie/cookie = new(H) - if(H.put_in_hands(cookie)) - H.update_inv_hands() - else - qdel(cookie) - log_admin("[key_name(H)] has their hands full, so they did not receive their cookie, spawned by [key_name(src.owner)].") - message_admins("[key_name(H)] has their hands full, so they did not receive their cookie, spawned by [key_name(src.owner)].") - return - - log_admin("[key_name(H)] got their cookie, spawned by [key_name(src.owner)].") - message_admins("[key_name(H)] got their cookie, spawned by [key_name(src.owner)].") - SSblackbox.record_feedback("amount", "admin_cookies_spawned", 1) - to_chat(H, "Your prayers have been answered!! You received the best cookie!") - SEND_SOUND(H, sound('sound/effects/pray_chaplain.ogg')) - - else if(href_list["adminsmite"]) - if(!check_rights(R_ADMIN|R_FUN)) - return - - var/mob/living/carbon/human/H = locate(href_list["adminsmite"]) in GLOB.mob_list - if(!H || !istype(H)) - to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") - return - - usr.client.smite(H) - - else if(href_list["CentComReply"]) - if(!check_rights(R_ADMIN)) - return - - var/mob/M = locate(href_list["CentComReply"]) - usr.client.admin_headset_message(M, RADIO_CHANNEL_CENTCOM) - - else if(href_list["SyndicateReply"]) - if(!check_rights(R_ADMIN)) - return - - var/mob/M = locate(href_list["SyndicateReply"]) - usr.client.admin_headset_message(M, RADIO_CHANNEL_SYNDICATE) - - else if(href_list["HeadsetMessage"]) - if(!check_rights(R_ADMIN)) - return - - var/mob/M = locate(href_list["HeadsetMessage"]) - usr.client.admin_headset_message(M) - - else if(href_list["reject_custom_name"]) - if(!check_rights(R_ADMIN)) - return - var/obj/item/station_charter/charter = locate(href_list["reject_custom_name"]) - if(istype(charter)) - charter.reject_proposed(usr) - else if(href_list["jumpto"]) - if(!isobserver(usr) && !check_rights(R_ADMIN)) - return - - var/mob/M = locate(href_list["jumpto"]) - usr.client.jumptomob(M) - - else if(href_list["getmob"]) - if(!check_rights(R_ADMIN)) - return - - if(alert(usr, "Confirm?", "Message", "Yes", "No") != "Yes") - return - var/mob/M = locate(href_list["getmob"]) - usr.client.Getmob(M) - - else if(href_list["sendmob"]) - if(!check_rights(R_ADMIN)) - return - - var/mob/M = locate(href_list["sendmob"]) - usr.client.sendmob(M) - - else if(href_list["narrateto"]) - if(!check_rights(R_ADMIN)) - return - - var/mob/M = locate(href_list["narrateto"]) - usr.client.cmd_admin_direct_narrate(M) - - else if(href_list["subtlemessage"]) - if(!check_rights(R_ADMIN)) - return - - var/mob/M = locate(href_list["subtlemessage"]) - usr.client.cmd_admin_subtle_message(M) - - else if(href_list["individuallog"]) - if(!check_rights(R_ADMIN)) - return - - var/mob/M = locate(href_list["individuallog"]) in GLOB.mob_list - if(!ismob(M)) - to_chat(usr, "This can only be used on instances of type /mob.") - return - - show_individual_logging_panel(M, href_list["log_src"], href_list["log_type"]) - else if(href_list["languagemenu"]) - if(!check_rights(R_ADMIN)) - return - - var/mob/M = locate(href_list["languagemenu"]) in GLOB.mob_list - if(!ismob(M)) - to_chat(usr, "This can only be used on instances of type /mob.") - return - var/datum/language_holder/H = M.get_language_holder() - H.open_language_menu(usr) - - else if(href_list["traitor"]) - if(!check_rights(R_ADMIN)) - return - - if(!SSticker.HasRoundStarted()) - alert("The game hasn't started yet!") - return - - var/mob/M = locate(href_list["traitor"]) - if(!ismob(M)) - var/datum/mind/D = M - if(!istype(D)) - to_chat(usr, "This can only be used on instances of type /mob and /mind") - return - else - D.traitor_panel() - else - show_traitor_panel(M) - - else if(href_list["borgpanel"]) - if(!check_rights(R_ADMIN)) - return - - var/mob/M = locate(href_list["borgpanel"]) - if(!iscyborg(M)) - to_chat(usr, "This can only be used on cyborgs") - else - open_borgopanel(M) - - else if(href_list["initmind"]) - if(!check_rights(R_ADMIN)) - return - var/mob/M = locate(href_list["initmind"]) - if(!ismob(M) || M.mind) - to_chat(usr, "This can only be used on instances on mindless mobs") - return - M.mind_initialize() - - else if(href_list["create_object"]) - if(!check_rights(R_SPAWN)) - return - return create_object(usr) - - else if(href_list["quick_create_object"]) - if(!check_rights(R_SPAWN)) - return - return quick_create_object(usr) - - else if(href_list["create_turf"]) - if(!check_rights(R_SPAWN)) - return - return create_turf(usr) - - else if(href_list["create_mob"]) - if(!check_rights(R_SPAWN)) - return - return create_mob(usr) - - else if(href_list["dupe_marked_datum"]) - if(!check_rights(R_SPAWN)) - return - return DuplicateObject(marked_datum, perfectcopy=1, newloc=get_turf(usr)) - - else if(href_list["object_list"]) //this is the laggiest thing ever - if(!check_rights(R_SPAWN)) - return - - var/atom/loc = usr.loc - - var/dirty_paths - if (istext(href_list["object_list"])) - dirty_paths = list(href_list["object_list"]) - else if (istype(href_list["object_list"], /list)) - dirty_paths = href_list["object_list"] - - var/paths = list() - - for(var/dirty_path in dirty_paths) - var/path = text2path(dirty_path) - if(!path) - continue - else if(!ispath(path, /obj) && !ispath(path, /turf) && !ispath(path, /mob)) - continue - paths += path - - if(!paths) - alert("The path list you sent is empty.") - return - if(length(paths) > 5) - alert("Select fewer object types, (max 5).") - return - - var/list/offset = splittext(href_list["offset"],",") - var/number = CLAMP(text2num(href_list["object_count"]), 1, 100) - var/X = offset.len > 0 ? text2num(offset[1]) : 0 - var/Y = offset.len > 1 ? text2num(offset[2]) : 0 - var/Z = offset.len > 2 ? text2num(offset[3]) : 0 - var/obj_dir = text2num(href_list["object_dir"]) - if(obj_dir && !(obj_dir in list(1,2,4,8,5,6,9,10))) - obj_dir = null - var/obj_name = sanitize(href_list["object_name"]) - - - var/atom/target //Where the object will be spawned - var/where = href_list["object_where"] - if (!( where in list("onfloor","inhand","inmarked") )) - where = "onfloor" - - - switch(where) - if("inhand") - if (!iscarbon(usr) && !iscyborg(usr)) - to_chat(usr, "Can only spawn in hand when you're a carbon mob or cyborg.") - where = "onfloor" - target = usr - - if("onfloor") - switch(href_list["offset_type"]) - if ("absolute") - target = locate(0 + X,0 + Y,0 + Z) - if ("relative") - target = locate(loc.x + X,loc.y + Y,loc.z + Z) - if("inmarked") - if(!marked_datum) - to_chat(usr, "You don't have any object marked. Abandoning spawn.") - return - else if(!istype(marked_datum, /atom)) - to_chat(usr, "The object you have marked cannot be used as a target. Target must be of type /atom. Abandoning spawn.") - return - else - target = marked_datum - - if(target) - for (var/path in paths) - for (var/i = 0; i < number; i++) - if(path in typesof(/turf)) - var/turf/O = target - var/turf/N = O.ChangeTurf(path) - if(N && obj_name) - N.name = obj_name - else - var/atom/O = new path(target) - if(!QDELETED(O)) - O.flags_1 |= ADMIN_SPAWNED_1 - if(obj_dir) - O.setDir(obj_dir) - if(obj_name) - O.name = obj_name - if(ismob(O)) - var/mob/M = O - M.real_name = obj_name - if(where == "inhand" && isliving(usr) && isitem(O)) - var/mob/living/L = usr - var/obj/item/I = O - L.put_in_hands(I) - if(iscyborg(L)) - var/mob/living/silicon/robot/R = L - if(R.module) - R.module.add_module(I, TRUE, TRUE) - R.activate_module(I) - - - if (number == 1) - log_admin("[key_name(usr)] created a [english_list(paths)]") - for(var/path in paths) - if(ispath(path, /mob)) - message_admins("[key_name_admin(usr)] created a [english_list(paths)]") - break - else - log_admin("[key_name(usr)] created [number]ea [english_list(paths)]") - for(var/path in paths) - if(ispath(path, /mob)) - message_admins("[key_name_admin(usr)] created [number]ea [english_list(paths)]") - break - return - - else if(href_list["secrets"]) - Secrets_topic(href_list["secrets"],href_list) - - else if(href_list["ac_view_wanted"]) //Admin newscaster Topic() stuff be here - if(!check_rights(R_ADMIN)) - return - src.admincaster_screen = 18 //The ac_ prefix before the hrefs stands for AdminCaster. - src.access_news_network() - - else if(href_list["ac_set_channel_name"]) - if(!check_rights(R_ADMIN)) - return - src.admincaster_feed_channel.channel_name = stripped_input(usr, "Provide a Feed Channel Name.", "Network Channel Handler", "") - while (findtext(src.admincaster_feed_channel.channel_name," ") == 1) - src.admincaster_feed_channel.channel_name = copytext(src.admincaster_feed_channel.channel_name,2,length(src.admincaster_feed_channel.channel_name)+1) - src.access_news_network() - - else if(href_list["ac_set_channel_lock"]) - if(!check_rights(R_ADMIN)) - return - src.admincaster_feed_channel.locked = !src.admincaster_feed_channel.locked - src.access_news_network() - - else if(href_list["ac_submit_new_channel"]) - if(!check_rights(R_ADMIN)) - return - var/check = 0 - for(var/datum/newscaster/feed_channel/FC in GLOB.news_network.network_channels) - if(FC.channel_name == src.admincaster_feed_channel.channel_name) - check = 1 - break - if(src.admincaster_feed_channel.channel_name == "" || src.admincaster_feed_channel.channel_name == "\[REDACTED\]" || check ) - src.admincaster_screen=7 - else - var/choice = alert("Please confirm Feed channel creation.","Network Channel Handler","Confirm","Cancel") - if(choice=="Confirm") - GLOB.news_network.CreateFeedChannel(src.admincaster_feed_channel.channel_name, src.admin_signature, src.admincaster_feed_channel.locked, 1) - SSblackbox.record_feedback("tally", "newscaster_channels", 1, src.admincaster_feed_channel.channel_name) - log_admin("[key_name(usr)] created command feed channel: [src.admincaster_feed_channel.channel_name]!") - src.admincaster_screen=5 - src.access_news_network() - - else if(href_list["ac_set_channel_receiving"]) - if(!check_rights(R_ADMIN)) - return - var/list/available_channels = list() - for(var/datum/newscaster/feed_channel/F in GLOB.news_network.network_channels) - available_channels += F.channel_name - src.admincaster_feed_channel.channel_name = adminscrub(input(usr, "Choose receiving Feed Channel.", "Network Channel Handler") in available_channels ) - src.access_news_network() - - else if(href_list["ac_set_new_message"]) - if(!check_rights(R_ADMIN)) - return - src.admincaster_feed_message.body = adminscrub(input(usr, "Write your Feed story.", "Network Channel Handler", "")) - while (findtext(src.admincaster_feed_message.returnBody(-1)," ") == 1) - src.admincaster_feed_message.body = copytext(src.admincaster_feed_message.returnBody(-1),2,length(src.admincaster_feed_message.returnBody(-1))+1) - src.access_news_network() - - else if(href_list["ac_submit_new_message"]) - if(!check_rights(R_ADMIN)) - return - if(src.admincaster_feed_message.returnBody(-1) =="" || src.admincaster_feed_message.returnBody(-1) =="\[REDACTED\]" || src.admincaster_feed_channel.channel_name == "" ) - src.admincaster_screen = 6 - else - GLOB.news_network.SubmitArticle(src.admincaster_feed_message.returnBody(-1), src.admin_signature, src.admincaster_feed_channel.channel_name, null, 1) - SSblackbox.record_feedback("amount", "newscaster_stories", 1) - src.admincaster_screen=4 - - for(var/obj/machinery/newscaster/NEWSCASTER in GLOB.allCasters) - NEWSCASTER.newsAlert(src.admincaster_feed_channel.channel_name) - - log_admin("[key_name(usr)] submitted a feed story to channel: [src.admincaster_feed_channel.channel_name]!") - src.access_news_network() - - else if(href_list["ac_create_channel"]) - if(!check_rights(R_ADMIN)) - return - src.admincaster_screen=2 - src.access_news_network() - - else if(href_list["ac_create_feed_story"]) - if(!check_rights(R_ADMIN)) - return - src.admincaster_screen=3 - src.access_news_network() - - else if(href_list["ac_menu_censor_story"]) - if(!check_rights(R_ADMIN)) - return - src.admincaster_screen=10 - src.access_news_network() - - else if(href_list["ac_menu_censor_channel"]) - if(!check_rights(R_ADMIN)) - return - src.admincaster_screen=11 - src.access_news_network() - - else if(href_list["ac_menu_wanted"]) - if(!check_rights(R_ADMIN)) - return - var/already_wanted = 0 - if(GLOB.news_network.wanted_issue.active) - already_wanted = 1 - - if(already_wanted) - src.admincaster_wanted_message.criminal = GLOB.news_network.wanted_issue.criminal - src.admincaster_wanted_message.body = GLOB.news_network.wanted_issue.body - src.admincaster_screen = 14 - src.access_news_network() - - else if(href_list["ac_set_wanted_name"]) - if(!check_rights(R_ADMIN)) - return - src.admincaster_wanted_message.criminal = adminscrub(input(usr, "Provide the name of the Wanted person.", "Network Security Handler", "")) - while(findtext(src.admincaster_wanted_message.criminal," ") == 1) - src.admincaster_wanted_message.criminal = copytext(admincaster_wanted_message.criminal,2,length(admincaster_wanted_message.criminal)+1) - src.access_news_network() - - else if(href_list["ac_set_wanted_desc"]) - if(!check_rights(R_ADMIN)) - return - src.admincaster_wanted_message.body = adminscrub(input(usr, "Provide the a description of the Wanted person and any other details you deem important.", "Network Security Handler", "")) - while (findtext(src.admincaster_wanted_message.body," ") == 1) - src.admincaster_wanted_message.body = copytext(src.admincaster_wanted_message.body,2,length(src.admincaster_wanted_message.body)+1) - src.access_news_network() - - else if(href_list["ac_submit_wanted"]) - if(!check_rights(R_ADMIN)) - return - var/input_param = text2num(href_list["ac_submit_wanted"]) - if(src.admincaster_wanted_message.criminal == "" || src.admincaster_wanted_message.body == "") - src.admincaster_screen = 16 - else - var/choice = alert("Please confirm Wanted Issue [(input_param==1) ? ("creation.") : ("edit.")]","Network Security Handler","Confirm","Cancel") - if(choice=="Confirm") - if(input_param==1) //If input_param == 1 we're submitting a new wanted issue. At 2 we're just editing an existing one. See the else below - GLOB.news_network.submitWanted(admincaster_wanted_message.criminal, admincaster_wanted_message.body, admin_signature, null, 1, 1) - src.admincaster_screen = 15 - else - GLOB.news_network.submitWanted(admincaster_wanted_message.criminal, admincaster_wanted_message.body, admin_signature) - src.admincaster_screen = 19 - log_admin("[key_name(usr)] issued a Station-wide Wanted Notification for [src.admincaster_wanted_message.criminal]!") - src.access_news_network() - - else if(href_list["ac_cancel_wanted"]) - if(!check_rights(R_ADMIN)) - return - var/choice = alert("Please confirm Wanted Issue removal.","Network Security Handler","Confirm","Cancel") - if(choice=="Confirm") - GLOB.news_network.deleteWanted() - src.admincaster_screen=17 - src.access_news_network() - - else if(href_list["ac_censor_channel_author"]) - if(!check_rights(R_ADMIN)) - return - var/datum/newscaster/feed_channel/FC = locate(href_list["ac_censor_channel_author"]) - FC.toggleCensorAuthor() - src.access_news_network() - - else if(href_list["ac_censor_channel_story_author"]) - if(!check_rights(R_ADMIN)) - return - var/datum/newscaster/feed_message/MSG = locate(href_list["ac_censor_channel_story_author"]) - MSG.toggleCensorAuthor() - src.access_news_network() - - else if(href_list["ac_censor_channel_story_body"]) - if(!check_rights(R_ADMIN)) - return - var/datum/newscaster/feed_message/MSG = locate(href_list["ac_censor_channel_story_body"]) - MSG.toggleCensorBody() - src.access_news_network() - - else if(href_list["ac_pick_d_notice"]) - if(!check_rights(R_ADMIN)) - return - var/datum/newscaster/feed_channel/FC = locate(href_list["ac_pick_d_notice"]) - src.admincaster_feed_channel = FC - src.admincaster_screen=13 - src.access_news_network() - - else if(href_list["ac_toggle_d_notice"]) - if(!check_rights(R_ADMIN)) - return - var/datum/newscaster/feed_channel/FC = locate(href_list["ac_toggle_d_notice"]) - FC.toggleCensorDclass() - src.access_news_network() - - else if(href_list["ac_view"]) - if(!check_rights(R_ADMIN)) - return - src.admincaster_screen=1 - src.access_news_network() - - else if(href_list["ac_setScreen"]) //Brings us to the main menu and resets all fields~ - if(!check_rights(R_ADMIN)) - return - src.admincaster_screen = text2num(href_list["ac_setScreen"]) - if (src.admincaster_screen == 0) - if(src.admincaster_feed_channel) - src.admincaster_feed_channel = new /datum/newscaster/feed_channel - if(src.admincaster_feed_message) - src.admincaster_feed_message = new /datum/newscaster/feed_message - if(admincaster_wanted_message) - admincaster_wanted_message = new /datum/newscaster/wanted_message - src.access_news_network() - - else if(href_list["ac_show_channel"]) - if(!check_rights(R_ADMIN)) - return - var/datum/newscaster/feed_channel/FC = locate(href_list["ac_show_channel"]) - src.admincaster_feed_channel = FC - src.admincaster_screen = 9 - src.access_news_network() - - else if(href_list["ac_pick_censor_channel"]) - if(!check_rights(R_ADMIN)) - return - var/datum/newscaster/feed_channel/FC = locate(href_list["ac_pick_censor_channel"]) - src.admincaster_feed_channel = FC - src.admincaster_screen = 12 - src.access_news_network() - - else if(href_list["ac_refresh"]) - if(!check_rights(R_ADMIN)) - return - src.access_news_network() - - else if(href_list["ac_set_signature"]) - if(!check_rights(R_ADMIN)) - return - src.admin_signature = adminscrub(input(usr, "Provide your desired signature.", "Network Identity Handler", "")) - src.access_news_network() - - else if(href_list["ac_del_comment"]) - if(!check_rights(R_ADMIN)) - return - var/datum/newscaster/feed_comment/FC = locate(href_list["ac_del_comment"]) - var/datum/newscaster/feed_message/FM = locate(href_list["ac_del_comment_msg"]) - FM.comments -= FC - qdel(FC) - src.access_news_network() - - else if(href_list["ac_lock_comment"]) - if(!check_rights(R_ADMIN)) - return - var/datum/newscaster/feed_message/FM = locate(href_list["ac_lock_comment"]) - FM.locked ^= 1 - src.access_news_network() - - else if(href_list["check_antagonist"]) - if(!check_rights(R_ADMIN)) - return - usr.client.check_antagonists() - - else if(href_list["kick_all_from_lobby"]) - if(!check_rights(R_ADMIN)) - return - if(SSticker.IsRoundInProgress()) - var/afkonly = text2num(href_list["afkonly"]) - if(alert("Are you sure you want to kick all [afkonly ? "AFK" : ""] clients from the lobby??","Message","Yes","Cancel") != "Yes") - to_chat(usr, "Kick clients from lobby aborted") - return - var/list/listkicked = kick_clients_in_lobby("You were kicked from the lobby by [usr.client.holder.fakekey ? "an Administrator" : "[usr.client.key]"].", afkonly) - - var/strkicked = "" - for(var/name in listkicked) - strkicked += "[name], " - message_admins("[key_name_admin(usr)] has kicked [afkonly ? "all AFK" : "all"] clients from the lobby. [length(listkicked)] clients kicked: [strkicked ? strkicked : "--"]") - log_admin("[key_name(usr)] has kicked [afkonly ? "all AFK" : "all"] clients from the lobby. [length(listkicked)] clients kicked: [strkicked ? strkicked : "--"]") - else - to_chat(usr, "You may only use this when the game is running.") - - else if(href_list["create_outfit"]) - if(!check_rights(R_ADMIN)) - return - - var/datum/outfit/O = new /datum/outfit - //swap this for js dropdowns sometime - O.name = href_list["outfit_name"] - O.uniform = text2path(href_list["outfit_uniform"]) - O.shoes = text2path(href_list["outfit_shoes"]) - O.gloves = text2path(href_list["outfit_gloves"]) - O.suit = text2path(href_list["outfit_suit"]) - O.head = text2path(href_list["outfit_head"]) - O.back = text2path(href_list["outfit_back"]) - O.mask = text2path(href_list["outfit_mask"]) - O.glasses = text2path(href_list["outfit_glasses"]) - O.r_hand = text2path(href_list["outfit_r_hand"]) - O.l_hand = text2path(href_list["outfit_l_hand"]) - O.suit_store = text2path(href_list["outfit_s_store"]) - O.l_pocket = text2path(href_list["outfit_l_pocket"]) - O.r_pocket = text2path(href_list["outfit_r_pocket"]) - O.id = text2path(href_list["outfit_id"]) - O.belt = text2path(href_list["outfit_belt"]) - O.ears = text2path(href_list["outfit_ears"]) - - GLOB.custom_outfits.Add(O) - message_admins("[key_name(usr)] created \"[O.name]\" outfit!") - - else if(href_list["set_selfdestruct_code"]) - if(!check_rights(R_ADMIN)) - return - var/code = random_nukecode() - for(var/obj/machinery/nuclearbomb/selfdestruct/SD in GLOB.nuke_list) - SD.r_code = code - message_admins("[key_name_admin(usr)] has set the self-destruct \ - code to \"[code]\".") - - else if(href_list["add_station_goal"]) - if(!check_rights(R_ADMIN)) - return - var/list/type_choices = typesof(/datum/station_goal) - var/picked = input("Choose goal type") in type_choices|null - if(!picked) - return - var/datum/station_goal/G = new picked() - if(picked == /datum/station_goal) - var/newname = input("Enter goal name:") as text|null - if(!newname) - return - G.name = newname - var/description = input("Enter CentCom message contents:") as message|null - if(!description) - return - G.report_message = description - message_admins("[key_name(usr)] created \"[G.name]\" station goal.") - SSticker.mode.station_goals += G - modify_goals() - - else if(href_list["viewruntime"]) - var/datum/error_viewer/error_viewer = locate(href_list["viewruntime"]) - if(!istype(error_viewer)) - to_chat(usr, "That runtime viewer no longer exists.") - return - - if(href_list["viewruntime_backto"]) - error_viewer.show_to(owner, locate(href_list["viewruntime_backto"]), href_list["viewruntime_linear"]) - else - error_viewer.show_to(owner, null, href_list["viewruntime_linear"]) - - else if(href_list["showrelatedacc"]) - if(!check_rights(R_ADMIN)) - return - var/client/C = locate(href_list["client"]) in GLOB.clients - var/thing_to_check - if(href_list["showrelatedacc"] == "cid") - thing_to_check = C.related_accounts_cid - else - thing_to_check = C.related_accounts_ip - thing_to_check = splittext(thing_to_check, ", ") - - - var/list/dat = list("Related accounts by [uppertext(href_list["showrelatedacc"])]:") - dat += thing_to_check - - usr << browse(dat.Join("
    "), "window=related_[C];size=420x300") - else if(href_list["centcomlookup"]) - if(!check_rights(R_ADMIN)) - return - - if(!CONFIG_GET(string/centcom_ban_db)) - to_chat(usr, "Centcom Galactic Ban DB is disabled!") - return - - var/ckey = href_list["centcomlookup"] - - // Make the request - var/datum/http_request/request = new() - request.prepare(RUSTG_HTTP_METHOD_GET, "[CONFIG_GET(string/centcom_ban_db)]/[ckey]", "", "") - request.begin_async() - UNTIL(request.is_complete() || !usr) - if (!usr) - return - var/datum/http_response/response = request.into_response() - - var/list/bans - - var/list/dat = list("") - - if(response.errored) - dat += "
    Failed to connect to CentCom." - else if(response.status_code != 200) - dat += "
    Failed to connect to CentCom. Status code: [response.status_code]" - else - if(response.body == "[]") - dat += "
    0 bans detected for [ckey]
    " - else - bans = json_decode(response["body"]) - dat += "
    [bans.len] ban\s detected for [ckey]
    " - for(var/list/ban in bans) - dat += "Server: [sanitize(ban["sourceName"])]
    " - dat += "RP Level: [sanitize(ban["sourceRoleplayLevel"])]
    " - dat += "Type: [sanitize(ban["type"])]
    " - dat += "Banned By: [sanitize(ban["bannedBy"])]
    " - dat += "Reason: [sanitize(ban["reason"])]
    " - dat += "Datetime: [sanitize(ban["bannedOn"])]
    " - var/expiration = ban["expires"] - dat += "Expires: [expiration ? "[sanitize(expiration)]" : "Permanent"]
    " - if(ban["type"] == "job") - dat += "Jobs: " - var/list/jobs = ban["jobs"] - dat += sanitize(jobs.Join(", ")) - dat += "
    " - dat += "
    " - - dat += "
    " - var/datum/browser/popup = new(usr, "centcomlookup-[ckey]", "
    Central Command Galactic Ban Database
    ", 700, 600) - popup.set_content(dat.Join()) - popup.open(0) - - - else if(href_list["modantagrep"]) - if(!check_rights(R_ADMIN)) - return - - var/mob/M = locate(href_list["mob"]) in GLOB.mob_list - var/client/C = M.client - usr.client.cmd_admin_mod_antag_rep(C, href_list["modantagrep"]) - show_player_panel(M) - - else if(href_list["slowquery"]) - if(!check_rights(R_ADMIN)) - return - var/answer = href_list["slowquery"] - if(answer == "yes") - log_query_debug("[usr.key] | Reported a server hang") - if(alert(usr, "Had you just press any admin buttons?", "Query server hang report", "Yes", "No") == "Yes") - var/response = input(usr,"What were you just doing?","Query server hang report") as null|text - if(response) - log_query_debug("[usr.key] | [response]") - else if(answer == "no") - log_query_debug("[usr.key] | Reported no server hang") - -/datum/admins/proc/HandleCMode() - if(!check_rights(R_ADMIN)) - return - - if(SSticker.HasRoundStarted()) - return alert(usr, "The game has already started.", null, null, null, null) - var/dat = {"What mode do you wish to play?
    "} - for(var/mode in config.modes) - dat += {"[config.mode_names[mode]]
    "} - dat += {"Secret
    "} - dat += {"Random
    "} - dat += {"Now: [GLOB.master_mode]"} - usr << browse(dat, "window=c_mode") - -/datum/admins/proc/HandleFSecret() - if(!check_rights(R_ADMIN)) - return - - if(SSticker.HasRoundStarted()) - return alert(usr, "The game has already started.", null, null, null, null) - if(GLOB.master_mode != "secret") - return alert(usr, "The game mode has to be secret!", null, null, null, null) - var/dat = {"What game mode do you want to force secret to be? Use this if you want to change the game mode, but want the players to believe it's secret. This will only work if the current game mode is secret.
    "} - for(var/mode in config.modes) - dat += {"[config.mode_names[mode]]
    "} - dat += {"Random (default)
    "} - dat += {"Now: [GLOB.secret_force_mode]"} - usr << browse(dat, "window=f_secret") - -/datum/admins/proc/makeMentor(ckey) - if(!usr.client) - return - if (!check_rights(0)) - return - if(!ckey) - return - var/client/C = GLOB.directory[ckey] - if(C) - if(check_rights_for(C, R_ADMIN,0)) - to_chat(usr, "The client chosen is an admin! Cannot mentorize.") - return - if(SSdbcore.Connect()) - var/datum/DBQuery/query_get_mentor = SSdbcore.NewQuery("SELECT id FROM [format_table_name("mentor")] WHERE ckey = '[ckey]'") - if(query_get_mentor.NextRow()) - to_chat(usr, "[ckey] is already a mentor.") - return - var/datum/DBQuery/query_add_mentor = SSdbcore.NewQuery("INSERT INTO `[format_table_name("mentor")]` (`id`, `ckey`) VALUES (null, '[ckey]')") - if(!query_add_mentor.warn_execute()) - return - var/datum/DBQuery/query_add_admin_log = SSdbcore.NewQuery("INSERT INTO `[format_table_name("admin_log")]` (`id` ,`datetime` ,`adminckey` ,`adminip` ,`log` ) VALUES (NULL , NOW( ) , '[usr.ckey]', '[usr.client.address]', 'Added new mentor [ckey]');") - if(!query_add_admin_log.warn_execute()) - return - else - to_chat(usr, "Failed to establish database connection. The changes will last only for the current round.") - new /datum/mentors(ckey) - to_chat(usr, "New mentor added.") - -/datum/admins/proc/removeMentor(ckey) - if(!usr.client) - return - if (!check_rights(0)) - return - if(!ckey) - return - var/client/C = GLOB.directory[ckey] - if(C) - if(check_rights_for(C, R_ADMIN,0)) - to_chat(usr, "The client chosen is an admin, not a mentor! Cannot de-mentorize.") - return - C.remove_mentor_verbs() - C.mentor_datum = null - GLOB.mentors -= C - if(SSdbcore.Connect()) - var/datum/DBQuery/query_remove_mentor = SSdbcore.NewQuery("DELETE FROM [format_table_name("mentor")] WHERE ckey = '[ckey]'") - if(!query_remove_mentor.warn_execute()) - return - var/datum/DBQuery/query_add_admin_log = SSdbcore.NewQuery("INSERT INTO `[format_table_name("admin_log")]` (`id` ,`datetime` ,`adminckey` ,`adminip` ,`log` ) VALUES (NULL , NOW( ) , '[usr.ckey]', '[usr.client.address]', 'Removed mentor [ckey]');") - if(!query_add_admin_log.warn_execute()) - return - else - to_chat(usr, "Failed to establish database connection. The changes will last only for the current round.") - to_chat(usr, "Mentor removed.") +/datum/admins/proc/CheckAdminHref(href, href_list) + var/auth = href_list["admin_token"] + . = auth && (auth == href_token || auth == GLOB.href_token) + if(.) + return + var/msg = !auth ? "no" : "a bad" + message_admins("[key_name_admin(usr)] clicked an href with [msg] authorization key!") + if(CONFIG_GET(flag/debug_admin_hrefs)) + message_admins("Debug mode enabled, call not blocked. Please ask your coders to review this round's logs.") + log_world("UAH: [href]") + return TRUE + log_admin_private("[key_name(usr)] clicked an href with [msg] authorization key! [href]") + +/datum/admins/Topic(href, href_list) + ..() + + if(usr.client != src.owner || !check_rights(0)) + message_admins("[usr.key] tried to use the admin panel without authorization.") + log_admin("[key_name(usr)] tried to use the admin panel without authorization.") + return + + if(!CheckAdminHref(href, href_list)) + return + + if(href_list["makementor"]) + makeMentor(href_list["makementor"]) + else if(href_list["removementor"]) + removeMentor(href_list["removementor"]) + + if(href_list["ahelp"]) + if(!check_rights(R_ADMIN, TRUE)) + return + + var/ahelp_ref = href_list["ahelp"] + var/datum/admin_help/AH = locate(ahelp_ref) + if(AH) + AH.Action(href_list["ahelp_action"]) + else + to_chat(usr, "Ticket [ahelp_ref] has been deleted!") + + else if(href_list["ahelp_tickets"]) + GLOB.ahelp_tickets.BrowseTickets(text2num(href_list["ahelp_tickets"])) + + else if(href_list["stickyban"]) + stickyban(href_list["stickyban"],href_list) + + else if(href_list["getplaytimewindow"]) + if(!check_rights(R_ADMIN)) + return + var/mob/M = locate(href_list["getplaytimewindow"]) in GLOB.mob_list + if(!M) + to_chat(usr, "ERROR: Mob not found.") + return + cmd_show_exp_panel(M.client) + + else if(href_list["toggleexempt"]) + if(!check_rights(R_ADMIN)) + return + var/client/C = locate(href_list["toggleexempt"]) in GLOB.clients + if(!C) + to_chat(usr, "ERROR: Client not found.") + return + toggle_exempt_status(C) + + else if(href_list["makeAntag"]) + if(!check_rights(R_ADMIN)) + return + if (!SSticker.mode) + to_chat(usr, "Not until the round starts!") + return + switch(href_list["makeAntag"]) + if("traitors") + if(src.makeTraitors()) + message_admins("[key_name_admin(usr)] created traitors.") + log_admin("[key_name(usr)] created traitors.") + else + message_admins("[key_name_admin(usr)] tried to create traitors. Unfortunately, there were no candidates available.") + log_admin("[key_name(usr)] failed to create traitors.") + if("changelings") + if(src.makeChangelings()) + message_admins("[key_name(usr)] created changelings.") + log_admin("[key_name(usr)] created changelings.") + else + message_admins("[key_name_admin(usr)] tried to create changelings. Unfortunately, there were no candidates available.") + log_admin("[key_name(usr)] failed to create changelings.") + if("revs") + if(src.makeRevs()) + message_admins("[key_name(usr)] started a revolution.") + log_admin("[key_name(usr)] started a revolution.") + else + message_admins("[key_name_admin(usr)] tried to start a revolution. Unfortunately, there were no candidates available.") + log_admin("[key_name(usr)] failed to start a revolution.") + if("cult") + if(src.makeCult()) + message_admins("[key_name(usr)] started a cult.") + log_admin("[key_name(usr)] started a cult.") + else + message_admins("[key_name_admin(usr)] tried to start a cult. Unfortunately, there were no candidates available.") + log_admin("[key_name(usr)] failed to start a cult.") + if("wizard") + message_admins("[key_name(usr)] is creating a wizard...") + if(src.makeWizard()) + message_admins("[key_name(usr)] created a wizard.") + log_admin("[key_name(usr)] created a wizard.") + else + message_admins("[key_name_admin(usr)] tried to create a wizard. Unfortunately, there were no candidates available.") + log_admin("[key_name(usr)] failed to create a wizard.") + if("nukeops") + message_admins("[key_name(usr)] is creating a nuke team...") + if(src.makeNukeTeam()) + message_admins("[key_name(usr)] created a nuke team.") + log_admin("[key_name(usr)] created a nuke team.") + else + message_admins("[key_name_admin(usr)] tried to create a nuke team. Unfortunately, there were not enough candidates available.") + log_admin("[key_name(usr)] failed to create a nuke team.") + if("ninja") + message_admins("[key_name(usr)] spawned a ninja.") + log_admin("[key_name(usr)] spawned a ninja.") + src.makeSpaceNinja() + if("aliens") + message_admins("[key_name(usr)] started an alien infestation.") + log_admin("[key_name(usr)] started an alien infestation.") + src.makeAliens() + if("deathsquad") + message_admins("[key_name(usr)] is creating a death squad...") + if(src.makeDeathsquad()) + message_admins("[key_name(usr)] created a death squad.") + log_admin("[key_name(usr)] created a death squad.") + else + message_admins("[key_name_admin(usr)] tried to create a death squad. Unfortunately, there were not enough candidates available.") + log_admin("[key_name(usr)] failed to create a death squad.") + if("blob") + var/strength = input("Set Blob Resource Gain Rate","Set Resource Rate",1) as num|null + if(!strength) + return + message_admins("[key_name(usr)] spawned a blob with base resource gain [strength].") + log_admin("[key_name(usr)] spawned a blob with base resource gain [strength].") + new/datum/round_event/ghost_role/blob(TRUE, strength) + if("centcom") + message_admins("[key_name(usr)] is creating a CentCom response team...") + if(src.makeEmergencyresponseteam()) + message_admins("[key_name(usr)] created a CentCom response team.") + log_admin("[key_name(usr)] created a CentCom response team.") + else + message_admins("[key_name_admin(usr)] tried to create a CentCom response team. Unfortunately, there were not enough candidates available.") + log_admin("[key_name(usr)] failed to create a CentCom response team.") + if("abductors") + message_admins("[key_name(usr)] is creating an abductor team...") + if(src.makeAbductorTeam()) + message_admins("[key_name(usr)] created an abductor team.") + log_admin("[key_name(usr)] created an abductor team.") + else + message_admins("[key_name_admin(usr)] tried to create an abductor team. Unfortunatly there were not enough candidates available.") + log_admin("[key_name(usr)] failed to create an abductor team.") + if("clockcult") + if(src.makeClockCult()) + message_admins("[key_name(usr)] started a clockwork cult.") + log_admin("[key_name(usr)] started a clockwork cult.") + else + message_admins("[key_name_admin(usr)] tried to start a clockwork cult. Unfortunately, there were no candidates available.") + log_admin("[key_name(usr)] failed to start a clockwork cult.") + if("revenant") + if(src.makeRevenant()) + message_admins("[key_name(usr)] created a revenant.") + log_admin("[key_name(usr)] created a revenant.") + else + message_admins("[key_name_admin(usr)] tried to create a revenant. Unfortunately, there were no candidates available.") + log_admin("[key_name(usr)] failed to create a revenant.") + //Hyper + if("lewdtraitors") + if(src.makeLewdtraitors()) + message_admins("[key_name(usr)] created a lewd traitor.") + log_admin("[key_name(usr)] created a lewd traitor.") + else + message_admins("[key_name_admin(usr)] tried to create a lewd traitor. Unfortunately, there were no candidates available.") + log_admin("[key_name(usr)] failed to create a lewd traitor.") + + else if(href_list["forceevent"]) + if(!check_rights(R_FUN)) + return + var/datum/round_event_control/E = locate(href_list["forceevent"]) in SSevents.control + if(E) + E.admin_setup(usr) + var/datum/round_event/event = E.runEvent() + if(event.announceWhen>0) + event.processing = FALSE + var/prompt = alert(usr, "Would you like to alert the crew?", "Alert", "Yes", "No", "Cancel") + switch(prompt) + if("Cancel") + event.kill() + return + if("No") + event.announceWhen = -1 + event.processing = TRUE + message_admins("[key_name_admin(usr)] has triggered an event. ([E.name])") + log_admin("[key_name(usr)] has triggered an event. ([E.name])") + return + + else if(href_list["dbsearchckey"] || href_list["dbsearchadmin"] || href_list["dbsearchip"] || href_list["dbsearchcid"]) + var/adminckey = href_list["dbsearchadmin"] + var/playerckey = href_list["dbsearchckey"] + var/ip = href_list["dbsearchip"] + var/cid = href_list["dbsearchcid"] + var/page = href_list["dbsearchpage"] + + DB_ban_panel(playerckey, adminckey, ip, cid, page) + return + + else if(href_list["dbbanedit"]) + var/banedit = href_list["dbbanedit"] + var/banid = text2num(href_list["dbbanid"]) + if(!banedit || !banid) + return + + DB_ban_edit(banid, banedit) + return + + else if(href_list["dbbanaddtype"]) + if(!check_rights(R_BAN)) + return + var/bantype = text2num(href_list["dbbanaddtype"]) + var/bankey = href_list["dbbanaddkey"] + var/banckey = ckey(bankey) + var/banip = href_list["dbbanaddip"] + var/bancid = href_list["dbbanaddcid"] + var/banduration = text2num(href_list["dbbaddduration"]) + var/banjob = href_list["dbbanaddjob"] + var/banreason = href_list["dbbanreason"] + var/banseverity = href_list["dbbanaddseverity"] + + switch(bantype) + if(BANTYPE_PERMA) + if(!banckey || !banreason || !banseverity) + to_chat(usr, "Not enough parameters (Requires ckey, severity, and reason).") + return + banduration = null + banjob = null + if(BANTYPE_TEMP) + if(!banckey || !banreason || !banduration || !banseverity) + to_chat(usr, "Not enough parameters (Requires ckey, reason, severity and duration).") + return + banjob = null + if(BANTYPE_JOB_PERMA) + if(!banckey || !banreason || !banjob || !banseverity) + to_chat(usr, "Not enough parameters (Requires ckey, severity, reason and job).") + return + banduration = null + if(BANTYPE_JOB_TEMP) + if(!banckey || !banreason || !banjob || !banduration || !banseverity) + to_chat(usr, "Not enough parameters (Requires ckey, severity, reason and job).") + return + if(BANTYPE_ADMIN_PERMA) + if(!banckey || !banreason || !banseverity) + to_chat(usr, "Not enough parameters (Requires ckey, severity and reason).") + return + banduration = null + banjob = null + if(BANTYPE_ADMIN_TEMP) + if(!banckey || !banreason || !banduration || !banseverity) + to_chat(usr, "Not enough parameters (Requires ckey, severity, reason and duration).") + return + banjob = null + + var/mob/playermob + + for(var/mob/M in GLOB.player_list) + if(M.ckey == banckey) + playermob = M + break + + + banreason = "(MANUAL BAN) "+banreason + + if(!playermob) + if(banip) + banreason = "[banreason] (CUSTOM IP)" + if(bancid) + banreason = "[banreason] (CUSTOM CID)" + else + message_admins("Ban process: A mob matching [playermob.key] was found at location [playermob.x], [playermob.y], [playermob.z]. Custom ip and computer id fields replaced with the ip and computer id from the located mob.") + + if(!DB_ban_record(bantype, playermob, banduration, banreason, banjob, bankey, banip, bancid )) + to_chat(usr, "Failed to apply ban.") + return + create_message("note", bankey, null, banreason, null, null, 0, 0, null, 0, banseverity) + + else if(href_list["editrightsbrowser"]) + edit_admin_permissions(0) + + else if(href_list["editrightsbrowserlog"]) + edit_admin_permissions(1, href_list["editrightstarget"], href_list["editrightsoperation"], href_list["editrightspage"]) + + if(href_list["editrightsbrowsermanage"]) + if(href_list["editrightschange"]) + change_admin_rank(ckey(href_list["editrightschange"]), href_list["editrightschange"], TRUE) + else if(href_list["editrightsremove"]) + remove_admin(ckey(href_list["editrightsremove"]), href_list["editrightsremove"], TRUE) + else if(href_list["editrightsremoverank"]) + remove_rank(href_list["editrightsremoverank"]) + edit_admin_permissions(2) + + else if(href_list["editrights"]) + edit_rights_topic(href_list) + + else if(href_list["gamemode_panel"]) + if(!check_rights(R_ADMIN)) + return + SSticker.mode.admin_panel() + + + else if(href_list["call_shuttle"]) + if(!check_rights(R_ADMIN)) + return + + + switch(href_list["call_shuttle"]) + if("1") + if(EMERGENCY_AT_LEAST_DOCKED) + return + SSshuttle.emergency.request() + log_admin("[key_name(usr)] called the Emergency Shuttle.") + message_admins("[key_name_admin(usr)] called the Emergency Shuttle to the station.") + + if("2") + if(EMERGENCY_AT_LEAST_DOCKED) + return + switch(SSshuttle.emergency.mode) + if(SHUTTLE_CALL) + SSshuttle.emergency.cancel() + log_admin("[key_name(usr)] sent the Emergency Shuttle back.") + message_admins("[key_name_admin(usr)] sent the Emergency Shuttle back.") + else + SSshuttle.emergency.cancel() + log_admin("[key_name(usr)] called the Emergency Shuttle.") + message_admins("[key_name_admin(usr)] called the Emergency Shuttle to the station.") + + + href_list["secrets"] = "check_antagonist" + + else if(href_list["edit_shuttle_time"]) + if(!check_rights(R_SERVER)) + return + + var/timer = input("Enter new shuttle duration (seconds):","Edit Shuttle Timeleft", SSshuttle.emergency.timeLeft() ) as num|null + if(!timer) + return + SSshuttle.emergency.setTimer(timer*10) + log_admin("[key_name(usr)] edited the Emergency Shuttle's timeleft to [timer] seconds.") + minor_announce("The emergency shuttle will reach its destination in [round(SSshuttle.emergency.timeLeft(600))] minutes.") + message_admins("[key_name_admin(usr)] edited the Emergency Shuttle's timeleft to [timer] seconds.") + href_list["secrets"] = "check_antagonist" + else if(href_list["trigger_centcom_recall"]) + if(!check_rights(R_ADMIN)) + return + + usr.client.trigger_centcom_recall() + + else if(href_list["toggle_continuous"]) + if(!check_rights(R_ADMIN)) + return + var/list/continuous = CONFIG_GET(keyed_list/continuous) + if(!continuous[SSticker.mode.config_tag]) + continuous[SSticker.mode.config_tag] = TRUE + else + continuous[SSticker.mode.config_tag] = FALSE + + message_admins("[key_name_admin(usr)] toggled the round to [continuous[SSticker.mode.config_tag] ? "continue if all antagonists die" : "end with the antagonists"].") + check_antagonists() + + else if(href_list["toggle_midround_antag"]) + if(!check_rights(R_ADMIN)) + return + + var/list/midround_antag = CONFIG_GET(keyed_list/midround_antag) + if(!midround_antag[SSticker.mode.config_tag]) + midround_antag[SSticker.mode.config_tag] = TRUE + else + midround_antag[SSticker.mode.config_tag] = FALSE + + message_admins("[key_name_admin(usr)] toggled the round to [midround_antag[SSticker.mode.config_tag] ? "use" : "skip"] the midround antag system.") + check_antagonists() + + else if(href_list["alter_midround_time_limit"]) + if(!check_rights(R_ADMIN)) + return + + var/timer = input("Enter new maximum time",, CONFIG_GET(number/midround_antag_time_check)) as num|null + if(!timer) + return + CONFIG_SET(number/midround_antag_time_check, timer) + message_admins("[key_name_admin(usr)] edited the maximum midround antagonist time to [timer] minutes.") + check_antagonists() + + else if(href_list["alter_midround_life_limit"]) + if(!check_rights(R_ADMIN)) + return + + var/ratio = input("Enter new life ratio",, CONFIG_GET(number/midround_antag_life_check) * 100) as num + if(!ratio) + return + CONFIG_SET(number/midround_antag_life_check, ratio / 100) + + message_admins("[key_name_admin(usr)] edited the midround antagonist living crew ratio to [ratio]% alive.") + check_antagonists() + + else if(href_list["toggle_noncontinuous_behavior"]) + if(!check_rights(R_ADMIN)) + return + + if(!SSticker.mode.round_ends_with_antag_death) + SSticker.mode.round_ends_with_antag_death = 1 + else + SSticker.mode.round_ends_with_antag_death = 0 + + message_admins("[key_name_admin(usr)] edited the midround antagonist system to [SSticker.mode.round_ends_with_antag_death ? "end the round" : "continue as extended"] upon failure.") + check_antagonists() + + else if(href_list["delay_round_end"]) + if(!check_rights(R_SERVER)) + return + if(!SSticker.delay_end) + SSticker.admin_delay_notice = input(usr, "Enter a reason for delaying the round end", "Round Delay Reason") as null|text + if(isnull(SSticker.admin_delay_notice)) + return + else + SSticker.admin_delay_notice = null + SSticker.delay_end = !SSticker.delay_end + var/reason = SSticker.delay_end ? "for reason: [SSticker.admin_delay_notice]" : "."//laziness + var/msg = "[SSticker.delay_end ? "delayed" : "undelayed"] the round end [reason]" + log_admin("[key_name(usr)] [msg]") + message_admins("[key_name_admin(usr)] [msg]") + href_list["secrets"] = "check_antagonist" + if(SSticker.ready_for_reboot && !SSticker.delay_end) //we undelayed after standard reboot would occur + SSticker.standard_reboot() + + else if(href_list["end_round"]) + if(!check_rights(R_ADMIN)) + return + + message_admins("[key_name_admin(usr)] is considering ending the round.") + if(alert(usr, "This will end the round, are you SURE you want to do this?", "Confirmation", "Yes", "No") == "Yes") + if(alert(usr, "Final Confirmation: End the round NOW?", "Confirmation", "Yes", "No") == "Yes") + message_admins("[key_name_admin(usr)] has ended the round.") + SSticker.force_ending = 1 //Yeah there we go APC destroyed mission accomplished + return + else + message_admins("[key_name_admin(usr)] decided against ending the round.") + else + message_admins("[key_name_admin(usr)] decided against ending the round.") + + else if(href_list["simplemake"]) + if(!check_rights(R_SPAWN)) + return + + var/mob/M = locate(href_list["mob"]) + if(!ismob(M)) + to_chat(usr, "This can only be used on instances of type /mob.") + return + + var/delmob = 0 + switch(alert("Delete old mob?","Message","Yes","No","Cancel")) + if("Cancel") + return + if("Yes") + delmob = 1 + + log_admin("[key_name(usr)] has used rudimentary transformation on [key_name(M)]. Transforming to [href_list["simplemake"]].; deletemob=[delmob]") + message_admins("[key_name_admin(usr)] has used rudimentary transformation on [key_name_admin(M)]. Transforming to [href_list["simplemake"]].; deletemob=[delmob]") + switch(href_list["simplemake"]) + if("observer") + M.change_mob_type( /mob/dead/observer , null, null, delmob ) + if("drone") + M.change_mob_type( /mob/living/carbon/alien/humanoid/drone , null, null, delmob ) + if("hunter") + M.change_mob_type( /mob/living/carbon/alien/humanoid/hunter , null, null, delmob ) + if("queen") + M.change_mob_type( /mob/living/carbon/alien/humanoid/royal/queen , null, null, delmob ) + if("praetorian") + M.change_mob_type( /mob/living/carbon/alien/humanoid/royal/praetorian , null, null, delmob ) + if("sentinel") + M.change_mob_type( /mob/living/carbon/alien/humanoid/sentinel , null, null, delmob ) + if("larva") + M.change_mob_type( /mob/living/carbon/alien/larva , null, null, delmob ) + if("human") + var/posttransformoutfit = usr.client.robust_dress_shop() + var/mob/living/carbon/human/newmob = M.change_mob_type( /mob/living/carbon/human , null, null, delmob ) + if(posttransformoutfit && istype(newmob)) + newmob.equipOutfit(posttransformoutfit) + if("slime") + M.change_mob_type( /mob/living/simple_animal/slime , null, null, delmob ) + if("monkey") + M.change_mob_type( /mob/living/carbon/monkey , null, null, delmob ) + if("robot") + M.change_mob_type( /mob/living/silicon/robot , null, null, delmob ) + if("cat") + M.change_mob_type( /mob/living/simple_animal/pet/cat , null, null, delmob ) + if("runtime") + M.change_mob_type( /mob/living/simple_animal/pet/cat/Runtime , null, null, delmob ) + if("corgi") + M.change_mob_type( /mob/living/simple_animal/pet/dog/corgi , null, null, delmob ) + if("ian") + M.change_mob_type( /mob/living/simple_animal/pet/dog/corgi/Ian , null, null, delmob ) + if("pug") + M.change_mob_type( /mob/living/simple_animal/pet/dog/pug , null, null, delmob ) + if("crab") + M.change_mob_type( /mob/living/simple_animal/crab , null, null, delmob ) + if("coffee") + M.change_mob_type( /mob/living/simple_animal/crab/Coffee , null, null, delmob ) + if("parrot") + M.change_mob_type( /mob/living/simple_animal/parrot , null, null, delmob ) + if("polyparrot") + M.change_mob_type( /mob/living/simple_animal/parrot/Poly , null, null, delmob ) + if("constructarmored") + M.change_mob_type( /mob/living/simple_animal/hostile/construct/armored , null, null, delmob ) + if("constructbuilder") + M.change_mob_type( /mob/living/simple_animal/hostile/construct/builder , null, null, delmob ) + if("constructwraith") + M.change_mob_type( /mob/living/simple_animal/hostile/construct/wraith , null, null, delmob ) + if("shade") + M.change_mob_type( /mob/living/simple_animal/shade , null, null, delmob ) + + + /////////////////////////////////////new ban stuff + else if(href_list["unbanf"]) + if(!check_rights(R_BAN)) + return + + var/banfolder = href_list["unbanf"] + GLOB.Banlist.cd = "/base/[banfolder]" + var/key = GLOB.Banlist["key"] + if(alert(usr, "Are you sure you want to unban [key]?", "Confirmation", "Yes", "No") == "Yes") + if(RemoveBan(banfolder)) + unbanpanel() + else + alert(usr, "This ban has already been lifted / does not exist.", "Error", "Ok") + unbanpanel() + + else if(href_list["unbane"]) + if(!check_rights(R_BAN)) + return + + UpdateTime() + var/reason + + var/banfolder = href_list["unbane"] + GLOB.Banlist.cd = "/base/[banfolder]" + var/reason2 = GLOB.Banlist["reason"] + var/temp = GLOB.Banlist["temp"] + + var/minutes = GLOB.Banlist["minutes"] + + var/banned_key = GLOB.Banlist["key"] + GLOB.Banlist.cd = "/base" + + var/duration + + switch(alert("Temporary Ban for [banned_key]?",,"Yes","No")) + if("Yes") + temp = 1 + var/mins = 0 + if(minutes > GLOB.CMinutes) + mins = minutes - GLOB.CMinutes + mins = input(usr,"How long (in minutes)? (Default: 1440)","Ban time",mins ? mins : 1440) as num|null + if(mins <= 0) + to_chat(usr, "[mins] is not a valid duration.") + return + minutes = GLOB.CMinutes + mins + duration = GetExp(minutes) + reason = input(usr,"Please State Reason For Banning [banned_key].","Reason",reason2) as message|null + if(!reason) + return + if("No") + temp = 0 + duration = "Perma" + reason = input(usr,"Please State Reason For Banning [banned_key].","Reason",reason2) as message|null + if(!reason) + return + + log_admin_private("[key_name(usr)] edited [banned_key]'s ban. Reason: [reason] Duration: [duration]") + ban_unban_log_save("[key_name(usr)] edited [banned_key]'s ban. Reason: [reason] Duration: [duration]") + message_admins("[key_name_admin(usr)] edited [banned_key]'s ban. Reason: [reason] Duration: [duration]") + GLOB.Banlist.cd = "/base/[banfolder]" + WRITE_FILE(GLOB.Banlist["reason"], reason) + WRITE_FILE(GLOB.Banlist["temp"], temp) + WRITE_FILE(GLOB.Banlist["minutes"], minutes) + WRITE_FILE(GLOB.Banlist["bannedby"], usr.ckey) + GLOB.Banlist.cd = "/base" + unbanpanel() + + /////////////////////////////////////new ban stuff + + else if(href_list["appearanceban"]) + if(!check_rights(R_BAN)) + return + var/mob/M = locate(href_list["appearanceban"]) + if(!ismob(M)) + to_chat(usr, "This can only be used on instances of type /mob") + return + if(!M.ckey) //sanity + to_chat(usr, "This mob has no ckey") + return + + + if(jobban_isbanned(M, "appearance")) + switch(alert("Remove appearance ban?","Please Confirm","Yes","No")) + if("Yes") + ban_unban_log_save("[key_name(usr)] removed [key_name(M)]'s appearance ban.") + log_admin_private("[key_name(usr)] removed [key_name(M)]'s appearance ban.") + DB_ban_unban(M.ckey, BANTYPE_ANY_JOB, "appearance") + if(M.client) + jobban_buildcache(M.client) + message_admins("[key_name_admin(usr)] removed [key_name_admin(M)]'s appearance ban.") + to_chat(M, "[usr.client.key] has removed your appearance ban.") + + else switch(alert("Appearance ban [M.key]?",,"Yes","No", "Cancel")) + if("Yes") + var/reason = input(usr,"Please State Reason.","Reason") as message|null + if(!reason) + return + var/severity = input("Set the severity of the note/ban.", "Severity", null, null) as null|anything in list("High", "Medium", "Minor", "None") + if(!severity) + return + if(!DB_ban_record(BANTYPE_JOB_PERMA, M, -1, reason, "appearance")) + to_chat(usr, "Failed to apply ban.") + return + if(M.client) + jobban_buildcache(M.client) + ban_unban_log_save("[key_name(usr)] appearance banned [key_name(M)]. reason: [reason]") + log_admin_private("[key_name(usr)] appearance banned [key_name(M)]. \nReason: [reason]") + create_message("note", M.key, null, "Appearance banned - [reason]", null, null, 0, 0, null, 0, severity) + message_admins("[key_name_admin(usr)] appearance banned [key_name_admin(M)].") + to_chat(M, "You have been appearance banned by [usr.client.key].") + to_chat(M, "The reason is: [reason]") + to_chat(M, "Appearance ban can be lifted only upon request.") + var/bran = CONFIG_GET(string/banappeals) + if(bran) + to_chat(M, "To try to resolve this matter head to [bran]") + else + to_chat(M, "No ban appeals URL has been set.") + if("No") + return + + else if(href_list["jobban2"]) + if(!check_rights(R_BAN)) + return + var/mob/M = locate(href_list["jobban2"]) + if(!ismob(M)) + to_chat(usr, "This can only be used on instances of type /mob.") + return + + if(!M.ckey) //sanity + to_chat(usr, "This mob has no ckey.") + return + + var/dat = "Job-Ban Panel: [key_name(M)]" + + /***********************************WARNING!************************************ + The jobban stuff looks mangled and disgusting + But it looks beautiful in-game + -Nodrak + ************************************WARNING!***********************************/ + var/counter = 0 +//Regular jobs + //Command (Blue) + dat += "" + dat += "" + for(var/jobPos in GLOB.command_positions) + if(!jobPos) + continue + if(jobban_isbanned(M, jobPos)) + dat += "" + counter++ + else + dat += "" + counter++ + + if(counter >= 6) //So things dont get squiiiiished! + dat += "" + counter = 0 + dat += "
    Command Positions
    [jobPos][jobPos]
    " + + //Security (Red) + counter = 0 + dat += "" + dat += "" + for(var/jobPos in GLOB.security_positions) + if(!jobPos) + continue + if(jobban_isbanned(M, jobPos)) + dat += "" + counter++ + else + dat += "" + counter++ + + if(counter >= 5) //So things dont get squiiiiished! + dat += "" + counter = 0 + dat += "
    Security Positions
    [jobPos][jobPos]
    " + + //Engineering (Yellow) + counter = 0 + dat += "" + dat += "" + for(var/jobPos in GLOB.engineering_positions) + if(!jobPos) + continue + if(jobban_isbanned(M, jobPos)) + dat += "" + counter++ + else + dat += "" + counter++ + + if(counter >= 5) //So things dont get squiiiiished! + dat += "" + counter = 0 + dat += "
    Engineering Positions
    [jobPos][jobPos]
    " + + //Medical (White) + counter = 0 + dat += "" + dat += "" + for(var/jobPos in GLOB.medical_positions) + if(!jobPos) + continue + if(jobban_isbanned(M, jobPos)) + dat += "" + counter++ + else + dat += "" + counter++ + + if(counter >= 5) //So things dont get squiiiiished! + dat += "" + counter = 0 + dat += "
    Medical Positions
    [jobPos][jobPos]
    " + + //Science (Purple) + counter = 0 + dat += "" + dat += "" + for(var/jobPos in GLOB.science_positions) + if(!jobPos) + continue + if(jobban_isbanned(M, jobPos)) + dat += "" + counter++ + else + dat += "" + counter++ + + if(counter >= 5) //So things dont get squiiiiished! + dat += "" + counter = 0 + dat += "
    Science Positions
    [jobPos][jobPos]
    " + + //Supply (Brown) + counter = 0 + dat += "" + dat += "" + for(var/jobPos in GLOB.supply_positions) + if(!jobPos) + continue + if(jobban_isbanned(M, jobPos)) + dat += "" + counter++ + else + dat += "" + counter++ + + if(counter >= 5) //So things dont get COPYPASTE! + dat += "" + counter = 0 + dat += "
    Supply Positions
    [jobPos][jobPos]
    " + + //Civilian (Grey) + counter = 0 + dat += "" + dat += "" + for(var/jobPos in GLOB.civilian_positions) + if(!jobPos) + continue + if(jobban_isbanned(M, jobPos)) + dat += "" + counter++ + else + dat += "" + counter++ + + if(counter >= 5) //So things dont get squiiiiished! + dat += "" + counter = 0 + dat += "
    Civilian Positions
    [jobPos][jobPos]
    " + + //Non-Human (Green) + counter = 0 + dat += "" + dat += "" + for(var/jobPos in GLOB.nonhuman_positions) + if(!jobPos) + continue + if(jobban_isbanned(M, jobPos)) + dat += "" + counter++ + else + dat += "" + counter++ + + if(counter >= 5) //So things dont get squiiiiished! + dat += "" + counter = 0 + + dat += "
    Non-human Positions
    [jobPos][jobPos]
    " + + //Ghost Roles (light light gray) + dat += "" + dat += "" + + //pAI + if(jobban_isbanned(M, ROLE_PAI)) + dat += "" + else + dat += "" + + + //Drones + if(jobban_isbanned(M, ROLE_DRONE)) + dat += "" + else + dat += "" + + + //Positronic Brains + if(jobban_isbanned(M, ROLE_POSIBRAIN)) + dat += "" + else + dat += "" + + //Sentience Potion Spawn + if(jobban_isbanned(M, ROLE_SENTIENCE)) + dat += "" + else + dat += "" + + //Deathsquad + if(jobban_isbanned(M, ROLE_DEATHSQUAD)) + dat += "" + else + dat += "" + + //Lavaland roles + if(jobban_isbanned(M, ROLE_LAVALAND)) + dat += "" + else + dat += "" + + dat += "
    Ghost Roles
    pAIpAIDroneDronePosibrainPosibrainSentience Potion SpawnSentience Potion SpawnDeathsquadDeathsquadLavalandLavaland
    " + + //Antagonist (Orange) + var/isbanned_dept = jobban_isbanned(M, ROLE_SYNDICATE) + dat += "" + dat += "" + + //Traitor + if(jobban_isbanned(M, ROLE_TRAITOR) || isbanned_dept) + dat += "" + else + dat += "" + + //Changeling + if(jobban_isbanned(M, ROLE_CHANGELING) || isbanned_dept) + dat += "" + else + dat += "" + + //Nuke Operative + if(jobban_isbanned(M, ROLE_OPERATIVE) || isbanned_dept) + dat += "" + else + dat += "" + + //Revolutionary + if(jobban_isbanned(M, ROLE_REV) || isbanned_dept) + dat += "" + else + dat += "" + + //Cultist + if(jobban_isbanned(M, ROLE_CULTIST) || isbanned_dept) + dat += "" + else + dat += "" + + dat += "" //So things dont get squished. + + //Servant of Ratvar + if(jobban_isbanned(M, ROLE_SERVANT_OF_RATVAR) || isbanned_dept) + dat += "" + else + dat += "" + + //Wizard + if(jobban_isbanned(M, ROLE_WIZARD) || isbanned_dept) + dat += "" + else + dat += "" + + //Abductor + if(jobban_isbanned(M, ROLE_ABDUCTOR) || isbanned_dept) + dat += "" + else + dat += "" + + //Alien + if(jobban_isbanned(M, ROLE_ALIEN) || isbanned_dept) + dat += "" + else + dat += "" + + //Gang + if(jobban_isbanned(M, ROLE_GANG) || isbanned_dept) + dat += "" + else + dat += "" + + + //Other Roles (black) + dat += "
    Antagonist Positions | " + dat += "Team Antagonists | " + dat += "Conversion Antagonists
    TraitorTraitorChangelingChangelingNuke OperativeNuke OperativeRevolutionaryRevolutionaryCultistCultist
    ServantServantWizardWizardAbductorAbductorAlienAlienGangGang
    " + dat += "" + + //Mind Transfer Potion + if(jobban_isbanned(M, ROLE_MIND_TRANSFER)) + dat += "" + else + dat += "" + + dat += "
    Other Roles
    Mind Transfer PotionMind Transfer Potion
    " + usr << browse(dat, "window=jobban2;size=800x450") + return + + //JOBBAN'S INNARDS + else if(href_list["jobban3"]) + if(!check_rights(R_BAN)) + return + var/mob/M = locate(href_list["jobban4"]) + if(!ismob(M)) + to_chat(usr, "This can only be used on instances of type /mob") + return + if(!SSjob) + to_chat(usr, "Jobs subsystem not initialized yet!") + return + //get jobs for department if specified, otherwise just return the one job in a list. + var/list/joblist = list() + switch(href_list["jobban3"]) + if("commanddept") + for(var/jobPos in GLOB.command_positions) + if(!jobPos) + continue + joblist += jobPos + if("securitydept") + for(var/jobPos in GLOB.security_positions) + if(!jobPos) + continue + joblist += jobPos + if("engineeringdept") + for(var/jobPos in GLOB.engineering_positions) + if(!jobPos) + continue + joblist += jobPos + if("medicaldept") + for(var/jobPos in GLOB.medical_positions) + if(!jobPos) + continue + joblist += jobPos + if("sciencedept") + for(var/jobPos in GLOB.science_positions) + if(!jobPos) + continue + joblist += jobPos + if("supplydept") + for(var/jobPos in GLOB.supply_positions) + if(!jobPos) + continue + joblist += jobPos + if("civiliandept") + for(var/jobPos in GLOB.civilian_positions) + if(!jobPos) + continue + joblist += jobPos + if("nonhumandept") + for(var/jobPos in GLOB.nonhuman_positions) + if(!jobPos) + continue + joblist += jobPos + if("ghostroles") + joblist += list(ROLE_PAI, ROLE_POSIBRAIN, ROLE_DRONE , ROLE_DEATHSQUAD, ROLE_LAVALAND, ROLE_SENTIENCE) + if("teamantags") + joblist += list(ROLE_OPERATIVE, ROLE_REV, ROLE_CULTIST, ROLE_SERVANT_OF_RATVAR, ROLE_ABDUCTOR, ROLE_ALIEN, ROLE_GANG) + if("convertantags") + joblist += list(ROLE_REV, ROLE_CULTIST, ROLE_SERVANT_OF_RATVAR, ROLE_ALIEN) + if("otherroles") + joblist += list(ROLE_MIND_TRANSFER) + else + joblist += href_list["jobban3"] + + //Create a list of unbanned jobs within joblist + var/list/notbannedlist = list() + for(var/job in joblist) + if(!jobban_isbanned(M, job)) + notbannedlist += job + + //Banning comes first + if(notbannedlist.len) //at least 1 unbanned job exists in joblist so we have stuff to ban. + var/severity = null + switch(alert("Temporary Ban for [M.key]?",,"Yes","No", "Cancel")) + if("Yes") + var/mins = input(usr,"How long (in minutes)?","Ban time",1440) as num|null + if(mins <= 0) + to_chat(usr, "[mins] is not a valid duration.") + return + var/reason = input(usr,"Please State Reason For Banning [M.key].","Reason") as message|null + if(!reason) + return + severity = input("Set the severity of the note/ban.", "Severity", null, null) as null|anything in list("High", "Medium", "Minor", "None") + if(!severity) + return + var/msg + for(var/job in notbannedlist) + if(!DB_ban_record(BANTYPE_JOB_TEMP, M, mins, reason, job)) + to_chat(usr, "Failed to apply ban.") + return + if(M.client) + jobban_buildcache(M.client) + ban_unban_log_save("[key_name(usr)] temp-jobbanned [key_name(M)] from [job] for [mins] minutes. reason: [reason]") + log_admin_private("[key_name(usr)] temp-jobbanned [key_name(M)] from [job] for [mins] minutes.") + if(!msg) + msg = job + else + msg += ", [job]" + create_message("note", M.key, null, "Banned from [msg] - [reason]", null, null, 0, 0, null, 0, severity) + message_admins("[key_name_admin(usr)] banned [key_name_admin(M)] from [msg] for [mins] minutes.") + to_chat(M, "You have been [(msg == ("ooc" || "appearance")) ? "banned" : "jobbanned"] by [usr.client.key] from: [msg].") + to_chat(M, "The reason is: [reason]") + to_chat(M, "This jobban will be lifted in [mins] minutes.") + href_list["jobban2"] = 1 // lets it fall through and refresh + return 1 + if("No") + var/reason = input(usr,"Please State Reason For Banning [M.key].","Reason") as message|null + severity = input("Set the severity of the note/ban.", "Severity", null, null) as null|anything in list("High", "Medium", "Minor", "None") + if(!severity) + return + if(reason) + var/msg + for(var/job in notbannedlist) + if(!DB_ban_record(BANTYPE_JOB_PERMA, M, -1, reason, job)) + to_chat(usr, "Failed to apply ban.") + return + if(M.client) + jobban_buildcache(M.client) + ban_unban_log_save("[key_name(usr)] perma-jobbanned [key_name(M)] from [job]. reason: [reason]") + log_admin_private("[key_name(usr)] perma-banned [key_name(M)] from [job]") + if(!msg) + msg = job + else + msg += ", [job]" + create_message("note", M.key, null, "Banned from [msg] - [reason]", null, null, 0, 0, null, 0, severity) + message_admins("[key_name_admin(usr)] banned [key_name_admin(M)] from [msg].") + to_chat(M, "You have been [(msg == ("ooc" || "appearance")) ? "banned" : "jobbanned"] by [usr.client.key] from: [msg].") + to_chat(M, "The reason is: [reason]") + to_chat(M, "Jobban can be lifted only upon request.") + href_list["jobban2"] = 1 // lets it fall through and refresh + return 1 + if("Cancel") + return + + //Unbanning joblist + //all jobs in joblist are banned already OR we didn't give a reason (implying they shouldn't be banned) + if(joblist.len) //at least 1 banned job exists in joblist so we have stuff to unban. + var/msg + for(var/job in joblist) + var/reason = jobban_isbanned(M, job) + if(!reason) + continue //skip if it isn't jobbanned anyway + switch(alert("Job: '[job]' Reason: '[reason]' Un-jobban?","Please Confirm","Yes","No")) + if("Yes") + ban_unban_log_save("[key_name(usr)] unjobbanned [key_name(M)] from [job]") + log_admin_private("[key_name(usr)] unbanned [key_name(M)] from [job]") + DB_ban_unban(M.ckey, BANTYPE_ANY_JOB, job) + if(M.client) + jobban_buildcache(M.client) + if(!msg) + msg = job + else + msg += ", [job]" + else + continue + if(msg) + message_admins("[key_name_admin(usr)] unbanned [key_name_admin(M)] from [msg].") + to_chat(M, "You have been un-jobbanned by [usr.client.key] from [msg].") + href_list["jobban2"] = 1 // lets it fall through and refresh + return 1 + return 0 //we didn't do anything! + + else if(href_list["boot2"]) + if(!check_rights(R_ADMIN)) + return + var/mob/M = locate(href_list["boot2"]) + if(ismob(M)) + if(!check_if_greater_rights_than(M.client)) + to_chat(usr, "Error: They have more rights than you do.") + return + if(alert(usr, "Kick [key_name(M)]?", "Confirm", "Yes", "No") != "Yes") + return + if(!M) + to_chat(usr, "Error: [M] no longer exists!") + return + if(!M.client) + to_chat(usr, "Error: [M] no longer has a client!") + return + to_chat(M, "You have been kicked from the server by [usr.client.holder.fakekey ? "an Administrator" : "[usr.client.key]"].") + log_admin("[key_name(usr)] kicked [key_name(M)].") + message_admins("[key_name_admin(usr)] kicked [key_name_admin(M)].") + qdel(M.client) + + else if(href_list["addmessage"]) + if(!check_rights(R_ADMIN)) + return + var/target_key = href_list["addmessage"] + create_message("message", target_key, secret = 0) + + else if(href_list["addnote"]) + if(!check_rights(R_ADMIN)) + return + var/target_key = href_list["addnote"] + create_message("note", target_key) + + else if(href_list["addwatch"]) + if(!check_rights(R_ADMIN)) + return + var/target_key = href_list["addwatch"] + create_message("watchlist entry", target_key, secret = 1) + + else if(href_list["addmemo"]) + if(!check_rights(R_ADMIN)) + return + create_message("memo", secret = 0, browse = 1) + + else if(href_list["addmessageempty"]) + if(!check_rights(R_ADMIN)) + return + create_message("message", secret = 0) + + else if(href_list["addnoteempty"]) + if(!check_rights(R_ADMIN)) + return + create_message("note") + + else if(href_list["addwatchempty"]) + if(!check_rights(R_ADMIN)) + return + create_message("watchlist entry", secret = 1) + + else if(href_list["deletemessage"]) + if(!check_rights(R_ADMIN)) + return + var/safety = alert("Delete message/note?",,"Yes","No"); + if (safety == "Yes") + var/message_id = href_list["deletemessage"] + delete_message(message_id) + + else if(href_list["deletemessageempty"]) + if(!check_rights(R_ADMIN)) + return + var/safety = alert("Delete message/note?",,"Yes","No"); + if (safety == "Yes") + var/message_id = href_list["deletemessageempty"] + delete_message(message_id, browse = TRUE) + + else if(href_list["editmessage"]) + if(!check_rights(R_ADMIN)) + return + var/message_id = href_list["editmessage"] + edit_message(message_id) + + else if(href_list["editmessageempty"]) + if(!check_rights(R_ADMIN)) + return + var/message_id = href_list["editmessageempty"] + edit_message(message_id, browse = 1) + + else if(href_list["editmessageexpiry"]) + if(!check_rights(R_ADMIN)) + return + var/message_id = href_list["editmessageexpiry"] + edit_message_expiry(message_id) + + else if(href_list["editmessageexpiryempty"]) + if(!check_rights(R_ADMIN)) + return + var/message_id = href_list["editmessageexpiryempty"] + edit_message_expiry(message_id, browse = 1) + + else if(href_list["editmessageseverity"]) + if(!check_rights(R_ADMIN)) + return + var/message_id = href_list["editmessageseverity"] + edit_message_severity(message_id) + + else if(href_list["secretmessage"]) + if(!check_rights(R_ADMIN)) + return + var/message_id = href_list["secretmessage"] + toggle_message_secrecy(message_id) + + else if(href_list["searchmessages"]) + if(!check_rights(R_ADMIN)) + return + var/target = href_list["searchmessages"] + browse_messages(index = target) + + else if(href_list["nonalpha"]) + if(!check_rights(R_ADMIN)) + return + var/target = href_list["nonalpha"] + target = text2num(target) + browse_messages(index = target) + + else if(href_list["showmessages"]) + if(!check_rights(R_ADMIN)) + return + var/target = href_list["showmessages"] + browse_messages(index = target) + + else if(href_list["showmemo"]) + if(!check_rights(R_ADMIN)) + return + browse_messages("memo") + + else if(href_list["showwatch"]) + if(!check_rights(R_ADMIN)) + return + browse_messages("watchlist entry") + + else if(href_list["showwatchfilter"]) + if(!check_rights(R_ADMIN)) + return + browse_messages("watchlist entry", filter = 1) + + else if(href_list["showmessageckey"]) + if(!check_rights(R_ADMIN)) + return + var/target = href_list["showmessageckey"] + var/agegate = TRUE + if (href_list["showall"]) + agegate = FALSE + browse_messages(target_ckey = target, agegate = agegate) + + else if(href_list["showmessageckeylinkless"]) + var/target = href_list["showmessageckeylinkless"] + browse_messages(target_ckey = target, linkless = 1) + + else if(href_list["messageedits"]) + if(!check_rights(R_ADMIN)) + return + var/message_id = sanitizeSQL("[href_list["messageedits"]]") + var/datum/DBQuery/query_get_message_edits = SSdbcore.NewQuery("SELECT edits FROM [format_table_name("messages")] WHERE id = '[message_id]'") + if(!query_get_message_edits.warn_execute()) + qdel(query_get_message_edits) + return + if(query_get_message_edits.NextRow()) + var/edit_log = query_get_message_edits.item[1] + if(!QDELETED(usr)) + var/datum/browser/browser = new(usr, "Note edits", "Note edits") + browser.set_content(jointext(edit_log, "")) + browser.open() + qdel(query_get_message_edits) + + else if(href_list["newban"]) + if(!check_rights(R_BAN)) + return + + var/mob/M = locate(href_list["newban"]) + if(!ismob(M)) + return + + if(M.client && M.client.holder) + return //admins cannot be banned. Even if they could, the ban doesn't affect them anyway + + switch(alert("Temporary Ban for [M.key]?",,"Yes","No", "Cancel")) + if("Yes") + var/mins = input(usr,"How long (in minutes)?","Ban time",1440) as num|null + if(mins <= 0) + to_chat(usr, "[mins] is not a valid duration.") + return + var/reason = input(usr,"Please State Reason For Banning [M.key].","Reason") as message|null + if(!reason) + return + if(!DB_ban_record(BANTYPE_TEMP, M, mins, reason)) + to_chat(usr, "Failed to apply ban.") + return + AddBan(M.ckey, M.computer_id, reason, usr.ckey, 1, mins) + ban_unban_log_save("[key_name(usr)] has banned [key_name(M)]. - Reason: [reason] - This will be removed in [mins] minutes.") + to_chat(M, "You have been banned by [usr.client.key].\nReason: [reason]") + to_chat(M, "This is a temporary ban, it will be removed in [mins] minutes. The round ID is [GLOB.round_id].") + var/bran = CONFIG_GET(string/banappeals) + if(bran) + to_chat(M, "To try to resolve this matter head to [bran]") + else + to_chat(M, "No ban appeals URL has been set.") + log_admin_private("[key_name(usr)] has banned [key_name(M)].\nReason: [key_name(M)]\nThis will be removed in [mins] minutes.") + var/msg = "[key_name_admin(usr)] has banned [key_name_admin(M)].\nReason: [reason]\nThis will be removed in [mins] minutes." + message_admins(msg) + var/datum/admin_help/AH = M.client ? M.client.current_ticket : null + if(AH) + AH.Resolve() + qdel(M.client) + if("No") + var/reason = input(usr,"Please State Reason For Banning [M.key].","Reason") as message|null + if(!reason) + return + switch(alert(usr,"IP ban?",,"Yes","No","Cancel")) + if("Cancel") + return + if("Yes") + AddBan(M.ckey, M.computer_id, reason, usr.ckey, 0, 0, M.lastKnownIP) + if("No") + AddBan(M.ckey, M.computer_id, reason, usr.ckey, 0, 0) + to_chat(M, "You have been banned by [usr.client.key].\nReason: [reason]") + to_chat(M, "This is a permanent ban. The round ID is [GLOB.round_id].") + var/bran = CONFIG_GET(string/banappeals) + if(bran) + to_chat(M, "To try to resolve this matter head to [bran]") + else + to_chat(M, "No ban appeals URL has been set.") + if(!DB_ban_record(BANTYPE_PERMA, M, -1, reason)) + to_chat(usr, "Failed to apply ban.") + return + ban_unban_log_save("[key_name(usr)] has permabanned [key_name(M)]. - Reason: [reason] - This is a permanent ban.") + log_admin_private("[key_name(usr)] has banned [key_name(M)].\nReason: [reason]\nThis is a permanent ban.") + var/msg = "[key_name_admin(usr)] has banned [key_name_admin(M)].\nReason: [reason]\nThis is a permanent ban." + message_admins(msg) + var/datum/admin_help/AH = M.client ? M.client.current_ticket : null + if(AH) + AH.Resolve() + qdel(M.client) + if("Cancel") + return + + else if(href_list["mute"]) + if(!check_rights(R_ADMIN)) + return + cmd_admin_mute(href_list["mute"], text2num(href_list["mute_type"])) + + else if(href_list["c_mode"]) + return HandleCMode() + + else if(href_list["f_secret"]) + return HandleFSecret() + +//Dynamic mode + else if(href_list["f_dynamic_roundstart"]) + if(!check_rights(R_ADMIN)) + return + if(SSticker && SSticker.mode) + return alert(usr, "The game has already started.", null, null, null, null) + if(GLOB.master_mode != "dynamic") + return alert(usr, "The game mode has to be dynamic mode.", null, null, null, null) + var/roundstart_rules = list() + for (var/rule in subtypesof(/datum/dynamic_ruleset/roundstart)) + var/datum/dynamic_ruleset/roundstart/newrule = new rule() + roundstart_rules[newrule.name] = newrule + var/added_rule = input(usr,"What ruleset do you want to force? This will bypass threat level and population restrictions.", "Rigging Roundstart", null) as null|anything in roundstart_rules + if (added_rule) + GLOB.dynamic_forced_roundstart_ruleset += roundstart_rules[added_rule] + log_admin("[key_name(usr)] set [added_rule] to be a forced roundstart ruleset.") + message_admins("[key_name(usr)] set [added_rule] to be a forced roundstart ruleset.", 1) + Game() + + else if(href_list["f_dynamic_roundstart_clear"]) + if(!check_rights(R_ADMIN)) + return + GLOB.dynamic_forced_roundstart_ruleset = list() + Game() + log_admin("[key_name(usr)] cleared the rigged roundstart rulesets. The mode will pick them as normal.") + message_admins("[key_name(usr)] cleared the rigged roundstart rulesets. The mode will pick them as normal.", 1) + + else if(href_list["f_dynamic_roundstart_remove"]) + if(!check_rights(R_ADMIN)) + return + var/datum/dynamic_ruleset/roundstart/rule = locate(href_list["f_dynamic_roundstart_remove"]) + GLOB.dynamic_forced_roundstart_ruleset -= rule + Game() + log_admin("[key_name(usr)] removed [rule] from the forced roundstart rulesets.") + message_admins("[key_name(usr)] removed [rule] from the forced roundstart rulesets.", 1) + + else if(href_list["f_dynamic_latejoin"]) + if(!check_rights(R_ADMIN)) + return + if(!SSticker || !SSticker.mode) + return alert(usr, "The game must start first.", null, null, null, null) + if(GLOB.master_mode != "dynamic") + return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null) + var/latejoin_rules = list() + for (var/rule in subtypesof(/datum/dynamic_ruleset/latejoin)) + var/datum/dynamic_ruleset/latejoin/newrule = new rule() + latejoin_rules[newrule.name] = newrule + var/added_rule = input(usr,"What ruleset do you want to force upon the next latejoiner? This will bypass threat level and population restrictions.", "Rigging Latejoin", null) as null|anything in latejoin_rules + if (added_rule) + var/datum/game_mode/dynamic/mode = SSticker.mode + mode.forced_latejoin_rule = latejoin_rules[added_rule] + log_admin("[key_name(usr)] set [added_rule] to proc on the next latejoin.") + message_admins("[key_name(usr)] set [added_rule] to proc on the next latejoin.", 1) + Game() + + else if(href_list["f_dynamic_latejoin_clear"]) + if(!check_rights(R_ADMIN)) + return + if (SSticker && SSticker.mode && istype(SSticker.mode,/datum/game_mode/dynamic)) + var/datum/game_mode/dynamic/mode = SSticker.mode + mode.forced_latejoin_rule = null + Game() + log_admin("[key_name(usr)] cleared the forced latejoin ruleset.") + message_admins("[key_name(usr)] cleared the forced latejoin ruleset.", 1) + + else if(href_list["f_dynamic_midround"]) + if(!check_rights(R_ADMIN)) + return + if(!SSticker || !SSticker.mode) + return alert(usr, "The game must start first.", null, null, null, null) + if(GLOB.master_mode != "dynamic") + return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null) + var/midround_rules = list() + for (var/rule in subtypesof(/datum/dynamic_ruleset/midround)) + var/datum/dynamic_ruleset/midround/newrule = new rule() + midround_rules[newrule.name] = rule + var/added_rule = input(usr,"What ruleset do you want to force right now? This will bypass threat level and population restrictions.", "Execute Ruleset", null) as null|anything in midround_rules + if (added_rule) + var/datum/game_mode/dynamic/mode = SSticker.mode + log_admin("[key_name(usr)] executed the [added_rule] ruleset.") + message_admins("[key_name(usr)] executed the [added_rule] ruleset.", 1) + mode.picking_specific_rule(midround_rules[added_rule],1) + + else if (href_list["f_dynamic_options"]) + if(!check_rights(R_ADMIN)) + return + + if(SSticker && SSticker.mode) + return alert(usr, "The game has already started.", null, null, null, null) + if(GLOB.master_mode != "dynamic") + return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null) + + dynamic_mode_options(usr) + + else if(href_list["f_dynamic_roundstart_centre"]) + if(!check_rights(R_ADMIN)) + return + if(SSticker && SSticker.mode) + return alert(usr, "The game has already started.", null, null, null, null) + if(GLOB.master_mode != "dynamic") + return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null) + + var/new_centre = input(usr,"Change the centre of the dynamic mode threat curve. A negative value will give a more peaceful round ; a positive value, a round with higher threat. Any number between -5 and +5 is allowed.", "Change curve centre", null) as num + if (new_centre < -5 || new_centre > 5) + return alert(usr, "Only values between -5 and +5 are allowed.", null, null, null, null) + + log_admin("[key_name(usr)] changed the distribution curve center to [new_centre].") + message_admins("[key_name(usr)] changed the distribution curve center to [new_centre]", 1) + GLOB.dynamic_curve_centre = new_centre + dynamic_mode_options(usr) + + else if(href_list["f_dynamic_roundstart_width"]) + if(!check_rights(R_ADMIN)) + return + if(SSticker && SSticker.mode) + return alert(usr, "The game has already started.", null, null, null, null) + if(GLOB.master_mode != "dynamic") + return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null) + + var/new_width = input(usr,"Change the width of the dynamic mode threat curve. A higher value will favour extreme rounds ; a lower value, a round closer to the average. Any Number between 0.5 and 4 are allowed.", "Change curve width", null) as num + if (new_width < 0.5 || new_width > 4) + return alert(usr, "Only values between 0.5 and +2.5 are allowed.", null, null, null, null) + + log_admin("[key_name(usr)] changed the distribution curve width to [new_width].") + message_admins("[key_name(usr)] changed the distribution curve width to [new_width]", 1) + GLOB.dynamic_curve_width = new_width + dynamic_mode_options(usr) + + else if(href_list["f_dynamic_roundstart_latejoin_min"]) + if(!check_rights(R_ADMIN)) + return + if(SSticker && SSticker.mode) + return alert(usr, "The game has already started.", null, null, null, null) + if(GLOB.master_mode != "dynamic") + return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null) + var/new_min = input(usr,"Change the minimum delay of latejoin injection in minutes.", "Change latejoin injection delay minimum", null) as num + if(new_min <= 0) + return alert(usr, "The minimum can't be zero or lower.", null, null, null, null) + if((new_min MINUTES) > GLOB.dynamic_latejoin_delay_max) + return alert(usr, "The minimum must be lower than the maximum.", null, null, null, null) + + log_admin("[key_name(usr)] changed the latejoin injection minimum delay to [new_min] minutes.") + message_admins("[key_name(usr)] changed the latejoin injection minimum delay to [new_min] minutes", 1) + GLOB.dynamic_latejoin_delay_min = (new_min MINUTES) + dynamic_mode_options(usr) + + else if(href_list["f_dynamic_roundstart_latejoin_max"]) + if(!check_rights(R_ADMIN)) + return + if(SSticker && SSticker.mode) + return alert(usr, "The game has already started.", null, null, null, null) + if(GLOB.master_mode != "dynamic") + return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null) + var/new_max = input(usr,"Change the maximum delay of latejoin injection in minutes.", "Change latejoin injection delay maximum", null) as num + if(new_max <= 0) + return alert(usr, "The maximum can't be zero or lower.", null, null, null, null) + if((new_max MINUTES) < GLOB.dynamic_latejoin_delay_min) + return alert(usr, "The maximum must be higher than the minimum.", null, null, null, null) + + log_admin("[key_name(usr)] changed the latejoin injection maximum delay to [new_max] minutes.") + message_admins("[key_name(usr)] changed the latejoin injection maximum delay to [new_max] minutes", 1) + GLOB.dynamic_latejoin_delay_max = (new_max MINUTES) + dynamic_mode_options(usr) + + else if(href_list["f_dynamic_roundstart_midround_min"]) + if(!check_rights(R_ADMIN)) + return + if(SSticker && SSticker.mode) + return alert(usr, "The game has already started.", null, null, null, null) + if(GLOB.master_mode != "dynamic") + return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null) + var/new_min = input(usr,"Change the minimum delay of midround injection in minutes.", "Change midround injection delay minimum", null) as num + if(new_min <= 0) + return alert(usr, "The minimum can't be zero or lower.", null, null, null, null) + if((new_min MINUTES) > GLOB.dynamic_midround_delay_max) + return alert(usr, "The minimum must be lower than the maximum.", null, null, null, null) + + log_admin("[key_name(usr)] changed the midround injection minimum delay to [new_min] minutes.") + message_admins("[key_name(usr)] changed the midround injection minimum delay to [new_min] minutes", 1) + GLOB.dynamic_midround_delay_min = (new_min MINUTES) + dynamic_mode_options(usr) + + else if(href_list["f_dynamic_roundstart_midround_max"]) + if(!check_rights(R_ADMIN)) + return + if(SSticker && SSticker.mode) + return alert(usr, "The game has already started.", null, null, null, null) + if(GLOB.master_mode != "dynamic") + return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null) + var/new_max = input(usr,"Change the maximum delay of midround injection in minutes.", "Change midround injection delay maximum", null) as num + if(new_max <= 0) + return alert(usr, "The maximum can't be zero or lower.", null, null, null, null) + if((new_max MINUTES) > GLOB.dynamic_midround_delay_max) + return alert(usr, "The maximum must be higher than the minimum.", null, null, null, null) + + log_admin("[key_name(usr)] changed the midround injection maximum delay to [new_max] minutes.") + message_admins("[key_name(usr)] changed the midround injection maximum delay to [new_max] minutes", 1) + GLOB.dynamic_midround_delay_max = (new_max MINUTES) + dynamic_mode_options(usr) + + else if(href_list["f_dynamic_force_extended"]) + if(!check_rights(R_ADMIN)) + return + + if(GLOB.master_mode != "dynamic") + return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null) + + GLOB.dynamic_forced_extended = !GLOB.dynamic_forced_extended + log_admin("[key_name(usr)] set 'forced_extended' to [GLOB.dynamic_forced_extended].") + message_admins("[key_name(usr)] set 'forced_extended' to [GLOB.dynamic_forced_extended].") + dynamic_mode_options(usr) + + else if(href_list["f_dynamic_no_stacking"]) + if(!check_rights(R_ADMIN)) + return + + if(GLOB.master_mode != "dynamic") + return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null) + + GLOB.dynamic_no_stacking = !GLOB.dynamic_no_stacking + log_admin("[key_name(usr)] set 'no_stacking' to [GLOB.dynamic_no_stacking].") + message_admins("[key_name(usr)] set 'no_stacking' to [GLOB.dynamic_no_stacking].") + dynamic_mode_options(usr) + + else if(href_list["f_dynamic_classic_secret"]) + if(!check_rights(R_ADMIN)) + return + + if(GLOB.master_mode != "dynamic") + return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null) + + GLOB.dynamic_classic_secret = !GLOB.dynamic_classic_secret + log_admin("[key_name(usr)] set 'classic_secret' to [GLOB.dynamic_classic_secret].") + message_admins("[key_name(usr)] set 'classic_secret' to [GLOB.dynamic_classic_secret].") + dynamic_mode_options(usr) + + else if(href_list["f_dynamic_stacking_limit"]) + if(!check_rights(R_ADMIN)) + return + + if(GLOB.master_mode != "dynamic") + return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null) + + GLOB.dynamic_stacking_limit = input(usr,"Change the threat limit at which round-endings rulesets will start to stack.", "Change stacking limit", null) as num + log_admin("[key_name(usr)] set 'stacking_limit' to [GLOB.dynamic_stacking_limit].") + message_admins("[key_name(usr)] set 'stacking_limit' to [GLOB.dynamic_stacking_limit].") + dynamic_mode_options(usr) + + else if(href_list["f_dynamic_high_pop_limit"]) + if(!check_rights(R_ADMIN)) + return + + if(SSticker && SSticker.mode) + return alert(usr, "The game has already started.", null, null, null, null) + + if(GLOB.master_mode != "dynamic") + return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null) + + var/new_value = input(usr, "Enter the high-pop override threshold for dynamic mode.", "High pop override") as num + if (new_value < 0) + return alert(usr, "Only positive values allowed!", null, null, null, null) + GLOB.dynamic_high_pop_limit = new_value + + log_admin("[key_name(usr)] set 'high_pop_limit' to [GLOB.dynamic_high_pop_limit].") + message_admins("[key_name(usr)] set 'high_pop_limit' to [GLOB.dynamic_high_pop_limit].") + dynamic_mode_options(usr) + + else if(href_list["f_dynamic_forced_threat"]) + if(!check_rights(R_ADMIN)) + return + + if(SSticker && SSticker.mode) + return alert(usr, "The game has already started.", null, null, null, null) + + if(GLOB.master_mode != "dynamic") + return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null) + + var/new_value = input(usr, "Enter the forced threat level for dynamic mode.", "Forced threat level") as num + if (new_value > 100) + return alert(usr, "The value must be be under 100.", null, null, null, null) + GLOB.dynamic_forced_threat_level = new_value + + log_admin("[key_name(usr)] set 'forced_threat_level' to [GLOB.dynamic_forced_threat_level].") + message_admins("[key_name(usr)] set 'forced_threat_level' to [GLOB.dynamic_forced_threat_level].") + dynamic_mode_options(usr) + + else if(href_list["f_dynamic_chaos_level"]) + if(!check_rights(R_ADMIN)) + return + + if(SSticker && SSticker.mode) + return alert(usr, "The game has already started.", null, null, null, null) + + if(GLOB.master_mode != "dynamic") + return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null) + + var/new_value = input(usr, "Enter the chaos level for dynamic mode.", "Chaos level") as num + if (new_value > 5 || new_value < 0) + return alert(usr, "The value must be between 0 and 5.", null, null, null, null) + GLOB.dynamic_chaos_level = new_value + + log_admin("[key_name(usr)] set 'dynamic_chaos_level' to [GLOB.dynamic_chaos_level].") + message_admins("[key_name(usr)] set 'dynamic_chaos_level' to [GLOB.dynamic_chaos_level].") + dynamic_mode_options(usr) +//End Dynamic mode + + else if(href_list["c_mode2"]) + if(!check_rights(R_ADMIN|R_SERVER)) + return + + if (SSticker.HasRoundStarted()) + return alert(usr, "The game has already started.", null, null, null, null) + GLOB.master_mode = href_list["c_mode2"] + log_admin("[key_name(usr)] set the mode as [GLOB.master_mode].") + message_admins("[key_name_admin(usr)] set the mode as [GLOB.master_mode].") + to_chat(world, "The mode is now: [GLOB.master_mode]") + Game() // updates the main game menu + SSticker.save_mode(GLOB.master_mode) + HandleCMode() + + else if(href_list["f_secret2"]) + if(!check_rights(R_ADMIN|R_SERVER)) + return + + if(SSticker.HasRoundStarted()) + return alert(usr, "The game has already started.", null, null, null, null) + if(GLOB.master_mode != "secret") + return alert(usr, "The game mode has to be secret!", null, null, null, null) + GLOB.secret_force_mode = href_list["f_secret2"] + log_admin("[key_name(usr)] set the forced secret mode as [GLOB.secret_force_mode].") + message_admins("[key_name_admin(usr)] set the forced secret mode as [GLOB.secret_force_mode].") + Game() // updates the main game menu + HandleFSecret() + + else if(href_list["monkeyone"]) + if(!check_rights(R_SPAWN)) + return + + var/mob/living/carbon/human/H = locate(href_list["monkeyone"]) + if(!istype(H)) + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human.") + return + + log_admin("[key_name(usr)] attempting to monkeyize [key_name(H)].") + message_admins("[key_name_admin(usr)] attempting to monkeyize [key_name_admin(H)].") + H.monkeyize() + + else if(href_list["humanone"]) + if(!check_rights(R_SPAWN)) + return + + var/mob/living/carbon/monkey/Mo = locate(href_list["humanone"]) + if(!istype(Mo)) + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/monkey.") + return + + log_admin("[key_name(usr)] attempting to humanize [key_name(Mo)].") + message_admins("[key_name_admin(usr)] attempting to humanize [key_name_admin(Mo)].") + Mo.humanize() + + else if(href_list["corgione"]) + if(!check_rights(R_SPAWN)) + return + + var/mob/living/carbon/human/H = locate(href_list["corgione"]) + if(!istype(H)) + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human.") + return + + log_admin("[key_name(usr)] attempting to corgize [key_name(H)].") + message_admins("[key_name_admin(usr)] attempting to corgize [key_name_admin(H)].") + H.corgize() + + + else if(href_list["forcespeech"]) + if(!check_rights(R_FUN)) + return + + var/mob/M = locate(href_list["forcespeech"]) + if(!ismob(M)) + to_chat(usr, "this can only be used on instances of type /mob.") + + var/speech = input("What will [key_name(M)] say?", "Force speech", "")// Don't need to sanitize, since it does that in say(), we also trust our admins. + if(!speech) + return + M.say(speech, forced = "admin speech") + speech = sanitize(speech) // Nah, we don't trust them + log_admin("[key_name(usr)] forced [key_name(M)] to say: [speech]") + message_admins("[key_name_admin(usr)] forced [key_name_admin(M)] to say: [speech]") + + else if(href_list["makeeligible"]) + if(!check_rights(R_ADMIN)) + return + var/mob/M = locate(href_list["makeeligible"]) + if(!ismob(M)) + to_chat(usr, "this can only be used on instances of type /mob.") + if(M.ckey in GLOB.client_ghost_timeouts) + GLOB.client_ghost_timeouts -= M.ckey + + else if(href_list["sendtoprison"]) + if(!check_rights(R_ADMIN)) + return + + var/mob/M = locate(href_list["sendtoprison"]) + if(!ismob(M)) + to_chat(usr, "This can only be used on instances of type /mob.") + return + if(isAI(M)) + to_chat(usr, "This cannot be used on instances of type /mob/living/silicon/ai.") + return + + if(alert(usr, "Send [key_name(M)] to Prison?", "Message", "Yes", "No") != "Yes") + return + + M.forceMove(pick(GLOB.prisonwarp)) + to_chat(M, "You have been sent to Prison!") + + log_admin("[key_name(usr)] has sent [key_name(M)] to Prison!") + message_admins("[key_name_admin(usr)] has sent [key_name_admin(M)] to Prison!") + + else if(href_list["sendbacktolobby"]) + if(!check_rights(R_ADMIN)) + return + + var/mob/M = locate(href_list["sendbacktolobby"]) + + if(!isobserver(M)) + to_chat(usr, "You can only send ghost players back to the Lobby.") + return + + if(!M.client) + to_chat(usr, "[M] doesn't seem to have an active client.") + return + + if(alert(usr, "Send [key_name(M)] back to Lobby?", "Message", "Yes", "No") != "Yes") + return + + log_admin("[key_name(usr)] has sent [key_name(M)] back to the Lobby.") + message_admins("[key_name(usr)] has sent [key_name(M)] back to the Lobby.") + + var/mob/dead/new_player/NP = new() + NP.ckey = M.ckey + qdel(M) + + else if(href_list["tdome1"]) + if(!check_rights(R_FUN)) + return + + if(alert(usr, "Confirm?", "Message", "Yes", "No") != "Yes") + return + + var/mob/M = locate(href_list["tdome1"]) + if(!isliving(M)) + to_chat(usr, "This can only be used on instances of type /mob/living.") + return + if(isAI(M)) + to_chat(usr, "This cannot be used on instances of type /mob/living/silicon/ai.") + return + var/mob/living/L = M + + for(var/obj/item/I in L) + L.dropItemToGround(I, TRUE) + + L.Unconscious(100) + sleep(5) + L.forceMove(pick(GLOB.tdome1)) + spawn(50) + to_chat(L, "You have been sent to the Thunderdome.") + log_admin("[key_name(usr)] has sent [key_name(L)] to the thunderdome. (Team 1)") + message_admins("[key_name_admin(usr)] has sent [key_name_admin(L)] to the thunderdome. (Team 1)") + + else if(href_list["tdome2"]) + if(!check_rights(R_FUN)) + return + + if(alert(usr, "Confirm?", "Message", "Yes", "No") != "Yes") + return + + var/mob/M = locate(href_list["tdome2"]) + if(!isliving(M)) + to_chat(usr, "This can only be used on instances of type /mob/living.") + return + if(isAI(M)) + to_chat(usr, "This cannot be used on instances of type /mob/living/silicon/ai.") + return + var/mob/living/L = M + + for(var/obj/item/I in L) + L.dropItemToGround(I, TRUE) + + L.Unconscious(100) + sleep(5) + L.forceMove(pick(GLOB.tdome2)) + spawn(50) + to_chat(L, "You have been sent to the Thunderdome.") + log_admin("[key_name(usr)] has sent [key_name(L)] to the thunderdome. (Team 2)") + message_admins("[key_name_admin(usr)] has sent [key_name_admin(L)] to the thunderdome. (Team 2)") + + else if(href_list["tdomeadmin"]) + if(!check_rights(R_FUN)) + return + + if(alert(usr, "Confirm?", "Message", "Yes", "No") != "Yes") + return + + var/mob/M = locate(href_list["tdomeadmin"]) + if(!isliving(M)) + to_chat(usr, "This can only be used on instances of type /mob/living.") + return + if(isAI(M)) + to_chat(usr, "This cannot be used on instances of type /mob/living/silicon/ai.") + return + var/mob/living/L = M + + L.Unconscious(100) + sleep(5) + L.forceMove(pick(GLOB.tdomeadmin)) + spawn(50) + to_chat(L, "You have been sent to the Thunderdome.") + log_admin("[key_name(usr)] has sent [key_name(L)] to the thunderdome. (Admin.)") + message_admins("[key_name_admin(usr)] has sent [key_name_admin(L)] to the thunderdome. (Admin.)") + + else if(href_list["tdomeobserve"]) + if(!check_rights(R_FUN)) + return + + if(alert(usr, "Confirm?", "Message", "Yes", "No") != "Yes") + return + + var/mob/M = locate(href_list["tdomeobserve"]) + if(!isliving(M)) + to_chat(usr, "This can only be used on instances of type /mob/living.") + return + if(isAI(M)) + to_chat(usr, "This cannot be used on instances of type /mob/living/silicon/ai.") + return + var/mob/living/L = M + + for(var/obj/item/I in L) + L.dropItemToGround(I, TRUE) + + if(ishuman(L)) + var/mob/living/carbon/human/observer = L + observer.equip_to_slot_or_del(new /obj/item/clothing/under/suit_jacket(observer), SLOT_W_UNIFORM) + observer.equip_to_slot_or_del(new /obj/item/clothing/shoes/sneakers/black(observer), SLOT_SHOES) + L.Unconscious(100) + sleep(5) + L.forceMove(pick(GLOB.tdomeobserve)) + spawn(50) + to_chat(L, "You have been sent to the Thunderdome.") + log_admin("[key_name(usr)] has sent [key_name(L)] to the thunderdome. (Observer.)") + message_admins("[key_name_admin(usr)] has sent [key_name_admin(L)] to the thunderdome. (Observer.)") + + else if(href_list["revive"]) + if(!check_rights(R_ADMIN)) + return + + var/mob/living/L = locate(href_list["revive"]) + if(!istype(L)) + to_chat(usr, "This can only be used on instances of type /mob/living.") + return + + L.revive(full_heal = 1, admin_revive = 1) + message_admins("Admin [key_name_admin(usr)] healed / revived [key_name_admin(L)]!") + log_admin("[key_name(usr)] healed / Revived [key_name(L)].") + + else if(href_list["makeai"]) + if(!check_rights(R_SPAWN)) + return + + var/mob/living/carbon/human/H = locate(href_list["makeai"]) + if(!istype(H)) + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human.") + return + + message_admins("Admin [key_name_admin(usr)] AIized [key_name_admin(H)]!") + log_admin("[key_name(usr)] AIized [key_name(H)].") + H.AIize() + + else if(href_list["makealien"]) + if(!check_rights(R_SPAWN)) + return + + var/mob/living/carbon/human/H = locate(href_list["makealien"]) + if(!istype(H)) + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human.") + return + + usr.client.cmd_admin_alienize(H) + + else if(href_list["makeslime"]) + if(!check_rights(R_SPAWN)) + return + + var/mob/living/carbon/human/H = locate(href_list["makeslime"]) + if(!istype(H)) + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human.") + return + + usr.client.cmd_admin_slimeize(H) + + else if(href_list["makeblob"]) + if(!check_rights(R_SPAWN)) + return + + var/mob/living/carbon/human/H = locate(href_list["makeblob"]) + if(!istype(H)) + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human.") + return + + usr.client.cmd_admin_blobize(H) + + + else if(href_list["makerobot"]) + if(!check_rights(R_SPAWN)) + return + + var/mob/living/carbon/human/H = locate(href_list["makerobot"]) + if(!istype(H)) + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human.") + return + + usr.client.cmd_admin_robotize(H) + + else if(href_list["makeanimal"]) + if(!check_rights(R_SPAWN)) + return + + var/mob/M = locate(href_list["makeanimal"]) + if(isnewplayer(M)) + to_chat(usr, "This cannot be used on instances of type /mob/dead/new_player.") + return + + usr.client.cmd_admin_animalize(M) + + else if(href_list["adminplayeropts"]) + var/mob/M = locate(href_list["adminplayeropts"]) + show_player_panel(M) + + else if(href_list["adminplayerobservefollow"]) + if(!isobserver(usr) && !check_rights(R_ADMIN)) + return + + var/atom/movable/AM = locate(href_list["adminplayerobservefollow"]) + + var/client/C = usr.client + if(!isobserver(usr)) + C.admin_ghost() + var/mob/dead/observer/A = C.mob + A.ManualFollow(AM) + + else if(href_list["admingetmovable"]) + if(!check_rights(R_ADMIN)) + return + + var/atom/movable/AM = locate(href_list["admingetmovable"]) + if(QDELETED(AM)) + return + AM.forceMove(get_turf(usr)) + + else if(href_list["adminplayerobservecoodjump"]) + if(!isobserver(usr) && !check_rights(R_ADMIN)) + return + + var/x = text2num(href_list["X"]) + var/y = text2num(href_list["Y"]) + var/z = text2num(href_list["Z"]) + + var/client/C = usr.client + if(!isobserver(usr)) + C.admin_ghost() + sleep(2) + C.jumptocoord(x,y,z) + + else if(href_list["adminchecklaws"]) + if(!check_rights(R_ADMIN)) + return + output_ai_laws() + + else if(href_list["admincheckdevilinfo"]) + if(!check_rights(R_ADMIN)) + return + var/mob/M = locate(href_list["admincheckdevilinfo"]) + output_devil_info(M) + + else if(href_list["adminmoreinfo"]) + var/mob/M = locate(href_list["adminmoreinfo"]) in GLOB.mob_list + if(!ismob(M)) + to_chat(usr, "This can only be used on instances of type /mob.") + return + + var/location_description = "" + var/special_role_description = "" + var/health_description = "" + var/gender_description = "" + var/turf/T = get_turf(M) + + //Location + if(isturf(T)) + if(isarea(T.loc)) + location_description = "([M.loc == T ? "at coordinates " : "in [M.loc] at coordinates "] [T.x], [T.y], [T.z] in area [T.loc])" + else + location_description = "([M.loc == T ? "at coordinates " : "in [M.loc] at coordinates "] [T.x], [T.y], [T.z])" + + //Job + antagonist + if(M.mind) + special_role_description = "Role: [M.mind.assigned_role]; Antagonist: [M.mind.special_role]" + else + special_role_description = "Role: Mind datum missing Antagonist: Mind datum missing" + + //Health + if(isliving(M)) + var/mob/living/L = M + var/status + switch (M.stat) + if(CONSCIOUS) + status = "Alive" + if(SOFT_CRIT) + status = "Dying" + if(UNCONSCIOUS) + status = "[L.InCritical() ? "Unconscious and Dying" : "Unconscious"]" + if(DEAD) + status = "Dead" + health_description = "Status = [status]" + health_description += "
    Oxy: [L.getOxyLoss()] - Tox: [L.getToxLoss()] - Fire: [L.getFireLoss()] - Brute: [L.getBruteLoss()] - Clone: [L.getCloneLoss()] - Brain: [L.getOrganLoss(ORGAN_SLOT_BRAIN)] - Stamina: [L.getStaminaLoss()]" + else + health_description = "This mob type has no health to speak of." + + //Gender + switch(M.gender) + if(MALE,FEMALE) + gender_description = "[M.gender]" + else + gender_description = "[M.gender]" + + to_chat(src.owner, "Info about [M.name]: ") + to_chat(src.owner, "Mob type = [M.type]; Gender = [gender_description] Damage = [health_description]") + to_chat(src.owner, "Name = [M.name]; Real_name = [M.real_name]; Mind_name = [M.mind?"[M.mind.name]":""]; Key = [M.key];") + to_chat(src.owner, "Location = [location_description];") + to_chat(src.owner, "[special_role_description]") + to_chat(src.owner, ADMIN_FULLMONTY_NONAME(M)) + + else if(href_list["addjobslot"]) + if(!check_rights(R_ADMIN)) + return + + var/Add = href_list["addjobslot"] + + for(var/datum/job/job in SSjob.occupations) + if(job.title == Add) + job.total_positions += 1 + break + + src.manage_free_slots() + + + else if(href_list["customjobslot"]) + if(!check_rights(R_ADMIN)) + return + + var/Add = href_list["customjobslot"] + + for(var/datum/job/job in SSjob.occupations) + if(job.title == Add) + var/newtime = null + newtime = input(usr, "How many jebs do you want?", "Add wanted posters", "[newtime]") as num|null + if(!newtime) + to_chat(src.owner, "Setting to amount of positions filled for the job") + job.total_positions = job.current_positions + break + job.total_positions = newtime + + src.manage_free_slots() + + else if(href_list["removejobslot"]) + if(!check_rights(R_ADMIN)) + return + + var/Remove = href_list["removejobslot"] + + for(var/datum/job/job in SSjob.occupations) + if(job.title == Remove && job.total_positions - job.current_positions > 0) + job.total_positions -= 1 + break + + src.manage_free_slots() + + else if(href_list["unlimitjobslot"]) + if(!check_rights(R_ADMIN)) + return + + var/Unlimit = href_list["unlimitjobslot"] + + for(var/datum/job/job in SSjob.occupations) + if(job.title == Unlimit) + job.total_positions = -1 + break + + src.manage_free_slots() + + else if(href_list["limitjobslot"]) + if(!check_rights(R_ADMIN)) + return + + var/Limit = href_list["limitjobslot"] + + for(var/datum/job/job in SSjob.occupations) + if(job.title == Limit) + job.total_positions = job.current_positions + break + + src.manage_free_slots() + + + else if(href_list["adminspawncookie"]) + if(!check_rights(R_ADMIN|R_FUN)) + return + + var/mob/living/carbon/human/H = locate(href_list["adminspawncookie"]) + if(!ishuman(H)) + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human.") + return + + var/obj/item/reagent_containers/food/snacks/cookie/cookie = new(H) + if(H.put_in_hands(cookie)) + H.update_inv_hands() + else + qdel(cookie) + log_admin("[key_name(H)] has their hands full, so they did not receive their cookie, spawned by [key_name(src.owner)].") + message_admins("[key_name(H)] has their hands full, so they did not receive their cookie, spawned by [key_name(src.owner)].") + return + + log_admin("[key_name(H)] got their cookie, spawned by [key_name(src.owner)].") + message_admins("[key_name(H)] got their cookie, spawned by [key_name(src.owner)].") + SSblackbox.record_feedback("amount", "admin_cookies_spawned", 1) + to_chat(H, "Your prayers have been answered!! You received the best cookie!") + SEND_SOUND(H, sound('sound/effects/pray_chaplain.ogg')) + + else if(href_list["adminsmite"]) + if(!check_rights(R_ADMIN|R_FUN)) + return + + var/mob/living/carbon/human/H = locate(href_list["adminsmite"]) in GLOB.mob_list + if(!H || !istype(H)) + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") + return + + usr.client.smite(H) + + else if(href_list["CentComReply"]) + if(!check_rights(R_ADMIN)) + return + + var/mob/M = locate(href_list["CentComReply"]) + usr.client.admin_headset_message(M, RADIO_CHANNEL_CENTCOM) + + else if(href_list["SyndicateReply"]) + if(!check_rights(R_ADMIN)) + return + + var/mob/M = locate(href_list["SyndicateReply"]) + usr.client.admin_headset_message(M, RADIO_CHANNEL_SYNDICATE) + + else if(href_list["HeadsetMessage"]) + if(!check_rights(R_ADMIN)) + return + + var/mob/M = locate(href_list["HeadsetMessage"]) + usr.client.admin_headset_message(M) + + else if(href_list["reject_custom_name"]) + if(!check_rights(R_ADMIN)) + return + var/obj/item/station_charter/charter = locate(href_list["reject_custom_name"]) + if(istype(charter)) + charter.reject_proposed(usr) + else if(href_list["jumpto"]) + if(!isobserver(usr) && !check_rights(R_ADMIN)) + return + + var/mob/M = locate(href_list["jumpto"]) + usr.client.jumptomob(M) + + else if(href_list["getmob"]) + if(!check_rights(R_ADMIN)) + return + + if(alert(usr, "Confirm?", "Message", "Yes", "No") != "Yes") + return + var/mob/M = locate(href_list["getmob"]) + usr.client.Getmob(M) + + else if(href_list["sendmob"]) + if(!check_rights(R_ADMIN)) + return + + var/mob/M = locate(href_list["sendmob"]) + usr.client.sendmob(M) + + else if(href_list["narrateto"]) + if(!check_rights(R_ADMIN)) + return + + var/mob/M = locate(href_list["narrateto"]) + usr.client.cmd_admin_direct_narrate(M) + + else if(href_list["subtlemessage"]) + if(!check_rights(R_ADMIN)) + return + + var/mob/M = locate(href_list["subtlemessage"]) + usr.client.cmd_admin_subtle_message(M) + + else if(href_list["individuallog"]) + if(!check_rights(R_ADMIN)) + return + + var/mob/M = locate(href_list["individuallog"]) in GLOB.mob_list + if(!ismob(M)) + to_chat(usr, "This can only be used on instances of type /mob.") + return + + show_individual_logging_panel(M, href_list["log_src"], href_list["log_type"]) + else if(href_list["languagemenu"]) + if(!check_rights(R_ADMIN)) + return + + var/mob/M = locate(href_list["languagemenu"]) in GLOB.mob_list + if(!ismob(M)) + to_chat(usr, "This can only be used on instances of type /mob.") + return + var/datum/language_holder/H = M.get_language_holder() + H.open_language_menu(usr) + + else if(href_list["traitor"]) + if(!check_rights(R_ADMIN)) + return + + if(!SSticker.HasRoundStarted()) + alert("The game hasn't started yet!") + return + + var/mob/M = locate(href_list["traitor"]) + if(!ismob(M)) + var/datum/mind/D = M + if(!istype(D)) + to_chat(usr, "This can only be used on instances of type /mob and /mind") + return + else + D.traitor_panel() + else + show_traitor_panel(M) + + else if(href_list["borgpanel"]) + if(!check_rights(R_ADMIN)) + return + + var/mob/M = locate(href_list["borgpanel"]) + if(!iscyborg(M)) + to_chat(usr, "This can only be used on cyborgs") + else + open_borgopanel(M) + + else if(href_list["initmind"]) + if(!check_rights(R_ADMIN)) + return + var/mob/M = locate(href_list["initmind"]) + if(!ismob(M) || M.mind) + to_chat(usr, "This can only be used on instances on mindless mobs") + return + M.mind_initialize() + + else if(href_list["create_object"]) + if(!check_rights(R_SPAWN)) + return + return create_object(usr) + + else if(href_list["quick_create_object"]) + if(!check_rights(R_SPAWN)) + return + return quick_create_object(usr) + + else if(href_list["create_turf"]) + if(!check_rights(R_SPAWN)) + return + return create_turf(usr) + + else if(href_list["create_mob"]) + if(!check_rights(R_SPAWN)) + return + return create_mob(usr) + + else if(href_list["dupe_marked_datum"]) + if(!check_rights(R_SPAWN)) + return + return DuplicateObject(marked_datum, perfectcopy=1, newloc=get_turf(usr)) + + else if(href_list["object_list"]) //this is the laggiest thing ever + if(!check_rights(R_SPAWN)) + return + + var/atom/loc = usr.loc + + var/dirty_paths + if (istext(href_list["object_list"])) + dirty_paths = list(href_list["object_list"]) + else if (istype(href_list["object_list"], /list)) + dirty_paths = href_list["object_list"] + + var/paths = list() + + for(var/dirty_path in dirty_paths) + var/path = text2path(dirty_path) + if(!path) + continue + else if(!ispath(path, /obj) && !ispath(path, /turf) && !ispath(path, /mob)) + continue + paths += path + + if(!paths) + alert("The path list you sent is empty.") + return + if(length(paths) > 5) + alert("Select fewer object types, (max 5).") + return + + var/list/offset = splittext(href_list["offset"],",") + var/number = CLAMP(text2num(href_list["object_count"]), 1, 100) + var/X = offset.len > 0 ? text2num(offset[1]) : 0 + var/Y = offset.len > 1 ? text2num(offset[2]) : 0 + var/Z = offset.len > 2 ? text2num(offset[3]) : 0 + var/obj_dir = text2num(href_list["object_dir"]) + if(obj_dir && !(obj_dir in list(1,2,4,8,5,6,9,10))) + obj_dir = null + var/obj_name = sanitize(href_list["object_name"]) + + + var/atom/target //Where the object will be spawned + var/where = href_list["object_where"] + if (!( where in list("onfloor","inhand","inmarked") )) + where = "onfloor" + + + switch(where) + if("inhand") + if (!iscarbon(usr) && !iscyborg(usr)) + to_chat(usr, "Can only spawn in hand when you're a carbon mob or cyborg.") + where = "onfloor" + target = usr + + if("onfloor") + switch(href_list["offset_type"]) + if ("absolute") + target = locate(0 + X,0 + Y,0 + Z) + if ("relative") + target = locate(loc.x + X,loc.y + Y,loc.z + Z) + if("inmarked") + if(!marked_datum) + to_chat(usr, "You don't have any object marked. Abandoning spawn.") + return + else if(!istype(marked_datum, /atom)) + to_chat(usr, "The object you have marked cannot be used as a target. Target must be of type /atom. Abandoning spawn.") + return + else + target = marked_datum + + if(target) + for (var/path in paths) + for (var/i = 0; i < number; i++) + if(path in typesof(/turf)) + var/turf/O = target + var/turf/N = O.ChangeTurf(path) + if(N && obj_name) + N.name = obj_name + else + var/atom/O = new path(target) + if(!QDELETED(O)) + O.flags_1 |= ADMIN_SPAWNED_1 + if(obj_dir) + O.setDir(obj_dir) + if(obj_name) + O.name = obj_name + if(ismob(O)) + var/mob/M = O + M.real_name = obj_name + if(where == "inhand" && isliving(usr) && isitem(O)) + var/mob/living/L = usr + var/obj/item/I = O + L.put_in_hands(I) + if(iscyborg(L)) + var/mob/living/silicon/robot/R = L + if(R.module) + R.module.add_module(I, TRUE, TRUE) + R.activate_module(I) + + + if (number == 1) + log_admin("[key_name(usr)] created a [english_list(paths)]") + for(var/path in paths) + if(ispath(path, /mob)) + message_admins("[key_name_admin(usr)] created a [english_list(paths)]") + break + else + log_admin("[key_name(usr)] created [number]ea [english_list(paths)]") + for(var/path in paths) + if(ispath(path, /mob)) + message_admins("[key_name_admin(usr)] created [number]ea [english_list(paths)]") + break + return + + else if(href_list["secrets"]) + Secrets_topic(href_list["secrets"],href_list) + + else if(href_list["ac_view_wanted"]) //Admin newscaster Topic() stuff be here + if(!check_rights(R_ADMIN)) + return + src.admincaster_screen = 18 //The ac_ prefix before the hrefs stands for AdminCaster. + src.access_news_network() + + else if(href_list["ac_set_channel_name"]) + if(!check_rights(R_ADMIN)) + return + src.admincaster_feed_channel.channel_name = stripped_input(usr, "Provide a Feed Channel Name.", "Network Channel Handler", "") + while (findtext(src.admincaster_feed_channel.channel_name," ") == 1) + src.admincaster_feed_channel.channel_name = copytext(src.admincaster_feed_channel.channel_name,2,length(src.admincaster_feed_channel.channel_name)+1) + src.access_news_network() + + else if(href_list["ac_set_channel_lock"]) + if(!check_rights(R_ADMIN)) + return + src.admincaster_feed_channel.locked = !src.admincaster_feed_channel.locked + src.access_news_network() + + else if(href_list["ac_submit_new_channel"]) + if(!check_rights(R_ADMIN)) + return + var/check = 0 + for(var/datum/newscaster/feed_channel/FC in GLOB.news_network.network_channels) + if(FC.channel_name == src.admincaster_feed_channel.channel_name) + check = 1 + break + if(src.admincaster_feed_channel.channel_name == "" || src.admincaster_feed_channel.channel_name == "\[REDACTED\]" || check ) + src.admincaster_screen=7 + else + var/choice = alert("Please confirm Feed channel creation.","Network Channel Handler","Confirm","Cancel") + if(choice=="Confirm") + GLOB.news_network.CreateFeedChannel(src.admincaster_feed_channel.channel_name, src.admin_signature, src.admincaster_feed_channel.locked, 1) + SSblackbox.record_feedback("tally", "newscaster_channels", 1, src.admincaster_feed_channel.channel_name) + log_admin("[key_name(usr)] created command feed channel: [src.admincaster_feed_channel.channel_name]!") + src.admincaster_screen=5 + src.access_news_network() + + else if(href_list["ac_set_channel_receiving"]) + if(!check_rights(R_ADMIN)) + return + var/list/available_channels = list() + for(var/datum/newscaster/feed_channel/F in GLOB.news_network.network_channels) + available_channels += F.channel_name + src.admincaster_feed_channel.channel_name = adminscrub(input(usr, "Choose receiving Feed Channel.", "Network Channel Handler") in available_channels ) + src.access_news_network() + + else if(href_list["ac_set_new_message"]) + if(!check_rights(R_ADMIN)) + return + src.admincaster_feed_message.body = adminscrub(input(usr, "Write your Feed story.", "Network Channel Handler", "")) + while (findtext(src.admincaster_feed_message.returnBody(-1)," ") == 1) + src.admincaster_feed_message.body = copytext(src.admincaster_feed_message.returnBody(-1),2,length(src.admincaster_feed_message.returnBody(-1))+1) + src.access_news_network() + + else if(href_list["ac_submit_new_message"]) + if(!check_rights(R_ADMIN)) + return + if(src.admincaster_feed_message.returnBody(-1) =="" || src.admincaster_feed_message.returnBody(-1) =="\[REDACTED\]" || src.admincaster_feed_channel.channel_name == "" ) + src.admincaster_screen = 6 + else + GLOB.news_network.SubmitArticle(src.admincaster_feed_message.returnBody(-1), src.admin_signature, src.admincaster_feed_channel.channel_name, null, 1) + SSblackbox.record_feedback("amount", "newscaster_stories", 1) + src.admincaster_screen=4 + + for(var/obj/machinery/newscaster/NEWSCASTER in GLOB.allCasters) + NEWSCASTER.newsAlert(src.admincaster_feed_channel.channel_name) + + log_admin("[key_name(usr)] submitted a feed story to channel: [src.admincaster_feed_channel.channel_name]!") + src.access_news_network() + + else if(href_list["ac_create_channel"]) + if(!check_rights(R_ADMIN)) + return + src.admincaster_screen=2 + src.access_news_network() + + else if(href_list["ac_create_feed_story"]) + if(!check_rights(R_ADMIN)) + return + src.admincaster_screen=3 + src.access_news_network() + + else if(href_list["ac_menu_censor_story"]) + if(!check_rights(R_ADMIN)) + return + src.admincaster_screen=10 + src.access_news_network() + + else if(href_list["ac_menu_censor_channel"]) + if(!check_rights(R_ADMIN)) + return + src.admincaster_screen=11 + src.access_news_network() + + else if(href_list["ac_menu_wanted"]) + if(!check_rights(R_ADMIN)) + return + var/already_wanted = 0 + if(GLOB.news_network.wanted_issue.active) + already_wanted = 1 + + if(already_wanted) + src.admincaster_wanted_message.criminal = GLOB.news_network.wanted_issue.criminal + src.admincaster_wanted_message.body = GLOB.news_network.wanted_issue.body + src.admincaster_screen = 14 + src.access_news_network() + + else if(href_list["ac_set_wanted_name"]) + if(!check_rights(R_ADMIN)) + return + src.admincaster_wanted_message.criminal = adminscrub(input(usr, "Provide the name of the Wanted person.", "Network Security Handler", "")) + while(findtext(src.admincaster_wanted_message.criminal," ") == 1) + src.admincaster_wanted_message.criminal = copytext(admincaster_wanted_message.criminal,2,length(admincaster_wanted_message.criminal)+1) + src.access_news_network() + + else if(href_list["ac_set_wanted_desc"]) + if(!check_rights(R_ADMIN)) + return + src.admincaster_wanted_message.body = adminscrub(input(usr, "Provide the a description of the Wanted person and any other details you deem important.", "Network Security Handler", "")) + while (findtext(src.admincaster_wanted_message.body," ") == 1) + src.admincaster_wanted_message.body = copytext(src.admincaster_wanted_message.body,2,length(src.admincaster_wanted_message.body)+1) + src.access_news_network() + + else if(href_list["ac_submit_wanted"]) + if(!check_rights(R_ADMIN)) + return + var/input_param = text2num(href_list["ac_submit_wanted"]) + if(src.admincaster_wanted_message.criminal == "" || src.admincaster_wanted_message.body == "") + src.admincaster_screen = 16 + else + var/choice = alert("Please confirm Wanted Issue [(input_param==1) ? ("creation.") : ("edit.")]","Network Security Handler","Confirm","Cancel") + if(choice=="Confirm") + if(input_param==1) //If input_param == 1 we're submitting a new wanted issue. At 2 we're just editing an existing one. See the else below + GLOB.news_network.submitWanted(admincaster_wanted_message.criminal, admincaster_wanted_message.body, admin_signature, null, 1, 1) + src.admincaster_screen = 15 + else + GLOB.news_network.submitWanted(admincaster_wanted_message.criminal, admincaster_wanted_message.body, admin_signature) + src.admincaster_screen = 19 + log_admin("[key_name(usr)] issued a Station-wide Wanted Notification for [src.admincaster_wanted_message.criminal]!") + src.access_news_network() + + else if(href_list["ac_cancel_wanted"]) + if(!check_rights(R_ADMIN)) + return + var/choice = alert("Please confirm Wanted Issue removal.","Network Security Handler","Confirm","Cancel") + if(choice=="Confirm") + GLOB.news_network.deleteWanted() + src.admincaster_screen=17 + src.access_news_network() + + else if(href_list["ac_censor_channel_author"]) + if(!check_rights(R_ADMIN)) + return + var/datum/newscaster/feed_channel/FC = locate(href_list["ac_censor_channel_author"]) + FC.toggleCensorAuthor() + src.access_news_network() + + else if(href_list["ac_censor_channel_story_author"]) + if(!check_rights(R_ADMIN)) + return + var/datum/newscaster/feed_message/MSG = locate(href_list["ac_censor_channel_story_author"]) + MSG.toggleCensorAuthor() + src.access_news_network() + + else if(href_list["ac_censor_channel_story_body"]) + if(!check_rights(R_ADMIN)) + return + var/datum/newscaster/feed_message/MSG = locate(href_list["ac_censor_channel_story_body"]) + MSG.toggleCensorBody() + src.access_news_network() + + else if(href_list["ac_pick_d_notice"]) + if(!check_rights(R_ADMIN)) + return + var/datum/newscaster/feed_channel/FC = locate(href_list["ac_pick_d_notice"]) + src.admincaster_feed_channel = FC + src.admincaster_screen=13 + src.access_news_network() + + else if(href_list["ac_toggle_d_notice"]) + if(!check_rights(R_ADMIN)) + return + var/datum/newscaster/feed_channel/FC = locate(href_list["ac_toggle_d_notice"]) + FC.toggleCensorDclass() + src.access_news_network() + + else if(href_list["ac_view"]) + if(!check_rights(R_ADMIN)) + return + src.admincaster_screen=1 + src.access_news_network() + + else if(href_list["ac_setScreen"]) //Brings us to the main menu and resets all fields~ + if(!check_rights(R_ADMIN)) + return + src.admincaster_screen = text2num(href_list["ac_setScreen"]) + if (src.admincaster_screen == 0) + if(src.admincaster_feed_channel) + src.admincaster_feed_channel = new /datum/newscaster/feed_channel + if(src.admincaster_feed_message) + src.admincaster_feed_message = new /datum/newscaster/feed_message + if(admincaster_wanted_message) + admincaster_wanted_message = new /datum/newscaster/wanted_message + src.access_news_network() + + else if(href_list["ac_show_channel"]) + if(!check_rights(R_ADMIN)) + return + var/datum/newscaster/feed_channel/FC = locate(href_list["ac_show_channel"]) + src.admincaster_feed_channel = FC + src.admincaster_screen = 9 + src.access_news_network() + + else if(href_list["ac_pick_censor_channel"]) + if(!check_rights(R_ADMIN)) + return + var/datum/newscaster/feed_channel/FC = locate(href_list["ac_pick_censor_channel"]) + src.admincaster_feed_channel = FC + src.admincaster_screen = 12 + src.access_news_network() + + else if(href_list["ac_refresh"]) + if(!check_rights(R_ADMIN)) + return + src.access_news_network() + + else if(href_list["ac_set_signature"]) + if(!check_rights(R_ADMIN)) + return + src.admin_signature = adminscrub(input(usr, "Provide your desired signature.", "Network Identity Handler", "")) + src.access_news_network() + + else if(href_list["ac_del_comment"]) + if(!check_rights(R_ADMIN)) + return + var/datum/newscaster/feed_comment/FC = locate(href_list["ac_del_comment"]) + var/datum/newscaster/feed_message/FM = locate(href_list["ac_del_comment_msg"]) + FM.comments -= FC + qdel(FC) + src.access_news_network() + + else if(href_list["ac_lock_comment"]) + if(!check_rights(R_ADMIN)) + return + var/datum/newscaster/feed_message/FM = locate(href_list["ac_lock_comment"]) + FM.locked ^= 1 + src.access_news_network() + + else if(href_list["check_antagonist"]) + if(!check_rights(R_ADMIN)) + return + usr.client.check_antagonists() + + else if(href_list["kick_all_from_lobby"]) + if(!check_rights(R_ADMIN)) + return + if(SSticker.IsRoundInProgress()) + var/afkonly = text2num(href_list["afkonly"]) + if(alert("Are you sure you want to kick all [afkonly ? "AFK" : ""] clients from the lobby??","Message","Yes","Cancel") != "Yes") + to_chat(usr, "Kick clients from lobby aborted") + return + var/list/listkicked = kick_clients_in_lobby("You were kicked from the lobby by [usr.client.holder.fakekey ? "an Administrator" : "[usr.client.key]"].", afkonly) + + var/strkicked = "" + for(var/name in listkicked) + strkicked += "[name], " + message_admins("[key_name_admin(usr)] has kicked [afkonly ? "all AFK" : "all"] clients from the lobby. [length(listkicked)] clients kicked: [strkicked ? strkicked : "--"]") + log_admin("[key_name(usr)] has kicked [afkonly ? "all AFK" : "all"] clients from the lobby. [length(listkicked)] clients kicked: [strkicked ? strkicked : "--"]") + else + to_chat(usr, "You may only use this when the game is running.") + + else if(href_list["create_outfit"]) + if(!check_rights(R_ADMIN)) + return + + var/datum/outfit/O = new /datum/outfit + //swap this for js dropdowns sometime + O.name = href_list["outfit_name"] + O.uniform = text2path(href_list["outfit_uniform"]) + O.shoes = text2path(href_list["outfit_shoes"]) + O.gloves = text2path(href_list["outfit_gloves"]) + O.suit = text2path(href_list["outfit_suit"]) + O.head = text2path(href_list["outfit_head"]) + O.back = text2path(href_list["outfit_back"]) + O.mask = text2path(href_list["outfit_mask"]) + O.glasses = text2path(href_list["outfit_glasses"]) + O.r_hand = text2path(href_list["outfit_r_hand"]) + O.l_hand = text2path(href_list["outfit_l_hand"]) + O.suit_store = text2path(href_list["outfit_s_store"]) + O.l_pocket = text2path(href_list["outfit_l_pocket"]) + O.r_pocket = text2path(href_list["outfit_r_pocket"]) + O.id = text2path(href_list["outfit_id"]) + O.belt = text2path(href_list["outfit_belt"]) + O.ears = text2path(href_list["outfit_ears"]) + + GLOB.custom_outfits.Add(O) + message_admins("[key_name(usr)] created \"[O.name]\" outfit!") + + else if(href_list["set_selfdestruct_code"]) + if(!check_rights(R_ADMIN)) + return + var/code = random_nukecode() + for(var/obj/machinery/nuclearbomb/selfdestruct/SD in GLOB.nuke_list) + SD.r_code = code + message_admins("[key_name_admin(usr)] has set the self-destruct \ + code to \"[code]\".") + + else if(href_list["add_station_goal"]) + if(!check_rights(R_ADMIN)) + return + var/list/type_choices = typesof(/datum/station_goal) + var/picked = input("Choose goal type") in type_choices|null + if(!picked) + return + var/datum/station_goal/G = new picked() + if(picked == /datum/station_goal) + var/newname = input("Enter goal name:") as text|null + if(!newname) + return + G.name = newname + var/description = input("Enter CentCom message contents:") as message|null + if(!description) + return + G.report_message = description + message_admins("[key_name(usr)] created \"[G.name]\" station goal.") + SSticker.mode.station_goals += G + modify_goals() + + else if(href_list["viewruntime"]) + var/datum/error_viewer/error_viewer = locate(href_list["viewruntime"]) + if(!istype(error_viewer)) + to_chat(usr, "That runtime viewer no longer exists.") + return + + if(href_list["viewruntime_backto"]) + error_viewer.show_to(owner, locate(href_list["viewruntime_backto"]), href_list["viewruntime_linear"]) + else + error_viewer.show_to(owner, null, href_list["viewruntime_linear"]) + + else if(href_list["showrelatedacc"]) + if(!check_rights(R_ADMIN)) + return + var/client/C = locate(href_list["client"]) in GLOB.clients + var/thing_to_check + if(href_list["showrelatedacc"] == "cid") + thing_to_check = C.related_accounts_cid + else + thing_to_check = C.related_accounts_ip + thing_to_check = splittext(thing_to_check, ", ") + + + var/list/dat = list("Related accounts by [uppertext(href_list["showrelatedacc"])]:") + dat += thing_to_check + + usr << browse(dat.Join("
    "), "window=related_[C];size=420x300") + else if(href_list["centcomlookup"]) + if(!check_rights(R_ADMIN)) + return + + if(!CONFIG_GET(string/centcom_ban_db)) + to_chat(usr, "Centcom Galactic Ban DB is disabled!") + return + + var/ckey = href_list["centcomlookup"] + + // Make the request + var/datum/http_request/request = new() + request.prepare(RUSTG_HTTP_METHOD_GET, "[CONFIG_GET(string/centcom_ban_db)]/[ckey]", "", "") + request.begin_async() + UNTIL(request.is_complete() || !usr) + if (!usr) + return + var/datum/http_response/response = request.into_response() + + var/list/bans + + var/list/dat = list("") + + if(response.errored) + dat += "
    Failed to connect to CentCom." + else if(response.status_code != 200) + dat += "
    Failed to connect to CentCom. Status code: [response.status_code]" + else + if(response.body == "[]") + dat += "
    0 bans detected for [ckey]
    " + else + bans = json_decode(response["body"]) + dat += "
    [bans.len] ban\s detected for [ckey]
    " + for(var/list/ban in bans) + dat += "Server: [sanitize(ban["sourceName"])]
    " + dat += "RP Level: [sanitize(ban["sourceRoleplayLevel"])]
    " + dat += "Type: [sanitize(ban["type"])]
    " + dat += "Banned By: [sanitize(ban["bannedBy"])]
    " + dat += "Reason: [sanitize(ban["reason"])]
    " + dat += "Datetime: [sanitize(ban["bannedOn"])]
    " + var/expiration = ban["expires"] + dat += "Expires: [expiration ? "[sanitize(expiration)]" : "Permanent"]
    " + if(ban["type"] == "job") + dat += "Jobs: " + var/list/jobs = ban["jobs"] + dat += sanitize(jobs.Join(", ")) + dat += "
    " + dat += "
    " + + dat += "
    " + var/datum/browser/popup = new(usr, "centcomlookup-[ckey]", "
    Central Command Galactic Ban Database
    ", 700, 600) + popup.set_content(dat.Join()) + popup.open(0) + + + else if(href_list["modantagrep"]) + if(!check_rights(R_ADMIN)) + return + + var/mob/M = locate(href_list["mob"]) in GLOB.mob_list + var/client/C = M.client + usr.client.cmd_admin_mod_antag_rep(C, href_list["modantagrep"]) + show_player_panel(M) + + else if(href_list["slowquery"]) + if(!check_rights(R_ADMIN)) + return + var/answer = href_list["slowquery"] + if(answer == "yes") + log_query_debug("[usr.key] | Reported a server hang") + if(alert(usr, "Had you just press any admin buttons?", "Query server hang report", "Yes", "No") == "Yes") + var/response = input(usr,"What were you just doing?","Query server hang report") as null|text + if(response) + log_query_debug("[usr.key] | [response]") + else if(answer == "no") + log_query_debug("[usr.key] | Reported no server hang") + +/datum/admins/proc/HandleCMode() + if(!check_rights(R_ADMIN)) + return + + if(SSticker.HasRoundStarted()) + return alert(usr, "The game has already started.", null, null, null, null) + var/dat = {"What mode do you wish to play?
    "} + for(var/mode in config.modes) + dat += {"[config.mode_names[mode]]
    "} + dat += {"Secret
    "} + dat += {"Random
    "} + dat += {"Now: [GLOB.master_mode]"} + usr << browse(dat, "window=c_mode") + +/datum/admins/proc/HandleFSecret() + if(!check_rights(R_ADMIN)) + return + + if(SSticker.HasRoundStarted()) + return alert(usr, "The game has already started.", null, null, null, null) + if(GLOB.master_mode != "secret") + return alert(usr, "The game mode has to be secret!", null, null, null, null) + var/dat = {"What game mode do you want to force secret to be? Use this if you want to change the game mode, but want the players to believe it's secret. This will only work if the current game mode is secret.
    "} + for(var/mode in config.modes) + dat += {"[config.mode_names[mode]]
    "} + dat += {"Random (default)
    "} + dat += {"Now: [GLOB.secret_force_mode]"} + usr << browse(dat, "window=f_secret") + +/datum/admins/proc/makeMentor(ckey) + if(!usr.client) + return + if (!check_rights(0)) + return + if(!ckey) + return + var/client/C = GLOB.directory[ckey] + if(C) + if(check_rights_for(C, R_ADMIN,0)) + to_chat(usr, "The client chosen is an admin! Cannot mentorize.") + return + if(SSdbcore.Connect()) + var/datum/DBQuery/query_get_mentor = SSdbcore.NewQuery("SELECT id FROM [format_table_name("mentor")] WHERE ckey = '[ckey]'") + if(query_get_mentor.NextRow()) + to_chat(usr, "[ckey] is already a mentor.") + return + var/datum/DBQuery/query_add_mentor = SSdbcore.NewQuery("INSERT INTO `[format_table_name("mentor")]` (`id`, `ckey`) VALUES (null, '[ckey]')") + if(!query_add_mentor.warn_execute()) + return + var/datum/DBQuery/query_add_admin_log = SSdbcore.NewQuery("INSERT INTO `[format_table_name("admin_log")]` (`id` ,`datetime` ,`adminckey` ,`adminip` ,`log` ) VALUES (NULL , NOW( ) , '[usr.ckey]', '[usr.client.address]', 'Added new mentor [ckey]');") + if(!query_add_admin_log.warn_execute()) + return + else + to_chat(usr, "Failed to establish database connection. The changes will last only for the current round.") + new /datum/mentors(ckey) + to_chat(usr, "New mentor added.") + +/datum/admins/proc/removeMentor(ckey) + if(!usr.client) + return + if (!check_rights(0)) + return + if(!ckey) + return + var/client/C = GLOB.directory[ckey] + if(C) + if(check_rights_for(C, R_ADMIN,0)) + to_chat(usr, "The client chosen is an admin, not a mentor! Cannot de-mentorize.") + return + C.remove_mentor_verbs() + C.mentor_datum = null + GLOB.mentors -= C + if(SSdbcore.Connect()) + var/datum/DBQuery/query_remove_mentor = SSdbcore.NewQuery("DELETE FROM [format_table_name("mentor")] WHERE ckey = '[ckey]'") + if(!query_remove_mentor.warn_execute()) + return + var/datum/DBQuery/query_add_admin_log = SSdbcore.NewQuery("INSERT INTO `[format_table_name("admin_log")]` (`id` ,`datetime` ,`adminckey` ,`adminip` ,`log` ) VALUES (NULL , NOW( ) , '[usr.ckey]', '[usr.client.address]', 'Removed mentor [ckey]');") + if(!query_add_admin_log.warn_execute()) + return + else + to_chat(usr, "Failed to establish database connection. The changes will last only for the current round.") + to_chat(usr, "Mentor removed.") diff --git a/code/modules/admin/verbs/modifyvariables.dm b/code/modules/admin/verbs/modifyvariables.dm index 6ddc75c7..9daa417a 100644 --- a/code/modules/admin/verbs/modifyvariables.dm +++ b/code/modules/admin/verbs/modifyvariables.dm @@ -1,644 +1,644 @@ -GLOBAL_LIST_INIT(VVlocked, list("vars", "datum_flags", "client", "virus", "viruses", "cuffed", "last_eaten", "unlock_content", "force_ending")) -GLOBAL_PROTECT(VVlocked) -GLOBAL_LIST_INIT(VVicon_edit_lock, list("icon", "icon_state", "overlays", "underlays", "resize")) -GLOBAL_PROTECT(VVicon_edit_lock) -GLOBAL_LIST_INIT(VVckey_edit, list("key", "ckey")) -GLOBAL_PROTECT(VVckey_edit) -GLOBAL_LIST_INIT(VVpixelmovement, list("step_x", "step_y", "bound_height", "bound_width", "bound_x", "bound_y")) -GLOBAL_PROTECT(VVpixelmovement) - - -/client/proc/vv_get_class(var/var_name, var/var_value) - if(isnull(var_value)) - . = VV_NULL - - else if (isnum(var_value)) - if (var_name in GLOB.bitfields) - . = VV_BITFIELD - else - . = VV_NUM - - else if (istext(var_value)) - if (findtext(var_value, "\n")) - . = VV_MESSAGE - else - . = VV_TEXT - - else if (isicon(var_value)) - . = VV_ICON - - else if (ismob(var_value)) - . = VV_MOB_REFERENCE - - else if (isloc(var_value)) - . = VV_ATOM_REFERENCE - - else if (istype(var_value, /client)) - . = VV_CLIENT - - else if (istype(var_value, /datum)) - . = VV_DATUM_REFERENCE - - else if (ispath(var_value)) - if (ispath(var_value, /atom)) - . = VV_ATOM_TYPE - else if (ispath(var_value, /datum)) - . = VV_DATUM_TYPE - else - . = VV_TYPE - - else if (islist(var_value)) - . = VV_LIST - - else if (isfile(var_value)) - . = VV_FILE - else - . = VV_NULL - -/client/proc/vv_get_value(class, default_class, current_value, list/restricted_classes, list/extra_classes, list/classes, var_name) - . = list("class" = class, "value" = null) - if (!class) - if (!classes) - classes = list ( - VV_NUM, - VV_TEXT, - VV_MESSAGE, - VV_ICON, - VV_ATOM_REFERENCE, - VV_DATUM_REFERENCE, - VV_MOB_REFERENCE, - VV_CLIENT, - VV_ATOM_TYPE, - VV_DATUM_TYPE, - VV_TYPE, - VV_FILE, - VV_NEW_ATOM, - VV_NEW_DATUM, - VV_NEW_TYPE, - VV_NEW_LIST, - VV_NULL, - VV_RESTORE_DEFAULT - ) - - if(holder && holder.marked_datum && !(VV_MARKED_DATUM in restricted_classes)) - classes += "[VV_MARKED_DATUM] ([holder.marked_datum.type])" - if (restricted_classes) - classes -= restricted_classes - - if (extra_classes) - classes += extra_classes - - .["class"] = input(src, "What kind of data?", "Variable Type", default_class) as null|anything in classes - if (holder && holder.marked_datum && .["class"] == "[VV_MARKED_DATUM] ([holder.marked_datum.type])") - .["class"] = VV_MARKED_DATUM - - - switch(.["class"]) - if (VV_TEXT) - .["value"] = input("Enter new text:", "Text", current_value) as null|text - if (.["value"] == null) - .["class"] = null - return - if (VV_MESSAGE) - .["value"] = input("Enter new text:", "Text", current_value) as null|message - if (.["value"] == null) - .["class"] = null - return - - - if (VV_NUM) - .["value"] = input("Enter new number:", "Num", current_value) as null|num - if (.["value"] == null) - .["class"] = null - return - - if (VV_BITFIELD) - .["value"] = input_bitfield(usr, "Editing bitfield: [var_name]", var_name, current_value) - if (.["value"] == null) - .["class"] = null - return - - if (VV_ATOM_TYPE) - .["value"] = pick_closest_path(FALSE) - if (.["value"] == null) - .["class"] = null - return - - if (VV_DATUM_TYPE) - .["value"] = pick_closest_path(FALSE, get_fancy_list_of_datum_types()) - if (.["value"] == null) - .["class"] = null - return - - if (VV_TYPE) - var/type = current_value - var/error = "" - do - type = input("Enter type:[error]", "Type", type) as null|text - if (!type) - break - type = text2path(type) - error = "\nType not found, Please try again" - while(!type) - if (!type) - .["class"] = null - return - .["value"] = type - - - if (VV_ATOM_REFERENCE) - var/type = pick_closest_path(FALSE) - var/subtypes = vv_subtype_prompt(type) - if (subtypes == null) - .["class"] = null - return - var/list/things = vv_reference_list(type, subtypes) - var/value = input("Select reference:", "Reference", current_value) as null|anything in things - if (!value) - .["class"] = null - return - .["value"] = things[value] - - if (VV_DATUM_REFERENCE) - var/type = pick_closest_path(FALSE, get_fancy_list_of_datum_types()) - var/subtypes = vv_subtype_prompt(type) - if (subtypes == null) - .["class"] = null - return - var/list/things = vv_reference_list(type, subtypes) - var/value = input("Select reference:", "Reference", current_value) as null|anything in things - if (!value) - .["class"] = null - return - .["value"] = things[value] - - if (VV_MOB_REFERENCE) - var/type = pick_closest_path(FALSE, make_types_fancy(typesof(/mob))) - var/subtypes = vv_subtype_prompt(type) - if (subtypes == null) - .["class"] = null - return - var/list/things = vv_reference_list(type, subtypes) - var/value = input("Select reference:", "Reference", current_value) as null|anything in things - if (!value) - .["class"] = null - return - .["value"] = things[value] - - - - if (VV_CLIENT) - .["value"] = input("Select reference:", "Reference", current_value) as null|anything in GLOB.clients - if (.["value"] == null) - .["class"] = null - return - - - if (VV_FILE) - .["value"] = input("Pick file:", "File") as null|file - if (.["value"] == null) - .["class"] = null - return - - - if (VV_ICON) - .["value"] = input("Pick icon:", "Icon") as null|icon - if (.["value"] == null) - .["class"] = null - return - - - if (VV_MARKED_DATUM) - .["value"] = holder.marked_datum - if (.["value"] == null) - .["class"] = null - return - - - if (VV_NEW_ATOM) - var/type = pick_closest_path(FALSE) - if (!type) - .["class"] = null - return - .["type"] = type - var/atom/newguy = new type() - newguy.datum_flags |= DF_VAR_EDITED - .["value"] = newguy - - if (VV_NEW_DATUM) - var/type = pick_closest_path(FALSE, get_fancy_list_of_datum_types()) - if (!type) - .["class"] = null - return - .["type"] = type - var/datum/newguy = new type() - newguy.datum_flags |= DF_VAR_EDITED - .["value"] = newguy - - if (VV_NEW_TYPE) - var/type = current_value - var/error = "" - do - type = input("Enter type:[error]", "Type", type) as null|text - if (!type) - break - type = text2path(type) - error = "\nType not found, Please try again" - while(!type) - if (!type) - .["class"] = null - return - .["type"] = type - var/datum/newguy = new type() - if(istype(newguy)) - newguy.datum_flags |= DF_VAR_EDITED - .["value"] = newguy - - - if (VV_NEW_LIST) - .["value"] = list() - .["type"] = /list - -/client/proc/vv_parse_text(O, new_var) - if(O && findtext(new_var,"\[")) - var/process_vars = alert(usr,"\[] detected in string, process as variables?","Process Variables?","Yes","No") - if(process_vars == "Yes") - . = string2listofvars(new_var, O) - -//do they want you to include subtypes? -//FALSE = no subtypes, strict exact type pathing (or the type doesn't have subtypes) -//TRUE = Yes subtypes -//NULL = User cancelled at the prompt or invalid type given -/client/proc/vv_subtype_prompt(var/type) - if (!ispath(type)) - return - var/list/subtypes = subtypesof(type) - if (!subtypes || !subtypes.len) - return FALSE - if (subtypes && subtypes.len) - switch(alert("Strict object type detection?", "Type detection", "Strictly this type","This type and subtypes", "Cancel")) - if("Strictly this type") - return FALSE - if("This type and subtypes") - return TRUE - else - return - -/client/proc/vv_reference_list(type, subtypes) - . = list() - var/list/types = list(type) - if (subtypes) - types = typesof(type) - - var/list/fancytypes = make_types_fancy(types) - - for(var/fancytype in fancytypes) //swap the assoication - types[fancytypes[fancytype]] = fancytype - - var/things = get_all_of_type(type, subtypes) - - var/i = 0 - for(var/thing in things) - var/datum/D = thing - i++ - //try one of 3 methods to shorten the type text: - // fancy type, - // fancy type with the base type removed from the begaining, - // the type with the base type removed from the begaining - var/fancytype = types[D.type] - if (findtext(fancytype, types[type])) - fancytype = copytext(fancytype, length(types[type])+1) - var/shorttype = copytext("[D.type]", length("[type]")+1) - if (length(shorttype) > length(fancytype)) - shorttype = fancytype - if (!length(shorttype)) - shorttype = "/" - - .["[D]([shorttype])[REF(D)]#[i]"] = D - -/client/proc/mod_list_add_ass(atom/O) //hehe - - var/list/L = vv_get_value(restricted_classes = list(VV_RESTORE_DEFAULT)) - var/class = L["class"] - if (!class) - return - var/var_value = L["value"] - - if(class == VV_TEXT || class == VV_MESSAGE) - var/list/varsvars = vv_parse_text(O, var_value) - for(var/V in varsvars) - var_value = replacetext(var_value,"\[[V]]","[O.vars[V]]") - - return var_value - - -/client/proc/mod_list_add(list/L, atom/O, original_name, objectvar) - var/list/LL = vv_get_value(restricted_classes = list(VV_RESTORE_DEFAULT)) - var/class = LL["class"] - if (!class) - return - var/var_value = LL["value"] - - if(class == VV_TEXT || class == VV_MESSAGE) - var/list/varsvars = vv_parse_text(O, var_value) - for(var/V in varsvars) - var_value = replacetext(var_value,"\[[V]]","[O.vars[V]]") - - if (O) - L = L.Copy() - - L += var_value - - switch(alert("Would you like to associate a value with the list entry?",,"Yes","No")) - if("Yes") - L[var_value] = mod_list_add_ass(O) //hehe - if (O) - if (O.vv_edit_var(objectvar, L) == FALSE) - to_chat(src, "Your edit was rejected by the object.") - return - log_world("### ListVarEdit by [src]: [(O ? O.type : "/list")] [objectvar]: ADDED=[var_value]") - log_admin("[key_name(src)] modified [original_name]'s [objectvar]: ADDED=[var_value]") - message_admins("[key_name_admin(src)] modified [original_name]'s [objectvar]: ADDED=[var_value]") - -/client/proc/mod_list(list/L, atom/O, original_name, objectvar, index, autodetect_class = FALSE) - if(!check_rights(R_VAREDIT)) - return - if(!istype(L, /list)) - to_chat(src, "Not a List.") - return - - if(L.len > 1000) - var/confirm = alert(src, "The list you're trying to edit is very long, continuing may crash the server.", "Warning", "Continue", "Abort") - if(confirm != "Continue") - return - - - - var/list/names = list() - for (var/i in 1 to L.len) - var/key = L[i] - var/value - if (IS_NORMAL_LIST(L) && !isnum(key)) - value = L[key] - if (value == null) - value = "null" - names["#[i] [key] = [value]"] = i - if (!index) - var/variable = input("Which var?","Var") as null|anything in names + "(ADD VAR)" + "(CLEAR NULLS)" + "(CLEAR DUPES)" + "(SHUFFLE)" - - if(variable == null) - return - - if(variable == "(ADD VAR)") - mod_list_add(L, O, original_name, objectvar) - return - - if(variable == "(CLEAR NULLS)") - L = L.Copy() - listclearnulls(L) - if (!O.vv_edit_var(objectvar, L)) - to_chat(src, "Your edit was rejected by the object.") - return - log_world("### ListVarEdit by [src]: [O.type] [objectvar]: CLEAR NULLS") - log_admin("[key_name(src)] modified [original_name]'s [objectvar]: CLEAR NULLS") - message_admins("[key_name_admin(src)] modified [original_name]'s list [objectvar]: CLEAR NULLS") - return - - if(variable == "(CLEAR DUPES)") - L = uniqueList(L) - if (!O.vv_edit_var(objectvar, L)) - to_chat(src, "Your edit was rejected by the object.") - return - log_world("### ListVarEdit by [src]: [O.type] [objectvar]: CLEAR DUPES") - log_admin("[key_name(src)] modified [original_name]'s [objectvar]: CLEAR DUPES") - message_admins("[key_name_admin(src)] modified [original_name]'s list [objectvar]: CLEAR DUPES") - return - - if(variable == "(SHUFFLE)") - L = shuffle(L) - if (!O.vv_edit_var(objectvar, L)) - to_chat(src, "Your edit was rejected by the object.") - return - log_world("### ListVarEdit by [src]: [O.type] [objectvar]: SHUFFLE") - log_admin("[key_name(src)] modified [original_name]'s [objectvar]: SHUFFLE") - message_admins("[key_name_admin(src)] modified [original_name]'s list [objectvar]: SHUFFLE") - return - - index = names[variable] - - - var/assoc_key - if (index == null) - return - var/assoc = 0 - var/prompt = alert(src, "Do you want to edit the key or its assigned value?", "Associated List", "Key", "Assigned Value", "Cancel") - if (prompt == "Cancel") - return - if (prompt == "Assigned Value") - assoc = 1 - assoc_key = L[index] - var/default - var/variable - if (assoc) - variable = L[assoc_key] - else - variable = L[index] - - default = vv_get_class(objectvar, variable) - - to_chat(src, "Variable appears to be [uppertext(default)].") - - to_chat(src, "Variable contains: [variable]") - - if(default == VV_NUM) - var/dir_text = "" - var/tdir = variable - if(tdir > 0 && tdir < 16) - if(tdir & 1) - dir_text += "NORTH" - if(tdir & 2) - dir_text += "SOUTH" - if(tdir & 4) - dir_text += "EAST" - if(tdir & 8) - dir_text += "WEST" - - if(dir_text) - to_chat(usr, "If a direction, direction is: [dir_text]") - - var/original_var = variable - - if (O) - L = L.Copy() - var/class - if(autodetect_class) - if (default == VV_TEXT) - default = VV_MESSAGE - class = default - var/list/LL = vv_get_value(default_class = default, current_value = original_var, restricted_classes = list(VV_RESTORE_DEFAULT), extra_classes = list(VV_LIST, "DELETE FROM LIST")) - class = LL["class"] - if (!class) - return - var/new_var = LL["value"] - - if(class == VV_MESSAGE) - class = VV_TEXT - - switch(class) //Spits a runtime error if you try to modify an entry in the contents list. Dunno how to fix it, yet. - if(VV_LIST) - mod_list(variable, O, original_name, objectvar) - - if("DELETE FROM LIST") - L.Cut(index, index+1) - if (O) - if (O.vv_edit_var(objectvar, L)) - to_chat(src, "Your edit was rejected by the object.") - return - log_world("### ListVarEdit by [src]: [O.type] [objectvar]: REMOVED=[html_encode("[original_var]")]") - log_admin("[key_name(src)] modified [original_name]'s [objectvar]: REMOVED=[original_var]") - message_admins("[key_name_admin(src)] modified [original_name]'s [objectvar]: REMOVED=[original_var]") - return - - if(VV_TEXT) - var/list/varsvars = vv_parse_text(O, new_var) - for(var/V in varsvars) - new_var = replacetext(new_var,"\[[V]]","[O.vars[V]]") - - - if(assoc) - L[assoc_key] = new_var - else - L[index] = new_var - if (O) - if (O.vv_edit_var(objectvar, L) == FALSE) - to_chat(src, "Your edit was rejected by the object.") - return - log_world("### ListVarEdit by [src]: [(O ? O.type : "/list")] [objectvar]: [original_var]=[new_var]") - log_admin("[key_name(src)] modified [original_name]'s [objectvar]: [original_var]=[new_var]") - message_admins("[key_name_admin(src)] modified [original_name]'s varlist [objectvar]: [original_var]=[new_var]") - -/proc/vv_varname_lockcheck(param_var_name) - if(param_var_name in GLOB.VVlocked) - if(!check_rights(R_DEBUG)) - return FALSE - if(param_var_name in GLOB.VVckey_edit) - if(!check_rights(R_SPAWN|R_DEBUG)) - return FALSE - if(param_var_name in GLOB.VVicon_edit_lock) - if(!check_rights(R_FUN|R_DEBUG)) - return FALSE - if(param_var_name in GLOB.VVpixelmovement) - if(!check_rights(R_DEBUG)) - return FALSE - var/prompt = alert(usr, "Editing this var may irreparably break tile gliding for the rest of the round. THIS CAN'T BE UNDONE", "DANGER", "ABORT ", "Continue", " ABORT") - if (prompt != "Continue") - return FALSE - return TRUE - - -/client/proc/modify_variables(atom/O, param_var_name = null, autodetect_class = 0) - if(!check_rights(R_VAREDIT)) - return - - var/class - var/variable - var/var_value - - if(param_var_name) - if(!param_var_name in O.vars) - to_chat(src, "A variable with this name ([param_var_name]) doesn't exist in this datum ([O])") - return - variable = param_var_name - - else - var/list/names = list() - for (var/V in O.vars) - names += V - - names = sortList(names) - - variable = input("Which var?","Var") as null|anything in names - if(!variable) - return - - if(!O.can_vv_get(variable)) - return - - var_value = O.vars[variable] - if(!vv_varname_lockcheck(variable)) - return - if(istype(O, /datum/armor)) - var/prompt = alert(src, "Editing this var changes this value on potentially thousands of items that share the same combination of armor values. If you want to edit the armor of just one item, use the \"Modify armor values\" dropdown item", "DANGER", "ABORT ", "Continue", " ABORT") - if (prompt != "Continue") - return - - - var/default = vv_get_class(variable, var_value) - - if(isnull(default)) - to_chat(src, "Unable to determine variable type.") - else - to_chat(src, "Variable appears to be [uppertext(default)].") - - to_chat(src, "Variable contains: [var_value]") - - if(default == VV_NUM) - var/dir_text = "" - if(var_value > 0 && var_value < 16) - if(var_value & 1) - dir_text += "NORTH" - if(var_value & 2) - dir_text += "SOUTH" - if(var_value & 4) - dir_text += "EAST" - if(var_value & 8) - dir_text += "WEST" - - if(dir_text) - to_chat(src, "If a direction, direction is: [dir_text]") - - if(autodetect_class && default != VV_NULL) - if (default == VV_TEXT) - default = VV_MESSAGE - class = default - - var/list/value = vv_get_value(class, default, var_value, extra_classes = list(VV_LIST), var_name = variable) - class = value["class"] - - if (!class) - return - var/var_new = value["value"] - - if(class == VV_MESSAGE) - class = VV_TEXT - - var/original_name = "[O]" - - switch(class) - if(VV_LIST) - if(!islist(var_value)) - mod_list(list(), O, original_name, variable) - - mod_list(var_value, O, original_name, variable) - return - - if(VV_RESTORE_DEFAULT) - var_new = initial(O.vars[variable]) - - if(VV_TEXT) - var/list/varsvars = vv_parse_text(O, var_new) - for(var/V in varsvars) - var_new = replacetext(var_new,"\[[V]]","[O.vars[V]]") - - - if (O.vv_edit_var(variable, var_new) == FALSE) - to_chat(src, "Your edit was rejected by the object.") - return - vv_update_display(O, "varedited", VV_MSG_EDITED) - SEND_GLOBAL_SIGNAL(COMSIG_GLOB_VAR_EDIT, args) - log_world("### VarEdit by [key_name(src)]: [O.type] [variable]=[var_value] => [var_new]") - log_admin("[key_name(src)] modified [original_name]'s [variable] from [html_encode("[var_value]")] to [html_encode("[var_new]")]") - var/msg = "[key_name_admin(src)] modified [original_name]'s [variable] from [var_value] to [var_new]" - message_admins(msg) - admin_ticket_log(O, msg) +GLOBAL_LIST_INIT(VVlocked, list("vars", "datum_flags", "client", "virus", "viruses", "cuffed", "last_eaten", "unlock_content", "force_ending")) +GLOBAL_PROTECT(VVlocked) +GLOBAL_LIST_INIT(VVicon_edit_lock, list("icon", "icon_state", "overlays", "underlays", "resize")) +GLOBAL_PROTECT(VVicon_edit_lock) +GLOBAL_LIST_INIT(VVckey_edit, list("key", "ckey")) +GLOBAL_PROTECT(VVckey_edit) +GLOBAL_LIST_INIT(VVpixelmovement, list("step_x", "step_y", "bound_height", "bound_width", "bound_x", "bound_y")) +GLOBAL_PROTECT(VVpixelmovement) + + +/client/proc/vv_get_class(var/var_name, var/var_value) + if(isnull(var_value)) + . = VV_NULL + + else if (isnum(var_value)) + if (var_name in GLOB.bitfields) + . = VV_BITFIELD + else + . = VV_NUM + + else if (istext(var_value)) + if (findtext(var_value, "\n")) + . = VV_MESSAGE + else + . = VV_TEXT + + else if (isicon(var_value)) + . = VV_ICON + + else if (ismob(var_value)) + . = VV_MOB_REFERENCE + + else if (isloc(var_value)) + . = VV_ATOM_REFERENCE + + else if (istype(var_value, /client)) + . = VV_CLIENT + + else if (istype(var_value, /datum)) + . = VV_DATUM_REFERENCE + + else if (ispath(var_value)) + if (ispath(var_value, /atom)) + . = VV_ATOM_TYPE + else if (ispath(var_value, /datum)) + . = VV_DATUM_TYPE + else + . = VV_TYPE + + else if (islist(var_value)) + . = VV_LIST + + else if (isfile(var_value)) + . = VV_FILE + else + . = VV_NULL + +/client/proc/vv_get_value(class, default_class, current_value, list/restricted_classes, list/extra_classes, list/classes, var_name) + . = list("class" = class, "value" = null) + if (!class) + if (!classes) + classes = list ( + VV_NUM, + VV_TEXT, + VV_MESSAGE, + VV_ICON, + VV_ATOM_REFERENCE, + VV_DATUM_REFERENCE, + VV_MOB_REFERENCE, + VV_CLIENT, + VV_ATOM_TYPE, + VV_DATUM_TYPE, + VV_TYPE, + VV_FILE, + VV_NEW_ATOM, + VV_NEW_DATUM, + VV_NEW_TYPE, + VV_NEW_LIST, + VV_NULL, + VV_RESTORE_DEFAULT + ) + + if(holder && holder.marked_datum && !(VV_MARKED_DATUM in restricted_classes)) + classes += "[VV_MARKED_DATUM] ([holder.marked_datum.type])" + if (restricted_classes) + classes -= restricted_classes + + if (extra_classes) + classes += extra_classes + + .["class"] = input(src, "What kind of data?", "Variable Type", default_class) as null|anything in classes + if (holder && holder.marked_datum && .["class"] == "[VV_MARKED_DATUM] ([holder.marked_datum.type])") + .["class"] = VV_MARKED_DATUM + + + switch(.["class"]) + if (VV_TEXT) + .["value"] = input("Enter new text:", "Text", current_value) as null|text + if (.["value"] == null) + .["class"] = null + return + if (VV_MESSAGE) + .["value"] = input("Enter new text:", "Text", current_value) as null|message + if (.["value"] == null) + .["class"] = null + return + + + if (VV_NUM) + .["value"] = input("Enter new number:", "Num", current_value) as null|num + if (.["value"] == null) + .["class"] = null + return + + if (VV_BITFIELD) + .["value"] = input_bitfield(usr, "Editing bitfield: [var_name]", var_name, current_value) + if (.["value"] == null) + .["class"] = null + return + + if (VV_ATOM_TYPE) + .["value"] = pick_closest_path(FALSE) + if (.["value"] == null) + .["class"] = null + return + + if (VV_DATUM_TYPE) + .["value"] = pick_closest_path(FALSE, get_fancy_list_of_datum_types()) + if (.["value"] == null) + .["class"] = null + return + + if (VV_TYPE) + var/type = current_value + var/error = "" + do + type = input("Enter type:[error]", "Type", type) as null|text + if (!type) + break + type = text2path(type) + error = "\nType not found, Please try again" + while(!type) + if (!type) + .["class"] = null + return + .["value"] = type + + + if (VV_ATOM_REFERENCE) + var/type = pick_closest_path(FALSE) + var/subtypes = vv_subtype_prompt(type) + if (subtypes == null) + .["class"] = null + return + var/list/things = vv_reference_list(type, subtypes) + var/value = input("Select reference:", "Reference", current_value) as null|anything in things + if (!value) + .["class"] = null + return + .["value"] = things[value] + + if (VV_DATUM_REFERENCE) + var/type = pick_closest_path(FALSE, get_fancy_list_of_datum_types()) + var/subtypes = vv_subtype_prompt(type) + if (subtypes == null) + .["class"] = null + return + var/list/things = vv_reference_list(type, subtypes) + var/value = input("Select reference:", "Reference", current_value) as null|anything in things + if (!value) + .["class"] = null + return + .["value"] = things[value] + + if (VV_MOB_REFERENCE) + var/type = pick_closest_path(FALSE, make_types_fancy(typesof(/mob))) + var/subtypes = vv_subtype_prompt(type) + if (subtypes == null) + .["class"] = null + return + var/list/things = vv_reference_list(type, subtypes) + var/value = input("Select reference:", "Reference", current_value) as null|anything in things + if (!value) + .["class"] = null + return + .["value"] = things[value] + + + + if (VV_CLIENT) + .["value"] = input("Select reference:", "Reference", current_value) as null|anything in GLOB.clients + if (.["value"] == null) + .["class"] = null + return + + + if (VV_FILE) + .["value"] = input("Pick file:", "File") as null|file + if (.["value"] == null) + .["class"] = null + return + + + if (VV_ICON) + .["value"] = input("Pick icon:", "Icon") as null|icon + if (.["value"] == null) + .["class"] = null + return + + + if (VV_MARKED_DATUM) + .["value"] = holder.marked_datum + if (.["value"] == null) + .["class"] = null + return + + + if (VV_NEW_ATOM) + var/type = pick_closest_path(FALSE) + if (!type) + .["class"] = null + return + .["type"] = type + var/atom/newguy = new type() + newguy.datum_flags |= DF_VAR_EDITED + .["value"] = newguy + + if (VV_NEW_DATUM) + var/type = pick_closest_path(FALSE, get_fancy_list_of_datum_types()) + if (!type) + .["class"] = null + return + .["type"] = type + var/datum/newguy = new type() + newguy.datum_flags |= DF_VAR_EDITED + .["value"] = newguy + + if (VV_NEW_TYPE) + var/type = current_value + var/error = "" + do + type = input("Enter type:[error]", "Type", type) as null|text + if (!type) + break + type = text2path(type) + error = "\nType not found, Please try again" + while(!type) + if (!type) + .["class"] = null + return + .["type"] = type + var/datum/newguy = new type() + if(istype(newguy)) + newguy.datum_flags |= DF_VAR_EDITED + .["value"] = newguy + + + if (VV_NEW_LIST) + .["value"] = list() + .["type"] = /list + +/client/proc/vv_parse_text(O, new_var) + if(O && findtext(new_var,"\[")) + var/process_vars = alert(usr,"\[] detected in string, process as variables?","Process Variables?","Yes","No") + if(process_vars == "Yes") + . = string2listofvars(new_var, O) + +//do they want you to include subtypes? +//FALSE = no subtypes, strict exact type pathing (or the type doesn't have subtypes) +//TRUE = Yes subtypes +//NULL = User cancelled at the prompt or invalid type given +/client/proc/vv_subtype_prompt(var/type) + if (!ispath(type)) + return + var/list/subtypes = subtypesof(type) + if (!subtypes || !subtypes.len) + return FALSE + if (subtypes && subtypes.len) + switch(alert("Strict object type detection?", "Type detection", "Strictly this type","This type and subtypes", "Cancel")) + if("Strictly this type") + return FALSE + if("This type and subtypes") + return TRUE + else + return + +/client/proc/vv_reference_list(type, subtypes) + . = list() + var/list/types = list(type) + if (subtypes) + types = typesof(type) + + var/list/fancytypes = make_types_fancy(types) + + for(var/fancytype in fancytypes) //swap the assoication + types[fancytypes[fancytype]] = fancytype + + var/things = get_all_of_type(type, subtypes) + + var/i = 0 + for(var/thing in things) + var/datum/D = thing + i++ + //try one of 3 methods to shorten the type text: + // fancy type, + // fancy type with the base type removed from the begaining, + // the type with the base type removed from the begaining + var/fancytype = types[D.type] + if (findtext(fancytype, types[type])) + fancytype = copytext(fancytype, length(types[type])+1) + var/shorttype = copytext("[D.type]", length("[type]")+1) + if (length(shorttype) > length(fancytype)) + shorttype = fancytype + if (!length(shorttype)) + shorttype = "/" + + .["[D]([shorttype])[REF(D)]#[i]"] = D + +/client/proc/mod_list_add_ass(atom/O) //hehe + + var/list/L = vv_get_value(restricted_classes = list(VV_RESTORE_DEFAULT)) + var/class = L["class"] + if (!class) + return + var/var_value = L["value"] + + if(class == VV_TEXT || class == VV_MESSAGE) + var/list/varsvars = vv_parse_text(O, var_value) + for(var/V in varsvars) + var_value = replacetext(var_value,"\[[V]]","[O.vars[V]]") + + return var_value + + +/client/proc/mod_list_add(list/L, atom/O, original_name, objectvar) + var/list/LL = vv_get_value(restricted_classes = list(VV_RESTORE_DEFAULT)) + var/class = LL["class"] + if (!class) + return + var/var_value = LL["value"] + + if(class == VV_TEXT || class == VV_MESSAGE) + var/list/varsvars = vv_parse_text(O, var_value) + for(var/V in varsvars) + var_value = replacetext(var_value,"\[[V]]","[O.vars[V]]") + + if (O) + L = L.Copy() + + L += var_value + + switch(alert("Would you like to associate a value with the list entry?",,"Yes","No")) + if("Yes") + L[var_value] = mod_list_add_ass(O) //hehe + if (O) + if (O.vv_edit_var(objectvar, L) == FALSE) + to_chat(src, "Your edit was rejected by the object.") + return + log_world("### ListVarEdit by [src]: [(O ? O.type : "/list")] [objectvar]: ADDED=[var_value]") + log_admin("[key_name(src)] modified [original_name]'s [objectvar]: ADDED=[var_value]") + message_admins("[key_name_admin(src)] modified [original_name]'s [objectvar]: ADDED=[var_value]") + +/client/proc/mod_list(list/L, atom/O, original_name, objectvar, index, autodetect_class = FALSE) + if(!check_rights(R_VAREDIT)) + return + if(!istype(L, /list)) + to_chat(src, "Not a List.") + return + + if(L.len > 1000) + var/confirm = alert(src, "The list you're trying to edit is very long, continuing may crash the server.", "Warning", "Continue", "Abort") + if(confirm != "Continue") + return + + + + var/list/names = list() + for (var/i in 1 to L.len) + var/key = L[i] + var/value + if (IS_NORMAL_LIST(L) && !isnum(key)) + value = L[key] + if (value == null) + value = "null" + names["#[i] [key] = [value]"] = i + if (!index) + var/variable = input("Which var?","Var") as null|anything in names + "(ADD VAR)" + "(CLEAR NULLS)" + "(CLEAR DUPES)" + "(SHUFFLE)" + + if(variable == null) + return + + if(variable == "(ADD VAR)") + mod_list_add(L, O, original_name, objectvar) + return + + if(variable == "(CLEAR NULLS)") + L = L.Copy() + listclearnulls(L) + if (!O.vv_edit_var(objectvar, L)) + to_chat(src, "Your edit was rejected by the object.") + return + log_world("### ListVarEdit by [src]: [O.type] [objectvar]: CLEAR NULLS") + log_admin("[key_name(src)] modified [original_name]'s [objectvar]: CLEAR NULLS") + message_admins("[key_name_admin(src)] modified [original_name]'s list [objectvar]: CLEAR NULLS") + return + + if(variable == "(CLEAR DUPES)") + L = uniqueList(L) + if (!O.vv_edit_var(objectvar, L)) + to_chat(src, "Your edit was rejected by the object.") + return + log_world("### ListVarEdit by [src]: [O.type] [objectvar]: CLEAR DUPES") + log_admin("[key_name(src)] modified [original_name]'s [objectvar]: CLEAR DUPES") + message_admins("[key_name_admin(src)] modified [original_name]'s list [objectvar]: CLEAR DUPES") + return + + if(variable == "(SHUFFLE)") + L = shuffle(L) + if (!O.vv_edit_var(objectvar, L)) + to_chat(src, "Your edit was rejected by the object.") + return + log_world("### ListVarEdit by [src]: [O.type] [objectvar]: SHUFFLE") + log_admin("[key_name(src)] modified [original_name]'s [objectvar]: SHUFFLE") + message_admins("[key_name_admin(src)] modified [original_name]'s list [objectvar]: SHUFFLE") + return + + index = names[variable] + + + var/assoc_key + if (index == null) + return + var/assoc = 0 + var/prompt = alert(src, "Do you want to edit the key or its assigned value?", "Associated List", "Key", "Assigned Value", "Cancel") + if (prompt == "Cancel") + return + if (prompt == "Assigned Value") + assoc = 1 + assoc_key = L[index] + var/default + var/variable + if (assoc) + variable = L[assoc_key] + else + variable = L[index] + + default = vv_get_class(objectvar, variable) + + to_chat(src, "Variable appears to be [uppertext(default)].") + + to_chat(src, "Variable contains: [variable]") + + if(default == VV_NUM) + var/dir_text = "" + var/tdir = variable + if(tdir > 0 && tdir < 16) + if(tdir & 1) + dir_text += "NORTH" + if(tdir & 2) + dir_text += "SOUTH" + if(tdir & 4) + dir_text += "EAST" + if(tdir & 8) + dir_text += "WEST" + + if(dir_text) + to_chat(usr, "If a direction, direction is: [dir_text]") + + var/original_var = variable + + if (O) + L = L.Copy() + var/class + if(autodetect_class) + if (default == VV_TEXT) + default = VV_MESSAGE + class = default + var/list/LL = vv_get_value(default_class = default, current_value = original_var, restricted_classes = list(VV_RESTORE_DEFAULT), extra_classes = list(VV_LIST, "DELETE FROM LIST")) + class = LL["class"] + if (!class) + return + var/new_var = LL["value"] + + if(class == VV_MESSAGE) + class = VV_TEXT + + switch(class) //Spits a runtime error if you try to modify an entry in the contents list. Dunno how to fix it, yet. + if(VV_LIST) + mod_list(variable, O, original_name, objectvar) + + if("DELETE FROM LIST") + L.Cut(index, index+1) + if (O) + if (O.vv_edit_var(objectvar, L)) + to_chat(src, "Your edit was rejected by the object.") + return + log_world("### ListVarEdit by [src]: [O.type] [objectvar]: REMOVED=[html_encode("[original_var]")]") + log_admin("[key_name(src)] modified [original_name]'s [objectvar]: REMOVED=[original_var]") + message_admins("[key_name_admin(src)] modified [original_name]'s [objectvar]: REMOVED=[original_var]") + return + + if(VV_TEXT) + var/list/varsvars = vv_parse_text(O, new_var) + for(var/V in varsvars) + new_var = replacetext(new_var,"\[[V]]","[O.vars[V]]") + + + if(assoc) + L[assoc_key] = new_var + else + L[index] = new_var + if (O) + if (O.vv_edit_var(objectvar, L) == FALSE) + to_chat(src, "Your edit was rejected by the object.") + return + log_world("### ListVarEdit by [src]: [(O ? O.type : "/list")] [objectvar]: [original_var]=[new_var]") + log_admin("[key_name(src)] modified [original_name]'s [objectvar]: [original_var]=[new_var]") + message_admins("[key_name_admin(src)] modified [original_name]'s varlist [objectvar]: [original_var]=[new_var]") + +/proc/vv_varname_lockcheck(param_var_name) + if(param_var_name in GLOB.VVlocked) + if(!check_rights(R_DEBUG)) + return FALSE + if(param_var_name in GLOB.VVckey_edit) + if(!check_rights(R_SPAWN|R_DEBUG)) + return FALSE + if(param_var_name in GLOB.VVicon_edit_lock) + if(!check_rights(R_FUN|R_DEBUG)) + return FALSE + if(param_var_name in GLOB.VVpixelmovement) + if(!check_rights(R_DEBUG)) + return FALSE + var/prompt = alert(usr, "Editing this var may irreparably break tile gliding for the rest of the round. THIS CAN'T BE UNDONE", "DANGER", "ABORT ", "Continue", " ABORT") + if (prompt != "Continue") + return FALSE + return TRUE + + +/client/proc/modify_variables(atom/O, param_var_name = null, autodetect_class = 0) + if(!check_rights(R_VAREDIT)) + return + + var/class + var/variable + var/var_value + + if(param_var_name) + if(!param_var_name in O.vars) + to_chat(src, "A variable with this name ([param_var_name]) doesn't exist in this datum ([O])") + return + variable = param_var_name + + else + var/list/names = list() + for (var/V in O.vars) + names += V + + names = sortList(names) + + variable = input("Which var?","Var") as null|anything in names + if(!variable) + return + + if(!O.can_vv_get(variable)) + return + + var_value = O.vars[variable] + if(!vv_varname_lockcheck(variable)) + return + if(istype(O, /datum/armor)) + var/prompt = alert(src, "Editing this var changes this value on potentially thousands of items that share the same combination of armor values. If you want to edit the armor of just one item, use the \"Modify armor values\" dropdown item", "DANGER", "ABORT ", "Continue", " ABORT") + if (prompt != "Continue") + return + + + var/default = vv_get_class(variable, var_value) + + if(isnull(default)) + to_chat(src, "Unable to determine variable type.") + else + to_chat(src, "Variable appears to be [uppertext(default)].") + + to_chat(src, "Variable contains: [var_value]") + + if(default == VV_NUM) + var/dir_text = "" + if(var_value > 0 && var_value < 16) + if(var_value & 1) + dir_text += "NORTH" + if(var_value & 2) + dir_text += "SOUTH" + if(var_value & 4) + dir_text += "EAST" + if(var_value & 8) + dir_text += "WEST" + + if(dir_text) + to_chat(src, "If a direction, direction is: [dir_text]") + + if(autodetect_class && default != VV_NULL) + if (default == VV_TEXT) + default = VV_MESSAGE + class = default + + var/list/value = vv_get_value(class, default, var_value, extra_classes = list(VV_LIST), var_name = variable) + class = value["class"] + + if (!class) + return + var/var_new = value["value"] + + if(class == VV_MESSAGE) + class = VV_TEXT + + var/original_name = "[O]" + + switch(class) + if(VV_LIST) + if(!islist(var_value)) + mod_list(list(), O, original_name, variable) + + mod_list(var_value, O, original_name, variable) + return + + if(VV_RESTORE_DEFAULT) + var_new = initial(O.vars[variable]) + + if(VV_TEXT) + var/list/varsvars = vv_parse_text(O, var_new) + for(var/V in varsvars) + var_new = replacetext(var_new,"\[[V]]","[O.vars[V]]") + + + if (O.vv_edit_var(variable, var_new) == FALSE) + to_chat(src, "Your edit was rejected by the object.") + return + vv_update_display(O, "varedited", VV_MSG_EDITED) + SEND_GLOBAL_SIGNAL(COMSIG_GLOB_VAR_EDIT, args) + log_world("### VarEdit by [key_name(src)]: [O.type] [variable]=[var_value] => [var_new]") + log_admin("[key_name(src)] modified [original_name]'s [variable] from [html_encode("[var_value]")] to [html_encode("[var_new]")]") + var/msg = "[key_name_admin(src)] modified [original_name]'s [variable] from [var_value] to [var_new]" + message_admins(msg) + admin_ticket_log(O, msg) return TRUE \ No newline at end of file diff --git a/tools/Redirector/textprocs.dm b/tools/Redirector/textprocs.dm index bd10fc3c..aedafe39 100644 --- a/tools/Redirector/textprocs.dm +++ b/tools/Redirector/textprocs.dm @@ -1,153 +1,153 @@ -/* - Written by contributor Doohl for the /tg/station Open Source project, hosted on Google Code. - (2012) - - NOTE: The below functions are part of BYOND user Deadron's "TextHandling" library. - [ http://www.byond.com/developer/Deadron/TextHandling ] - */ - - -proc - /////////////////// - // Reading files // - /////////////////// - dd_file2list(file_path, separator = "\n") - var/file - if (isfile(file_path)) - file = file_path - else - file = file(file_path) - return dd_text2list(file2text(file), separator) - - - //////////////////// - // Replacing text // - //////////////////// - dd_replacetext(text, search_string, replacement_string) - // A nice way to do this is to split the text into an array based on the search_string, - // then put it back together into text using replacement_string as the new separator. - var/list/textList = dd_text2list(text, search_string) - return dd_list2text(textList, replacement_string) - - - dd_replaceText(text, search_string, replacement_string) - var/list/textList = dd_text2List(text, search_string) - return dd_list2text(textList, replacement_string) - - - ///////////////////// - // Prefix checking // - ///////////////////// - dd_hasprefix(text, prefix) - var/start = 1 - var/end = lentext(prefix) + 1 - return findtext(text, prefix, start, end) - - dd_hasPrefix(text, prefix) - var/start = 1 - var/end = lentext(prefix) + 1 - return findtextEx(text, prefix, start, end) - - - ///////////////////// - // Suffix checking // - ///////////////////// - dd_hassuffix(text, suffix) - var/start = length(text) - length(suffix) - if (start) - return findtext(text, suffix, start) - - dd_hasSuffix(text, suffix) - var/start = length(text) - length(suffix) - if (start) - return findtextEx(text, suffix, start) - - ///////////////////////////// - // Turning text into lists // - ///////////////////////////// - dd_text2list(text, separator) - var/textlength = lentext(text) - var/separatorlength = lentext(separator) - var/list/textList = new /list() - var/searchPosition = 1 - var/findPosition = 1 - var/buggyText - while (1) // Loop forever. - findPosition = findtext(text, separator, searchPosition, 0) - buggyText = copytext(text, searchPosition, findPosition) // Everything from searchPosition to findPosition goes into a list element. - textList += "[buggyText]" // Working around weird problem where "text" != "text" after this copytext(). - - searchPosition = findPosition + separatorlength // Skip over separator. - if (findPosition == 0) // Didn't find anything at end of string so stop here. - return textList - else - if (searchPosition > textlength) // Found separator at very end of string. - textList += "" // So add empty element. - return textList - - dd_text2List(text, separator) - var/textlength = lentext(text) - var/separatorlength = lentext(separator) - var/list/textList = new /list() - var/searchPosition = 1 - var/findPosition = 1 - var/buggyText - while (1) // Loop forever. - findPosition = findtextEx(text, separator, searchPosition, 0) - buggyText = copytext(text, searchPosition, findPosition) // Everything from searchPosition to findPosition goes into a list element. - textList += "[buggyText]" // Working around weird problem where "text" != "text" after this copytext(). - - searchPosition = findPosition + separatorlength // Skip over separator. - if (findPosition == 0) // Didn't find anything at end of string so stop here. - return textList - else - if (searchPosition > textlength) // Found separator at very end of string. - textList += "" // So add empty element. - return textList - - dd_list2text(list/the_list, separator) - var/total = the_list.len - if (total == 0) // Nothing to work with. - return - - var/newText = "[the_list[1]]" // Treats any object/number as text also. - var/count - for (count = 2, count <= total, count++) - if (separator) - newText += separator - newText += "[the_list[count]]" - return newText - - dd_centertext(message, length) - var/new_message = message - var/size = length(message) - if (size == length) - return new_message - if (size > length) - return copytext(new_message, 1, length + 1) - - // Need to pad text to center it. - var/delta = length - size - if (delta == 1) - // Add one space after it. - return new_message + " " - - // Is this an odd number? If so, add extra space to front. - if (delta % 2) - new_message = " " + new_message - delta-- - - // Divide delta in 2, add those spaces to both ends. - delta = delta / 2 - var/spaces = "" - for (var/count = 1, count <= delta, count++) - spaces += " " - return spaces + new_message + spaces - - dd_limittext(message, length) - // Truncates text to limit if necessary. - var/size = length(message) - if (size <= length) - return message - else +/* + Written by contributor Doohl for the /tg/station Open Source project, hosted on Google Code. + (2012) + + NOTE: The below functions are part of BYOND user Deadron's "TextHandling" library. + [ http://www.byond.com/developer/Deadron/TextHandling ] + */ + + +proc + /////////////////// + // Reading files // + /////////////////// + dd_file2list(file_path, separator = "\n") + var/file + if (isfile(file_path)) + file = file_path + else + file = file(file_path) + return dd_text2list(file2text(file), separator) + + + //////////////////// + // Replacing text // + //////////////////// + dd_replacetext(text, search_string, replacement_string) + // A nice way to do this is to split the text into an array based on the search_string, + // then put it back together into text using replacement_string as the new separator. + var/list/textList = dd_text2list(text, search_string) + return dd_list2text(textList, replacement_string) + + + dd_replaceText(text, search_string, replacement_string) + var/list/textList = dd_text2List(text, search_string) + return dd_list2text(textList, replacement_string) + + + ///////////////////// + // Prefix checking // + ///////////////////// + dd_hasprefix(text, prefix) + var/start = 1 + var/end = length(prefix) + 1 + return findtext(text, prefix, start, end) + + dd_hasPrefix(text, prefix) + var/start = 1 + var/end = length(prefix) + 1 + return findtextEx(text, prefix, start, end) + + + ///////////////////// + // Suffix checking // + ///////////////////// + dd_hassuffix(text, suffix) + var/start = length(text) - length(suffix) + if (start) + return findtext(text, suffix, start) + + dd_hasSuffix(text, suffix) + var/start = length(text) - length(suffix) + if (start) + return findtextEx(text, suffix, start) + + ///////////////////////////// + // Turning text into lists // + ///////////////////////////// + dd_text2list(text, separator) + var/textlength = length(text) + var/separatorlength = length(separator) + var/list/textList = new /list() + var/searchPosition = 1 + var/findPosition = 1 + var/buggyText + while (1) // Loop forever. + findPosition = findtext(text, separator, searchPosition, 0) + buggyText = copytext(text, searchPosition, findPosition) // Everything from searchPosition to findPosition goes into a list element. + textList += "[buggyText]" // Working around weird problem where "text" != "text" after this copytext(). + + searchPosition = findPosition + separatorlength // Skip over separator. + if (findPosition == 0) // Didn't find anything at end of string so stop here. + return textList + else + if (searchPosition > textlength) // Found separator at very end of string. + textList += "" // So add empty element. + return textList + + dd_text2List(text, separator) + var/textlength = length(text) + var/separatorlength = length(separator) + var/list/textList = new /list() + var/searchPosition = 1 + var/findPosition = 1 + var/buggyText + while (1) // Loop forever. + findPosition = findtextEx(text, separator, searchPosition, 0) + buggyText = copytext(text, searchPosition, findPosition) // Everything from searchPosition to findPosition goes into a list element. + textList += "[buggyText]" // Working around weird problem where "text" != "text" after this copytext(). + + searchPosition = findPosition + separatorlength // Skip over separator. + if (findPosition == 0) // Didn't find anything at end of string so stop here. + return textList + else + if (searchPosition > textlength) // Found separator at very end of string. + textList += "" // So add empty element. + return textList + + dd_list2text(list/the_list, separator) + var/total = the_list.len + if (total == 0) // Nothing to work with. + return + + var/newText = "[the_list[1]]" // Treats any object/number as text also. + var/count + for (count = 2, count <= total, count++) + if (separator) + newText += separator + newText += "[the_list[count]]" + return newText + + dd_centertext(message, length) + var/new_message = message + var/size = length(message) + if (size == length) + return new_message + if (size > length) + return copytext(new_message, 1, length + 1) + + // Need to pad text to center it. + var/delta = length - size + if (delta == 1) + // Add one space after it. + return new_message + " " + + // Is this an odd number? If so, add extra space to front. + if (delta % 2) + new_message = " " + new_message + delta-- + + // Divide delta in 2, add those spaces to both ends. + delta = delta / 2 + var/spaces = "" + for (var/count = 1, count <= delta, count++) + spaces += " " + return spaces + new_message + spaces + + dd_limittext(message, length) + // Truncates text to limit if necessary. + var/size = length(message) + if (size <= length) + return message + else return copytext(message, 1, length + 1) \ No newline at end of file