diff --git a/code/game/objects/items/mail.dm b/code/game/objects/items/mail.dm index 149e4968402..c9fddbe19d6 100644 --- a/code/game/objects/items/mail.dm +++ b/code/game/objects/items/mail.dm @@ -107,25 +107,45 @@ var/tag = uppertext(GLOB.TAGGERLOCATIONS[destination_tag.currTag]) to_chat(user, span_notice("*[tag]*")) sort_tag = destination_tag.currTag - playsound(loc, 'sound/machines/twobeep_high.ogg', 100, TRUE) + playsound(loc, 'sound/machines/twobeep_high.ogg', vol = 100, vary = TRUE) + +/obj/item/mail/multitool_act(mob/living/user, obj/item/tool) + if(user.get_inactive_held_item() == src) + balloon_alert(user, "nothing to disable!") + return TRUE + balloon_alert(user, "hold it!") + return FALSE + /obj/item/mail/attack_self(mob/user) + if(!unwrap(user)) + return FALSE + return after_unwrap(user) + +/// proc for unwrapping a mail. Goes just for an unwrapping procces, returns FALSE if it fails. +/obj/item/mail/proc/unwrap(mob/user) if(recipient_ref) var/datum/mind/recipient = recipient_ref.resolve() // If the recipient's mind has gone, then anyone can open their mail // whether a mind can actually be qdel'd is an exercise for the reader if(recipient && recipient != user?.mind) to_chat(user, span_notice("You can't open somebody else's mail! That's illegal!")) - return + return FALSE - to_chat(user, span_notice("You start to unwrap the package...")) + balloon_alert(user, "unwrapping...") if(!do_after(user, 1.5 SECONDS, target = user)) - return - user.temporarilyRemoveItemFromInventory(src, TRUE) - if(contents.len) - user.put_in_hands(contents[1]) - playsound(loc, 'sound/items/poster_ripped.ogg', 50, TRUE) + return FALSE + return TRUE + +// proc that goes after unwrapping a mail. +/obj/item/mail/proc/after_unwrap(mob/user) + user.temporarilyRemoveItemFromInventory(src, force = TRUE) + for(var/obj/item/stuff as anything in contents) // Mail and envelope actually can have more than 1 item. + user.put_in_hands(stuff) + playsound(loc, 'sound/items/poster_ripped.ogg', vol = 50, vary = TRUE) qdel(src) + return TRUE + /obj/item/mail/examine_more(mob/user) . = ..() @@ -332,3 +352,153 @@ /obj/item/paper/fluff/junkmail_generic/Initialize(mapload) default_raw_text = pick(GLOB.junkmail_messages) return ..() + +/obj/item/mail/traitor + var/armed = FALSE + var/datum/weakref/made_by_ref + /// Cached information about who made it for logging purposes + var/made_by_cached_name + /// Cached information about who made it for logging purposes + var/made_by_cached_ckey + goodie_count = 0 + +/obj/item/mail/traitor/envelope + name = "envelope" + icon_state = "mail_large" + stamp_max = 2 + stamp_offset_y = 5 + +/obj/item/mail/traitor/after_unwrap(mob/user) + user.temporarilyRemoveItemFromInventory(src, force = TRUE) + playsound(loc, 'sound/items/poster_ripped.ogg', vol = 50, vary = TRUE) + for(var/obj/item/stuff as anything in contents) // Mail and envelope actually can have more than 1 item. + if(user.put_in_hands(stuff) && armed) + log_bomber(user, "opened armed mail made by [made_by_cached_name] ([made_by_cached_ckey]), activating", stuff) + INVOKE_ASYNC(stuff, TYPE_PROC_REF(/obj/item, attack_self), user) + qdel(src) + return TRUE + +/obj/item/mail/traitor/multitool_act(mob/living/user, obj/item/tool) + if(armed == FALSE || user.get_inactive_held_item() != src) + return ..() + if(IS_WEAKREF_OF(user.mind, made_by_ref)) + balloon_alert(user, "disarming trap...") + if(!do_after(user, 2 SECONDS, target = src)) + return FALSE + balloon_alert(user, "disarmed") + playsound(src, 'sound/machines/defib_ready.ogg', vol = 100, vary = TRUE) + armed = FALSE + return TRUE + else + balloon_alert(user, "tinkering with something...") + + if(!do_after(user, 2 SECONDS, target = src)) + after_unwrap(user) + return FALSE + if(prob(50)) + balloon_alert(user, "disarmed something...?") + playsound(src, 'sound/machines/defib_ready.ogg', vol = 100, vary = TRUE) + armed = FALSE + return TRUE + else + after_unwrap(user) + return TRUE + +/obj/item/storage/mail_counterfeit_device + name = "GLA-2 mail counterfeit device" + desc = "Device that actually able to counterfeit NT's mail. This device also able to place a trap inside of mail for malicious actions. Trap will \"activate\" any item inside of mail. Also it might be used for contraband purposes. Integrated micro-computer will give you great configuration optionality for your needs." + w_class = WEIGHT_CLASS_NORMAL + icon = 'icons/obj/device_syndie.dmi' + icon_state = "mail_counterfeit_device" + +/obj/item/storage/mail_counterfeit_device/Initialize(mapload) + . = ..() + atom_storage.max_slots = 1 + atom_storage.allow_big_nesting = TRUE + atom_storage.max_specific_storage = WEIGHT_CLASS_NORMAL + +/obj/item/storage/mail_counterfeit_device/examine_more(mob/user) + . = ..() + . += span_notice("You notice the manufacture marking on the side of the device...") + . += "\t[span_info("Guerilla Letter Assembler")]" + . += "\t[span_info("GLA Postal Service, right on schedule.")]" + return . + +/obj/item/storage/mail_counterfeit_device/attack_self(mob/user, modifiers) + var/mail_type = tgui_alert(user, "Make it look like an envelope or like normal mail?", "Mail Counterfeiting", list("Mail", "Envelope")) + if(isnull(mail_type)) + return FALSE + if(loc != user) + return FALSE + mail_type = lowertext(mail_type) + + var/mail_armed = tgui_alert(user, "Arm it?", "Mail Counterfeiting", list("Yes", "No")) == "Yes" + if(isnull(mail_armed)) + return FALSE + if(loc != user) + return FALSE + + var/list/mail_recipients = list("Anyone") + var/list/mail_recipients_for_input = list("Anyone") + var/list/used_names = list() + for(var/datum/record/locked/person in sort_record(GLOB.manifest.locked)) + if(isnull(person.mind_ref)) + continue + mail_recipients += person.mind_ref + mail_recipients_for_input += avoid_assoc_duplicate_keys(person.name, used_names) + + var/recipient = tgui_input_list(user, "Choose a recipient", "Mail Counterfeiting", mail_recipients_for_input) + if(isnull(recipient)) + return FALSE + if(!(src in user.contents)) + return FALSE + + var/index = mail_recipients_for_input.Find(recipient) + + var/obj/item/mail/traitor/shady_mail + if(mail_type == "mail") + shady_mail = new /obj/item/mail/traitor + else + shady_mail = new /obj/item/mail/traitor/envelope + + shady_mail.made_by_cached_ckey = user.ckey + shady_mail.made_by_cached_name = user.mind.name + + if(index == 1) + var/mail_name = tgui_input_text(user, "Enter mail title, or leave it blank", "Mail Counterfeiting") + if(!(src in user.contents)) + return FALSE + if(reject_bad_text(mail_name, ascii_only = FALSE)) + shady_mail.name = mail_name + else + shady_mail.name = mail_type + else + shady_mail.initialize_for_recipient(mail_recipients[index]) + + atom_storage.hide_contents(user) + user.temporarilyRemoveItemFromInventory(src, force = TRUE) + shady_mail.contents += contents + shady_mail.armed = mail_armed + shady_mail.made_by_ref = WEAKREF(user.mind) + user.put_in_hands(shady_mail) + qdel(src) + +/// Unobtainable item mostly for (b)admin purposes. +/obj/item/storage/mail_counterfeit_device/advanced + name = "GLA-MACRO mail counterfeit device" + +/obj/item/storage/mail_counterfeit_device/advanced/Initialize(mapload) + . = ..() + desc += " This model is highly advanced and capable of compressing items, making mail's storage space comparable to standard backpack." + create_storage(max_slots = 21, max_total_storage = 21) + atom_storage.allow_big_nesting = TRUE + +/// Unobtainable item mostly for (b)admin purposes. +/obj/item/storage/mail_counterfeit_device/bluespace + name = "GLA-ULTRA mail counterfeit device" + +/obj/item/storage/mail_counterfeit_device/bluespace/Initialize(mapload) + . = ..() + desc += " This model is the most advanced and capable of performing crazy bluespace compressions, making mail's storage space comparable to bluespace backpack." + create_storage(max_specific_storage = WEIGHT_CLASS_GIGANTIC, max_total_storage = 35, max_slots = 30) + atom_storage.allow_big_nesting = TRUE diff --git a/code/game/objects/items/storage/uplink_kits.dm b/code/game/objects/items/storage/uplink_kits.dm index c9ace922962..7fb901eebc5 100644 --- a/code/game/objects/items/storage/uplink_kits.dm +++ b/code/game/objects/items/storage/uplink_kits.dm @@ -426,6 +426,14 @@ new /obj/item/grenade/empgrenade(src) new /obj/item/implanter/emp(src) +/obj/item/storage/box/syndie_kit/mail_counterfeit + name = "mail counterfeit kit" + desc = "A box full of mail counterfeit devices. Nothing stops the mail." + +/obj/item/storage/box/syndie_kit/mail_counterfeit/PopulateContents() + for(var/i in 1 to 6) + new /obj/item/storage/mail_counterfeit_device(src) + /obj/item/storage/box/syndie_kit/chemical name = "chemical kit" diff --git a/code/modules/unit_tests/_unit_tests.dm b/code/modules/unit_tests/_unit_tests.dm index 22b1693f392..eaf25edf81f 100644 --- a/code/modules/unit_tests/_unit_tests.dm +++ b/code/modules/unit_tests/_unit_tests.dm @@ -230,6 +230,7 @@ #include "timer_sanity.dm" #include "trait_addition_and_removal.dm" #include "traitor.dm" +#include "traitor_mail_content_check.dm" #include "turf_icons.dm" #include "tutorial_sanity.dm" #include "unit_test.dm" diff --git a/code/modules/unit_tests/traitor_mail_content_check.dm b/code/modules/unit_tests/traitor_mail_content_check.dm new file mode 100644 index 00000000000..6d14d9d1428 --- /dev/null +++ b/code/modules/unit_tests/traitor_mail_content_check.dm @@ -0,0 +1,10 @@ +/// Makes sure that fake mail doesn't get random goods. +/datum/unit_test/traitor_mail_content_check + +/datum/unit_test/traitor_mail_content_check/Run() + var/mob/living/carbon/human/person = allocate(/mob/living/carbon/human/consistent) + person.mind_initialize() + var/obj/item/mail/traitor/test_mail = allocate(/obj/item/mail/traitor) + person.mind.set_assigned_role(SSjob.GetJobType(/datum/job/captain)) + test_mail.initialize_for_recipient(person.mind) + TEST_ASSERT_EQUAL(test_mail.contents.len, 0, "/obj/item/mail/traitor should not have items after initialize_for_recipient proc!") diff --git a/code/modules/uplink/uplink_items/job.dm b/code/modules/uplink/uplink_items/job.dm index be21458ee87..32788fad169 100644 --- a/code/modules/uplink/uplink_items/job.dm +++ b/code/modules/uplink/uplink_items/job.dm @@ -16,6 +16,15 @@ limited_stock = 1 //please don't spam deadchat surplus = 5 +/datum/uplink_item/role_restricted/mail_counterfeit_kit + name = "GLA Brand Mail Counterfeit Kit" + desc = "A box full of mail counterfeit devices. Devices that actually able to counterfeit NT's mail. Those devices also able to place a trap inside of mail for malicious actions. Trap will \"activate\" any item inside of mail. Also counterfieted mail might be used for contraband purposes. Integrated micro-computer will give you great configuration optionality for your needs. \nNothing stops the mail." + item = /obj/item/storage/box/syndie_kit/mail_counterfeit + cost = 2 + illegal_tech = FALSE + restricted_roles = list(JOB_CARGO_TECHNICIAN, JOB_QUARTERMASTER) + surplus = 5 + /datum/uplink_item/role_restricted/bureaucratic_error name = "Organic Capital Disturbance Virus" desc = "Randomizes job positions presented to new hires. May lead to too many/too few security officers and/or clowns. Single use." diff --git a/code/modules/uplink/uplink_items/stealthy_tools.dm b/code/modules/uplink/uplink_items/stealthy_tools.dm index 6cc5a12bd97..60f007ebae7 100644 --- a/code/modules/uplink/uplink_items/stealthy_tools.dm +++ b/code/modules/uplink/uplink_items/stealthy_tools.dm @@ -92,6 +92,13 @@ surplus = 30 illegal_tech = FALSE +/datum/uplink_item/stealthy_tools/mail_counterfeit + name = "GLA Brand Mail Counterfeit Device" + desc = "Device that actually able to counterfeit NT's mail. This device also able to place a trap inside of mail for malicious actions. Trap will \"activate\" any item inside of mail. Also it might be used for contraband purposes. Integrated micro-computer will give you great configuration optionality for your needs." + item = /obj/item/storage/mail_counterfeit_device + cost = 1 + surplus = 30 + /datum/uplink_item/stealthy_tools/telecomm_blackout name = "Disable Telecomms" desc = "When purchased, a virus will be uploaded to the telecommunication processing servers to temporarily disable themselves." diff --git a/icons/obj/device_syndie.dmi b/icons/obj/device_syndie.dmi index 5eaa05d0914..25ac0ef9b39 100644 Binary files a/icons/obj/device_syndie.dmi and b/icons/obj/device_syndie.dmi differ