diff --git a/code/__DEFINES/_click.dm b/code/__DEFINES/_click.dm index 820f71d3668c1c..6dff5a063e9fdb 100644 --- a/code/__DEFINES/_click.dm +++ b/code/__DEFINES/_click.dm @@ -2,33 +2,33 @@ //this is mostly for ease of use and for finding all the things that use say RIGHT_CLICK rather then just searching "right" -//Mouse buttons pressed/held/released +//Mouse buttons pressed/held/released #define RIGHT_CLICK "right" #define MIDDLE_CLICK "middle" #define LEFT_CLICK "left" -//Keys held down during the mouse action +//Keys held down during the mouse action #define CTRL_CLICK "ctrl" #define ALT_CLICK "alt" #define SHIFT_CLICK "shift" -//Cells involved if using a Grid control +//Cells involved if using a Grid control #define DRAG_CELL "drag-cell" #define DROP_CELL "drop-cell" -//The button used for dragging (only sent for unrelated mouse up/down messages during a drag) +//The button used for dragging (only sent for unrelated mouse up/down messages during a drag) #define DRAG "drag" -//If the mouse is over a link in maptext, or this event is related to clicking such a link +//If the mouse is over a link in maptext, or this event is related to clicking such a link #define LINK "link" -//Pixel coordinates relative to the icon's position on screen +//Pixel coordinates relative to the icon's position on screen #define VIS_X "vis-x" #define VIS_Y "vis-y" -//Pixel coordinates within the icon, in the icon's coordinate space +//Pixel coordinates within the icon, in the icon's coordinate space #define ICON_X "icon-x" #define ICON_Y "icon-y" -//Pixel coordinates in screen_loc format ("[tile_x]:[pixel_x],[tile_y]:[pixel_y]") +//Pixel coordinates in screen_loc format ("[tile_x]:[pixel_x],[tile_y]:[pixel_y]") #define SCREEN_LOC "screen-loc" diff --git a/code/__DEFINES/devices.dm b/code/__DEFINES/devices.dm index d28e16ff4330a3..a392f06cb1e002 100644 --- a/code/__DEFINES/devices.dm +++ b/code/__DEFINES/devices.dm @@ -16,9 +16,42 @@ #define CART_DRONEPHONE (1<<14) #define CART_DRONEACCESS (1<<15) +/// PDA ui menu defines +#define PDA_UI_HUB 0 +#define PDA_UI_NOTEKEEPER 1 +#define PDA_UI_MESSENGER 2 +#define PDA_UI_READ_MESSAGES 21 +#define PDA_UI_ATMOS_SCAN 3 +#define PDA_UI_SKILL_TRACKER 4 +/// mode is divided by on return +#define PDA_UI_RETURN_DIVIDER 10 +/// if the new mode from return is between these, go straight to the hub. +#define PDA_UI_REDIRECT_HUB_MIN 4 +#define PDA_UI_REDIRECT_HUB_MAX 9 +#define PDA_UI_CREW_MANIFEST 41 +#define PDA_UI_STATUS_DISPLAY 42 +#define PDA_UI_POWER_MONITOR 43 +#define PDA_UI_POWER_MONITOR_SELECTED 433 +#define PDA_UI_MED_RECORDS 44 +#define PDA_UI_MED_RECORD_SELECTED 441 +#define PDA_UI_SEC_RECORDS 45 +#define PDA_UI_SEC_RECORD_SELECTED 451 +#define PDA_UI_SUPPLY_RECORDS 46 +#define PDA_UI_SILO_LOGS 47 +#define PDA_UI_BOTS_ACCESS 48 +#define PDA_UI_JANNIE_LOCATOR 49 +#define PDA_UI_EMOJI_GUIDE 50 +#define PDA_UI_SIGNALER 51 +#define PDA_UI_NEWSCASTER 52 +#define PDA_UI_NEWSCASTER_ERROR 53 + + // Used by PDA and cartridge code to reduce repetitiveness of spritesheets #define PDAIMG(what) {""} +// Used to stringify message targets before sending the signal datum. +#define STRINGIFY_PDA_TARGET(name, job) "[name] ([job])" + //N-spect scanner defines #define INSPECTOR_PRINT_SOUND_MODE_NORMAL 1 #define INSPECTOR_PRINT_SOUND_MODE_CLASSIC 2 diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm index b537151fcd5957..dbe7517a11084f 100644 --- a/code/__DEFINES/traits.dm +++ b/code/__DEFINES/traits.dm @@ -541,6 +541,11 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai /// Hearing trait that is from the hearing component #define CIRCUIT_HEAR_TRAIT "circuit_hear" +/// PDA Traits. This one makes PDAs explode if the user opens the messages menu +#define TRAIT_PDA_MESSAGE_MENU_RIGGED "pda_message_menu_rigged" +/// This one denotes a PDA has received a rigged message and will explode when the user tries to reply to a rigged PDA message +#define TRAIT_PDA_CAN_EXPLODE "pda_can_explode" + /// If present on a [/mob/living/carbon], will make them appear to have a medium level disease on health HUDs. #define TRAIT_DISEASELIKE_SEVERITY_MEDIUM "diseaselike_severity_medium" diff --git a/code/datums/components/uplink.dm b/code/datums/components/uplink.dm index 70dcc4258f886c..a9f8d2f0970b9f 100644 --- a/code/datums/components/uplink.dm +++ b/code/datums/components/uplink.dm @@ -304,7 +304,7 @@ interact(null, user) to_chat(user, span_hear("The PDA softly beeps.")) user << browse(null, "window=pda") - master.mode = 0 + master.ui_mode = PDA_UI_HUB return COMPONENT_STOP_RINGTONE_CHANGE /datum/component/uplink/proc/check_detonate() diff --git a/code/game/machinery/computer/security.dm b/code/game/machinery/computer/security.dm index a9c086a222b788..8ec08474a797ed 100644 --- a/code/game/machinery/computer/security.dm +++ b/code/game/machinery/computer/security.dm @@ -871,8 +871,8 @@ What a mess.*/ "name" = "Security Citation", "job" = "Citation Server", "message" = message, - "targets" = list("[P.owner] ([P.ownjob])"), - "automated" = 1 + "targets" = list(STRINGIFY_PDA_TARGET(P.owner, P.ownjob)), + "automated" = TRUE )) signal.send_to_receivers() usr.log_message("(PDA: Citation Server) sent \"[message]\" to [signal.format_target()]", LOG_PDA) diff --git a/code/game/machinery/telecomms/computers/message.dm b/code/game/machinery/telecomms/computers/message.dm index 6cbee13344dc4f..a3652af28514e7 100644 --- a/code/game/machinery/telecomms/computers/message.dm +++ b/code/game/machinery/telecomms/computers/message.dm @@ -420,7 +420,7 @@ "name" = "[customsender]", "job" = "[customjob]", "message" = custommessage, - "targets" = list("[customrecepient.owner] ([customrecepient.ownjob])") + "targets" = list(STRINGIFY_PDA_TARGET(customrecepient.owner, customrecepient.ownjob)) )) // this will log the signal and transmit it to the target linkedServer.receive_information(signal, null) diff --git a/code/game/objects/items/devices/PDA/PDA.dm b/code/game/objects/items/devices/PDA/PDA.dm index 161fa3124eaf44..ccb1633d90a7d2 100644 --- a/code/game/objects/items/devices/PDA/PDA.dm +++ b/code/game/objects/items/devices/PDA/PDA.dm @@ -41,7 +41,7 @@ GLOBAL_LIST_EMPTY(PDAs) /// Current cartridge var/obj/item/cartridge/cartridge = null /// Controls what menu the PDA will display. 0 is hub; the rest are either built in or based on cartridge. - var/mode = 0 + var/ui_mode = PDA_UI_HUB /// Icon to be overlayed for message alerts. Taken from the pda icon file. var/icon_alert = "pda-r" /// Icon to be overlayed when an active pAI is slotted in. @@ -224,6 +224,10 @@ GLOBAL_LIST_EMPTY(PDAs) to_chat(user, span_warning("You don't have the dexterity to do this!")) return + if(HAS_TRAIT(src, TRAIT_PDA_MESSAGE_MENU_RIGGED) && ui_mode == PDA_UI_MESSENGER) + explode(user, from_message_menu = TRUE) + return + ..() var/datum/asset/spritesheet/assets = get_asset_datum(/datum/asset/spritesheet/simple/pda) @@ -240,12 +244,12 @@ GLOBAL_LIST_EMPTY(PDAs) dat += "[PDAIMG(refresh)]Refresh" - if ((!isnull(cartridge)) && (mode == 0)) + if ((!isnull(cartridge)) && ui_mode == PDA_UI_HUB) dat += " | [PDAIMG(eject)]Eject [cartridge]" - if (mode) + if (ui_mode != PDA_UI_HUB) dat += " | [PDAIMG(menu)]Return" - if (mode == 0) + else dat += "
" dat += "
Toggle Font" dat += " | Change Color" @@ -259,8 +263,8 @@ GLOBAL_LIST_EMPTY(PDAs) dat += "Warning: No owner information entered. Please swipe card.

