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 +={"
-
-Crime |
-Details |
-Author |
-Time Added |
-
"}
+
+ Crime |
+ Details |
+ Author |
+ Time Added |
+
+ "}
for(var/datum/data/crime/c in active3.fields["crim"])
menu += "[c.crimeName] | "
menu += "[c.crimeDetails] | "
@@ -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