diff --git a/code/__DEFINES/chat.dm b/code/__DEFINES/chat.dm index 3626d744a499..edace04ca2ec 100644 --- a/code/__DEFINES/chat.dm +++ b/code/__DEFINES/chat.dm @@ -27,6 +27,7 @@ #define MESSAGE_TYPE_ATTACKLOG "attacklog" #define MESSAGE_TYPE_DEBUG "debug" #define MESSAGE_TYPE_SUBTLE "subtle" // DARKPACK EDIT ADD - SUBTLE +#define MESSAGE_TYPE_MENTOR "mentor" // DARKPACK EDIT ADD - MENTORS /// Max length of chat message in characters #define CHAT_MESSAGE_MAX_LENGTH 110 diff --git a/code/__DEFINES/logging.dm b/code/__DEFINES/logging.dm index 62ee94f072fc..ecaa3226d0bb 100644 --- a/code/__DEFINES/logging.dm +++ b/code/__DEFINES/logging.dm @@ -161,6 +161,7 @@ #define LOG_CATEGORY_GAME_VOTE "game-vote" #define LOG_CATEGORY_GAME_WHISPER "game-whisper" #define LOG_CATEGORY_GAME_GHOST_POLLS "game-ghost-polls" +#define LOG_CATEGORY_GAME_MENTOR "game-mentor" // DARKPACK EDIT ADD - Mentors // HREF categories #define LOG_CATEGORY_HREF "href" diff --git a/code/__DEFINES/span.dm b/code/__DEFINES/span.dm index fe70a5366b76..d6fe98f9572f 100644 --- a/code/__DEFINES/span.dm +++ b/code/__DEFINES/span.dm @@ -91,6 +91,8 @@ #define span_memo(str) ("" + str + "") #define span_memoedit(str) ("" + str + "") #define span_policy(str) ("" + str + "") +#define span_mentor(str) ("" + str + "") // DARKPACK EDIT ADD - MENTOR +#define span_mentornotice(str) ("" + str + "") // DARKPACK EDIT ADD - MENTOR #define span_message(str) ("" + str + "") #define span_mind_control(str) ("" + str + "") #define span_minorannounce(str) ("" + str + "") diff --git a/code/game/world.dm b/code/game/world.dm index a243dd09e383..e07668eb6547 100644 --- a/code/game/world.dm +++ b/code/game/world.dm @@ -164,7 +164,7 @@ GLOBAL_VAR(restart_counter) SetupLogs() load_admins(initial = TRUE) - + load_mentors(initial = TRUE) // DARKPACK EDIT ADD - Mentors load_poll_data() // Initialize RETA system - code/modules/reta/reta_system.dm diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm index 7d4b20ee66df..1da97852223c 100644 --- a/code/modules/client/client_procs.dm +++ b/code/modules/client/client_procs.dm @@ -38,6 +38,11 @@ GLOBAL_LIST_INIT(unrecommended_builds, list( if(!usr || usr != mob) //stops us calling Topic for somebody else's client. Also helps prevent usr=null return + // DARKPACK EDIT ADD BEGIN - MENTOR + if(mentor_client_procs(href_list)) + return + // DARKPACK EDIT ADD END + #ifndef TESTING if (LOWER_TEXT(hsrc_command) == "_debug") //disable the integrated byond vv in the client side debugging tools since it doesn't respect vv read protections return diff --git a/config/admin_ranks.txt b/config/admin_ranks.txt index e09a3b570974..c3215410ef78 100644 --- a/config/admin_ranks.txt +++ b/config/admin_ranks.txt @@ -83,3 +83,10 @@ Name = Coder Include = DEBUG VAREDIT SERVER SPAWN POLL Exclude = AUTOADMIN Edit = + +// DARKPACK EDIT ADD START - Mentors +Name = Mentor +Include = +Exclude = AUTOADMIN +Edit = +// DARKPACK EDIT ADD END - Mentors diff --git a/config/darkpack_config.txt b/config/darkpack_config.txt index bd565bc978fd..5e8029099242 100644 --- a/config/darkpack_config.txt +++ b/config/darkpack_config.txt @@ -1,3 +1,11 @@ +## Defines whether or not mentors can see ckeys alongside mobnames. +## uncomment to hide ckeys from mentors +#MENTORS_MOBNAME_ONLY + +## Defines whether the server uses the legacy mentor system with mentors.txt or the SQL system. +## uncomment to use mentors.txt instead of SQL +#MENTOR_LEGACY_SYSTEM + ## Uncomment to use Requiem jump windup #JUMP_WINDUP ## Comment to remove Apocrypha post-jump slowdown diff --git a/modular_darkpack/master_files/code/controllers/configuration/entries/darkpack_config_entries.dm b/modular_darkpack/master_files/code/controllers/configuration/entries/darkpack_config_entries.dm new file mode 100644 index 000000000000..4f39472d1b55 --- /dev/null +++ b/modular_darkpack/master_files/code/controllers/configuration/entries/darkpack_config_entries.dm @@ -0,0 +1,6 @@ +/// Defines whether or not mentors can see ckeys alongside mobnames. +/datum/config_entry/flag/mentors_mobname_only + +/// Defines whether the server uses the legacy mentor system with mentors.txt or the SQL system. +/datum/config_entry/flag/mentor_legacy_system + protection = CONFIG_ENTRY_LOCKED diff --git a/modular_darkpack/master_files/code/modules/client/preferences/_admin.dm b/modular_darkpack/master_files/code/modules/client/preferences/_admin.dm new file mode 100644 index 000000000000..3720993e1c5e --- /dev/null +++ b/modular_darkpack/master_files/code/modules/client/preferences/_admin.dm @@ -0,0 +1,8 @@ +/datum/preference/toggle/admin + abstract_type = /datum/preference/toggle/admin + +/datum/preference/toggle/admin/is_accessible(datum/preferences/preferences) + if (!..(preferences)) + return FALSE + + return is_admin(preferences.parent) diff --git a/modular_darkpack/master_files/code/modules/client/preferences/auto_dementor.dm b/modular_darkpack/master_files/code/modules/client/preferences/auto_dementor.dm new file mode 100644 index 000000000000..5268db765597 --- /dev/null +++ b/modular_darkpack/master_files/code/modules/client/preferences/auto_dementor.dm @@ -0,0 +1,5 @@ +/datum/preference/toggle/admin/auto_dementor + category = PREFERENCE_CATEGORY_GAME_PREFERENCES + savefile_key = "auto_dementor_pref" + savefile_identifier = PREFERENCE_PLAYER + default_value = FALSE // We want people to not automatically dementor by default, otherwise they just don't know about the fact they're mentors. diff --git a/modular_darkpack/master_files/code/modules/logging/categories/log_categories_game.dm b/modular_darkpack/master_files/code/modules/logging/categories/log_categories_game.dm new file mode 100644 index 000000000000..fbae2fe2d25f --- /dev/null +++ b/modular_darkpack/master_files/code/modules/logging/categories/log_categories_game.dm @@ -0,0 +1,4 @@ +/datum/log_category/game_mentor + category = LOG_CATEGORY_GAME_MENTOR + master_category = /datum/log_category/game + diff --git a/modular_darkpack/modules/mentor/code/_globalvars.dm b/modular_darkpack/modules/mentor/code/_globalvars.dm new file mode 100644 index 000000000000..6a1ce0c19d98 --- /dev/null +++ b/modular_darkpack/modules/mentor/code/_globalvars.dm @@ -0,0 +1,3 @@ +//all clients whom are mentors +GLOBAL_LIST_EMPTY(mentors) +GLOBAL_PROTECT(mentors) diff --git a/modular_darkpack/modules/mentor/code/client_procs.dm b/modular_darkpack/modules/mentor/code/client_procs.dm new file mode 100644 index 000000000000..2e64191f0f6a --- /dev/null +++ b/modular_darkpack/modules/mentor/code/client_procs.dm @@ -0,0 +1,43 @@ +/client/New() + . = ..() + mentor_datum_set() + +/client/Destroy() + if(GLOB.mentors[src]) + GLOB.mentors -= src + + return ..() + +/client/proc/mentor_client_procs(href_list) + if(href_list["mentor_msg"]) + if(CONFIG_GET(flag/mentors_mobname_only)) + var/mob/M = locate(href_list["mentor_msg"]) + cmd_mentor_pm(M,null) + else + cmd_mentor_pm(href_list["mentor_msg"],null) + return TRUE + +/client/proc/mentor_datum_set() + mentor_datum = GLOB.mentor_datums[ckey] + if(!mentor_datum && is_admin(src)) // admin with no mentor datum? let's fix that + new /datum/mentors(ckey) + + if(mentor_datum) + mentor_datum.owner = src + GLOB.mentors[src] = TRUE + add_mentor_verbs() + + if(check_rights_for(src, R_ADMIN) && prefs.read_preference(/datum/preference/toggle/admin/auto_dementor)) + cmd_mentor_dementor() + + +/** + * Returns whether or not the user is qualified as a mentor. + * + * Arguments: + * * admin_bypass - Whether or not admins can succeed this check, even if they + * do not actually possess the role. Defaults to `TRUE`. + */ +/client/proc/is_mentor(admin_bypass = TRUE) + if(mentor_datum || (admin_bypass && check_rights_for(src, R_ADMIN))) + return TRUE diff --git a/modular_darkpack/modules/mentor/code/dementor.dm b/modular_darkpack/modules/mentor/code/dementor.dm new file mode 100644 index 000000000000..075f8d6ad1c5 --- /dev/null +++ b/modular_darkpack/modules/mentor/code/dementor.dm @@ -0,0 +1,22 @@ +/client/proc/cmd_mentor_dementor() + set category = "Mentor" + set name = "dementor" + if(!is_mentor()) + return + remove_mentor_verbs() + GLOB.mentors -= src + to_chat(src, span_interface("You are no longer a mentor.")) + log_mentor("MENTOR: [src] dementored.") + add_verb(src,/client/proc/cmd_mentor_rementor) + +/client/proc/cmd_mentor_rementor() + set category = "Mentor" + set name = "rementor" + if(!is_mentor()) + return + add_mentor_verbs() + GLOB.mentors[src] = TRUE + to_chat(src, span_interface("You are now a mentor.")) + log_mentor("MENTOR: [src] rementored.") + remove_verb(src,/client/proc/cmd_mentor_rementor) + diff --git a/modular_darkpack/modules/mentor/code/logging.dm b/modular_darkpack/modules/mentor/code/logging.dm new file mode 100644 index 000000000000..fe6f7142adcc --- /dev/null +++ b/modular_darkpack/modules/mentor/code/logging.dm @@ -0,0 +1,15 @@ +GLOBAL_LIST_EMPTY(mentorlog) +GLOBAL_PROTECT(mentorlog) + +/proc/log_mentor(text, list/data) + GLOB.mentorlog.Add(text) + logger.Log(LOG_CATEGORY_GAME_MENTOR, text, data) + +/datum/admins/proc/MentorLogSecret() + var/dat = "Mentor Log
" + for(var/l in GLOB.mentorlog) + dat += "
  • [l]
  • " + + if(!GLOB.mentorlog.len) + dat += "No mentors have done anything this round!" + usr << browse(dat, "window=mentor_log") diff --git a/modular_darkpack/modules/mentor/code/mentor.dm b/modular_darkpack/modules/mentor/code/mentor.dm new file mode 100644 index 000000000000..232254e6114c --- /dev/null +++ b/modular_darkpack/modules/mentor/code/mentor.dm @@ -0,0 +1,123 @@ +GLOBAL_LIST_EMPTY(mentor_datums) +GLOBAL_PROTECT(mentor_datums) + +GLOBAL_VAR_INIT(mentor_href_token, GenerateToken()) +GLOBAL_PROTECT(mentor_href_token) + +/datum/mentors + var/name = "someone's mentor datum" + var/client/owner // the actual mentor, client type + var/target // the mentor's ckey + var/href_token // href token for mentor commands, uses the same token used by admins. + +/datum/mentors/New(ckey) + if(!ckey) + QDEL_IN(src, 0) + CRASH("Mentor datum created without a ckey") + target = ckey(ckey) + name = "[ckey]'s mentor datum" + href_token = GenerateToken() + GLOB.mentor_datums[target] = src + //set the owner var and load commands + owner = GLOB.directory[ckey] + if(owner) + owner.mentor_datum = src + owner.add_mentor_verbs() + if(!check_rights_for(owner, R_ADMIN,0)) // don't add admins to mentor list. + GLOB.mentors[owner] = TRUE + +/datum/mentors/proc/remove_mentor() + if(owner) + owner.remove_mentor_verbs() + GLOB.mentors -= owner + owner.mentor_datum = null + owner = null + log_admin_private("[target] was removed from the rank of mentor.") + GLOB.mentor_datums -= target + qdel(src) + +/datum/mentors/proc/CheckMentorHREF(href, href_list) + var/auth = href_list["mentor_token"] + . = auth && (auth == href_token || auth == GLOB.mentor_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]") + +/proc/RawMentorHrefToken(forceGlobal = FALSE) + var/tok = GLOB.mentor_href_token + if(!forceGlobal && usr) + var/client/C = usr.client + to_chat(world, C) + to_chat(world, usr) + if(!C) + CRASH("No client for HrefToken()!") + var/datum/mentors/holder = C.mentor_datum + if(holder) + tok = holder.href_token + return tok + +/proc/MentorHrefToken(forceGlobal = FALSE) + return "mentor_token=[RawMentorHrefToken(forceGlobal)]" + +/proc/load_mentors(no_update, initial = FALSE) + if(!initial) + if(!global.config.PreConfigReload()) + return + + var/dbfail + if(!CONFIG_GET(flag/mentor_legacy_system) && !SSdbcore.Connect()) + message_admins("Failed to connect to database while loading mentors. Loading from backup.") + log_sql("Failed to connect to database while loading mentors. Loading from backup.") + dbfail = TRUE + + GLOB.mentor_datums.Cut() + for(var/client/C in GLOB.mentors) + C.remove_mentor_verbs() + C.mentor_datum = null + GLOB.mentors.Cut() + + if(CONFIG_GET(flag/mentor_legacy_system)) + var/list/lines = world.file2list("config/mentors.txt") + for(var/line in lines) + if(!length(line)) + continue + if(findtextEx(line, "#", 1, 2)) + continue + new /datum/mentors(line) + return + + if(!dbfail) + var/datum/db_query/query_load_mentors = SSdbcore.NewQuery("SELECT ckey FROM [format_table_name("mentor")]") + if(!query_load_mentors.warn_execute()) + message_admins("Error loading mentors from database. Loading from backup.") + log_sql("Error loading mentors from database. Loading from backup.") + dbfail = TRUE + else + while(query_load_mentors.NextRow()) + var/mentor_ckey = ckey(query_load_mentors.item[1]) + new /datum/mentors(mentor_ckey) + QDEL_NULL(query_load_mentors) + + if(dbfail) + var/backup_file = file2text("data/mentors_backup.json") + if(backup_file == null) + log_world("Unable to locate mentors backup file.") + return + var/list/backup_file_json = json_decode(backup_file) + for(var/backup_mentor_ckey in backup_file_json["mentors"]) + if(GLOB.mentor_datums[ckey(backup_mentor_ckey)]) + continue + new /datum/mentors(ckey(backup_mentor_ckey)) + + return dbfail + + +/client + /// Acts the same way holder does towards admin: it holds the mentor datum. if set, the guy's a mentor. + var/datum/mentors/mentor_datum diff --git a/modular_darkpack/modules/mentor/code/mentor_verbs.dm b/modular_darkpack/modules/mentor/code/mentor_verbs.dm new file mode 100644 index 000000000000..1852dee4c8c6 --- /dev/null +++ b/modular_darkpack/modules/mentor/code/mentor_verbs.dm @@ -0,0 +1,12 @@ +GLOBAL_LIST_INIT(mentor_verbs, list( + /client/proc/cmd_mentor_say, + /client/proc/cmd_mentor_dementor + )) +GLOBAL_PROTECT(mentor_verbs) + +/client/proc/add_mentor_verbs() + if(mentor_datum) + add_verb(src, GLOB.mentor_verbs) + +/client/proc/remove_mentor_verbs() + remove_verb(src, GLOB.mentor_verbs) diff --git a/modular_darkpack/modules/mentor/code/mentorhelp.dm b/modular_darkpack/modules/mentor/code/mentorhelp.dm new file mode 100644 index 000000000000..e174205ed0a5 --- /dev/null +++ b/modular_darkpack/modules/mentor/code/mentorhelp.dm @@ -0,0 +1,103 @@ +/client/verb/mentorhelp(msg as text) + set category = "Mentor" + set name = "Mentorhelp" + + //clean the input msg + if(!msg) + return + + //remove out mentorhelp verb temporarily to prevent spamming of mentors. + remove_verb(src, /client/verb/mentorhelp) + spawn(30 SECONDS) // Gotta love BYOND, god this is disgusting + add_verb(src, /client/verb/mentorhelp) // 30 second cool-down for mentorhelp + + msg = sanitize(copytext_char(msg, 1, MAX_MESSAGE_LEN)) + if(!msg || !mob) + return + + var/show_char = CONFIG_GET(flag/mentors_mobname_only) + var/mentor_msg = span_mentor("MENTORHELP: [key_name_mentor(src, TRUE, FALSE, TRUE, show_char)]: [msg]") + log_mentor("MENTORHELP: [key_name_mentor(src, FALSE, FALSE, FALSE, FALSE)]: [msg]") + + for(var/mentor in GLOB.mentors) + var/client/mentor_client = mentor + if(mentor_client) + SEND_SOUND(mentor_client, 'sound/items/bikehorn.ogg') + to_chat(mentor_client, mentor_msg) + + to_chat(src, span_mentor("PM to-Mentors: [msg]")) + return + +/proc/get_mentor_counts() + . = list("total" = 0, "afk" = 0, "present" = 0) + for(var/mentor in GLOB.mentors) + var/client/mentor_client = mentor + .["total"]++ + if(mentor_client.is_afk()) + .["afk"]++ + else + .["present"]++ + +/proc/key_name_mentor(whom, include_link = null, include_name = FALSE, include_follow = FALSE, char_name_only = FALSE) + var/mob/target_mob + var/client/target_client + var/key + var/ckey + + if(!whom) + return "*null*" + if(istype(whom, /client)) + target_client = whom + target_mob = target_client?.mob + key = target_client?.key + ckey = target_client?.ckey + else if(ismob(whom)) + target_mob = whom + target_client = target_mob.client + key = target_mob.key + ckey = target_mob.ckey + else if(istext(whom)) + key = whom + ckey = ckey(whom) + target_client = GLOB.directory[ckey] + if(target_client) + target_mob = target_client?.mob + else + return "*invalid*" + + . = "" + + if(!ckey) + include_link = FALSE + + if(key) + if(include_link) + if(CONFIG_GET(flag/mentors_mobname_only)) + . += "" + else + . += "" + + if(target_client && target_client?.holder && target_client?.holder.fakekey) + . += "Administrator" + else if (char_name_only && CONFIG_GET(flag/mentors_mobname_only)) + if(istype(target_client?.mob,/mob/dead/new_player) || istype(target_client?.mob, /mob/dead/observer)) //If they're in the lobby or observing, display their ckey + . += key + else if(target_client && target_client?.mob) //If they're playing/in the round, only show the mob name + . += target_client?.mob.name + else //If for some reason neither of those are applicable and they're mentorhelping, show ckey + . += key + else + . += key + if(!target_client) + . += "\[DC\]" + + if(include_link) + . += "" + else + . += "*no key*" + + /* + if(include_follow) + . += " (F)" + */ + return . diff --git a/modular_darkpack/modules/mentor/code/mentorpm.dm b/modular_darkpack/modules/mentor/code/mentorpm.dm new file mode 100644 index 000000000000..75626a0c4cac --- /dev/null +++ b/modular_darkpack/modules/mentor/code/mentorpm.dm @@ -0,0 +1,93 @@ +//shows a list of clients we could send PMs to, then forwards our choice to cmd_Mentor_pm +/client/proc/cmd_mentor_pm_panel() // We're not using this and I'm debating removing the code as it's dead and useless. We don't need mentors PMing people out of the blue. That's not really how we operate. + set category = "Mentor" + set name = "Mentor PM" + if(!is_mentor()) + to_chat(src, span_danger("Error: Mentor-PM-Panel: Only Mentors and Admins may use this command.")) + return + var/list/client/targets[0] + for(var/client/T) // What a cursed proc this is + targets["[T]"] = T + + var/list/sorted = sort_list(targets) + var/target = input(src, "To whom shall we send a message?", "Mentor PM", null) in sorted|null + cmd_mentor_pm(targets[target], null) + SSblackbox.record_feedback("tally", "Mentor_verb", TRUE, "APM") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/** + * Takes input from cmd_mentor_pm_context, cmd_Mentor_pm_panel or /client/Topic and sends them a PM. + * Fetching a message if needed. src is the sender and target is the target client + * + * Arguments: + * * whom - The target of the mentor PM. + * * msg - The content of the mentor PM. + */ +/client/proc/cmd_mentor_pm(whom, msg) + var/client/target + if(ismob(whom)) + var/mob/mob_target = whom + target = mob_target.client + else if(istext(whom)) + target = GLOB.directory[whom] + else if(istype(whom,/client)) + target = whom + if(!target) + if(is_mentor()) + to_chat(src, span_danger("Error: Mentor-PM: Client not found.")) + else + mentorhelp(msg) //Mentor we are replying to left. Mentorhelp instead(check below) + return + + if(is_mentor(whom)) + to_chat(GLOB.mentors, span_purple(span_mentor("[src] has started replying to [whom]'s mhelp."))) + + //get message text, limit it's length.and clean/escape html + if(!msg) + msg = tgui_input_text(src, "Message:", "Private message") + + if(!msg) + if (is_mentor(whom)) + to_chat(GLOB.mentors, span_mentor(span_purple("[src] has stopped their reply to [whom]'s mhelp."))) + return + + if(!target) + if(is_mentor()) + to_chat(src, span_danger("Error: Mentor-PM: Client not found.")) + else + mentorhelp(msg) //Mentor we are replying to has vanished, Mentorhelp instead (how the fuck does this work?let's hope it works,shrug) + return + + // Neither party is a mentor, they shouldn't be PMing! + if (!target.is_mentor() && !is_mentor()) + return + + if(!msg) + if (is_mentor(whom)) + to_chat(GLOB.mentors, span_mentor(span_purple("[src] has stopped their reply to [whom]'s mhelp."))) + return + log_mentor("Mentor PM: [key_name(src)]->[key_name(target)]: [msg]") + + msg = emoji_parse(msg) + SEND_SOUND(target, 'sound/items/bikehorn.ogg') + var/show_char = CONFIG_GET(flag/mentors_mobname_only) + if(target.is_mentor()) + if(is_mentor())//both are mentors + to_chat(target, span_mentor(span_purple("Mentor PM from-[key_name_mentor(src, target, TRUE, FALSE, FALSE)]: [msg]"))) + to_chat(src, span_mentor(span_blue("Mentor PM to-[key_name_mentor(target, target, TRUE, FALSE, FALSE)]: [msg]"))) + + else //recipient is a mentor but sender is not + to_chat(target, span_mentor(span_purple("Reply PM from-[key_name_mentor(src, target, TRUE, FALSE, show_char)]: [msg]"))) + to_chat(src, span_mentor("Mentor PM to-[key_name_mentor(target, target, TRUE, FALSE, FALSE)]: [msg]")) + + else + if(is_mentor()) //sender is a mentor but recipient is not. + to_chat(target, span_mentor(span_purple("Mentor PM from-[key_name_mentor(src, target, TRUE, FALSE, FALSE)]: [msg]"))) + to_chat(src, span_mentor("Mentor PM to-[key_name_mentor(target, target, TRUE, FALSE, show_char)]: [msg]")) + + //we don't use message_Mentors here because the sender/receiver might get it too // We should make it an argument for that proc to ignore the sender, then. :( + var/show_char_sender = !is_mentor() && CONFIG_GET(flag/mentors_mobname_only) + var/show_char_recip = !target.is_mentor() && CONFIG_GET(flag/mentors_mobname_only) + for(var/it in GLOB.mentors) + var/client/mentor = it + if(mentor?.key != key && mentor?.key != target.key) //check client/mentor is an Mentor and isn't the sender or recipient + to_chat(mentor, span_mentor("Mentor PM: [key_name_mentor(src, mentor, FALSE, FALSE, show_char_sender)]->[key_name_mentor(target, mentor, FALSE, FALSE, show_char_recip)]: [span_blue(msg)]")) //inform mentor diff --git a/modular_darkpack/modules/mentor/code/mentorsay.dm b/modular_darkpack/modules/mentor/code/mentorsay.dm new file mode 100644 index 000000000000..77d83d3c9363 --- /dev/null +++ b/modular_darkpack/modules/mentor/code/mentorsay.dm @@ -0,0 +1,19 @@ +/client/proc/cmd_mentor_say(msg as text) + set category = "Mentor" + set name = "Msay" //Gave this shit a shorter name so you only have to time out "msay" rather than "mentor say" to use it --NeoFite + set hidden = 1 + if(!is_mentor()) + return + + msg = copytext_char(sanitize(msg), 1, MAX_MESSAGE_LEN) + if(!msg) + return + + msg = emoji_parse(msg) + log_mentor("MSAY: [key_name(src)] : [msg]") + + if(check_rights_for(src, R_ADMIN,0)) + msg = span_mentor("MENTOR: [key_name(src, 0, 0)]: [msg]") + else + msg = span_mentor("MENTOR: [key_name(src, 0, 0)]: [msg]") + to_chat(GLOB.admins | GLOB.mentors, msg) diff --git a/modular_darkpack/modules/mentor/code/mentorwho.dm b/modular_darkpack/modules/mentor/code/mentorwho.dm new file mode 100644 index 000000000000..efcaa7f759d8 --- /dev/null +++ b/modular_darkpack/modules/mentor/code/mentorwho.dm @@ -0,0 +1,22 @@ +/client/verb/mentorwho() + set category = "Mentor" + set name = "Mentorwho" + var/msg = "Current Mentors:\n" + for(var/X in GLOB.mentors) + var/client/C = X + if(!C) + GLOB.mentors -= C + continue // weird runtime that happens randomly + var/suffix = "" + if(holder) + if(isobserver(C.mob)) + suffix += " - Observing" + else if(istype(C.mob,/mob/dead/new_player)) + suffix += " - Lobby" + else + suffix += " - Playing" + + if(C.is_afk()) + suffix += " (AFK)" + msg += span_infoplain("\t[C][suffix]\n") + to_chat(src, msg) diff --git a/modular_darkpack/modules/mentor/readme.md b/modular_darkpack/modules/mentor/readme.md new file mode 100644 index 000000000000..db0ac70a2648 --- /dev/null +++ b/modular_darkpack/modules/mentor/readme.md @@ -0,0 +1,31 @@ +## Title: Mentor + +MODULE ID: MENTOR + +### Description: + +Adds a mentor system, allowing for players to send a-help like messages to mentors, asking them about game mechanics and help. + +### TG Proc Changes: + +- code/modules/client/client_procs.dm > client/Topic() +- /code/modules/admin/secrets.dm > /datum/admins/proc/Secrets_topic(), /datum/admins/proc/Secrets() + +### Defines: + +- N/A + +### Master file additions + +- N/A + +### Included files that are not contained in this module: + +- `tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/darkpack_auto_dementor.tsx` + +### Credits: + +chazzyjazzy - porting to Darkpack + +Azarak - Porting, tweaks +Poojawa - Implementation diff --git a/tgstation.dme b/tgstation.dme index 7c0a3128bd57..8dbf4a51e5e2 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -6986,6 +6986,7 @@ #include "modular_darkpack\master_files\code\__HELPERS\maths.dm" #include "modular_darkpack\master_files\code\_globalvars\configuration.dm" #include "modular_darkpack\master_files\code\_onclick\hud\new_player.dm" +#include "modular_darkpack\master_files\code\controllers\configuration\entries\darkpack_config_entries.dm" #include "modular_darkpack\master_files\code\controllers\configuration\entries\game_options.dm" #include "modular_darkpack\master_files\code\controllers\configuration\entries\general.dm" #include "modular_darkpack\master_files\code\datums\actions\action.dm" @@ -7018,12 +7019,15 @@ #include "modular_darkpack\master_files\code\game\turfs\open\space\space.dm" #include "modular_darkpack\master_files\code\modules\cargo\expressconsole.dm" #include "modular_darkpack\master_files\code\modules\client\preferences_savefile.dm" +#include "modular_darkpack\master_files\code\modules\client\preferences\_admin.dm" #include "modular_darkpack\master_files\code\modules\client\preferences\_preference.dm" +#include "modular_darkpack\master_files\code\modules\client\preferences\auto_dementor.dm" #include "modular_darkpack\master_files\code\modules\client\preferences\country_of_origin.dm" #include "modular_darkpack\master_files\code\modules\economy\account.dm" #include "modular_darkpack\master_files\code\modules\fishing\sources\subtypes\structures.dm" #include "modular_darkpack\master_files\code\modules\fishing\sources\subtypes\turfs.dm" #include "modular_darkpack\master_files\code\modules\hydroponics\grown.dm" +#include "modular_darkpack\master_files\code\modules\logging\categories\log_categories_game.dm" #include "modular_darkpack\master_files\code\modules\mob\mob_defines.dm" #include "modular_darkpack\master_files\code\modules\mob\mob_helpers.dm" #include "modular_darkpack\master_files\code\modules\mob\dead\observer\observer.dm" @@ -7377,6 +7381,16 @@ #include "modular_darkpack\modules\masquerade\code\subsystem\masquerade.dm" #include "modular_darkpack\modules\matrix\code\job.dm" #include "modular_darkpack\modules\matrix\code\matrix.dm" +#include "modular_darkpack\modules\mentor\code\_globalvars.dm" +#include "modular_darkpack\modules\mentor\code\client_procs.dm" +#include "modular_darkpack\modules\mentor\code\dementor.dm" +#include "modular_darkpack\modules\mentor\code\logging.dm" +#include "modular_darkpack\modules\mentor\code\mentor.dm" +#include "modular_darkpack\modules\mentor\code\mentor_verbs.dm" +#include "modular_darkpack\modules\mentor\code\mentorhelp.dm" +#include "modular_darkpack\modules\mentor\code\mentorpm.dm" +#include "modular_darkpack\modules\mentor\code\mentorsay.dm" +#include "modular_darkpack\modules\mentor\code\mentorwho.dm" #include "modular_darkpack\modules\merits_flaws\code\_darkpack_quirk.dm" #include "modular_darkpack\modules\merits_flaws\code\config.dm" #include "modular_darkpack\modules\merits_flaws\code\negative_quirks\amnesia.dm" diff --git a/tgui/packages/tgui-panel/chat/constants.ts b/tgui/packages/tgui-panel/chat/constants.ts index f910853732b6..6c096b9a309a 100644 --- a/tgui/packages/tgui-panel/chat/constants.ts +++ b/tgui/packages/tgui-panel/chat/constants.ts @@ -30,6 +30,7 @@ export const MESSAGE_TYPE_WARNING = 'warning'; export const MESSAGE_TYPE_DEADCHAT = 'deadchat'; export const MESSAGE_TYPE_OOC = 'ooc'; export const MESSAGE_TYPE_LOOC = 'looc'; // DARKPACK EDIT ADD +export const MESSAGE_TYPE_MENTOR = 'mentor'; // DARKPACK EDIT ADD - MENTOR export const MESSAGE_TYPE_ADMINPM = 'adminpm'; export const MESSAGE_TYPE_COMBAT = 'combat'; export const MESSAGE_TYPE_ADMINCHAT = 'adminchat'; @@ -133,6 +134,12 @@ export const MESSAGE_TYPES: MessageType[] = [ description: 'Subtle and Subtler actions.', selector: '.subtle, .subtler', }, // DARKPACK EDIT ADD END + { // DARKPACK EDIT ADD START - MENTOR + type: MESSAGE_TYPE_MENTOR, + name: 'Mentor Log', + description: 'Mentor PMs and other mentor things.', + selector: '.mentor, .mentornotice', + }, // DARKPACK EDIT ADD END { type: MESSAGE_TYPE_UNKNOWN, name: 'Unsorted', diff --git a/tgui/packages/tgui-panel/styles/tgchat/chat-dark.scss b/tgui/packages/tgui-panel/styles/tgchat/chat-dark.scss index e85ad03be6dd..d7aa5202147e 100644 --- a/tgui/packages/tgui-panel/styles/tgchat/chat-dark.scss +++ b/tgui/packages/tgui-panel/styles/tgchat/chat-dark.scss @@ -1299,6 +1299,10 @@ $border-width-px: $border-width * 1px; /* DARKPACK EDIT ADD START */ +.mentor { + color: #8a2be2; +} + .looc { color: #d8b555; } diff --git a/tgui/packages/tgui-panel/styles/tgchat/chat-light.scss b/tgui/packages/tgui-panel/styles/tgchat/chat-light.scss index 8076e62204f8..9d4284568f8a 100644 --- a/tgui/packages/tgui-panel/styles/tgchat/chat-light.scss +++ b/tgui/packages/tgui-panel/styles/tgchat/chat-light.scss @@ -1280,6 +1280,10 @@ $border-width-px: $border-width * 1px; /* DARKPACK EDIT ADD START */ +.mentor { + color: #8a2be2; +} + .looc { color: #6699cc; font-weight: bold; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/darkpack_auto_dementor.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/darkpack_auto_dementor.tsx new file mode 100644 index 000000000000..f1e08fae6efc --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/darkpack_auto_dementor.tsx @@ -0,0 +1,9 @@ +// THIS IS A DARKPACK UI FILE +import { CheckboxInput, type FeatureToggle } from '../base'; + +export const auto_dementor_pref: FeatureToggle = { + name: 'Auto dementor', + category: 'ADMIN', + description: 'When enabled, you will automatically dementor.', + component: CheckboxInput, +};