" dat += "[PDAIMG(refresh)]Retry" else - switch (mode) - if (0) + switch (ui_mode) + if (PDA_UI_HUB) dat += "

PERSONAL DATA ASSISTANT v.1.2

" dat += "Owner: [owner], [ownjob]
" dat += text("ID: [id ? "[id.registered_name], [id.assignment]" : "----------"]") @@ -275,40 +279,40 @@ GLOBAL_LIST_EMPTY(PDAs) dat += "

General Functions

" dat += "
" if (cartridge.access & CART_ENGINE) dat += "

Engineering Functions

" dat += "" if (cartridge.access & CART_MEDICAL) dat += "

Medical Functions

" dat += "" if (cartridge.access & CART_SECURITY) dat += "

Security Functions

" dat += "" if(cartridge.access & CART_QUARTERMASTER) dat += "

Quartermaster Functions:

" dat += "" dat += "" @@ -316,15 +320,15 @@ GLOBAL_LIST_EMPTY(PDAs) dat += "" - if (1) + if (PDA_UI_NOTEKEEPER) dat += "

[PDAIMG(notes)] Notekeeper V2.2

" dat += "Edit
" if(notescanned) dat += "(This is a scanned image, editing it may cause some text formatting to change.)
" dat += "
[(!notehtml ? note : notehtml)]" - if (2) + if (PDA_UI_MESSENGER) dat += "

