GLOBAL_VAR_INIT(OOC_COLOR, null)//If this is null, use the CSS for OOC. Otherwise, use a custom colour. GLOBAL_VAR_INIT(normal_ooc_colour, "#002eb8") ///talking in OOC uses this /client/verb/ooc(msg as text) set name = VERB_OOC if(GLOB.say_disabled) //This is here to try to identify lag problems to_chat(usr, span_danger("Speech is currently admin-disabled.")) return var/client_initalized = VALIDATE_CLIENT_INITIALIZATION(src) if(isnull(mob) || !client_initalized) if(!client_initalized) unvalidated_client_error() // we only want to throw this warning message when it's directly related to client failure. to_chat(usr, span_warning("Failed to send your OOC message. You attempted to send the following message:\n[span_big(msg)]")) return if(isnull(holder)) if(!GLOB.ooc_allowed) to_chat(src, span_danger("OOC is globally muted.")) return if(!GLOB.dooc_allowed && (mob.stat == DEAD)) to_chat(usr, span_danger("OOC for dead mobs has been turned off.")) return if(prefs.muted & MUTE_OOC) to_chat(src, span_danger("You cannot use OOC (muted).")) return if(is_banned_from(ckey, "OOC")) to_chat(src, span_danger("You have been banned from OOC.")) return if(QDELETED(src)) return msg = trim(copytext_char(sanitize(msg), 1, MAX_MESSAGE_LEN)) var/raw_msg = msg var/list/filter_result = is_ooc_filtered(msg) if (!CAN_BYPASS_FILTER(usr) && filter_result) REPORT_CHAT_FILTER_TO_USER(usr, filter_result) log_filter("OOC", msg, filter_result) return // Protect filter bypassers from themselves. // Demote hard filter results to soft filter results if necessary due to the danger of accidentally speaking in OOC. var/list/soft_filter_result = filter_result || is_soft_ooc_filtered(msg) if (soft_filter_result) if(tgui_alert(usr,"Your message contains \"[soft_filter_result[CHAT_FILTER_INDEX_WORD]]\". \"[soft_filter_result[CHAT_FILTER_INDEX_REASON]]\", Are you sure you want to say it?", "Soft Blocked Word", list("Yes", "No")) != "Yes") return message_admins("[ADMIN_LOOKUPFLW(usr)] has passed the soft filter for \"[soft_filter_result[CHAT_FILTER_INDEX_WORD]]\" they may be using a disallowed term. Message: \"[html_encode(msg)]\"") log_admin_private("[key_name(usr)] has passed the soft filter for \"[soft_filter_result[CHAT_FILTER_INDEX_WORD]]\" they may be using a disallowed term. Message: \"[msg]\"") if(!msg) return msg = emoji_parse(msg) if(SSticker.HasRoundStarted() && ((msg[1] in list(".",";",":","#")) || findtext_char(msg, "say", 1, 5))) if(tgui_alert(usr,"Your message \"[raw_msg]\" looks like it was meant for in game communication, say it in OOC?", "Meant for OOC?", list("Yes", "No")) != "Yes") return if(!holder) if(handle_spam_prevention(msg,MUTE_OOC)) return if(findtext(msg, "byond://")) to_chat(src, span_boldannounce("Advertising other servers is not allowed.")) log_admin("[key_name(src)] has attempted to advertise in OOC: [msg]") message_admins("[key_name_admin(src)] has attempted to advertise in OOC: [msg]") return if(!(get_chat_toggles(src) & CHAT_OOC)) to_chat(src, span_danger("You have OOC muted.")) return mob.log_talk(raw_msg, LOG_OOC) var/keyname = key if(CONFIG_GET(flag/enable_cross_server_ooc)) //SKYRAT EDIT ADDITION send_ooc_to_other_server(ckey, msg) //SKYRAT EDIT ADDITION if(prefs.unlock_content) if(prefs.toggles & MEMBER_PUBLIC) keyname = "[icon2html('icons/ui/chat/member_content.dmi', world, "blag")][keyname]" if(prefs.hearted) var/datum/asset/spritesheet_batched/sheet = get_asset_datum(/datum/asset/spritesheet_batched/chat) keyname = "[sheet.icon_tag("emoji-heart")][keyname]" //The linkify span classes and linkify=TRUE below make ooc text get clickable chat href links if you pass in something resembling a url for(var/client/receiver as anything in GLOB.clients) if(!receiver.prefs) // Client being created or deleted. Despite all, this can be null. continue if(!(get_chat_toggles(receiver) & CHAT_OOC)) continue if(holder?.fakekey in receiver.prefs.ignoring) continue var/avoid_highlight = receiver == src if(holder) if(!holder.fakekey || receiver.holder) if(check_rights_for(src, R_ADMIN)) var/ooc_color = ooc_colour ? ooc_colour : prefs.read_preference(/datum/preference/color/ooc_color) to_chat(receiver, span_adminooc("[CONFIG_GET(flag/allow_admin_ooccolor) && ooc_color ? "" :"" ][span_prefix("OOC:")] [keyname][holder.fakekey ? "/([holder.fakekey])" : ""]: [msg]"), avoid_highlighting = avoid_highlight) else to_chat(receiver, span_adminobserverooc(span_prefix("OOC: [keyname][holder.fakekey ? "/([holder.fakekey])" : ""]: [msg]")), avoid_highlighting = avoid_highlight) else if(GLOB.OOC_COLOR) to_chat(receiver, "[span_prefix("OOC:")] [holder.fakekey ? holder.fakekey : key]: [msg]", avoid_highlighting = avoid_highlight) else to_chat(receiver, span_ooc(span_prefix("OOC: [holder.fakekey ? holder.fakekey : key]: [msg]")), avoid_highlighting = avoid_highlight) else if(!(key in receiver.prefs.ignoring)) if(ooc_colour) to_chat(receiver, "[span_prefix("OOC:")] [keyname]: [msg]", avoid_highlighting = avoid_highlight) else if(GLOB.OOC_COLOR) to_chat(receiver, "[span_prefix("OOC:")] [keyname]: [msg]", avoid_highlighting = avoid_highlight) else to_chat(receiver, span_ooc(span_prefix("OOC: [keyname]: [msg]")), avoid_highlighting = avoid_highlight) /proc/toggle_ooc(toggle = null) if(toggle != null) //if we're specifically en/disabling ooc if(toggle != GLOB.ooc_allowed) GLOB.ooc_allowed = toggle else return else //otherwise just toggle it GLOB.ooc_allowed = !GLOB.ooc_allowed to_chat(world, "The OOC channel has been globally [GLOB.ooc_allowed ? "enabled" : "disabled"].") /proc/toggle_dooc(toggle = null) if(toggle != null) if(toggle != GLOB.dooc_allowed) GLOB.dooc_allowed = toggle else return else GLOB.dooc_allowed = !GLOB.dooc_allowed /client/proc/set_ooc() set name = "Set Player OOC Color" set desc = "Modifies player OOC Color" set category = "Server" if(IsAdminAdvancedProcCall()) return ADMIN_VERB(set_ooc_color, R_FUN, "Set Player OOC Color", "Modifies the global OOC color.", ADMIN_CATEGORY_SERVER) var/newColor = tgui_color_picker(user, "Please select the new player OOC color.", "OOC color") if(isnull(newColor)) return var/new_color = sanitize_color(newColor) message_admins("[key_name_admin(user)] has set the players' ooc color to [new_color].") log_admin("[key_name_admin(user)] has set the player ooc color to [new_color].") GLOB.OOC_COLOR = new_color /client/proc/reset_ooc() set name = "Reset Player OOC Color" set desc = "Returns player OOC Color to default" set category = "Server" if(IsAdminAdvancedProcCall()) return ADMIN_VERB(reset_ooc_color, R_FUN, "Reset Player OOC Color", "Returns player OOC color to default.", ADMIN_CATEGORY_SERVER) if(tgui_alert(user, "Are you sure you want to reset the OOC color of all players?", "Reset Player OOC Color", list("Yes", "No")) != "Yes") return message_admins("[key_name_admin(user)] has reset the players' ooc color.") log_admin("[key_name_admin(user)] has reset player ooc color.") GLOB.OOC_COLOR = null //Checks admin notice /client/verb/admin_notice() set name = "Adminnotice" set category = "Admin" set desc = "Check the admin notice if it has been set" if(GLOB.admin_notice) to_chat(src, "[span_boldnotice("Admin Notice:")]\n \t [GLOB.admin_notice]") else to_chat(src, span_notice("There are no admin notices at the moment.")) /client/verb/motd() set name = "MOTD" set category = "OOC" set desc ="Check the Message of the Day" var/motd = global.config.motd if(motd) to_chat(src, "
[motd]
", handle_whitespace=FALSE) else to_chat(src, span_notice("The Message of the Day has not been set.")) /client/proc/self_notes() set name = "View Admin Remarks" set category = "OOC" set desc = "View the notes that admins have written about you" if(!CONFIG_GET(flag/see_own_notes)) to_chat(usr, span_notice("Sorry, that function is not enabled on this server.")) return browse_messages(null, usr.ckey, null, TRUE) /client/proc/self_playtime() set name = "View tracked playtime" set category = "OOC" set desc = "View the amount of playtime for roles the server has tracked." if(!CONFIG_GET(flag/use_exp_tracking)) to_chat(usr, span_notice("Sorry, tracking is currently disabled.")) return new /datum/job_report_menu(src, usr) // Ignore verb /client/verb/select_ignore() set name = "Ignore" set category = "OOC" set desc ="Ignore a player's messages on the OOC channel" // Make a list to choose players from var/list/players = list() // Use keys and fakekeys for the same purpose var/displayed_key = "" // Try to add every player who's online to the list for(var/client/C in GLOB.clients) // Don't add ourself if(C == src) continue // Don't add players we've already ignored if they're not using a fakekey if((C.key in prefs.ignoring) && !C.holder?.fakekey) continue // Don't add players using a fakekey we've already ignored if(C.holder?.fakekey in prefs.ignoring) continue // Use the player's fakekey if they're using one if(C.holder?.fakekey) displayed_key = C.holder.fakekey // Use the player's key if they're not using a fakekey else displayed_key = C.key // Check if both we and the player are ghosts and they're not using a fakekey if(isobserver(mob) && isobserver(C.mob) && !C.holder?.fakekey) // Show us if the player is a ghost or not after their displayed key // Add the player's displayed key to the list players["[displayed_key](ghost)"] = displayed_key // Add the player's displayed key to the list if we or the player aren't a ghost or they're using a fakekey else players[displayed_key] = displayed_key // Check if the list is empty if(!length(players)) // Express that there are no players we can ignore in chat to_chat(src, span_infoplain("There are no other players you can ignore!")) // Stop running return // Sort the list players = sort_list(players) // Request the player to ignore var/selection = tgui_input_list(src, "Select a player", "Ignore", players) // Stop running if we didn't receieve a valid selection if(isnull(selection) || !(selection in players)) return // Store the selected player selection = players[selection] // Check if the selected player is on our ignore list if(selection in prefs.ignoring) // Express that the selected player is already on our ignore list in chat to_chat(src, span_infoplain("You are already ignoring [selection]!")) // Stop running return // Add the selected player to our ignore list prefs.ignoring.Add(selection) // Save our preferences prefs.save_preferences() // Express that we've ignored the selected player in chat to_chat(src, span_infoplain("You are now ignoring [selection] on the OOC channel.")) // Unignore verb /client/verb/select_unignore() set name = "Unignore" set category = "OOC" set desc = "Stop ignoring a player's messages on the OOC channel" // Check if we've ignored any players if(!length(prefs.ignoring)) // Express that we haven't ignored any players in chat to_chat(src, span_infoplain("You haven't ignored any players!")) // Stop running return // Request the player to unignore var/selection = tgui_input_list(src, "Select a player", "Unignore", prefs.ignoring) // Stop running if we didn't receive a selection if(isnull(selection)) return // Check if the selected player is not on our ignore list if(!(selection in prefs.ignoring)) // Express that the selected player is not on our ignore list in chat to_chat(src, span_infoplain("You are not ignoring [selection]!")) // Stop running return // Remove the selected player from our ignore list prefs.ignoring.Remove(selection) // Save our preferences prefs.save_preferences() // Express that we've unignored the selected player in chat to_chat(src, span_infoplain("You are no longer ignoring [selection] on the OOC channel.")) /client/proc/show_previous_roundend_report() set name = "Your Last Round" set category = "OOC" set desc = "View the last round end report you've seen" SSticker.show_roundend_report(src, report_type = PERSONAL_LAST_ROUND) /client/proc/show_servers_last_roundend_report() set name = "Server's Last Round" set category = "OOC" set desc = "View the last round end report from this server" SSticker.show_roundend_report(src, report_type = SERVER_LAST_ROUND) /client/verb/fit_viewport() set name = "Fit Viewport" set category = "OOC" set desc = "Fit the width of the map window to match the viewport" // Fetch aspect ratio var/view_size = getviewsize(view) var/aspect_ratio = view_size[1] / view_size[2] // Calculate desired pixel width using window size and aspect ratio var/list/sizes = params2list(winget(src, "[SKIN_MAINWINDOW_SPLIT];[SKIN_MAPWINDOW]", "size")) // Client closed the window? Some other error? This is unexpected behaviour, let's // CRASH with some info. if(!sizes["[SKIN_MAPWINDOW].size"]) CRASH("sizes does not contain mapwindow.size key. This means a winget failed to return what we wanted. --- sizes var: [sizes] --- sizes length: [length(sizes)]") var/list/map_size = splittext(sizes["[SKIN_MAPWINDOW].size"], "x") var/split_size = splittext(sizes["[SKIN_MAINWINDOW_SPLIT].size"], "x") var/split_width = text2num(split_size[1]) // Window is minimized, we can't get proper data so return to avoid division by 0 if (!split_width) return // Gets the type of zoom we're currently using from our view datum // If it's 0 we do our pixel calculations based off the size of the mapwindow // If it's not, we already know how big we want our window to be, since zoom is the exact pixel ratio of the map var/zoom_value = src.view_size?.zoom || 0 var/desired_width = 0 if(zoom_value) desired_width = round(view_size[1] * zoom_value * ICON_SIZE_X) else // Looks like we expect mapwindow.size to be "ixj" where i and j are numbers. // If we don't get our expected 2 outputs, let's give some useful error info. if(length(map_size) != 2) CRASH("map_size of incorrect length --- map_size var: [map_size] --- map_size length: [length(map_size)]") var/height = text2num(map_size[2]) desired_width = round(height * aspect_ratio) if (text2num(map_size[1]) == desired_width) // Nothing to do return // Avoid auto-resizing the statpanel and chat into nothing. desired_width = min(desired_width, split_width - 300) // Calculate and apply a best estimate // +4 pixels are for the width of the splitter's handle var/pct = 100 * (desired_width + 4) / split_width winset(src, SKIN_MAINWINDOW_SPLIT, "splitter=[pct]") // Apply an ever-lowering offset until we finish or fail var/delta for(var/safety in 1 to 10) var/after_size = winget(src, SKIN_MAPWINDOW, "size") map_size = splittext(after_size, "x") var/got_width = text2num(map_size[1]) if (got_width == desired_width) // success return else if (isnull(delta)) // calculate a probable delta value based on the difference delta = 100 * (desired_width - got_width) / split_width else if ((delta > 0 && got_width > desired_width) || (delta < 0 && got_width < desired_width)) // if we overshot, halve the delta and reverse direction delta = -delta/2 pct += delta winset(src, SKIN_MAINWINDOW_SPLIT, "splitter=[pct]") /// Attempt to automatically fit the viewport, assuming the user wants it /client/proc/attempt_auto_fit_viewport() if (!prefs?.read_preference(/datum/preference/toggle/auto_fit_viewport)) return // No need to attempt to fit the viewport on non-initialized clients as they'll auto-fit viewport right before finishing init if(fully_created) INVOKE_ASYNC(src, VERB_REF(fit_viewport)) /client/verb/policy() set name = "Show Policy" set desc = "Show special server rules related to your current character." set category = "OOC" //Collect keywords var/list/keywords = mob.get_policy_keywords() var/header = get_policy(POLICY_VERB_HEADER) var/list/policytext = list(header) var/anything = FALSE for(var/keyword in keywords) var/p = get_policy(keyword) if(p) policytext += p policytext += "
" anything = TRUE if(!anything) policytext += "No related rules found." var/datum/browser/browser = new(usr, "policy", "Server Policy", 600, 500) browser.set_content(policytext.Join("")) browser.open() /client/verb/fix_stat_panel() set name = "Fix Stat Panel" set hidden = TRUE init_verbs() /client/proc/export_preferences() set name = "Export Preferences" set desc = "Export your current preferences to a file." set category = "OOC" ASSERT(prefs, "User attempted to export preferences while preferences were null!") // what the fuck prefs.savefile.export_json_to_client(usr, ckey) /client/verb/map_vote_tally_count() set name = "Show Map Vote Tallies" set desc = "View the current map vote tally counts." set category = "OOC" to_chat(mob, SSmap_vote.tally_printout) /client/verb/linkforumaccount() set category = "OOC" set name = "Link Forum Account" set desc = "Validates your byond account to your forum account. Required to post on the forums." var/uri = CONFIG_GET(string/forum_link_uri) if(!uri) to_chat(src, span_warning("This feature is disabled.")) return if (!SSdbcore.Connect()) to_chat(src, span_danger("No connection to the database.")) return if (is_guest_key(ckey)) to_chat(src, span_danger("Guests can not link accounts.")) return var/token = generate_account_link_token() var/datum/db_query/query_set_token = SSdbcore.NewQuery("INSERT INTO phpbb.tg_byond_oauth_tokens (`token`, `key`) VALUES (:token, :key)", list("token" = token, "key" = key)) if(!query_set_token.Execute()) to_chat(src, span_danger("Failed to insert account link token into database, please try again later.")) qdel(query_set_token) return qdel(query_set_token) to_chat(src, "Now opening a window to login to your forum account, your account will automatically be linked the moment you log in. If this window doesn't load, Please go to [uri]?token=[token] - This link will expire in 30 minutes.") src << link("[uri]?token=[token]") /client/proc/generate_account_link_token() var/static/entropychain if (!entropychain) if (fexists("data/entropychain.txt")) entropychain = file2text("entropychain.txt") else entropychain = "LOL THERE IS NO ENTROPY #HEATDEATH" else if (prob(rand(1,15))) text2file("data/entropychain.txt", entropychain) var/datum/db_query/query_get_token = SSdbcore.NewQuery("SELECT [random_string()], [random_string()]", list(random_string_args(entropychain), random_string_args(entropychain))) if(!query_get_token.Execute()) to_chat(src, span_danger("Failed to get random string token from database. (Error #1)")) qdel(query_get_token) return if(!query_get_token.NextRow()) to_chat(src, span_danger("Could not locate your token in the database. (Error #2)")) qdel(query_get_token) return entropychain = "[query_get_token.item[2]]" return query_get_token.item[1] /client/proc/random_string() return "SHA2(CONCAT(RAND(),UUID(),?,RAND(),UUID()), 512)" /client/proc/random_string_args(entropychain) return "[entropychain][GUID()][rand()*rand(999999)][world.time][GUID()][rand()*rand(999999)][world.timeofday][GUID()][rand()*rand(999999)][world.realtime][GUID()][rand()*rand(999999)][time2text(world.timeofday)][GUID()][rand()*rand(999999)][world.tick_usage][computer_id][address][ckey][key][GUID()][rand()*rand(999999)]"