diff --git a/code/modules/client/client procs.dm b/code/modules/client/client procs.dm
index 24df219632..b98f707aef 100644
--- a/code/modules/client/client procs.dm
+++ b/code/modules/client/client procs.dm
@@ -178,6 +178,7 @@
GLOB.directory[ckey] = src
GLOB.ahelp_tickets.ClientLogin(src)
+ GLOB.mhelp_tickets.ClientLogin(src)
//Admin Authorisation
holder = admin_datums[ckey]
@@ -263,6 +264,7 @@
holder.owner = null
GLOB.admins -= src
GLOB.ahelp_tickets.ClientLogout(src)
+ GLOB.mhelp_tickets.ClientLogout(src)
GLOB.directory -= ckey
GLOB.clients -= src
return ..()
diff --git a/code/modules/mentor/mentor.dm b/code/modules/mentor/mentor.dm
new file mode 100644
index 0000000000..bf3259d61a
--- /dev/null
+++ b/code/modules/mentor/mentor.dm
@@ -0,0 +1,245 @@
+/client
+ var/datum/mentor/mentorholder = null
+
+var/list/mentor_datums = list()
+
+var/list/mentor_verbs_default = list(
+ /client/proc/cmd_mentor_ticket_panel,
+ /client/proc/cmd_mentor_say,
+ /client/proc/cmd_dementor
+)
+
+/datum/mentor
+ var/client/owner = null
+
+/datum/mentor/New(ckey)
+ if(!ckey)
+ error("Mentor datum created without a ckey argument. Datum has been deleted")
+ qdel(src)
+ return
+ mentor_datums[ckey] = src
+
+/datum/mentor/proc/associate(client/C)
+ if(istype(C))
+ owner = C
+ owner.mentorholder = src
+ owner.add_mentor_verbs()
+ GLOB.mentors |= C
+
+/datum/mentor/proc/disassociate()
+ if(owner)
+ GLOB.mentors -= owner
+ owner.remove_mentor_verbs()
+ owner.mentorholder = null
+ mentor_datums[owner.ckey] = null
+ qdel(src)
+
+/client/proc/add_mentor_verbs()
+ if(mentorholder)
+ verbs += mentor_verbs_default
+
+/client/proc/remove_mentor_verbs()
+ if(mentorholder)
+ verbs -= mentor_verbs_default
+
+/client/proc/make_mentor()
+ set category = "Special Verbs"
+ set name = "Make Mentor"
+ if(!holder)
+ to_chat(src, "Error: Only administrators may use this command.")
+ return
+ var/list/client/targets[0]
+ for(var/client/T in GLOB.clients)
+ targets["[T.key]"] = T
+ var/target = tgui_input_list(src,"Who do you want to make a mentor?","Make Mentor", sortList(targets))
+ if(!target)
+ return
+ var/client/C = targets[target]
+ if(has_mentor_powers(C) || C.deadmin_holder) // If an admin is deadminned you could mentor them and that will cause fuckery if they readmin
+ to_chat(src, "Error: They already have mentor powers.")
+ return
+ var/datum/mentor/M = new /datum/mentor(C.ckey)
+ M.associate(C)
+ to_chat(C, "You have been granted mentorship.")
+ to_chat(src, "You have made [C] a mentor.")
+ log_admin("[key_name(src)] made [key_name(C)] a mentor.")
+ feedback_add_details("admin_verb","Make Mentor") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
+
+/client/proc/unmake_mentor()
+ set category = "Special Verbs"
+ set name = "Unmake Mentor"
+ if(!holder)
+ to_chat(src, "Error: Only administrators may use this command.")
+ return
+ var/list/client/targets[0]
+ for(var/client/T in GLOB.mentors)
+ targets["[T.key]"] = T
+ var/target = tgui_input_list(src,"Which mentor do you want to unmake?","Unmake Mentor", sortList(targets))
+ if(!target)
+ return
+ var/client/C = targets[target]
+ C.mentorholder.disassociate()
+ to_chat(C, "Your mentorship has been revoked.")
+ to_chat(src, "You have revoked [C]'s mentorship.")
+ log_admin("[key_name(src)] revoked [key_name(C)]'s mentorship.")
+ feedback_add_details("admin_verb","Unmake Mentor") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
+
+/client/proc/cmd_mentor_say(msg as text)
+ set category = "Admin"
+ set name ="Mentorsay"
+
+ //check rights
+ if (!has_mentor_powers(src))
+ return
+
+ msg = sanitize(msg)
+ if (!msg)
+ return
+
+ log_admin("Mentorsay: [key_name(src)]: [msg]")
+
+ for(var/client/C in GLOB.mentors)
+ to_chat(C, create_text_tag("mentor", "MENTOR:", C) + " [src]: [msg]")
+ for(var/client/C in GLOB.admins)
+ to_chat(C, create_text_tag("mentor", "MENTOR:", C) + " [src]: [msg]")
+
+/proc/mentor_commands(href, href_list, client/C)
+ if(href_list["mhelp"])
+ var/mhelp_ref = href_list["mhelp"]
+ var/datum/mentor_help/MH = locate(mhelp_ref)
+ if (MH && istype(MH, /datum/mentor_help))
+ MH.Action(href_list["mhelp_action"])
+ else
+ to_chat(C, "Ticket [mhelp_ref] has been deleted!")
+
+ if (href_list["mhelp_tickets"])
+ GLOB.mhelp_tickets.BrowseTickets(text2num(href_list["mhelp_tickets"]))
+
+
+/datum/mentor/Topic(href, href_list)
+ ..()
+ if (usr.client != src.owner || (!usr.client.mentorholder))
+ log_admin("[key_name(usr)] tried to illegally use mentor functions.")
+ message_admins("[usr.key] tried to illegally use mentor functions.")
+ return
+
+ mentor_commands(href, href_list, usr)
+
+/client/proc/cmd_dementor()
+ set category = "Admin"
+ set name = "De-mentor"
+
+ if(tgui_alert(usr, "Confirm self-dementor for the round? You can't re-mentor yourself without someone promoting you.","Dementor",list("Yes","No")) == "Yes")
+ src.mentorholder.disassociate()
+
+/client/proc/cmd_mhelp_reply(whom)
+ if(prefs.muted & MUTE_ADMINHELP)
+ to_chat(src, "Error: Mentor-PM: You are unable to use admin PM-s (muted).")
+ return
+ var/client/C
+ if(istext(whom))
+ C = GLOB.directory[whom]
+ else if(istype(whom,/client))
+ C = whom
+ if(!C)
+ if(has_mentor_powers(src))
+ to_chat(src, "Error: Mentor-PM: Client not found.")
+ return
+
+ var/datum/mentor_help/MH = C.current_mentorhelp
+
+ if(MH)
+ message_mentors("[src] has started replying to [C]'s mentor help.")
+ var/msg = tgui_input_text(src,"Message:", "Private message to [C]")
+ if (!msg)
+ message_mentors("[src] has cancelled their reply to [C]'s mentor help.")
+ return
+ cmd_mentor_pm(whom, msg, MH)
+
+/proc/has_mentor_powers(client/C)
+ return C.holder || C.mentorholder
+
+/client/proc/cmd_mentor_pm(whom, msg, datum/mentor_help/MH)
+ set category = "Admin"
+ set name = "Mentor-PM"
+ set hidden = 1
+
+ if(prefs.muted & MUTE_ADMINHELP)
+ to_chat(src, "Error: Mentor-PM: You are unable to use admin PM-s (muted).")
+ return
+
+ //Not a mentor and no open ticket
+ if(!has_mentor_powers(src) && !current_mentorhelp)
+ to_chat(src, "You can no longer reply to this ticket, please open another one by using the Mentorhelp verb if need be.")
+ to_chat(src, "Message: [msg]")
+ return
+
+ var/client/recipient
+
+ if(istext(whom))
+ recipient = GLOB.directory[whom]
+
+ else if(istype(whom,/client))
+ recipient = whom
+
+ //get message text, limit it's length.and clean/escape html
+ if(!msg)
+ msg = tgui_input_text(src,"Message:", "Mentor-PM to [whom]")
+
+ if(!msg)
+ return
+
+ if(prefs.muted & MUTE_ADMINHELP)
+ to_chat(src, "Error: Mentor-PM: You are unable to use admin PM-s (muted).")
+ return
+
+ if(!recipient)
+ if(has_mentor_powers(src))
+ to_chat(src, "Error:Mentor-PM: Client not found.")
+ to_chat(src, msg)
+ else
+ log_admin("Mentorhelp: [key_name(src)]: [msg]")
+ current_mentorhelp.MessageNoRecipient(msg)
+ return
+
+ //Has mentor powers but the recipient no longer has an open ticket
+ if(has_mentor_powers(src) && !recipient.current_mentorhelp)
+ to_chat(src, "You can no longer reply to this ticket.")
+ to_chat(src, "Message: [msg]")
+ return
+
+ if (src.handle_spam_prevention(msg,MUTE_ADMINHELP))
+ return
+
+ msg = trim(sanitize(copytext(msg,1,MAX_MESSAGE_LEN)))
+ if(!msg)
+ return
+
+ var/interaction_message = "Mentor-PM from-[src] to-[recipient]: [msg]"
+
+ if (recipient.current_mentorhelp && !has_mentor_powers(recipient))
+ recipient.current_mentorhelp.AddInteraction(interaction_message)
+ if (src.current_mentorhelp && !has_mentor_powers(src))
+ src.current_mentorhelp.AddInteraction(interaction_message)
+
+ // It's a little fucky if they're both mentors, but while admins may need to adminhelp I don't really see any reason a mentor would have to mentorhelp since you can literally just ask any other mentors online
+ if (has_mentor_powers(recipient) && has_mentor_powers(src))
+ if (recipient.current_mentorhelp)
+ recipient.current_mentorhelp.AddInteraction(interaction_message)
+ if (src.current_mentorhelp)
+ src.current_mentorhelp.AddInteraction(interaction_message)
+
+ to_chat(recipient, "Mentor-PM from-[src]: [msg]")
+ to_chat(src, "Mentor-PM to-[recipient]: [msg]")
+
+ log_admin("[key_name(src)]->[key_name(recipient)]: [msg]")
+
+ if(recipient.is_preference_enabled(/datum/client_preference/play_mentorhelp_ping))
+ recipient << 'sound/effects/mentorhelp.mp3'
+
+ for(var/client/C in GLOB.mentors)
+ if (C != recipient && C != src)
+ to_chat(C, interaction_message)
+ for(var/client/C in GLOB.admins)
+ if (C != recipient && C != src)
+ to_chat(C, interaction_message)
\ No newline at end of file
diff --git a/code/modules/mentor/mentorhelp.dm b/code/modules/mentor/mentorhelp.dm
new file mode 100644
index 0000000000..e5f7ac49d8
--- /dev/null
+++ b/code/modules/mentor/mentorhelp.dm
@@ -0,0 +1,415 @@
+/client/var/datum/mentor_help/current_mentorhelp
+
+//
+//TICKET MANAGER
+//
+
+GLOBAL_DATUM_INIT(mhelp_tickets, /datum/mentor_help_tickets, new)
+
+/datum/mentor_help_tickets
+ var/list/active_tickets = list()
+ var/list/resolved_tickets = list()
+
+ var/obj/effect/statclick/mticket_list/astatclick = new(null, null, AHELP_ACTIVE)
+ var/obj/effect/statclick/mticket_list/rstatclick = new(null, null, AHELP_RESOLVED)
+
+/datum/mentor_help_tickets/Destroy()
+ QDEL_LIST(active_tickets)
+ QDEL_LIST(resolved_tickets)
+ QDEL_NULL(astatclick)
+ QDEL_NULL(rstatclick)
+ return ..()
+
+//private
+/datum/mentor_help_tickets/proc/ListInsert(datum/mentor_help/new_ticket)
+ var/list/mticket_list
+ switch(new_ticket.state)
+ if(AHELP_ACTIVE)
+ mticket_list = active_tickets
+ if(AHELP_RESOLVED)
+ mticket_list = resolved_tickets
+ else
+ CRASH("Invalid ticket state: [new_ticket.state]")
+ var/num_closed = mticket_list.len
+ if(num_closed)
+ for(var/I in 1 to num_closed)
+ var/datum/mentor_help/MH = mticket_list[I]
+ if(MH.id > new_ticket.id)
+ mticket_list.Insert(I, new_ticket)
+ return
+ mticket_list += new_ticket
+
+//opens the ticket listings, only two states here
+/datum/mentor_help_tickets/proc/BrowseTickets(state)
+ var/list/l2b
+ var/title
+ switch(state)
+ if(AHELP_ACTIVE)
+ l2b = active_tickets
+ title = "Active Tickets"
+ if(AHELP_RESOLVED)
+ l2b = resolved_tickets
+ title = "Resolved Tickets"
+ if(!l2b)
+ return
+ var/list/dat = list("
[title]")
+ dat += "Refresh
"
+ for(var/datum/mentor_help/MH as anything in l2b)
+ dat += "Ticket #[MH.id]: [MH.initiator_ckey]: [MH.name]
"
+
+ usr << browse(dat.Join(), "window=mhelp_list[state];size=600x480")
+
+//Tickets statpanel
+/datum/mentor_help_tickets/proc/stat_entry()
+ var/num_disconnected = 0
+ stat("Active Tickets:", astatclick.update("[active_tickets.len]"))
+ for(var/datum/mentor_help/MH as anything in active_tickets)
+ if(MH.initiator)
+ stat("#[MH.id]. [MH.initiator_ckey]:", MH.statclick.update())
+ else
+ ++num_disconnected
+ if(num_disconnected)
+ stat("Disconnected:", astatclick.update("[num_disconnected]"))
+ stat("Resolved Tickets:", rstatclick.update("[resolved_tickets.len]"))
+
+//Reassociate still open ticket if one exists
+/datum/mentor_help_tickets/proc/ClientLogin(client/C)
+ C.current_mentorhelp = CKey2ActiveTicket(C.ckey)
+ if(C.current_mentorhelp)
+ C.current_mentorhelp.AddInteraction("Client reconnected.")
+ C.current_mentorhelp.initiator = C
+
+//Dissasociate ticket
+/datum/mentor_help_tickets/proc/ClientLogout(client/C)
+ if(C.current_mentorhelp)
+ C.current_mentorhelp.AddInteraction("Client disconnected.")
+ C.current_mentorhelp.initiator = null
+ C.current_mentorhelp = null
+
+//Get a ticket given a ckey
+/datum/mentor_help_tickets/proc/CKey2ActiveTicket(ckey)
+ for(var/datum/admin_help/MH as anything in active_tickets)
+ if(MH.initiator_ckey == ckey)
+ return MH
+
+//
+//TICKET LIST STATCLICK
+//
+
+/obj/effect/statclick/mticket_list
+ var/current_state
+
+/obj/effect/statclick/mticket_list/New(loc, name, state)
+ current_state = state
+ ..()
+
+/obj/effect/statclick/mticket_list/Click()
+ GLOB.mhelp_tickets.BrowseTickets(current_state)
+
+//
+//TICKET DATUM
+//
+
+/datum/mentor_help
+ var/id
+ var/name
+ var/state = AHELP_ACTIVE
+
+ var/opened_at
+ var/closed_at
+
+ var/client/initiator //semi-misnomer, it's the person who ahelped/was bwoinked
+ var/initiator_ckey
+ var/initiator_key_name
+
+ var/list/_interactions //use AddInteraction() or, preferably, admin_ticket_log()
+
+ var/obj/effect/statclick/ahelp/statclick
+
+ var/static/ticket_counter = 0
+
+//call this on its own to create a ticket, don't manually assign current_mentorhelp
+//msg is the title of the ticket: usually the ahelp text
+/datum/mentor_help/New(msg, client/C)
+ //clean the input msg
+ msg = sanitize(copytext(msg,1,MAX_MESSAGE_LEN))
+ if(!msg || !C || !C.mob)
+ qdel(src)
+ return
+
+ id = ++ticket_counter
+ opened_at = world.time
+
+ name = msg
+
+ initiator = C
+ initiator_ckey = C.ckey
+ initiator_key_name = key_name(initiator, FALSE, TRUE)
+ if(initiator.current_mentorhelp) //This is a bug
+ log_debug("Ticket erroneously left open by code")
+ initiator.current_mentorhelp.AddInteraction("Ticket erroneously left open by code")
+ initiator.current_mentorhelp.Resolve()
+ initiator.current_mentorhelp = src
+
+ statclick = new(null, src)
+ _interactions = list()
+
+ log_admin("Mentorhelp: [key_name(C)]: [msg]")
+ MessageNoRecipient(msg)
+ //show it to the person adminhelping too
+ to_chat(C, "Mentor-PM to-Mentors: [name]")
+
+ GLOB.mhelp_tickets.active_tickets += src
+
+/datum/mentor_help/Destroy()
+ RemoveActive()
+ GLOB.mhelp_tickets.resolved_tickets -= src
+ return ..()
+
+/datum/mentor_help/proc/AddInteraction(formatted_message)
+ _interactions += "[gameTimestamp()]: [formatted_message]"
+
+//private
+/datum/mentor_help/proc/ClosureLinks(ref_src)
+ if(!ref_src)
+ ref_src = "\ref[src]"
+ . = " (RSLVE)"
+
+//private
+/datum/mentor_help/proc/LinkedReplyName(ref_src)
+ if(!ref_src)
+ ref_src = "\ref[src]"
+ return "[initiator_ckey]"
+
+//private
+/datum/mentor_help/proc/TicketHref(msg, ref_src, action = "ticket")
+ if(!ref_src)
+ ref_src = "\ref[src]"
+ return "[msg]"
+
+//message from the initiator without a target, all people with mentor powers will see this
+/datum/mentor_help/proc/MessageNoRecipient(msg)
+ var/ref_src = "\ref[src]"
+ var/chat_msg = "(ESCALATE) Ticket [TicketHref("#[id]", ref_src)]: [LinkedReplyName(ref_src)]: [msg]"
+ AddInteraction("[LinkedReplyName(ref_src)]: [msg]")
+ for (var/client/C in GLOB.mentors)
+ if (C.is_preference_enabled(/datum/client_preference/play_mentorhelp_ping))
+ C << 'sound/effects/mentorhelp.mp3'
+ for (var/client/C in GLOB.admins)
+ if (C.is_preference_enabled(/datum/client_preference/play_mentorhelp_ping))
+ C << 'sound/effects/mentorhelp.mp3'
+ message_mentors(chat_msg)
+
+//Reopen a closed ticket
+/datum/mentor_help/proc/Reopen()
+ if(state == AHELP_ACTIVE)
+ to_chat(usr, "This ticket is already open.")
+ return
+
+ if(GLOB.mhelp_tickets.CKey2ActiveTicket(initiator_ckey))
+ to_chat(usr, "This user already has an active ticket, cannot reopen this one.")
+ return
+
+ statclick = new(null, src)
+ GLOB.mhelp_tickets.active_tickets += src
+ GLOB.mhelp_tickets.resolved_tickets -= src
+ switch(state)
+ if(AHELP_RESOLVED)
+ feedback_dec("mhelp_resolve")
+ state = AHELP_ACTIVE
+ closed_at = null
+ if(initiator)
+ initiator.current_mentorhelp = src
+
+ AddInteraction("Reopened by [usr.ckey]")
+ if(initiator)
+ to_chat(initiator, "Ticket [TicketHref("#[id]")] was reopened by [usr.ckey].")
+ var/msg = "Ticket [TicketHref("#[id]")] reopened by [usr.ckey]."
+ message_mentors(msg)
+ log_admin(msg)
+ feedback_inc("mhelp_reopen")
+ TicketPanel() //can only be done from here, so refresh it
+
+//private
+/datum/mentor_help/proc/RemoveActive()
+ if(state != AHELP_ACTIVE)
+ return
+ closed_at = world.time
+ QDEL_NULL(statclick)
+ GLOB.mhelp_tickets.active_tickets -= src
+ if(initiator && initiator.current_mentorhelp == src)
+ initiator.current_mentorhelp = null
+
+//Mark open ticket as resolved/legitimate, returns mentorhelp verb
+/datum/mentor_help/proc/Resolve(silent = FALSE)
+ if(state != AHELP_ACTIVE)
+ return
+ RemoveActive()
+ state = AHELP_RESOLVED
+ GLOB.mhelp_tickets.ListInsert(src)
+
+ AddInteraction("Resolved by [usr.ckey].")
+ if(initiator)
+ to_chat(initiator, "Ticket [TicketHref("#[id]")] was marked resolved by [usr.ckey].")
+ if(!silent)
+ feedback_inc("mhelp_resolve")
+ var/msg = "Ticket [TicketHref("#[id]")] resolved by [usr.ckey]"
+ message_mentors(msg)
+ log_admin(msg)
+
+//Show the ticket panel
+/datum/mentor_help/proc/TicketPanel()
+ var/list/dat = list("Ticket #[id]")
+ var/ref_src = "\ref[src]"
+ dat += "Mentor Help Ticket #[id]: [LinkedReplyName(ref_src)]
"
+ dat += "State: "
+ switch(state)
+ if(AHELP_ACTIVE)
+ dat += "OPEN"
+ if(AHELP_RESOLVED)
+ dat += "RESOLVED"
+ else
+ dat += "UNKNOWN"
+ dat += "[GLOB.TAB][TicketHref("Refresh", ref_src)]"
+ if(state != AHELP_ACTIVE)
+ dat += "[GLOB.TAB][TicketHref("Reopen", ref_src, "reopen")]"
+ dat += "
Opened at: [gameTimestamp(wtime = opened_at)] (Approx [(world.time - opened_at) / 600] minutes ago)"
+ if(closed_at)
+ dat += "
Closed at: [gameTimestamp(wtime = closed_at)] (Approx [(world.time - closed_at) / 600] minutes ago)"
+ dat += "
"
+ if(initiator)
+ dat += "Actions: [Context(ref_src)]
"
+ else
+ dat += "DISCONNECTED[GLOB.TAB][ClosureLinks(ref_src)]
"
+ dat += "
Log:
"
+ for(var/I in _interactions)
+ dat += "[I]
"
+
+ usr << browse(dat.Join(), "window=mhelp[id];size=620x480")
+
+//Kick ticket to admins
+/datum/mentor_help/proc/Escalate()
+ if(tgui_alert(usr, "Really escalate this ticket to admins? No mentors will ever be able to interact with it again if you do.","Escalate",list("Yes","No")) != "Yes")
+ return
+ if (src.initiator == null) // You can't escalate a mentorhelp of someone who's logged out because it won't create the adminhelp properly
+ to_chat(usr, "Error: client not found, unable to escalate.")
+ return
+ var/datum/admin_help/AH = new /datum/admin_help(src.name, src.initiator, FALSE)
+ message_mentors("[usr.ckey] escalated Ticket [TicketHref("#[id]")]")
+ log_admin("[key_name(usr)] escalated mentorhelp [src.name]")
+ to_chat(src.initiator, "[usr.ckey] escalated your mentorhelp to admins.")
+ AH._interactions = src._interactions
+ GLOB.mhelp_tickets.active_tickets -= src
+ GLOB.mhelp_tickets.resolved_tickets -= src
+ qdel(src)
+
+/datum/mentor_help/proc/Context(ref_src)
+ if(!ref_src)
+ ref_src = "\ref[src]"
+ if(state == AHELP_ACTIVE)
+ . += ClosureLinks(ref_src)
+ if(state != AHELP_RESOLVED)
+ . += " (ESCALATE)"
+
+//Forwarded action from admin/Topic OR mentor/Topic depending on which rank the caller has
+/datum/mentor_help/proc/Action(action)
+ switch(action)
+ if("ticket")
+ TicketPanel()
+ if("reply")
+ usr.client.cmd_mhelp_reply(initiator)
+ if("resolve")
+ Resolve()
+ if("reopen")
+ Reopen()
+ if("escalate")
+ Escalate()
+
+//
+// TICKET STATCLICK
+//
+
+/obj/effect/statclick/mhelp
+ var/datum/mentor_help/mhelp_datum
+
+/obj/effect/statclick/mhelp/New(loc, datum/mentor_help/MH)
+ mhelp_datum = MH
+ ..(loc)
+
+/obj/effect/statclick/mhelp/update()
+ return ..(mhelp_datum.name)
+
+/obj/effect/statclick/mhelp/Click()
+ mhelp_datum.TicketPanel()
+
+/obj/effect/statclick/mhelp/Destroy()
+ mhelp_datum = null
+ return ..()
+
+//
+// CLIENT PROCS
+//
+
+/client/verb/mentorhelp(msg as text)
+ set category = "Admin"
+ set name = "Mentorhelp"
+
+ if(say_disabled) //This is here to try to identify lag problems
+ to_chat(usr, "Speech is currently admin-disabled.")
+ return
+
+ //handle muting and automuting
+ if(prefs.muted & MUTE_ADMINHELP)
+ to_chat(src, "Error: Mentor-PM: You cannot send adminhelps (Muted).")
+ return
+ if(handle_spam_prevention(msg,MUTE_ADMINHELP))
+ return
+
+ if(!msg)
+ return
+
+ //remove out adminhelp verb temporarily to prevent spamming of admins.
+ src.verbs -= /client/verb/mentorhelp
+ spawn(600)
+ src.verbs += /client/verb/mentorhelp // 1 minute cool-down for mentorhelps
+
+ feedback_add_details("admin_verb","Mentorhelp") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
+ if(current_mentorhelp)
+ if(tgui_alert(usr, "You already have a ticket open. Is this for the same issue?","Duplicate?",list("Yes","No")) != "No")
+ if(current_mentorhelp)
+ log_admin("Mentorhelp: [key_name(src)]: [msg]")
+ current_mentorhelp.MessageNoRecipient(msg)
+ to_chat(usr, "Mentor-PM to-Mentors: [msg]")
+ return
+ else
+ to_chat(usr, "Ticket not found, creating new one...")
+ else
+ current_mentorhelp.AddInteraction("[usr.ckey] opened a new ticket.")
+ current_mentorhelp.Resolve()
+
+ new /datum/mentor_help(msg, src, FALSE)
+
+//admin proc
+/client/proc/cmd_mentor_ticket_panel()
+ set name = "Mentor Ticket List"
+ set category = "Admin"
+
+ var/browse_to
+
+ switch(tgui_input_list(usr, "Display which ticket list?", "List Choice", list("Active Tickets", "Resolved Tickets")))
+ if("Active Tickets")
+ browse_to = AHELP_ACTIVE
+ if("Resolved Tickets")
+ browse_to = AHELP_RESOLVED
+ else
+ return
+
+ GLOB.mhelp_tickets.BrowseTickets(browse_to)
+
+/proc/message_mentors(var/msg)
+ msg = "Mentor: [msg]"
+
+ for(var/client/C in GLOB.mentors)
+ to_chat(C, msg)
+ for(var/client/C in GLOB.admins)
+ to_chat(C, msg)
\ No newline at end of file