[PDAIMG(mail)] SpaceMessenger V3.9.6

" dat += "[PDAIMG(bell)]Ringer: [silent == 1 ? "Off" : "On"] | " dat += "[PDAIMG(mail)]Send / Receive: [toff == 1 ? "Off" : "On"] | " @@ -470,7 +474,8 @@ GLOBAL_LIST_EMPTY(PDAs) add_fingerprint(U) U.set_machine(src) - switch(href_list["choice"]) + var/choice = text2num(href_list["choice"]) || href_list["choice"] + switch(choice) //BASIC FUNCTIONS=================================== @@ -503,12 +508,9 @@ GLOBAL_LIST_EMPTY(PDAs) playsound(src, 'sound/machines/terminal_select.ogg', 15, TRUE) if("Return")//Return - if(mode<=9) - mode = 0 - else - mode = round(mode/10) - if(mode==4 || mode == 5)//Fix for cartridges. Redirects to hub. - mode = 0 + ui_mode = round(ui_mode/PDA_UI_RETURN_DIVIDER) + if(ISINRANGE(ui_mode, PDA_UI_REDIRECT_HUB_MIN, PDA_UI_REDIRECT_HUB_MAX))//Fix for cartridges. Redirects to hub. + ui_mode = PDA_UI_HUB if(!silent) playsound(src, 'sound/machines/terminal_select.ogg', 15, TRUE) if ("Authenticate")//Checks for ID @@ -526,28 +528,27 @@ GLOBAL_LIST_EMPTY(PDAs) //MENU FUNCTIONS=================================== - if("0")//Hub - mode = 0 - if(!silent) - playsound(src, 'sound/machines/terminal_select.ogg', 15, TRUE) - if("1")//Notes - mode = 1 + if(PDA_UI_HUB) + ui_mode = PDA_UI_HUB if(!silent) playsound(src, 'sound/machines/terminal_select.ogg', 15, TRUE) - if("2")//Messenger - mode = 2 + if(PDA_UI_NOTEKEEPER) + ui_mode = PDA_UI_NOTEKEEPER if(!silent) playsound(src, 'sound/machines/terminal_select.ogg', 15, TRUE) - if("21")//Read messeges - mode = 21 + if(PDA_UI_MESSENGER) + if(HAS_TRAIT(src, TRAIT_PDA_MESSAGE_MENU_RIGGED)) + explode(U, from_message_menu = TRUE) + return + ui_mode = PDA_UI_MESSENGER if(!silent) playsound(src, 'sound/machines/terminal_select.ogg', 15, TRUE) - if("3")//Atmos scan - mode = 3 + if(PDA_UI_READ_MESSAGES) + ui_mode = PDA_UI_MESSENGER if(!silent) playsound(src, 'sound/machines/terminal_select.ogg', 15, TRUE) - if("4")//Redirects to hub - mode = 0 + if(PDA_UI_ATMOS_SCAN) + ui_mode = PDA_UI_ATMOS_SCAN if(!silent) playsound(src, 'sound/machines/terminal_select.ogg', 15, TRUE) @@ -610,7 +611,7 @@ GLOBAL_LIST_EMPTY(PDAs) if ("Edit") var/n = stripped_multiline_input(U, "Please enter message", name, note) if (in_range(src, U) && loc == U) - if (mode == 1 && n) + if (ui_mode == PDA_UI_NOTEKEEPER && n) note = n notehtml = parsemarkdown(n, U) notescanned = FALSE @@ -639,6 +640,10 @@ GLOBAL_LIST_EMPTY(PDAs) return if("Message") create_message(U, locate(href_list["target"]) in GLOB.PDAs) + if("Mess_us_up") + if(!HAS_TRAIT(src, TRAIT_PDA_CAN_EXPLODE)) //in case someone ever tries to call this with forged hrefs + return + explode(U, locate(href_list["target"])) if("Sorting Mode") sort_by_job = !sort_by_job @@ -686,8 +691,8 @@ GLOBAL_LIST_EMPTY(PDAs) //LINK FUNCTIONS=================================== - else//Cartridge menu linking - mode = max(text2num(href_list["choice"]), 0) + else + ui_mode = max(choice, PDA_UI_HUB) else//If not in range, can't interact or not using the pda. U.unset_machine() @@ -696,7 +701,7 @@ GLOBAL_LIST_EMPTY(PDAs) //EXTRA FUNCTIONS=================================== - if (mode == 2 || mode == 21)//To clear message overlays. + if (ui_mode == PDA_UI_MESSENGER || ui_mode == PDA_UI_READ_MESSAGES)//To clear message overlays. update_appearance() if ((honkamt > 0) && (prob(60)))//For clown virus. @@ -739,7 +744,7 @@ GLOBAL_LIST_EMPTY(PDAs) update_slot_icon() -/obj/item/pda/proc/msg_input(mob/living/U = usr) +/obj/item/pda/proc/msg_input(mob/living/U = usr, rigged = FALSE) var/t = stripped_input(U, "Please enter message", name) if (!t || toff) return @@ -749,22 +754,27 @@ GLOBAL_LIST_EMPTY(PDAs) t = Gibberish(t, TRUE) return t -/obj/item/pda/proc/send_message(mob/living/user, list/obj/item/pda/targets, everyone) - var/message = msg_input(user) +/** + * Prompts the user to input and send a message to another PDA. + * the everyone arg is used for mass messaging from lawyer and captain carts. + * rigged for PDA bombs. fakename and fakejob for forged messages (also PDA bombs). + */ +/obj/item/pda/proc/send_message(mob/living/user, list/obj/item/pda/targets, everyone = FALSE, rigged = FALSE, fakename, fakejob) + var/message = msg_input(user, rigged) if(!message || !targets.len) - return + return FALSE if((last_text && world.time < last_text + 10) || (everyone && last_everyone && world.time < last_everyone + PDA_SPAM_DELAY)) - return + return FALSE var/list/filter_result = is_ic_filtered_for_pdas(message) if (filter_result) REPORT_CHAT_FILTER_TO_USER(user, filter_result) - return + return FALSE var/list/soft_filter_result = is_soft_ic_filtered_for_pdas(message) if (soft_filter_result) if(tgui_alert(usr,"Your message contains \"[soft_filter_result[CHAT_FILTER_INDEX_WORD]]\". \"[soft_filter_result[CHAT_FILTER_INDEX_REASON]]\", Are you sure you want to send it?", "Soft Blocked Word", list("Yes", "No")) != "Yes") - return + return FALSE message_admins("[ADMIN_LOOKUPFLW(usr)] has passed the soft filter for \"[soft_filter_result[CHAT_FILTER_INDEX_WORD]]\" they may be using a disallowed term in PDA messages. Message: \"[html_encode(message)]\"") log_admin_private("[key_name(usr)] has passed the soft filter for \"[soft_filter_result[CHAT_FILTER_INDEX_WORD]]\" they may be using a disallowed term in PDA messages. Message: \"[message]\"") @@ -774,23 +784,27 @@ GLOBAL_LIST_EMPTY(PDAs) var/list/string_targets = list() for (var/obj/item/pda/P in targets) if (P.owner && P.ownjob) // != src is checked by the UI - string_targets += "[P.owner] ([P.ownjob])" + string_targets += STRINGIFY_PDA_TARGET(P.owner, P.ownjob) for (var/obj/machinery/computer/message_monitor/M in targets) // In case of "Reply" to a message from a console, this will make the // message be logged successfully. If the console is impersonating // someone by matching their name and job, the reply will reach the // impersonated PDA. - string_targets += "[M.customsender] ([M.customjob])" + string_targets += STRINGIFY_PDA_TARGET(M.customsender, M.customjob) if (!string_targets.len) - return + return FALSE var/datum/signal/subspace/messaging/pda/signal = new(src, list( - "name" = "[owner]", - "job" = "[ownjob]", + "name" = "[fakename || owner]", + "job" = "[fakejob || ownjob]", "message" = message, "targets" = string_targets, "emojis" = allow_emojis, + "rigged" = rigged, )) + if(rigged) //Will skip the message server and go straight to the hub so it can't be cheesed by disabling the message server machine + signal.server_type = /obj/machinery/telecomms/hub + signal.data["reject"] = FALSE // Do not refuse the message if (picture) signal.data["photo"] = picture signal.send_to_receivers() @@ -800,7 +814,7 @@ GLOBAL_LIST_EMPTY(PDAs) to_chat(user, span_notice("ERROR: Server isn't responding.")) if(!silent) playsound(src, 'sound/machines/terminal_error.ogg', 15, TRUE) - return + return FALSE var/target_text = signal.format_target() if(allow_emojis) @@ -810,12 +824,14 @@ GLOBAL_LIST_EMPTY(PDAs) // Log it in our logs tnote += "→ To [target_text]:
[signal.format_message()]
" // Show it to ghosts - var/ghost_message = span_name("[owner] PDA Message --> [span_name("[target_text]")]: [signal.format_message()]") + var/ghost_message = span_name("[owner] [rigged ? "Rigged" : ""] PDA Message --> [span_name("[target_text]")]: [signal.format_message()]") for(var/mob/M in GLOB.player_list) if(isobserver(M) && (M.client?.prefs.chat_toggles & CHAT_GHOSTPDA)) to_chat(M, "[FOLLOW_LINK(M, user)] [ghost_message]") // Log in the talk log - user.log_talk(message, LOG_PDA, tag="PDA: [initial(name)] to [target_text]") + user.log_talk(message, LOG_PDA, tag="[rigged ? "Rigged" : ""] PDA: [initial(name)] to [target_text]") + if(rigged) + log_bomber(user, "Sent a Rigged PDA message (Name: [fakename || owner]. Job: [fakejob || ownjob]) to [english_list(string_targets)] [!is_special_character(user) ? "(TRIGGED BY NON-ANTAG)" : ""]") to_chat(user, span_info("PDA message sent to [target_text]: \"[message]\"")) if(!silent) playsound(src, 'sound/machines/terminal_success.ogg', 15, TRUE) @@ -824,9 +840,10 @@ GLOBAL_LIST_EMPTY(PDAs) last_text = world.time if (everyone) last_everyone = world.time + return TRUE /obj/item/pda/proc/receive_message(datum/signal/subspace/messaging/pda/signal) - tnote += "← From [signal.data["name"]] ([signal.data["job"]]):
[signal.format_message()]
" + tnote += "← From [signal.data["name"]] ([signal.data["job"]]):
[signal.format_message()]
" if (!silent) if(HAS_TRAIT(SSstation, STATION_TRAIT_PDA_GLITCHED)) @@ -843,7 +860,7 @@ GLOBAL_LIST_EMPTY(PDAs) L = get(src, /mob/living/silicon) if(L && (L.stat == CONSCIOUS || L.stat == SOFT_CRIT)) - var/reply = "(Reply)" + var/reply = "(Reply)" var/hrefstart var/hrefend if (isAI(L)) @@ -1138,10 +1155,15 @@ GLOBAL_LIST_EMPTY(PDAs) notescanned = TRUE to_chat(user, span_notice("Paper scanned. Saved to PDA's notekeeper.") ) - -/obj/item/pda/proc/explode() //This needs tuning. +/** + * Called when someone replies to a rigged PDA message. It explodes. + * from_message_menu : whether it's caused by the target opening the message menu too early. + */ +/obj/item/pda/proc/explode(mob/target, mob/bomber, from_message_menu = FALSE) var/turf/T = get_turf(src) + log_bomber(bomber, "PDA-bombed", target, "as [target.p_they()] tried to [from_message_menu ? "open the PDA message menu" : "reply to the rigged PDA message"] [bomber && !is_special_character(bomber) ? "(TRIGGED BY NON-ANTAG)" : ""]") + if (ismob(loc)) var/mob/M = loc M.show_message(span_userdanger("Your [src] explodes!"), MSG_VISUAL, span_warning("You hear a loud *pop*!"), MSG_AUDIBLE) @@ -1155,7 +1177,6 @@ GLOBAL_LIST_EMPTY(PDAs) else explosion(src, devastation_range = -1, heavy_impact_range = -1, light_impact_range = 2, flash_range = 3) qdel(src) - return /obj/item/pda/Destroy() GLOB.PDAs -= src diff --git a/code/game/objects/items/devices/PDA/PDA_types.dm b/code/game/objects/items/devices/PDA/PDA_types.dm index 216a618c5860c3..9b5db73659e9eb 100644 --- a/code/game/objects/items/devices/PDA/PDA_types.dm +++ b/code/game/objects/items/devices/PDA/PDA_types.dm @@ -36,7 +36,9 @@ silent = TRUE ttone = "silence" -/obj/item/pda/mime/msg_input(mob/living/U = usr) +/obj/item/pda/mime/msg_input(mob/living/U = usr, rigged = FALSE) + if(rigged) + return ..() if(emped || toff) return var/emojis = emoji_sanitize(stripped_input(U, "Please enter emojis", name)) diff --git a/code/game/objects/items/devices/PDA/cart.dm b/code/game/objects/items/devices/PDA/cart.dm index 8f7e418710c8bf..c8f845b9e3b8f2 100644 --- a/code/game/objects/items/devices/PDA/cart.dm +++ b/code/game/objects/items/devices/PDA/cart.dm @@ -193,31 +193,32 @@ /obj/item/cartridge/proc/generate_menu(mob/user) if(!host_pda) return - switch(host_pda.mode) - if(40) //signaller + switch(host_pda.ui_mode) + if(PDA_UI_SIGNALER) menu = "

