Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

The detomatix cart now sends forged messages that make PDAs explode if one tries to reply to them instead of detonating them right off the bat. #62494

Merged
merged 5 commits into from Nov 14, 2021
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions code/__DEFINES/devices.dm
Expand Up @@ -19,6 +19,9 @@
// Used by PDA and cartridge code to reduce repetitiveness of spritesheets
#define PDAIMG(what) {"<span class="pda16x16 [#what]"></span>"}

// 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
Expand Down
5 changes: 5 additions & 0 deletions code/__DEFINES/traits.dm
Expand Up @@ -534,6 +534,11 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
/// Trait applied when an integrated circuit/module becomes undupable
#define TRAIT_CIRCUIT_UNDUPABLE "circuit_undupable"

/// 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"

Expand Down
4 changes: 2 additions & 2 deletions code/game/machinery/computer/security.dm
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion code/game/machinery/telecomms/computers/message.dm
Expand Up @@ -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)
Expand Down
67 changes: 47 additions & 20 deletions code/game/objects/items/devices/PDA/PDA.dm
Expand Up @@ -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) && mode == 2)
Ghommie marked this conversation as resolved.
Show resolved Hide resolved
explode(user, from_message_menu = TRUE)
return

..()

var/datum/asset/spritesheet/assets = get_asset_datum(/datum/asset/spritesheet/simple/pda)
Expand Down Expand Up @@ -535,6 +539,9 @@ GLOBAL_LIST_EMPTY(PDAs)
if(!silent)
playsound(src, 'sound/machines/terminal_select.ogg', 15, TRUE)
if("2")//Messenger
if(HAS_TRAIT(src, TRAIT_PDA_MESSAGE_MENU_RIGGED))
explode(U, from_message_menu = TRUE)
return
mode = 2
if(!silent)
playsound(src, 'sound/machines/terminal_select.ogg', 15, TRUE)
Expand Down Expand Up @@ -639,6 +646,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
Expand Down Expand Up @@ -739,7 +750,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
Expand All @@ -749,22 +760,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]\"")

Expand All @@ -774,23 +790,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()
Expand All @@ -800,7 +820,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)
Expand All @@ -810,12 +830,14 @@ GLOBAL_LIST_EMPTY(PDAs)
// Log it in our logs
tnote += "<i><b>&rarr; To [target_text]:</b></i><br>[signal.format_message()]<br>"
// Show it to ghosts
var/ghost_message = span_name("[owner] </span><span class='game say'>PDA Message</span> --> [span_name("[target_text]")]: <span class='message'>[signal.format_message()]")
var/ghost_message = span_name("[owner] </span><span class='game say'>[rigged ? "Rigged" : ""] PDA Message</span> --> [span_name("[target_text]")]: <span class='message'>[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)
Expand All @@ -824,9 +846,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 += "<i><b>&larr; From <a href='byond://?src=[REF(src)];choice=Message;target=[REF(signal.source)]'>[signal.data["name"]]</a> ([signal.data["job"]]):</b></i><br>[signal.format_message()]<br>"
tnote += "<i><b>&larr; From <a href='byond://?src=[REF(src)];choice=[signal.data["rigged"] ? "Mess_us_up" : "Message"];target=[signal.data["rigged"] || REF(signal.source)]'>[signal.data["name"]]</a> ([signal.data["job"]]):</b></i><br>[signal.format_message()]<br>"

if (!silent)
if(HAS_TRAIT(SSstation, STATION_TRAIT_PDA_GLITCHED))
Expand All @@ -843,7 +866,7 @@ GLOBAL_LIST_EMPTY(PDAs)
L = get(src, /mob/living/silicon)

if(L && (L.stat == CONSCIOUS || L.stat == SOFT_CRIT))
var/reply = "(<a href='byond://?src=[REF(src)];choice=Message;skiprefresh=1;target=[REF(signal.source)]'>Reply</a>)"
var/reply = "(<a href='byond://?src=[REF(src)];choice=[signal.data["rigged"] ? "Mess_us_up" : "Message"];skiprefresh=1;target=[REF(signal.source)]'>Reply</a>)"
var/hrefstart
var/hrefend
if (isAI(L))
Expand Down Expand Up @@ -1138,10 +1161,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)
Expand All @@ -1155,7 +1183,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
Expand Down
4 changes: 3 additions & 1 deletion code/game/objects/items/devices/PDA/PDA_types.dm
Expand Up @@ -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))
Expand Down
50 changes: 32 additions & 18 deletions code/game/objects/items/devices/PDA/virus_cart.dm
Expand Up @@ -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"
Expand Down Expand Up @@ -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"
Expand Down
2 changes: 1 addition & 1 deletion code/modules/jobs/job_types/security_officer.dm
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions code/modules/mob/living/carbon/human/human.dm
Expand Up @@ -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)
Expand Down
6 changes: 3 additions & 3 deletions code/modules/uplink/uplink_items.dm
Expand Up @@ -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
Expand Down