diff --git a/code/datums/datumvars.dm b/code/datums/datumvars.dm
index 03367dc214eb..e79b3af975d6 100644
--- a/code/datums/datumvars.dm
+++ b/code/datums/datumvars.dm
@@ -1466,7 +1466,7 @@
var/log_msg = "[key_name(usr)] dealt [amount] amount of [Text] damage to [key_name(L)]"
message_admins("[key_name(usr)] dealt [amount] amount of [Text] damage to [ADMIN_LOOKUPFLW(L)]")
log_admin(log_msg)
- admin_ticket_log(L, "[log_msg]")
+ admin_ticket_log(L, log_msg)
vv_update_display(L, Text, "[newamt]")
else if(href_list["copyoutfit"])
if(!check_rights(R_SPAWN))
diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm
index 4fcc170889e6..171b7144f86e 100644
--- a/code/modules/admin/admin.dm
+++ b/code/modules/admin/admin.dm
@@ -761,6 +761,9 @@
set desc = "Edit mobs's memory and role"
set name = "Show Traitor Panel"
+ if(!check_rights(R_ADMIN))
+ return
+
if(!istype(M))
to_chat(usr, "This can only be used on instances of type /mob", confidential=TRUE)
return
@@ -979,6 +982,62 @@
return 1
+/datum/admins/proc/adminmoreinfo(mob/M)
+ if(!ismob(M))
+ to_chat(usr, "This can only be used on instances of type /mob.", confidential=TRUE)
+ 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]: ", confidential=TRUE)
+ to_chat(src.owner, "Mob type = [M.type]; Gender = [gender_description] Damage = [health_description]", confidential=TRUE)
+ to_chat(src.owner, "Name = [M.name]; Real_name = [M.real_name]; Mind_name = [M.mind?"[M.mind.name]":""]; Key = [M.key];", confidential=TRUE)
+ to_chat(src.owner, "Location = [location_description];", confidential=TRUE)
+ to_chat(src.owner, "[special_role_description]", confidential=TRUE)
+ to_chat(src.owner, ADMIN_FULLMONTY_NONAME(M), confidential=TRUE)
+
/client/proc/adminGreet(logout)
if(SSticker.HasRoundStarted())
var/string
diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm
index b71782e3cbc6..07911a8a17fd 100644
--- a/code/modules/admin/admin_verbs.dm
+++ b/code/modules/admin/admin_verbs.dm
@@ -393,7 +393,7 @@ GLOBAL_PROTECT(admin_verbs_hideable)
/client/proc/check_antagonists()
set name = "Check Antagonists"
set category = "Admin"
- if(holder)
+ if(check_rights(R_ADMIN))
// yogs start
log_admin("[key_name(usr)] checked antagonists.") //for tsar~
if((!isobserver(usr) && SSticker.HasRoundStarted()) || !check_rights(R_VAREDIT))
diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm
index 37d6f60f3768..af246612d3fc 100644
--- a/code/modules/admin/topic.dm
+++ b/code/modules/admin/topic.dm
@@ -1285,60 +1285,7 @@
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.", confidential=TRUE)
- 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]: ", confidential=TRUE)
- to_chat(src.owner, "Mob type = [M.type]; Gender = [gender_description] Damage = [health_description]", confidential=TRUE)
- to_chat(src.owner, "Name = [M.name]; Real_name = [M.real_name]; Mind_name = [M.mind?"[M.mind.name]":""]; Key = [M.key];", confidential=TRUE)
- to_chat(src.owner, "Location = [location_description];", confidential=TRUE)
- to_chat(src.owner, "[special_role_description]", confidential=TRUE)
- to_chat(src.owner, ADMIN_FULLMONTY_NONAME(M), confidential=TRUE)
+ adminmoreinfo(M)
else if(href_list["addjobslot"])
if(!check_rights(R_ADMIN))
@@ -1966,8 +1913,6 @@
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"])
diff --git a/code/modules/admin/verbs/adminhelp.dm b/code/modules/admin/verbs/adminhelp.dm
deleted file mode 100644
index edf1f76ec4b0..000000000000
--- a/code/modules/admin/verbs/adminhelp.dm
+++ /dev/null
@@ -1,697 +0,0 @@
-// yogs - This file is mirrored to yogstation/modules/admin
-/client/var/adminhelptimerid = 0 //a timer id for returning the ahelp verb
-/client/var/datum/admin_help/current_ticket //the current ticket the (usually) not-admin client is dealing with
-
-//
-//TICKET MANAGER
-//
-
-GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
-
-/datum/admin_help_tickets
- var/list/active_tickets = list()
- var/list/closed_tickets = list()
- var/list/resolved_tickets = list()
-
- var/obj/effect/statclick/ticket_list/astatclick = new(null, null, AHELP_ACTIVE)
- var/obj/effect/statclick/ticket_list/cstatclick = new(null, null, AHELP_CLOSED)
- var/obj/effect/statclick/ticket_list/rstatclick = new(null, null, AHELP_RESOLVED)
-
-/datum/admin_help_tickets/Destroy()
- QDEL_LIST(active_tickets)
- QDEL_LIST(closed_tickets)
- QDEL_LIST(resolved_tickets)
- QDEL_NULL(astatclick)
- QDEL_NULL(cstatclick)
- QDEL_NULL(rstatclick)
- return ..()
-
-/datum/admin_help_tickets/proc/TicketByID(id)
- var/list/lists = list(active_tickets, closed_tickets, resolved_tickets)
- for(var/I in lists)
- for(var/J in I)
- var/datum/admin_help/AH = J
- if(AH.id == id)
- return J
-
-/datum/admin_help_tickets/proc/TicketsByCKey(ckey)
- . = list()
- var/list/lists = list(active_tickets, closed_tickets, resolved_tickets)
- for(var/I in lists)
- for(var/J in I)
- var/datum/admin_help/AH = J
- if(AH.initiator_ckey == ckey)
- . += AH
-
-//private
-/datum/admin_help_tickets/proc/ListInsert(datum/admin_help/new_ticket)
- var/list/ticket_list
- switch(new_ticket.state)
- if(AHELP_ACTIVE)
- ticket_list = active_tickets
- if(AHELP_CLOSED)
- ticket_list = closed_tickets
- if(AHELP_RESOLVED)
- ticket_list = resolved_tickets
- else
- CRASH("Invalid ticket state: [new_ticket.state]")
- var/num_closed = ticket_list.len
- if(num_closed)
- for(var/I in 1 to num_closed)
- var/datum/admin_help/AH = ticket_list[I]
- if(AH.id > new_ticket.id)
- ticket_list.Insert(I, new_ticket)
- return
- ticket_list += new_ticket
-
-//opens the ticket listings for one of the 3 states
-/datum/admin_help_tickets/proc/BrowseTickets(state)
- var/list/l2b
- var/title
- switch(state)
- if(AHELP_ACTIVE)
- l2b = active_tickets
- title = "Active Tickets"
- if(AHELP_CLOSED)
- l2b = closed_tickets
- title = "Closed Tickets"
- if(AHELP_RESOLVED)
- l2b = resolved_tickets
- title = "Resolved Tickets"
- if(!l2b)
- return
- var/list/dat = list("
[title]")
- dat += "Refresh
"
- for(var/I in l2b)
- var/datum/admin_help/AH = I
- dat += "Ticket #[AH.id]: [AH.initiator_key_name]: [AH.name]
"
-
- usr << browse(dat.Join(), "window=ahelp_list[state];size=600x480")
-
-//Tickets statpanel
-/datum/admin_help_tickets/proc/stat_entry()
- SHOULD_CALL_PARENT(TRUE)
- SHOULD_NOT_SLEEP(TRUE)
- var/list/L = list()
- var/num_disconnected = 0
- L[++L.len] = list("Active Tickets:", "[astatclick.update("[active_tickets.len]")]", null, REF(astatclick))
- astatclick.update("[active_tickets.len]")
- for(var/I in active_tickets)
- var/datum/admin_help/AH = I
- if(AH.initiator)
- L[++L.len] = list("#[AH.id]. [AH.initiator_key_name]:", "[AH.statclick.update()]", REF(AH))
- else
- ++num_disconnected
- if(num_disconnected)
- L[++L.len] = list("Disconnected:", "[astatclick.update("[num_disconnected]")]", null, REF(astatclick))
- L[++L.len] = list("Closed Tickets:", "[cstatclick.update("[closed_tickets.len]")]", null, REF(cstatclick))
- L[++L.len] = list("Resolved Tickets:", "[rstatclick.update("[resolved_tickets.len]")]", null, REF(rstatclick))
- return L
-
-//Reassociate still open ticket if one exists
-/datum/admin_help_tickets/proc/ClientLogin(client/C)
- C.current_ticket = CKey2ActiveTicket(C.ckey)
- if(C.current_ticket)
- C.current_ticket.initiator = C
- C.current_ticket.AddInteraction("Client reconnected.")
-
-//Dissasociate ticket
-/datum/admin_help_tickets/proc/ClientLogout(client/C)
- if(C.current_ticket)
- C.current_ticket.AddInteraction("Client disconnected.")
- C.current_ticket.initiator = null
- C.current_ticket = null
-
-//Get a ticket given a ckey
-/datum/admin_help_tickets/proc/CKey2ActiveTicket(ckey)
- for(var/I in active_tickets)
- var/datum/admin_help/AH = I
- if(AH.initiator_ckey == ckey)
- return AH
-
-//
-//TICKET LIST STATCLICK
-//
-
-/obj/effect/statclick/ticket_list
- var/current_state
-
-/obj/effect/statclick/ticket_list/New(loc, name, state)
- current_state = state
- ..()
-
-/obj/effect/statclick/ticket_list/Click()
- GLOB.ahelp_tickets.BrowseTickets(current_state)
-
-//called by admin topic
-/obj/effect/statclick/ticket_list/proc/Action()
- Click()
-
-//
-//TICKET DATUM
-//
-
-/datum/admin_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/heard_by_no_admins = FALSE
-
- 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_ticket
-//msg is the title of the ticket: usually the ahelp text
-//is_bwoink is TRUE if this ticket was started by an admin PM
-/datum/admin_help/New(msg, client/C, is_bwoink)
- //clean the input msg
- msg = sanitize(copytext_char(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 = initiator.ckey
- initiator_key_name = key_name(initiator, FALSE, TRUE)
- if(initiator.current_ticket) //This is a bug
- stack_trace("Multiple ahelp current_tickets")
- initiator.current_ticket.AddInteraction("Ticket erroneously left open by code")
- initiator.current_ticket.Close()
- initiator.current_ticket = src
-
- TimeoutVerb()
-
- statclick = new(null, src)
- _interactions = list()
-
- if(is_bwoink)
- AddInteraction("[usr.client.ckey] PM'd [initiator.ckey]")
- message_admins("Ticket [TicketHref("#[id]")] created")
- else
- MessageNoRecipient(msg)
-
- //send it to irc if nobody is on and tell us how many were on
- var/admin_number_present = send2irc_adminless_only(initiator_ckey, "Ticket #[id]: [name]")
- log_admin_private("Ticket #[id]: [key_name(initiator)]: [name] - heard by [admin_number_present] non-AFK admins who have +BAN.")
- if(admin_number_present <= 0)
- to_chat(C, "No active admins are online, your adminhelp was sent to the admin irc.", confidential=TRUE)
- heard_by_no_admins = TRUE
-
- GLOB.ahelp_tickets.active_tickets += src
-
-/datum/admin_help/Destroy()
- RemoveActive()
- GLOB.ahelp_tickets.closed_tickets -= src
- GLOB.ahelp_tickets.resolved_tickets -= src
- return ..()
-
-/datum/admin_help/proc/AddInteraction(formatted_message)
- if(heard_by_no_admins && usr && usr.ckey != initiator_ckey)
- heard_by_no_admins = FALSE
- send2irc(initiator_ckey, "Ticket #[id]: Answered by [key_name(usr)]")
- _interactions += "[time_stamp()]: [formatted_message]"
-
-//Removes the ahelp verb and returns it after 2 minutes
-/datum/admin_help/proc/TimeoutVerb()
- remove_verb(initiator, /client/verb/adminhelp)
- initiator.adminhelptimerid = addtimer(CALLBACK(initiator, /client/proc/giveadminhelpverb), 1200, TIMER_STOPPABLE) //2 minute cooldown of admin helps
-
-//private
-/datum/admin_help/proc/FullMonty(ref_src)
- if(!ref_src)
- ref_src = "[REF(src)]"
- . = ADMIN_FULLMONTY_NONAME(initiator.mob)
- if(state == AHELP_ACTIVE)
- . += ClosureLinks(ref_src)
-
-//private
-/datum/admin_help/proc/ClosureLinks(ref_src)
- if(!ref_src)
- ref_src = "[REF(src)]"
- . = " (REJT)"
- . += " (IC)"
- . += " (CLOSE)"
- . += " (RSLVE)"
-
-//private
-/datum/admin_help/proc/LinkedReplyName(ref_src)
- if(!ref_src)
- ref_src = "[REF(src)]"
- return "[initiator_key_name]"
-
-//private
-/datum/admin_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 admins will see this
-//won't bug irc
-/datum/admin_help/proc/MessageNoRecipient(msg)
- msg = sanitize(copytext_char(msg, 1, MAX_MESSAGE_LEN))
- var/ref_src = "[REF(src)]"
- //Message to be sent to all admins
- var/admin_msg = "Ticket [TicketHref("#[id]", ref_src)]: [LinkedReplyName(ref_src)] [FullMonty(ref_src)]: [keywords_lookup(msg)]"
-
- AddInteraction("[LinkedReplyName(ref_src)]: [msg]")
- log_admin_private("Ticket #[id]: [key_name(initiator)]: [msg]")
-
- //send this msg to all admins
- for(var/client/X in GLOB.admins)
- if(X.prefs.toggles & SOUND_ADMINHELP)
- SEND_SOUND(X, sound('sound/effects/adminhelp.ogg'))
- window_flash(X, ignorepref = TRUE)
- to_chat(X,
- type = MESSAGE_TYPE_ADMINPM,
- html = admin_msg,
- confidential = TRUE)
-
- //show it to the person adminhelping too
- to_chat(initiator,
- type = MESSAGE_TYPE_ADMINPM,
- html = "PM to-Admins: [msg]",
- confidential = TRUE)
-
-//Reopen a closed ticket
-/datum/admin_help/proc/Reopen()
- if(state == AHELP_ACTIVE)
- to_chat(usr, "This ticket is already open.", confidential=TRUE)
- return
-
- if(GLOB.ahelp_tickets.CKey2ActiveTicket(initiator_ckey))
- to_chat(usr, "This user already has an active ticket, cannot reopen this one.", confidential=TRUE)
- return
-
- statclick = new(null, src)
- GLOB.ahelp_tickets.active_tickets += src
- GLOB.ahelp_tickets.closed_tickets -= src
- GLOB.ahelp_tickets.resolved_tickets -= src
- switch(state)
- if(AHELP_CLOSED)
- SSblackbox.record_feedback("tally", "ahelp_stats", -1, "closed")
- if(AHELP_RESOLVED)
- SSblackbox.record_feedback("tally", "ahelp_stats", -1, "resolved")
- state = AHELP_ACTIVE
- closed_at = null
- if(initiator)
- initiator.current_ticket = src
-
- AddInteraction("Reopened by [key_name_admin(usr)]")
- var/msg = "Ticket [TicketHref("#[id]")] reopened by [key_name_admin(usr)]."
- message_admins(msg)
- log_admin_private(msg)
- SSblackbox.record_feedback("tally", "ahelp_stats", 1, "reopened")
- TicketPanel() //can only be done from here, so refresh it
-
-//private
-/datum/admin_help/proc/RemoveActive()
- if(state != AHELP_ACTIVE)
- return
- closed_at = world.time
- QDEL_NULL(statclick)
- GLOB.ahelp_tickets.active_tickets -= src
- if(initiator && initiator.current_ticket == src)
- initiator.current_ticket = null
-
-//Mark open ticket as closed/meme
-/datum/admin_help/proc/Close(key_name = key_name_admin(usr), silent = FALSE)
- if(state != AHELP_ACTIVE)
- return
- RemoveActive()
- state = AHELP_CLOSED
- GLOB.ahelp_tickets.ListInsert(src)
- AddInteraction("Closed by [key_name].")
- if(!silent)
- SSblackbox.record_feedback("tally", "ahelp_stats", 1, "closed")
- var/msg = "Ticket [TicketHref("#[id]")] closed by [key_name]."
- message_admins(msg)
- log_admin_private(msg)
-
-//Mark open ticket as resolved/legitimate, returns ahelp verb
-/datum/admin_help/proc/Resolve(key_name = key_name_admin(usr), silent = FALSE)
- if(state != AHELP_ACTIVE)
- return
- RemoveActive()
- state = AHELP_RESOLVED
- GLOB.ahelp_tickets.ListInsert(src)
-
- addtimer(CALLBACK(initiator, /client/proc/giveadminhelpverb), 50)
-
- AddInteraction("Resolved by [key_name].")
- to_chat(initiator, "Your ticket has been resolved by an admin. The Adminhelp verb will be returned to you shortly.", confidential=TRUE)
- if(!silent)
- SSblackbox.record_feedback("tally", "ahelp_stats", 1, "resolved")
- var/msg = "Ticket [TicketHref("#[id]")] resolved by [key_name]"
- message_admins(msg)
- log_admin_private(msg)
-
-//Close and return ahelp verb, use if ticket is incoherent
-/datum/admin_help/proc/Reject(key_name = key_name_admin(usr))
- if(state != AHELP_ACTIVE)
- return
-
- if(initiator)
- initiator.giveadminhelpverb()
-
- SEND_SOUND(initiator, sound('sound/effects/adminhelp.ogg'))
-
- to_chat(initiator, "- AdminHelp Rejected! -", confidential=TRUE)
- to_chat(initiator, "Your admin help was rejected. The adminhelp verb has been returned to you so that you may try again.", confidential=TRUE)
- to_chat(initiator, "Please try to be calm, clear, and descriptive in admin helps, do not assume the admin has seen any related events, and clearly state the names of anybody you are reporting.", confidential=TRUE)
-
- SSblackbox.record_feedback("tally", "ahelp_stats", 1, "rejected")
- var/msg = "Ticket [TicketHref("#[id]")] rejected by [key_name]"
- message_admins(msg)
- log_admin_private(msg)
- AddInteraction("Rejected by [key_name].")
- Close(silent = TRUE)
-
-//Resolve ticket with IC Issue message
-/datum/admin_help/proc/ICIssue(key_name = key_name_admin(usr))
- if(state != AHELP_ACTIVE)
- return
-
- var/msg = "- AdminHelp marked as IC issue! -
"
- msg += "Your issue has been determined by an administrator to be an in character issue and does NOT require administrator intervention at this time. For further resolution you should pursue options that are in character."
-
- if(initiator)
- to_chat(initiator, msg, confidential=TRUE)
-
- SSblackbox.record_feedback("tally", "ahelp_stats", 1, "IC")
- msg = "Ticket [TicketHref("#[id]")] marked as IC by [key_name]"
- message_admins(msg)
- log_admin_private(msg)
- AddInteraction("Marked as IC issue by [key_name]")
- Resolve(silent = TRUE)
-
-//Show the ticket panel
-/datum/admin_help/proc/TicketPanel()
- var/list/dat = list("Ticket #[id]")
- var/ref_src = "[REF(src)]"
- dat += "Admin Help Ticket #[id]: [LinkedReplyName(ref_src)]
"
- dat += "State: "
- switch(state)
- if(AHELP_ACTIVE)
- dat += "OPEN"
- if(AHELP_RESOLVED)
- dat += "RESOLVED"
- if(AHELP_CLOSED)
- dat += "CLOSED"
- else
- dat += "UNKNOWN"
- dat += "[GLOB.TAB][TicketHref("Refresh", ref_src)][GLOB.TAB][TicketHref("Re-Title", ref_src, "retitle")]"
- if(state != AHELP_ACTIVE)
- dat += "[GLOB.TAB][TicketHref("Reopen", ref_src, "reopen")]"
- dat += "
Opened at: [gameTimestamp(wtime = opened_at)] (Approx [DisplayTimeText(world.time - opened_at)] ago)"
- if(closed_at)
- dat += "
Closed at: [gameTimestamp(wtime = closed_at)] (Approx [DisplayTimeText(world.time - closed_at)] ago)"
- dat += "
"
- if(initiator)
- dat += "Actions: [FullMonty(ref_src)]
"
- else
- dat += "DISCONNECTED[GLOB.TAB][ClosureLinks(ref_src)]
"
- dat += "
Log:
"
- for(var/I in _interactions)
- dat += "[I]
"
-
- usr << browse(dat.Join(), "window=ahelp[id];size=620x480")
-
-/datum/admin_help/proc/Retitle()
- var/new_title = input(usr, "Enter a title for the ticket", "Rename Ticket", name) as text|null
- if(new_title)
- name = new_title
- //not saying the original name cause it could be a long ass message
- var/msg = "Ticket [TicketHref("#[id]")] titled [name] by [key_name_admin(usr)]"
- message_admins(msg)
- log_admin_private(msg)
- TicketPanel() //we have to be here to do this
-
-//Forwarded action from admin/Topic
-/datum/admin_help/proc/Action(action)
- testing("Ahelp action: [action]")
- switch(action)
- if("ticket")
- TicketPanel()
- if("retitle")
- Retitle()
- if("reject")
- Reject()
- if("reply")
- usr.client.cmd_ahelp_reply(initiator)
- if("icissue")
- ICIssue()
- if("close")
- Close()
- if("resolve")
- Resolve()
- if("reopen")
- Reopen()
-
-//
-// TICKET STATCLICK
-//
-
-/obj/effect/statclick/ahelp
- var/datum/admin_help/ahelp_datum
-
-/obj/effect/statclick/ahelp/Initialize(mapload, datum/admin_help/AH)
- ahelp_datum = AH
- . = ..()
-
-/obj/effect/statclick/ahelp/update()
- return ..(ahelp_datum.name)
-
-/obj/effect/statclick/ahelp/Click()
- ahelp_datum.TicketPanel()
-
-/obj/effect/statclick/ahelp/Destroy()
- ahelp_datum = null
- return ..()
-
-//
-// CLIENT PROCS
-//
-
-/client/proc/giveadminhelpverb()
- add_verb(src, /client/verb/adminhelp)
- deltimer(adminhelptimerid)
- adminhelptimerid = 0
-
-// Used for methods where input via arg doesn't work
-/client/proc/get_adminhelp()
- var/msg = input(src, "Please describe your problem concisely and an admin will help as soon as they're able.", "Adminhelp contents") as text|null
- adminhelp(msg)
-
-/client/verb/adminhelp(msg as text)
- set category = "Admin"
- set name = "Adminhelp"
-
- if(GLOB.say_disabled) //This is here to try to identify lag problems
- to_chat(usr, "Speech is currently admin-disabled.", confidential=TRUE)
- return
-
- //handle muting and automuting
- if(prefs.muted & MUTE_ADMINHELP)
- to_chat(src, "Error: Admin-PM: You cannot send adminhelps (Muted).", confidential=TRUE)
- return
- if(handle_spam_prevention(msg,MUTE_ADMINHELP))
- return
-
- msg = trim(msg)
-
- if(!msg)
- return
-
- SSblackbox.record_feedback("tally", "admin_verb", 1, "Adminhelp") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
- if(current_ticket)
- if(alert(usr, "You already have a ticket open. Is this for the same issue?",,"Yes","No") != "No")
- if(current_ticket)
- current_ticket.MessageNoRecipient(msg)
- current_ticket.TimeoutVerb()
- return
- else
- to_chat(usr, "Ticket not found, creating new one...", confidential=TRUE)
- else
- current_ticket.AddInteraction("[key_name_admin(usr)] opened a new ticket.")
- current_ticket.Close()
-
- new /datum/admin_help(msg, src, FALSE)
-
-//
-// LOGGING
-//
-
-//Use this proc when an admin takes action that may be related to an open ticket on what
-//what can be a client, ckey, or mob
-/proc/admin_ticket_log(what, message)
- var/client/C
- var/mob/Mob = what
- if(istype(Mob))
- C = Mob.client
- else
- C = what
- if(istype(C) && C.current_ticket)
- C.current_ticket.AddInteraction(message)
- return C.current_ticket
- if(istext(what)) //ckey
- var/datum/admin_help/AH = GLOB.ahelp_tickets.CKey2ActiveTicket(what)
- if(AH)
- AH.AddInteraction(message)
- return AH
-
-//
-// HELPER PROCS
-//
-
-/proc/get_admin_counts(requiredflags = R_BAN)
- . = list("total" = list(), "noflags" = list(), "afk" = list(), "stealth" = list(), "present" = list())
- for(var/client/X in GLOB.admins)
- .["total"] += X
- if(requiredflags != 0 && !check_rights_for(X, requiredflags))
- .["noflags"] += X
- else if(X.is_afk())
- .["afk"] += X
- else if(X.holder.fakekey)
- .["stealth"] += X
- else
- .["present"] += X
-
-/proc/send2irc_adminless_only(source, msg, requiredflags = R_BAN)
- var/list/adm = get_admin_counts(requiredflags)
- var/list/activemins = adm["present"]
- . = activemins.len
- if(. <= 0)
- var/final = ""
- var/list/afkmins = adm["afk"]
- var/list/stealthmins = adm["stealth"]
- var/list/powerlessmins = adm["noflags"]
- var/list/allmins = adm["total"]
- if(!afkmins.len && !stealthmins.len && !powerlessmins.len)
- final = "[msg] - No admins online"
- else
- final = "[msg] - All admins stealthed\[[english_list(stealthmins)]\], AFK\[[english_list(afkmins)]\], or lacks +BAN\[[english_list(powerlessmins)]\]! Total: [allmins.len] "
- send2irc(source,final)
- send2otherserver(source,final)
-
-
-/proc/send2irc(msg,msg2)
- msg = replacetext(replacetext(msg, "\proper", ""), "\improper", "")
- msg2 = replacetext(replacetext(msg2, "\proper", ""), "\improper", "")
- world.TgsTargetedChatBroadcast("[msg] | [msg2]", TRUE)
-
-/proc/send2otherserver(source,msg,type = "Ahelp")
- var/comms_key = CONFIG_GET(string/comms_key)
- if(!comms_key)
- return
- var/list/message = list()
- message["message_sender"] = source
- message["message"] = msg
- message["source"] = "([CONFIG_GET(string/cross_comms_name)])"
- message["key"] = comms_key
- message += type
-
- var/list/servers = CONFIG_GET(keyed_list/cross_server)
- for(var/I in servers)
- world.Export("[servers[I]]?[list2params(message)]")
-
-
-/proc/ircadminwho()
- var/list/message = list("Admins: ")
- var/list/admin_keys = list()
- for(var/adm in GLOB.admins)
- var/client/C = adm
- admin_keys += "[C][C.holder.fakekey ? "(Stealth)" : ""][C.is_afk() ? "(AFK)" : ""]"
-
- for(var/admin in admin_keys)
- if(LAZYLEN(message) > 1)
- message += ", [admin]"
- else
- message += "[admin]"
-
- return jointext(message, "")
-
-/proc/keywords_lookup(msg,irc)
-
- //This is a list of words which are ignored by the parser when comparing message contents for names. MUST BE IN LOWER CASE!
- var/list/adminhelp_ignored_words = list("unknown","the","a","an","of","monkey","alien","as", "i")
-
- //explode the input msg into a list
- var/list/msglist = splittext(msg, " ")
-
- //generate keywords lookup
- var/list/surnames = list()
- var/list/forenames = list()
- var/list/ckeys = list()
- var/founds = ""
- for(var/mob/M in GLOB.mob_list)
- var/list/indexing = list(M.real_name, M.name)
- if(M.mind)
- indexing += M.mind.name
-
- for(var/string in indexing)
- var/list/L = splittext(string, " ")
- var/surname_found = 0
- //surnames
- for(var/i=L.len, i>=1, i--)
- var/word = ckey(L[i])
- if(word)
- surnames[word] = M
- surname_found = i
- break
- //forenames
- for(var/i=1, i(?|F) "
- continue
- msg += "[original_word] "
- if(irc)
- if(founds == "")
- return "Search Failed"
- else
- return founds
-
- return msg
diff --git a/code/modules/admin/verbs/adminjump.dm b/code/modules/admin/verbs/adminjump.dm
index f7c1dc013e37..91b4c74e49ff 100644
--- a/code/modules/admin/verbs/adminjump.dm
+++ b/code/modules/admin/verbs/adminjump.dm
@@ -103,9 +103,9 @@
return
var/atom/loc = get_turf(usr)
- log_admin("[key_name(usr)] teleported [key_name(M)] to [AREACOORD(loc)]")
- var/msg = "[key_name_admin(usr)] teleported [ADMIN_LOOKUPFLW(M)] to [ADMIN_VERBOSEJMP(loc)]"
- message_admins(msg)
+ message_admins("[key_name_admin(usr)] teleported [ADMIN_LOOKUPFLW(M)] to [ADMIN_VERBOSEJMP(loc)]")
+ var/msg = "[key_name(usr)] teleported [key_name(M)] to [AREACOORD(loc)]"
+ log_admin(msg)
admin_ticket_log(M, msg)
M.forceMove(loc)
SSblackbox.record_feedback("tally", "admin_verb", 1, "Get Mob") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
@@ -129,9 +129,9 @@
if(!M)
return
- log_admin("[key_name(usr)] teleported [key_name(M)]")
- var/msg = "[key_name_admin(usr)] teleported [ADMIN_LOOKUPFLW(M)]"
- message_admins(msg)
+ message_admins("[key_name_admin(usr)] teleported [ADMIN_LOOKUPFLW(M)]")
+ var/msg = "[key_name(usr)] teleported [key_name(M)]"
+ log_admin(msg)
admin_ticket_log(M, msg)
if(M)
M.forceMove(get_turf(usr))
@@ -148,10 +148,11 @@
if(A && istype(A))
if(M.forceMove(safepick(get_area_turfs(A))))
- log_admin("[key_name(usr)] teleported [key_name(M)] to [AREACOORD(A)]")
- var/msg = "[key_name_admin(usr)] teleported [ADMIN_LOOKUPFLW(M)] to [AREACOORD(A)]"
- message_admins(msg)
- admin_ticket_log(M, msg)
+ message_admins("[key_name_admin(usr)] teleported [ADMIN_LOOKUPFLW(M)] to [AREACOORD(A)]")
+
+ var/log_msg = "[key_name(usr)] teleported [key_name(M)] to [AREACOORD(A)]"
+ log_admin(log_msg)
+ admin_ticket_log(M, log_msg, TRUE)
else
to_chat(src, "Failed to move mob to a valid location.", confidential=TRUE)
SSblackbox.record_feedback("tally", "admin_verb", 1, "Send Mob") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
diff --git a/code/modules/admin/verbs/adminpm.dm b/code/modules/admin/verbs/adminpm.dm
index 4f32ec980b5d..00cd9f431092 100644
--- a/code/modules/admin/verbs/adminpm.dm
+++ b/code/modules/admin/verbs/adminpm.dm
@@ -182,7 +182,7 @@
if(irc)
to_chat(src, "PM to-Admins: [rawmsg]", confidential=TRUE)
- var/datum/admin_help/AH = admin_ticket_log(src, keywordparsedmsg) // yogs - Yog Tickets
+ var/datum/admin_help/AH = admin_ticket_log(src, rawmsg) // yogs - Yog Tickets
ircreplyamount--
send2irc("[AH ? "#[AH.id] " : ""]Reply: [ckey]", rawmsg)
else
@@ -368,7 +368,7 @@
html = "Click on the administrator's name to reply.",
confidential = TRUE) // yogs - Yog Tickets
- admin_ticket_log(C, "PM From [irc_tagged]: [msg]") // yogs - Yog Tickets
+ admin_ticket_log(C, "PM From [irc_tagged]: [msg]") // yogs - Yog Tickets
window_flash(C, ignorepref = TRUE)
//always play non-admin recipients the adminhelp sound
diff --git a/code/modules/admin/verbs/randomverbs.dm b/code/modules/admin/verbs/randomverbs.dm
index a70be6b8aac9..9c148501fbd0 100644
--- a/code/modules/admin/verbs/randomverbs.dm
+++ b/code/modules/admin/verbs/randomverbs.dm
@@ -41,7 +41,7 @@
to_chat(M, "You hear a voice in your head... [msg]")
log_admin("SubtlePM: [key_name(usr)] -> [key_name(M)] : [msg]")
- msg = " SubtleMessage: [key_name(usr)] -> [key_name(M)] : [msg]" // yogs - Yog Tickets
+ msg = "SubtleMessage: [key_name(usr)] -> [key_name(M)] : [msg]" // yogs - Yog Tickets
message_admins(msg)
admin_ticket_log(M, msg)
SSblackbox.record_feedback("tally", "admin_verb", 1, "Subtle Message") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
@@ -168,7 +168,7 @@
to_chat(M, msg)
log_admin("DirectNarrate: [key_name(usr)] to ([M.name]/[M.key]): [msg]")
- msg = " DirectNarrate: [key_name(usr)] to ([M.name]/[M.key]): [msg]" // yogs - Yog Tickets
+ msg = "DirectNarrate: [key_name(usr)] to ([M.name]/[M.key]): [msg]" // yogs - Yog Tickets
message_admins(msg)
admin_ticket_log(M, msg)
SSblackbox.record_feedback("tally", "admin_verb", 1, "Direct Narrate") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
diff --git a/code/modules/tgui/states/holder.dm b/code/modules/tgui/states/holder.dm
new file mode 100644
index 000000000000..667068d07054
--- /dev/null
+++ b/code/modules/tgui/states/holder.dm
@@ -0,0 +1,6 @@
+GLOBAL_DATUM_INIT(holder_state, /datum/ui_state/holder_state, new)
+
+/datum/ui_state/holder_state/can_use_topic(src_object, mob/user)
+ if(user.client && user.client.holder)
+ return UI_INTERACTIVE
+ return UI_CLOSE
diff --git a/tgui/packages/tgui/index.js b/tgui/packages/tgui/index.js
index 6ea1f6c4ceba..bccc0ef9160b 100644
--- a/tgui/packages/tgui/index.js
+++ b/tgui/packages/tgui/index.js
@@ -15,6 +15,7 @@ import './styles/themes/ntos.scss';
import './styles/themes/paper.scss';
import './styles/themes/retro.scss';
import './styles/themes/syndicate.scss';
+import './styles/themes/admintickets.scss';
import { perf } from 'common/perf';
import { setupHotReloading } from 'tgui-dev-server/link/client';
diff --git a/tgui/packages/tgui/interfaces/TicketListPanel.js b/tgui/packages/tgui/interfaces/TicketListPanel.js
new file mode 100644
index 000000000000..f4b933de5c32
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/TicketListPanel.js
@@ -0,0 +1,204 @@
+
+import { useBackend, useLocalState } from '../backend';
+import { Section, Collapsible, Button, Tabs, Flex } from '../components';
+import { Window } from '../layouts';
+import { Fragment } from 'inferno';
+
+export const TicketListPanel = (props, context) => {
+ const { act, data } = useBackend(context);
+
+ const FILTERS = [
+ "ALL",
+ "MY TICKETS",
+ "UNCLAIMED",
+ ];
+
+ const [filterType, setFilterType] = useLocalState(context, 'filterType', FILTERS[0]);
+
+ return (
+
+
+
+ {FILTERS.map(filter => (
+ setFilterType(filter)}>
+ {filter}
+
+ ))}
+
+
+
+
+ );
+};
+
+export const TicketListView = (props, context) => {
+ const { data, filter_type } = props;
+ const { act } = useBackend(context);
+
+ const open_count = data.unresolved_tickets.length;
+ const closed_count = data.resolved_tickets.length;
+ const total_count = open_count + closed_count;
+
+ const filterTicket = function (ticket) {
+ if (filter_type === "ALL") return true;
+ if (filter_type === "MY TICKETS" && ticket.admin_key === data.user_key) return true;
+ if (filter_type === "UNCLAIMED" && !ticket.admin_key) return true;
+ return false;
+ };
+
+ return (
+
+
+ {data.unresolved_tickets.filter(filterTicket).reverse().map(ticket => (
+
+ ))}
+
+
+ {data.resolved_tickets.filter(filterTicket).reverse().map(ticket => (
+
+ ))}
+
+
+ );
+};
+
+export const TicketSummary = (props, context) => {
+ const { ticket, user } = props;
+ const { act } = useBackend(context);
+
+ const buttons = [
+ [
+ {
+ name: 'View',
+ act: 'view',
+ icon: 'eye',
+ },
+ {
+ name: '',
+ act: 'adminmoreinfo',
+ icon: 'question',
+ disabled: !ticket.has_mob,
+ },
+ {
+ name: 'PP',
+ act: 'PP',
+ icon: 'user',
+ disabled: !ticket.has_mob,
+ },
+ {
+ name: 'VV',
+ act: 'VV',
+ icon: 'cog',
+ disabled: !ticket.has_mob,
+ },
+ {
+ name: 'FLW',
+ act: 'FLW',
+ icon: 'arrow-up',
+ disabled: !ticket.has_mob,
+ },
+ {
+ name: 'TP',
+ act: 'TP',
+ icon: 'book-dead',
+ disabled: !ticket.has_mob,
+ },
+ {
+ name: 'Logs',
+ act: 'Logs',
+ icon: 'file',
+ disabled: !ticket.has_mob,
+ },
+ ],
+ [
+ {
+ name: 'Administer',
+ act: 'Administer',
+ icon: 'folder-open',
+ },
+ {
+ name: 'Reject',
+ act: 'Reject',
+ icon: 'ban',
+ },
+ {
+ name: ticket.is_resolved ? 'Unresolve' : 'Resolve',
+ act: 'Resolve',
+ icon: 'check',
+ },
+ {
+ name: 'IC',
+ act: 'IC',
+ icon: 'male',
+ disabled: !ticket.has_client,
+ },
+ {
+ name: 'MHelp',
+ act: 'MHelp',
+ icon: 'info',
+ disabled: !ticket.has_client,
+ },
+ ],
+ ];
+
+ return (
+
+ Owner:
+
+ Admin: {ticket.admin_key ? ticket.admin_key : "UNCLAIMED"}
+ {!ticket.has_client ? "DISCONNECTED" : ""}
+
+ {buttons.map((button_row, i) => (
+
+ {button_row.map(button => (
+
+
+
+ ))}
+
+ ))}
+
+
+ );
+};
diff --git a/tgui/packages/tgui/interfaces/TicketPanel.js b/tgui/packages/tgui/interfaces/TicketPanel.js
new file mode 100644
index 000000000000..f531c960c285
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/TicketPanel.js
@@ -0,0 +1,217 @@
+
+import { useBackend, useLocalState } from '../backend';
+import { Section, Button, Table, Input, TextArea, Box, Flex } from '../components';
+import { Window } from '../layouts';
+import { KEY_ENTER } from 'common/keycodes';
+
+export const TicketPanel = (props, context) => {
+ const { act, data } = useBackend(context);
+
+ const buttons = [
+ [
+ {
+ name: '',
+ act: 'adminmoreinfo',
+ icon: 'question',
+ disabled: !data.has_mob,
+ },
+ {
+ name: 'PP',
+ act: 'PP',
+ icon: 'user',
+ disabled: !data.has_mob,
+ },
+ {
+ name: 'VV',
+ act: 'VV',
+ icon: 'cog',
+ disabled: !data.has_mob,
+ },
+ {
+ name: 'FLW',
+ act: 'FLW',
+ icon: 'arrow-up',
+ disabled: !data.has_mob,
+ },
+ {
+ name: 'TP',
+ act: 'TP',
+ icon: 'book-dead',
+ disabled: !data.has_mob,
+ },
+ {
+ name: 'Logs',
+ act: 'Logs',
+ icon: 'file',
+ disabled: !data.has_mob,
+ },
+ {
+ name: 'Smite',
+ act: 'Smite',
+ icon: 'bolt',
+ disabled: !data.has_mob,
+ },
+ ],
+ [
+ {
+ name: 'Administer',
+ act: 'Administer',
+ icon: 'folder-open',
+ },
+ {
+ name: data.popups ? 'Deactivate Popups' : 'Activate Popups',
+ act: 'togglePopups',
+ icon: 'window-restore',
+ selected: data.popups,
+ },
+ {
+ name: 'Reject',
+ act: 'Reject',
+ icon: 'ban',
+ },
+ ],
+ [
+ {
+ name: data.is_resolved ? 'Unresolve' : 'Resolve',
+ act: 'Resolve',
+ icon: 'check',
+ },
+ {
+ name: 'IC',
+ act: 'IC',
+ icon: 'male',
+ disabled: !data.has_client,
+ },
+ {
+ name: 'Wiki',
+ act: 'Wiki',
+ icon: 'film',
+ disabled: !data.has_mob,
+ },
+ {
+ name: 'Bug',
+ act: 'Bug',
+ icon: 'bug',
+ disabled: !data.has_mob,
+ },
+ {
+ name: 'MHelp',
+ act: 'MHelp',
+ icon: 'info',
+ disabled: !data.has_client,
+ },
+ ],
+ ];
+ if (data.is_admin) {
+ return (
+
+
+
+
+ Assigned Admin: {data.admin || 'Unassigned'}
+
+ Is{data.is_resolved ? '' : ' not'} resolved
+
+
+
+ Job: {data.role}
+ Antag: {data.antag || 'No'}
+ Location: {data.location}
+
+
+ {buttons.map((button_row, i) => (
+
+ {button_row.map(button => (
+
+
+
+ ))}
+
+ ))}
+
+
+
+
+
+ );
+ }
+ return (
+
+
+
+
+
+ );
+
+};
+
+export const TicketMessages = (props, context) => {
+ const { ticket, title } = props;
+ const { act } = useBackend(context);
+
+ const [
+ message,
+ setMessage,
+ ] = useLocalState(context, 'text', '');
+
+ return (
+
+ );
+
+};
diff --git a/tgui/packages/tgui/styles/themes/admintickets.scss b/tgui/packages/tgui/styles/themes/admintickets.scss
new file mode 100644
index 000000000000..fdd917234ab8
--- /dev/null
+++ b/tgui/packages/tgui/styles/themes/admintickets.scss
@@ -0,0 +1,30 @@
+@use '../base.scss';
+@use 'sass:meta';
+
+.theme-admintickets {
+ .Layout__content {
+ background-image: none;
+ font-size: base.em(15px);
+ }
+
+ @include meta.load-css('../components/Input.scss');
+
+ .replybox {
+ margin-top: 10px;
+ line-height: 2;
+
+ .Input__input {
+ top: base.em(3px)
+ }
+ }
+
+ .myticket {
+ .Section__titleText {
+ color: rgb(224, 213, 60);
+ }
+ }
+
+ .ticket_section {
+ font-size: base.em(14px);
+ }
+}
diff --git a/yogstation.dme b/yogstation.dme
index c1367aba2a16..389f817069ab 100644
--- a/yogstation.dme
+++ b/yogstation.dme
@@ -3026,6 +3026,7 @@
#include "code\modules\tgui\states\deep_inventory.dm"
#include "code\modules\tgui\states\default.dm"
#include "code\modules\tgui\states\hands.dm"
+#include "code\modules\tgui\states\holder.dm"
#include "code\modules\tgui\states\human_adjacent.dm"
#include "code\modules\tgui\states\inventory.dm"
#include "code\modules\tgui\states\language_menu.dm"
@@ -3289,6 +3290,7 @@
#include "yogstation\code\modules\admin\verbs\shuttle_verbs.dm"
#include "yogstation\code\modules\admin\verbs\spawnfloorcluwne.dm"
#include "yogstation\code\modules\admin\verbs\telecomms.dm"
+#include "yogstation\code\modules\admin\verbs\ticketpanel.dm"
#include "yogstation\code\modules\antagonists\_common\antag_datum.dm"
#include "yogstation\code\modules\antagonists\abductor\equipment\abduction_outfits.dm"
#include "yogstation\code\modules\antagonists\blob\blob\blobs\core.dm"
diff --git a/yogstation/code/modules/admin/verbs/adminhelp.dm b/yogstation/code/modules/admin/verbs/adminhelp.dm
index 0683fc415d1a..5d50b0c5e665 100644
--- a/yogstation/code/modules/admin/verbs/adminhelp.dm
+++ b/yogstation/code/modules/admin/verbs/adminhelp.dm
@@ -455,6 +455,10 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
//Show the ticket panel
/datum/admin_help/proc/TicketPanel()
+ if(GLOB.experimental_adminpanel)
+ ui_interact(usr)
+ return
+
var/reply_link = " Reply"
var/refresh_link = "
Refresh"
@@ -612,7 +616,10 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
set name = "Adminlisttickets"
set category = "Admin"
- view_tickets_main(TICKET_FLAG_LIST_ALL)
+ if(GLOB.experimental_adminpanel)
+ GLOB.ahelp_tickets.ui_interact(usr)
+ else
+ view_tickets_main(TICKET_FLAG_LIST_ALL)
/client/proc/view_tickets_main(var/flag)
flag = text2num(flag)
diff --git a/yogstation/code/modules/admin/verbs/ticketpanel.dm b/yogstation/code/modules/admin/verbs/ticketpanel.dm
new file mode 100644
index 000000000000..8b0c26701eed
--- /dev/null
+++ b/yogstation/code/modules/admin/verbs/ticketpanel.dm
@@ -0,0 +1,209 @@
+GLOBAL_VAR_INIT(experimental_adminpanel, TRUE)
+
+/datum/admin_help_tickets/ui_state(mob/user)
+ return GLOB.holder_state
+
+/datum/admin_help_tickets/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "TicketListPanel")
+ ui.open()
+
+/datum/admin_help_tickets/ui_data(mob/user)
+ . = list()
+ .["unresolved_tickets"] = list()
+ .["resolved_tickets"] = list()
+ .["user_key"] = user.key
+
+ for(var/datum/admin_help/ahelp as anything in GLOB.ahelp_tickets.tickets_list)
+ var/ticket_data = list()
+ ticket_data["name"] = ahelp.name
+ ticket_data["id"] = ahelp.id
+ ticket_data["initiator_key_name"] = ahelp.initiator_key_name
+ ticket_data["initiator_ckey"] = ahelp.initiator_ckey
+ ticket_data["admin_key"] = ahelp.handling_admin && ahelp.handling_admin.key
+ ticket_data["active"] = ahelp.state == AHELP_ACTIVE
+
+ ticket_data["has_client"] = !!ahelp.initiator
+ ticket_data["has_mob"] = ticket_data["has_client"] && !!ahelp.initiator.mob
+
+ if(ahelp.state == AHELP_ACTIVE)
+ .["unresolved_tickets"] += list(ticket_data)
+ else
+ .["resolved_tickets"] += list(ticket_data)
+
+/datum/admin_help_tickets/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
+ . = ..()
+ if(.)
+ return
+ var/datum/admin_help/ticket = tickets_list[params["id"]]
+ if(!ticket)
+ return FALSE
+
+ . = TRUE
+ if(ticket_ui_act(action, ticket))
+ return
+ switch(action)
+ if("view")
+ ticket.TicketPanel()
+ return
+ if("reply")
+ usr.client.cmd_admin_pm(ticket.initiator)
+ return
+ return FALSE
+
+/datum/admin_help/ui_state(mob/user)
+ return GLOB.always_state
+
+/datum/admin_help/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "TicketPanel")
+ ui.open()
+
+/datum/admin_help/ui_data(mob/user)
+ . = list()
+ .["is_admin"] = !!user.client.holder
+ .["name"] = name
+ .["id"] = id
+ .["admin"] = handling_admin && handling_admin.key
+ .["is_resolved"] = state != AHELP_ACTIVE
+ .["initiator_key_name"] = initiator_key_name
+ .["popups"] = popups_enabled
+
+ var/mob/initiator_mob = initiator && initiator.mob
+ var/datum/mind/initiator_mind = initiator_mob && initiator_mob.mind
+ .["has_client"] = !!initiator
+ .["has_mob"] = !!initiator_mob
+ .["role"] = initiator_mind && initiator_mind.assigned_role
+ .["antag"] = initiator_mind && initiator_mind.special_role
+
+ var/location = ""
+ if(initiator_mob)
+ var/turf/T = get_turf(initiator.mob)
+ location = "([initiator.mob.loc == T ? "" : "in [initiator.mob.loc] at "][T.x], [T.y], [T.z]"
+ if(isturf(T))
+ if(isarea(T.loc))
+ location += " in area [T.loc]"
+ location += ")"
+ .["location"] = location
+
+ .["log"] = list()
+
+ for(var/datum/ticket_log/TL as anything in _interactions)
+ var/log_data = list()
+ log_data["time"] = TL.gametime
+ log_data["user"] = TL.user
+ log_data["text"] = TL.text
+ log_data["for_admins"] = TL.for_admins
+ .["log"] += list(log_data)
+
+
+/datum/admin_help/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
+ . = ..()
+ if(.)
+ return
+ . = TRUE
+ if(ticket_ui_act(action, src))
+ return
+
+ switch(action)
+ if("send_message")
+ var/message = params["message"]
+ if(usr.client.holder)
+ usr.client.cmd_admin_pm(initiator, message)
+ return
+ if(usr.client.current_ticket != src)
+ to_chat(usr, "You are not able to reply to this ticket. To open a ticket, please use the adminhelp verb")
+ if(handling_admin)
+ usr.client.cmd_admin_pm(handling_admin, message)
+ else
+ MessageNoRecipient(message)
+ return
+ return FALSE
+
+/proc/ticket_ui_act(action, T)
+ var/datum/admin_help/ticket = T
+ if(!ticket)
+ return
+ . = TRUE
+ switch(action)
+ if("adminmoreinfo")
+ if(!ticket.initiator)
+ to_chat(usr, "Client not found")
+ return
+ usr.client.holder.adminmoreinfo(ticket.initiator.mob)
+ return
+ if("PP")
+ if(!ticket.initiator)
+ to_chat(usr, "Client not found")
+ return
+ usr.client.holder.show_player_panel(ticket.initiator.mob)
+ return
+ if("VV")
+ if(!ticket.initiator)
+ to_chat(usr, "Client not found")
+ return
+ usr.client.debug_variables(ticket.initiator.mob)
+ return
+ if("SM")
+ if(!ticket.initiator)
+ to_chat(usr, "Client not found")
+ return
+ usr.client.cmd_admin_subtle_message(ticket.initiator.mob)
+ return
+ if("FLW")
+ if(!ticket.initiator)
+ to_chat(usr, "Client not found")
+ return
+ usr.client.holder.observe_follow(ticket.initiator.mob)
+ return
+ if("CA")
+ usr.client.check_antagonists()
+ return
+ if("Resolve")
+ ticket.Resolve()
+ return
+ if("Reject")
+ ticket.Reject()
+ return
+ if("Close")
+ ticket.Close()
+ return
+ if("IC")
+ ticket.ICIssue()
+ return
+ if("MHelp")
+ ticket.MhelpQuestion()
+ return
+ if("togglePopups")
+ ticket.PopUps()
+ return
+ if("Administer")
+ ticket.Administer()
+ return
+ if("Wiki")
+ ticket.WikiIssue()
+ return
+ if("Bug")
+ ticket.GithubIssue()
+ return
+ if("TP")
+ if(!ticket.initiator)
+ to_chat(usr, "Client not found")
+ return
+ usr.client.holder.show_traitor_panel(ticket.initiator.mob)
+ return
+ if("Logs")
+ if(!ticket.initiator)
+ to_chat(usr, "Client not found")
+ return
+ show_individual_logging_panel(ticket.initiator.mob)
+ return
+ if("Smite")
+ if(!ticket.initiator)
+ to_chat(usr, "Client not found")
+ return
+ usr.client.smite(ticket.initiator.mob)
+ return
+ return FALSE
diff --git a/yogstation/interface/interface.dm b/yogstation/interface/interface.dm
index c3eb4237754b..4266edb87b2b 100644
--- a/yogstation/interface/interface.dm
+++ b/yogstation/interface/interface.dm
@@ -7,16 +7,14 @@
/client/verb/show_tickets()
set name = "Tickets"
set desc = "Show list of tickets"
- set hidden = 1
+ set hidden = TRUE
if(holder)
view_tickets()
else
- for(var/I in GLOB.ahelp_tickets.tickets_list)
- var/datum/admin_help/T = I
- if(compare_ckey(T.initiator_ckey, usr) && T.state == AHELP_ACTIVE)
- T.TicketPanel()
- return
+ if(current_ticket && current_ticket.state == AHELP_ACTIVE)
+ current_ticket.TicketPanel()
+ return
to_chat(src, "You have no open tickets!")
- return
\ No newline at end of file
+ return