[PDAIMG(signaler)] Remote Signaling System

" menu += {" -Send Signal
-Frequency: -- -- -[format_frequency(radio.frequency)] -+ -+
-
-Code: -- -- -[radio.code] -+ -+
"} - if (41) //crew manifest + Send Signal
+ Frequency: + - + - + [format_frequency(radio.frequency)] + + + +
+
+ Code: + - + - + [radio.code] + + + +
+ "} + if (PDA_UI_CREW_MANIFEST) menu = "

[PDAIMG(notes)] Crew Manifest

" menu += "
[GLOB.data_core.get_manifest_html(monochrome=TRUE)]
" - if (42) //status displays + if (PDA_UI_STATUS_DISPLAY) menu = "

[PDAIMG(status)] Station Status Display Interlink

" menu += "\[ Clear \]
" @@ -230,7 +231,7 @@ Code: menu += " Lockdown |" menu += " Biohazard \]
" - if (43) + if (PDA_UI_POWER_MONITOR) menu = "

[PDAIMG(power)] Power Monitors - Please select one


" powmonitor = null powermonitors = list() @@ -262,7 +263,7 @@ Code: menu += "" - if (433) + if (PDA_UI_POWER_MONITOR_SELECTED) menu = "

[PDAIMG(power)] Power Monitor


