diff --git a/code/__DEFINES/admin.dm b/code/__DEFINES/admin.dm index e267097ff81..bd9ac554882 100644 --- a/code/__DEFINES/admin.dm +++ b/code/__DEFINES/admin.dm @@ -68,6 +68,8 @@ #define ADMIN_TAG(datum) "(TAG)" #define ADMIN_LUAVIEW(state) "(VIEW STATE)" #define ADMIN_LUAVIEW_CHUNK(state, log_index) "(VIEW CODE)" +/// Displays "(SHOW)" in the chat, when clicked it tries to show atom(paper). First you need to set the request_state variable to TRUE for the paper. +#define ADMIN_SHOW_PAPER(atom) "(SHOW)" /atom/proc/Admin_Coordinates_Readable(area_name, admin_jump_ref) var/turf/T = Safe_COORD_Location() diff --git a/code/modules/admin/admin_fax_panel.dm b/code/modules/admin/admin_fax_panel.dm new file mode 100644 index 00000000000..169a089e519 --- /dev/null +++ b/code/modules/admin/admin_fax_panel.dm @@ -0,0 +1,145 @@ +/** + * If client have R_ADMIN flag, opens an admin fax panel. + */ +/client/proc/fax_panel() + set category = "Admin.Events" + set name = "Fax Panel" + + if(!check_rights(R_ADMIN)) + return + + var/datum/fax_panel_interface/ui = new(usr) + ui.ui_interact(usr) + +/// Admin Fax Panel. Tool for sending fax messages faster. +/datum/fax_panel_interface + /// All faxes in from machinery list() + var/available_faxes = list() + /// List with available stamps + var/stamp_list = list() + + /// Paper which admin edit and send. + var/obj/item/paper/fax_paper = new /obj/item/paper(null) + + /// Default name of fax. Used when field with fax name not edited. + var/sending_fax_name = "Secret" + /// Default name of paper. paper - bluh-bluh. Used when field with paper name not edited. + var/default_paper_name = "Standart Report" + +/datum/fax_panel_interface/New() + //Get all faxes, and save them to our list. + for(var/obj/machinery/fax/fax in GLOB.machines) + available_faxes += WEAKREF(fax) + + //Get all stamps + for(var/stamp in subtypesof(/obj/item/stamp)) + var/obj/item/stamp/real_stamp = new stamp() + if(!istype(real_stamp, /obj/item/stamp/chameleon) && !istype(real_stamp, /obj/item/stamp/mod)) + var/stamp_detail = real_stamp.get_writing_implement_details() + stamp_list += list(list(real_stamp.name, real_stamp.icon_state, stamp_detail["stamp_class"])) + + //Give our paper special status, to read everywhere. + fax_paper.request_state = TRUE + +/** + * Return fax if name exists + * Arguments: + * * name - Name of fax what we try to find. + */ +/datum/fax_panel_interface/proc/get_fax_by_name(name) + if(!length(available_faxes)) + return null + + for(var/datum/weakref/weakrefed_fax as anything in available_faxes) + var/obj/machinery/fax/potential_fax = weakrefed_fax.resolve() + if(potential_fax && istype(potential_fax)) + if(potential_fax.fax_name == name) + return potential_fax + return null + +/datum/fax_panel_interface/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "AdminFax") + ui.open() + +/datum/fax_panel_interface/ui_state(mob/user) + return GLOB.admin_state + +/datum/fax_panel_interface/ui_static_data(mob/user) + var/list/data = list() + + data["faxes"] = list() + data["stamps"] = list() + + for(var/stamp in stamp_list) + data["stamps"] += list(stamp[1]) // send only names. + + for(var/datum/weakref/weakrefed_fax as anything in available_faxes) + var/obj/machinery/fax/another_fax = weakrefed_fax.resolve() + if(another_fax && istype(another_fax)) + data["faxes"] += list(another_fax.fax_name) + + return data + +/datum/fax_panel_interface/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) + if(..()) + return + + if(!check_rights(R_ADMIN)) + return + + var/obj/machinery/fax/action_fax + + if(params["faxName"]) + action_fax = get_fax_by_name(params["faxName"]) + + switch(action) + + if("follow") + if(!isobserver(usr)) + usr.client?.admin_ghost() + + usr.client?.admin_follow(action_fax) + + if("preview") // see saved variant + if(!fax_paper) + return + fax_paper.ui_interact(usr) + + if("save") // save paper + if(params["paperName"]) + default_paper_name = params["paperName"] + if(params["fromWho"]) + sending_fax_name = params["fromWho"] + + fax_paper.clear_paper() + var/stamp + var/stamp_class + + for(var/needed_stamp in stamp_list) + if(needed_stamp[1] == params["stamp"]) + stamp = needed_stamp[2] + stamp_class = needed_stamp[3] + break + + fax_paper.name = "paper — [default_paper_name]" + fax_paper.add_raw_text(params["rawText"]) + + if(stamp) + fax_paper.add_stamp(stamp_class, params["stampX"], params["stampY"], params["stampAngle"], stamp) + + fax_paper.update_static_data(usr) // OK, it's work, and update UI. + + if("send") + //copy + var/obj/item/paper/our_fax = fax_paper.copy(/obj/item/paper) + our_fax.name = fax_paper.name + //send + action_fax.receive(our_fax, sending_fax_name) + message_admins("[key_name_admin(usr)] has send custom fax message to [action_fax.name][ADMIN_FLW(action_fax)][ADMIN_SHOW_PAPER(fax_paper)].") + log_admin("[key_name(usr)] has send custom fax message to [action_fax.name]") + + if("createPaper") + var/obj/item/paper/our_paper = fax_paper.copy(/obj/item/paper, usr.loc) + our_paper.name = fax_paper.name diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index 58a3b47f9ba..f0ef72fe19f 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -86,6 +86,7 @@ GLOBAL_PROTECT(admin_verbs_admin) /client/proc/list_dna, /client/proc/list_fingerprints, /client/proc/message_pda, /*send a message to somebody on PDA*/ + /client/proc/fax_panel, /*send a paper to fax*/ /datum/admins/proc/trophy_manager, ) GLOBAL_LIST_INIT(admin_verbs_ban, list(/client/proc/unban_panel, /client/proc/ban_panel, /client/proc/stickybanpanel)) diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm index d675b81df26..94ba7af49fb 100644 --- a/code/modules/admin/topic.dm +++ b/code/modules/admin/topic.dm @@ -1738,3 +1738,12 @@ editor.force_view_chunk = log_entry["chunk"] editor.force_modal = "viewChunk" editor.ui_interact(usr) + + else if(href_list["show_paper"]) + if(!check_rights(R_ADMIN)) + return + + var/obj/item/paper/paper_to_show = locate(href_list["show_paper"]) + if(!paper_to_show) + return + paper_to_show.ui_interact(usr) diff --git a/code/modules/paperwork/fax.dm b/code/modules/paperwork/fax.dm index 846705c61e4..f576dd0c238 100644 --- a/code/modules/paperwork/fax.dm +++ b/code/modules/paperwork/fax.dm @@ -44,6 +44,11 @@ /obj/item/holochip, /obj/item/card ) + /// List with a fake-networks(not a fax actually), for request manager. + var/list/special_networks = list( + list(fax_name = "Central Command", fax_id = "central_command", color = "teal", emag_needed = FALSE), + list(fax_name = "Sabotage Department", fax_id = "syndicate", color = "red", emag_needed = TRUE), + ) /obj/machinery/fax/Initialize(mapload) . = ..() @@ -227,6 +232,7 @@ data["syndicate_network"] = (syndicate_network || (obj_flags & EMAGGED)) data["has_paper"] = !!loaded_item_ref?.resolve() data["fax_history"] = fax_history + data["special_faxes"] = special_networks return data /obj/machinery/fax/ui_act(action, list/params) @@ -245,6 +251,7 @@ playsound(src, 'sound/machines/eject.ogg', 50, FALSE) update_appearance() return TRUE + if("send") var/obj/item/loaded = loaded_item_ref?.resolve() if (!loaded) @@ -255,6 +262,27 @@ loaded_item_ref = null update_appearance() return TRUE + + if("send_special") + var/obj/item/paper/fax_paper = loaded_item_ref?.resolve() + if(!istype(fax_paper)) + to_chat(usr, icon2html(src.icon, usr) + span_warning("Fax cannot send all above paper on this protected network, sorry.")) + return + + fax_paper.request_state = TRUE + fax_paper.loc = null + + INVOKE_ASYNC(src, PROC_REF(animate_object_travel), fax_paper, "fax_receive", find_overlay_state(fax_paper, "send")) + playsound(src, 'sound/machines/high_tech_confirm.ogg', 50, vary = FALSE) + + history_add("Send", params["name"]) + + GLOB.requests.fax_request(usr.client, "sent a fax message from [fax_name]/[fax_id] to [params["name"]]", fax_paper) + to_chat(GLOB.admins, span_adminnotice("[icon2html(src.icon, GLOB.admins)]FAX REQUEST: [ADMIN_FULLMONTY(usr)]: [span_linkify("sent a fax message from [fax_name]/[fax_id][ADMIN_FLW(src)] to [params["name"]]")] [ADMIN_SHOW_PAPER(fax_paper)]"), confidential = TRUE) + log_fax(fax_paper, params["id"], params["name"]) + loaded_item_ref = null + update_appearance() + if("history_clear") history_clear() return TRUE @@ -473,3 +501,4 @@ return CONTEXTUAL_SCREENTIP_SET return . + diff --git a/code/modules/paperwork/paper.dm b/code/modules/paperwork/paper.dm index 4ce5a54cff7..924080fec6a 100644 --- a/code/modules/paperwork/paper.dm +++ b/code/modules/paperwork/paper.dm @@ -63,6 +63,9 @@ /// state checking on if it should be shown to a viewer. var/datum/weakref/camera_holder + ///If TRUE, staff can read paper everywhere, but usually from requests panel. + var/request_state = FALSE + /obj/item/paper/Initialize(mapload) . = ..() pixel_x = base_pixel_x + rand(-9, 9) @@ -328,7 +331,7 @@ // Are we on fire? Hard to read if so if(resistance_flags & ON_FIRE) return UI_CLOSE - if(camera_holder && can_show_to_mob_through_camera(user)) + if(camera_holder && can_show_to_mob_through_camera(user) || request_state) return UI_UPDATE if(!in_range(user, src) && !isobserver(user)) return UI_CLOSE diff --git a/code/modules/requests/request.dm b/code/modules/requests/request.dm index 1acace4f969..ddbca243528 100644 --- a/code/modules/requests/request.dm +++ b/code/modules/requests/request.dm @@ -1,12 +1,3 @@ -/// Requests from prayers -#define REQUEST_PRAYER "request_prayer" -/// Requests for Centcom -#define REQUEST_CENTCOM "request_centcom" -/// Requests for the Syndicate -#define REQUEST_SYNDICATE "request_syndicate" -/// Requests for the nuke code -#define REQUEST_NUKE "request_nuke" - /** * # Request * @@ -27,10 +18,12 @@ var/owner_name /// The message associated with the request var/message + /// Just any information, which you can to send with request. For example paper datum. + var/additional_information /// When the request was created var/timestamp -/datum/request/New(client/requestee, type, request) +/datum/request/New(client/requestee, type, request, additional_info) if (!requestee) qdel(src) return @@ -39,5 +32,6 @@ owner_ckey = owner.ckey req_type = type message = request + additional_information = additional_info timestamp = world.time owner_name = key_name(requestee, FALSE) diff --git a/code/modules/requests/request_manager.dm b/code/modules/requests/request_manager.dm index de8fc5e0f63..fef7e9fb0dd 100644 --- a/code/modules/requests/request_manager.dm +++ b/code/modules/requests/request_manager.dm @@ -1,3 +1,14 @@ +/// Requests from prayers +#define REQUEST_PRAYER "request_prayer" +/// Requests for Centcom +#define REQUEST_CENTCOM "request_centcom" +/// Requests for the Syndicate +#define REQUEST_SYNDICATE "request_syndicate" +/// Requests for the nuke code +#define REQUEST_NUKE "request_nuke" +/// Requests somebody from fax +#define REQUEST_FAX "request_fax" + GLOBAL_DATUM_INIT(requests, /datum/request_manager, new) /** @@ -86,6 +97,16 @@ GLOBAL_DATUM_INIT(requests, /datum/request_manager, new) /datum/request_manager/proc/nuke_request(client/C, message) request_for_client(C, REQUEST_NUKE, message) +/** + * Creates a request for fax answer + * + * Arguments: + * * requester - The client who is sending the request + * * message - Paper with text.. some stamps.. and another things. + */ +/datum/request_manager/proc/fax_request(client/requester, message, additional_info) + request_for_client(requester, REQUEST_FAX, message, additional_info) + /** * Creates a request and registers the request with all necessary internal tracking lists * @@ -94,8 +115,8 @@ GLOBAL_DATUM_INIT(requests, /datum/request_manager, new) * * type - The type of request, see defines * * message - The message */ -/datum/request_manager/proc/request_for_client(client/C, type, message) - var/datum/request/request = new(C, type, message) +/datum/request_manager/proc/request_for_client(client/C, type, message, additional_info) + var/datum/request/request = new(C, type, message, additional_info) if (!requests[C.ckey]) requests[C.ckey] = list() requests[C.ckey] += request @@ -193,6 +214,13 @@ GLOBAL_DATUM_INIT(requests, /datum/request_manager, new) SD.r_code = code message_admins("[key_name_admin(usr)] has set the self-destruct code to \"[code]\".") return TRUE + if ("show") + if(request.req_type != REQUEST_FAX) + to_chat(usr, "Request doesn't have a paper to read.", confidential = TRUE) + return TRUE + var/obj/item/paper/request_message = request.additional_information + request_message.ui_interact(usr) + return TRUE /datum/request_manager/ui_data(mob/user) . = list( @@ -207,7 +235,14 @@ GLOBAL_DATUM_INIT(requests, /datum/request_manager, new) "owner_ckey" = request.owner_ckey, "owner_name" = request.owner_name, "message" = request.message, + "additional_info" = request.additional_information, "timestamp" = request.timestamp, "timestamp_str" = gameTimestamp(wtime = request.timestamp) ) .["requests"] += list(data) + +#undef REQUEST_PRAYER +#undef REQUEST_CENTCOM +#undef REQUEST_SYNDICATE +#undef REQUEST_NUKE +#undef REQUEST_FAX diff --git a/tgstation.dme b/tgstation.dme index 5fa491c80a9..489cd66868d 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -2062,6 +2062,7 @@ #include "code\modules\actionspeed\modifiers\mood.dm" #include "code\modules\actionspeed\modifiers\status_effects.dm" #include "code\modules\admin\admin.dm" +#include "code\modules\admin\admin_fax_panel.dm" #include "code\modules\admin\admin_investigate.dm" #include "code\modules\admin\admin_pda_message.dm" #include "code\modules\admin\admin_ranks.dm" diff --git a/tgui/packages/tgui/interfaces/AdminFax.js b/tgui/packages/tgui/interfaces/AdminFax.js new file mode 100644 index 00000000000..a2166fabda0 --- /dev/null +++ b/tgui/packages/tgui/interfaces/AdminFax.js @@ -0,0 +1,237 @@ +import { useBackend, useLocalState } from '../backend'; +import { Section, Box, Dropdown, Button, Input, TextArea, Divider, NumberInput, Tooltip, Knob } from '../components'; +import { Window } from '../layouts'; + +export const AdminFax = (props, context) => { + return ( + + + + + + ); +}; + +export const FaxMainPanel = (props, context) => { + const { act, data } = useBackend(context); + + const [fax, setFax] = useLocalState(context, 'fax', ''); + const [saved, setSaved] = useLocalState(context, 'saved', false); + const [paperName, setPaperName] = useLocalState(context, 'paperName', ''); + const [fromWho, setFromWho] = useLocalState(context, 'fromWho', ''); + const [rawText, setRawText] = useLocalState(context, 'rawText', ''); + const [stamp, setStamp] = useLocalState(context, 'stampType', ''); + const [stampCoordX, setStampCoordX] = useLocalState( + context, + 'stampCoordX', + 0 + ); + const [stampCoordY, setStampCoordY] = useLocalState( + context, + 'stampCoordY', + 0 + ); + const [stampAngle, setStampAngle] = useLocalState(context, 'stampAngle', 0); + if (stamp && data.stamps[0] !== 'None') { + data.stamps.unshift('None'); + } + return ( +
+
+ + + }> + + setFax(value)} + /> + +
+
+ act('preview', { + faxName: fax, + }) + }> + Preview + + }> + + setPaperName(v)} + /> + + + + + + + setFromWho(v)} + /> + + + + + + +