" if(!powmonitor || !powmonitor.get_powernet()) menu += span_danger("No connection
") @@ -283,24 +284,24 @@ Code: var/list/S = list(" Off","AOff"," On", " AOn") var/list/chg = list("N","C","F") -//Neither copytext nor copytext_char is appropriate here; neither 30 UTF-8 code units nor 30 code points equates to 30 columns of output. -//Some glyphs are very tall or very wide while others are small or even take up no space at all. -//Emojis can take modifiers which are many characters but render as only one glyph. -//A proper solution here (as far as Unicode goes, maybe not ideal as far as markup goes, a table would be better) -//would be to use [A.area.name] + //Neither copytext nor copytext_char is appropriate here; neither 30 UTF-8 code units nor 30 code points equates to 30 columns of output. + //Some glyphs are very tall or very wide while others are small or even take up no space at all. + //Emojis can take modifiers which are many characters but render as only one glyph. + //A proper solution here (as far as Unicode goes, maybe not ideal as far as markup goes, a table would be better) + //would be to use [A.area.name] for(var/obj/machinery/power/apc/A in L) menu += copytext_char(add_trailing(A.area.name, 30, " "), 1, 30) menu += " [S[A.equipment+1]] [S[A.lighting+1]] [S[A.environ+1]] [add_leading(display_power(A.lastused_total), 6, " ")] [A.cell ? "[add_leading(round(A.cell.percent()), 3, " ")]% [chg[A.charging+1]]" : " N/C"]
" menu += "" - if (44) //medical records //This thing only displays a single screen so it's hard to really get the sub-menu stuff working. + if (PDA_UI_MED_RECORDS) menu = "

[PDAIMG(medical)] Medical Record List

" if(GLOB.data_core.general) for(var/datum/data/record/R in sort_record(GLOB.data_core.general)) menu += "[R.fields["id"]]: [R.fields["name"]]
" menu += "
" - if(441) + if(PDA_UI_MED_RECORD_SELECTED) menu = "

[PDAIMG(medical)] Medical Record

" if(active1 in GLOB.data_core.general) @@ -337,14 +338,14 @@ Code: menu += "Record Lost!
" menu += "
" - if (45) //security records + if (PDA_UI_SEC_RECORDS) menu = "

[PDAIMG(cuffs)] Security Record List

" if(GLOB.data_core.general) for (var/datum/data/record/R in sort_record(GLOB.data_core.general)) menu += "
[R.fields["id"]]: [R.fields["name"]]
" menu += "
" - if(451) + if(PDA_UI_SEC_RECORD_SELECTED) menu = "

[PDAIMG(cuffs)] Security Record

" if(active1 in GLOB.data_core.general) @@ -367,12 +368,13 @@ Code: menu += text("
\nCrimes:") menu +={" - - - - - -"} + + + + + + + "} for(var/datum/data/crime/c in active3.fields["crim"]) menu += "" menu += "" @@ -387,7 +389,7 @@ Code: menu += "
" - if (47) //quartermaster order records + if (PDA_UI_SUPPLY_RECORDS) menu = "

[PDAIMG(crate)] Supply Record Interlink

" menu += "
Supply shuttle
" @@ -418,7 +420,7 @@ Code: menu += "
  • #[SO.id] - [SO.pack.name] requested by [SO.orderer]
  • " menu += "Upgrade NOW to Space Parts & Space Vendors PLUS for full remote order control and inventory management." - if (48) // quartermaster ore logs + if (PDA_UI_SILO_LOGS) menu = list("

    [PDAIMG(crate)] Ore Silo Logs

    ") if (GLOB.ore_silo_default) var/list/logs = GLOB.silo_access_logs[REF(GLOB.ore_silo_default)] @@ -436,7 +438,7 @@ Code: menu += "No ore silo detected!" menu = jointext(menu, "") - if (49) //janitorial locator + if (PDA_UI_JANNIE_LOCATOR) menu = "

    [PDAIMG(bucket)] Persistent Custodial Object Locator

    " var/turf/cl = get_turf(src) @@ -498,7 +500,7 @@ Code: menu += "ERROR: Unable to determine current location." menu += "

    Refresh GPS Locator" - if (53) // Newscaster + if (PDA_UI_NEWSCASTER) menu = "

    [PDAIMG(notes)] Newscaster Access

    " menu += "
    Current Newsfeed: [current_channel ? current_channel : "None"]
    " var/datum/newscaster/feed_channel/current @@ -520,10 +522,10 @@ Code: menu +="[comment.body]
    [comment.author] [comment.time_stamp]
    " menu += "
    Post Message" - if (54) // Beepsky, Medibot, Floorbot, and Cleanbot access + if (PDA_UI_BOTS_ACCESS) menu = "

    [PDAIMG(medbot)] Bots Interlink

    " bot_control() - if (55) // Emoji Guidebook for mimes + if (PDA_UI_EMOJI_GUIDE) menu = "

    [PDAIMG(emoji)] Emoji Guidebook

    " var/static/list/emoji_icon_states var/static/emoji_table @@ -539,7 +541,7 @@ Code: menu += "
    To use an emoji in a pda message, refer to the guide and add \":\" around the emoji. Your PDA supports the following emoji:
    " menu += emoji_table - if (99) //Newscaster message permission error + if (PDA_UI_NEWSCASTER_ERROR) //Newscaster message permission error menu = "
    ERROR : NOT AUTHORIZED [host_pda.id ? "" : "- ID SLOT EMPTY"]
    " return menu @@ -557,7 +559,7 @@ Code: active1 = find_record("id", href_list["target"], GLOB.data_core.general) if(active1) active2 = find_record("id", href_list["target"], GLOB.data_core.medical) - host_pda.mode = 441 + host_pda.ui_mode = PDA_UI_MED_RECORD_SELECTED if(!active2) active1 = null @@ -565,7 +567,7 @@ Code: active1 = find_record("id", href_list["target"], GLOB.data_core.general) if(active1) active3 = find_record("id", href_list["target"], GLOB.data_core.security) - host_pda.mode = 451 + host_pda.ui_mode = PDA_UI_SEC_RECORD_SELECTED if(!active3) active1 = null @@ -599,13 +601,13 @@ Code: if("Power Select") var/pnum = text2num(href_list["target"]) powmonitor = powermonitors[pnum] - host_pda.mode = 433 + host_pda.ui_mode = PDA_UI_POWER_MONITOR_SELECTED if("Supply Orders") - host_pda.mode =47 + host_pda.ui_mode = PDA_UI_SUPPLY_RECORDS if("Newscaster Access") - host_pda.mode = 53 + host_pda.ui_mode = PDA_UI_NEWSCASTER if("Newscaster Message") var/host_pda_owner_name = host_pda.id ? "[host_pda.id.registered_name] ([host_pda.id.assignment])" : "Unknown" @@ -615,16 +617,16 @@ Code: if (chan.channel_name == current_channel) current = chan if(current.locked && current.author != host_pda_owner_name) - host_pda.mode = 99 + host_pda.ui_mode = PDA_UI_NEWSCASTER_ERROR host_pda.Topic(null,list("choice"="Refresh")) return GLOB.news_network.SubmitArticle(message,host_pda.owner,current_channel) - host_pda.Topic(null,list("choice"=num2text(host_pda.mode))) + host_pda.Topic(null,list("choice"=num2text(host_pda.ui_mode))) return if("Newscaster Switch Channel") current_channel = host_pda.msg_input() - host_pda.Topic(null,list("choice"=num2text(host_pda.mode))) + host_pda.Topic(null,list("choice"=num2text(host_pda.ui_mode))) return //emoji previews diff --git a/code/game/objects/items/devices/PDA/virus_cart.dm b/code/game/objects/items/devices/PDA/virus_cart.dm index c601267e1ba7d7..2c282c63bfd386 100644 --- a/code/game/objects/items/devices/PDA/virus_cart.dm +++ b/code/game/objects/items/devices/PDA/virus_cart.dm @@ -15,7 +15,7 @@ /obj/item/cartridge/virus/special(mob/living/user, list/params) var/obj/item/pda/P = locate(params["target"]) in GLOB.PDAs //Leaving it alone in case it may do something useful, I guess. - send_virus(P,user) + INVOKE_ASYNC(src, .proc/send_virus, P, user) /obj/item/cartridge/virus/clown name = "\improper Honkworks 5.0 cartridge" @@ -56,27 +56,41 @@ icon_state = "cart" access = CART_REMOTE_DOOR remote_door_id = "smindicate" //Make sure this matches the syndicate shuttle's shield/door id!! //don't ask about the name, testing. - charges = 4 + charges = 6 -/obj/item/cartridge/virus/syndicate/send_virus(obj/item/pda/target, mob/living/U) +/obj/item/cartridge/virus/syndicate/send_virus(obj/item/pda/target, mob/living/user) if(charges <= 0) - to_chat(U, span_notice("Out of charges.")) + to_chat(user, span_notice("Out of charges.")) return - if(!isnull(target) && !target.toff) + if(!target || target.toff) + to_chat(user, span_alert("PDA not found.")) + return + + var/difficulty = 0 + if(target.cartridge) + difficulty += bit_count(target.cartridge.access&(CART_MEDICAL | CART_SECURITY | CART_ENGINE | CART_CLOWN | CART_JANITOR | CART_MANIFEST)) + if(target.cartridge.access & CART_MANIFEST) + difficulty++ //if cartridge has manifest access it has extra snowflake difficulty + if(SEND_SIGNAL(target, COMSIG_PDA_CHECK_DETONATE) & COMPONENT_PDA_NO_DETONATE || prob(difficulty * 15)) + user.show_message(span_danger("An error flashes on your [src]."), MSG_VISUAL) charges-- - var/difficulty = 0 - if(target.cartridge) - difficulty += bit_count(target.cartridge.access&(CART_MEDICAL | CART_SECURITY | CART_ENGINE | CART_CLOWN | CART_JANITOR | CART_MANIFEST)) - if(target.cartridge.access & CART_MANIFEST) - difficulty++ //if cartridge has manifest access it has extra snowflake difficulty - if(SEND_SIGNAL(target, COMSIG_PDA_CHECK_DETONATE) & COMPONENT_PDA_NO_DETONATE || prob(difficulty * 15)) - U.show_message(span_danger("An error flashes on your [src]."), MSG_VISUAL) - else - log_bomber(U, "triggered a PDA explosion on", target, "[!is_special_character(U) ? "(TRIGGED BY NON-ANTAG)" : ""]") - U.show_message(span_notice("Success!"), MSG_VISUAL) - target.explode() - else - to_chat(U, span_alert("PDA not found.")) + return + + var/original_host = host_pda + var/fakename = sanitize_name(stripped_input(user, "Enter a name for the rigged message.", "Forge Message", null, MAX_NAME_LEN), allow_numbers = TRUE) + if(!fakename || host_pda != original_host || !user.canUseTopic(host_pda, BE_CLOSE)) + return + var/fakejob = sanitize_name(stripped_input(user, "Enter a job for the rigged message.", "Forge Message", null, MAX_NAME_LEN), allow_numbers = TRUE) + if(!fakejob || host_pda != original_host || !user.canUseTopic(host_pda, BE_CLOSE)) + return + if(charges > 0 && host_pda.send_message(user, list(target), rigged = REF(user), fakename = fakename, fakejob = fakejob)) + charges-- + user.show_message(span_notice("Success!"), MSG_VISUAL) + //Rigs the PDA to explode if they try to outsmart us by using the message function menu. + var/reference = REF(src) + ADD_TRAIT(target, TRAIT_PDA_CAN_EXPLODE, reference) + ADD_TRAIT(target, TRAIT_PDA_MESSAGE_MENU_RIGGED, reference) + addtimer(TRAIT_CALLBACK_REMOVE(target, TRAIT_PDA_MESSAGE_MENU_RIGGED, reference), 10 SECONDS) /obj/item/cartridge/virus/frame name = "\improper F.R.A.M.E. cartridge" diff --git a/code/modules/jobs/job_types/security_officer.dm b/code/modules/jobs/job_types/security_officer.dm index 85d833f97f018c..8f5343e063c976 100644 --- a/code/modules/jobs/job_types/security_officer.dm +++ b/code/modules/jobs/job_types/security_officer.dm @@ -162,7 +162,7 @@ GLOBAL_LIST_EMPTY(security_officer_distribution) if (partners.len) for (var/obj/item/pda/pda as anything in GLOB.PDAs) if (pda.owner in partners) - targets += "[pda.owner] ([pda.ownjob])" + targets += STRINGIFY_PDA_TARGET(pda.owner, pda.ownjob) if (!targets.len) return diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index 35b0fe485b5ffb..f81e3efd922e86 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -290,8 +290,8 @@ "name" = "Security Citation", "job" = "Citation Server", "message" = message, - "targets" = list("[P.owner] ([P.ownjob])"), - "automated" = 1 + "targets" = list(STRINGIFY_PDA_TARGET(P.owner, P.ownjob)), + "automated" = TRUE )) signal.send_to_receivers() usr.log_message("(PDA: Citation Server) sent \"[message]\" to [signal.format_target()]", LOG_PDA) diff --git a/code/modules/uplink/uplink_items.dm b/code/modules/uplink/uplink_items.dm index c0b31074807d23..17d2b38bde11f2 100644 --- a/code/modules/uplink/uplink_items.dm +++ b/code/modules/uplink/uplink_items.dm @@ -994,11 +994,11 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) /datum/uplink_item/explosives/detomatix name = "Detomatix PDA Cartridge" - desc = "When inserted into a personal digital assistant, this cartridge gives you four opportunities to \ - detonate PDAs of crewmembers who have their message feature enabled. \ + desc = "When inserted into a personal digital assistant, this cartridge gives you the opportunity to \ + send up to six forged messages that will make PDAs of crewmembers explode when they try to reply to them. \ The concussive effect from the explosion will knock the recipient out for a short period, and deafen them for longer." item = /obj/item/cartridge/virus/syndicate - cost = 6 + cost = 4 restricted = TRUE /datum/uplink_item/explosives/emp
    CrimeDetailsAuthorTime Added
    CrimeDetailsAuthorTime Added
    [c.crimeName][c.crimeDetails]