diff --git a/code/__DEFINES/economy_defines.dm b/code/__DEFINES/economy_defines.dm
index bb08ac5c54e6..dc4a53f50ded 100644
--- a/code/__DEFINES/economy_defines.dm
+++ b/code/__DEFINES/economy_defines.dm
@@ -22,7 +22,7 @@
#define DEPARTMENT_BALANCE_HIGH 1500
/////BASE PAY AMOUNTS///
-#define CREW_BASE_PAY_LOW 125
+#define CREW_BASE_PAY_LOW 100
#define CREW_PAY_MEDIUM 250
#define CREW_PAY_HIGH 300
@@ -62,3 +62,6 @@
#define SUPPLY_VEND 9
#define DEFAULT_CRATE_VALUE 15
+
+/// mail deliveries
+#define MAIL_DELIVERY_BONUS 100
diff --git a/code/__DEFINES/misc_defines.dm b/code/__DEFINES/misc_defines.dm
index 81850bfc16ad..544e499beea4 100644
--- a/code/__DEFINES/misc_defines.dm
+++ b/code/__DEFINES/misc_defines.dm
@@ -552,3 +552,9 @@
#define BLOCKED_BURROWS "Blocked Burrows"
#define CLASSIC_CAVES "Classic Caves"
#define DEADLY_DEEPROCK "Deadly Deeprock"
+
+// Request console message priority defines
+
+#define RQ_NONEW_MESSAGES 0 // RQ_NONEWMESSAGES = no new message
+#define RQ_NORMALPRIORITY 1 // RQ_NORMALPRIORITY = normal priority
+#define RQ_HIGHPRIORITY 2 // RQ_HIGHPRIORITY = high priority
diff --git a/code/controllers/subsystem/SSeconomy.dm b/code/controllers/subsystem/SSeconomy.dm
index ab3f1dd6e67a..8f316ecc6742 100644
--- a/code/controllers/subsystem/SSeconomy.dm
+++ b/code/controllers/subsystem/SSeconomy.dm
@@ -92,6 +92,8 @@ SUBSYSTEM_DEF(economy)
var/next_paycheck_delay = 0
/// total paydays this round
var/payday_count = 0
+ /// Time until the next mail shipment
+ var/next_mail_delay = 0
var/global_paycheck_bonus = 0
var/global_paycheck_deduction = 0
@@ -132,6 +134,7 @@ SUBSYSTEM_DEF(economy)
centcom_message = "
---[station_time_timestamp()]---
Remember to stamp and send back the supply manifests.
"
next_paycheck_delay = 30 MINUTES + world.time
+ next_mail_delay = 15 MINUTES + world.time
/datum/controller/subsystem/economy/fire()
if(next_paycheck_delay <= world.time)
@@ -141,6 +144,11 @@ SUBSYSTEM_DEF(economy)
next_data_check = 10 MINUTES + world.time
record_economy_data()
process_job_tasks()
+ if(next_mail_delay <= world.time)
+ if(!is_admin_level(SSshuttle.supply.z) || SSshuttle.supply.areaInstance.moving)
+ return
+ next_mail_delay = 15 MINUTES + world.time
+ SSshuttle.mail_delivery()
/datum/controller/subsystem/economy/proc/record_economy_data()
economy_data["totalcash"] += total_space_cash
diff --git a/code/controllers/subsystem/SSshuttles.dm b/code/controllers/subsystem/SSshuttles.dm
index 35de41472a80..a229a3ab80cf 100644
--- a/code/controllers/subsystem/SSshuttles.dm
+++ b/code/controllers/subsystem/SSshuttles.dm
@@ -24,6 +24,8 @@ SUBSYSTEM_DEF(shuttle)
//supply shuttle stuff
var/obj/docking_port/mobile/supply/supply
+ /// Supply shuttle turfs to make mail be put down faster
+ var/static/list/supply_shuttle_turfs = list()
var/list/hidden_shuttle_turfs = list() //all turfs hidden from navigation computers associated with a list containing the image hiding them and the type of the turf they are pretending to be
var/list/hidden_shuttle_turf_images = list() //only the images from the above list
@@ -284,4 +286,23 @@ SUBSYSTEM_DEF(shuttle)
QDEL_LIST_CONTENTS(remove_images)
+/datum/controller/subsystem/shuttle/proc/mail_delivery()
+ for(var/obj/machinery/requests_console/console in GLOB.allRequestConsoles)
+ if(console.department != "Cargo Bay")
+ continue
+ console.createMessage("Messaging and Intergalactic Letters", "New Mail Crates ready to be ordered!", "A new mail crate is able to be shipped alongside your next orders!", RQ_NORMALPRIORITY)
+
+ if(!length(supply_shuttle_turfs))
+ for(var/turf/simulated/T in supply.areaInstance)
+ if(T.density)
+ continue
+ for(var/obj/structure/structure in T.contents)
+ if(structure.density)
+ continue
+ supply_shuttle_turfs += T
+ if(!length(supply_shuttle_turfs)) // In case some nutjob walled the supply shuttle 10 minutes into the round
+ return
+ var/turf/spawn_location = pick(supply_shuttle_turfs)
+ new /obj/structure/closet/crate/mail(spawn_location)
+
#undef CALL_SHUTTLE_REASON_LENGTH
diff --git a/code/datums/outfits/outfit_debug.dm b/code/datums/outfits/outfit_debug.dm
index a646f082bb63..dd7c0e4cd879 100644
--- a/code/datums/outfits/outfit_debug.dm
+++ b/code/datums/outfits/outfit_debug.dm
@@ -16,7 +16,6 @@
mask = /obj/item/clothing/mask/gas/welding/advanced
shoes = /obj/item/clothing/shoes/combat/swat
- // shit down here is mine
box = /obj/item/storage/box/debug/debugtools
suit_store = /obj/item/tank/internals/oxygen
gloves = /obj/item/clothing/gloves/combat
diff --git a/code/game/gamemodes/nuclear/nuclear.dm b/code/game/gamemodes/nuclear/nuclear.dm
index 1f380ef73d4e..914ae3b18f7e 100644
--- a/code/game/gamemodes/nuclear/nuclear.dm
+++ b/code/game/gamemodes/nuclear/nuclear.dm
@@ -292,6 +292,7 @@
U.hidden_uplink.uplink_owner="[synd_mob.key]"
U.hidden_uplink.uses = uplink_uses
synd_mob.equip_to_slot_or_del(U, slot_in_backpack)
+ synd_mob.mind.offstation_role = TRUE
if(synd_mob.dna.species)
var/race = synd_mob.dna.species.name
diff --git a/code/game/gamemodes/wizard/wizard.dm b/code/game/gamemodes/wizard/wizard.dm
index 4783089c03b4..015f595f6348 100644
--- a/code/game/gamemodes/wizard/wizard.dm
+++ b/code/game/gamemodes/wizard/wizard.dm
@@ -147,6 +147,7 @@
wizard_mob.equip_to_slot_or_del(spellbook, slot_l_hand)
wizard_mob.faction = list("wizard")
+ wizard_mob.mind.offstation_role = TRUE
diff --git a/code/game/jobs/job/support.dm b/code/game/jobs/job/support.dm
index eaef20e4fa96..d77defb864c8 100644
--- a/code/game/jobs/job/support.dm
+++ b/code/game/jobs/job/support.dm
@@ -47,6 +47,7 @@
selection_color = "#eeddbe"
access = list(ACCESS_MAINT_TUNNELS, ACCESS_MAILSORTING, ACCESS_CARGO, ACCESS_QM, ACCESS_MINT, ACCESS_MINING, ACCESS_MINING_STATION, ACCESS_MINERAL_STOREROOM)
minimal_access = list(ACCESS_MAINT_TUNNELS, ACCESS_CARGO, ACCESS_MAILSORTING, ACCESS_MINERAL_STOREROOM)
+ alt_titles = list("Mail Carrier", "Courier")
outfit = /datum/outfit/job/cargo_tech
/datum/outfit/job/cargo_tech
@@ -55,6 +56,7 @@
uniform = /obj/item/clothing/under/rank/cargo/tech
shoes = /obj/item/clothing/shoes/black
+ l_pocket = /obj/item/mail_scanner
l_ear = /obj/item/radio/headset/headset_cargo
id = /obj/item/card/id/supply
pda = /obj/item/pda/cargo
diff --git a/code/game/machinery/requests_console.dm b/code/game/machinery/requests_console.dm
index db575db584ac..b6a634ce3733 100644
--- a/code/game/machinery/requests_console.dm
+++ b/code/game/machinery/requests_console.dm
@@ -27,10 +27,6 @@
#define COM_ROLES list("Blueshield", "NT Representative", "Head of Personnel's Desk", "Captain's Desk", "Bridge")
#define SCI_ROLES list("Robotics", "Science", "Research Director's Desk")
-#define RQ_NONEW_MESSAGES 0
-#define RQ_NORMALPRIORITY 1
-#define RQ_HIGHPRIORITY 2
-
GLOBAL_LIST_EMPTY(req_console_assistance)
GLOBAL_LIST_EMPTY(req_console_supplies)
GLOBAL_LIST_EMPTY(req_console_information)
@@ -48,9 +44,6 @@ GLOBAL_LIST_EMPTY(allRequestConsoles)
var/list/message_log = list() //List of all messages
var/departmentType = 0 //Bitflag. Zero is reply-only. Map currently uses raw numbers instead of defines.
var/newmessagepriority = RQ_NONEW_MESSAGES
- // RQ_NONEWMESSAGES = no new message
- // RQ_NORMALPRIORITY = normal priority
- // RQ_HIGHPRIORITY = high priority
var/screen = RCS_MAINMENU
var/silent = FALSE // set to TRUE for it not to beep all the time
var/announcementConsole = FALSE
@@ -371,7 +364,3 @@ GLOBAL_LIST_EMPTY(allRequestConsoles)
sp.sortTag = tag_index
sp.update_desc()
print_cooldown = world.time + 600 //1 minute cooldown before you can print another label, but you can still configure the next one during this time
-
-#undef RQ_NONEW_MESSAGES
-#undef RQ_NORMALPRIORITY
-#undef RQ_HIGHPRIORITY
diff --git a/code/game/objects/items/stacks/sheets/mineral.dm b/code/game/objects/items/stacks/sheets/mineral.dm
index 85359257e12d..ee5c879d8f64 100644
--- a/code/game/objects/items/stacks/sheets/mineral.dm
+++ b/code/game/objects/items/stacks/sheets/mineral.dm
@@ -329,6 +329,9 @@ GLOBAL_LIST_INIT(sandbag_recipes, list (
materials = list(MAT_BANANIUM = MINERAL_MATERIAL_AMOUNT)
point_value = 50
+/obj/item/stack/sheet/mineral/bananium/ten
+ amount = 10
+
/obj/item/stack/sheet/mineral/bananium/fifty
amount = 50
@@ -348,6 +351,9 @@ GLOBAL_LIST_INIT(sandbag_recipes, list (
wall_allowed = FALSE //no tranquilite walls in code
point_value = 50
+/obj/item/stack/sheet/mineral/tranquillite/ten
+ amount = 10
+
/obj/item/stack/sheet/mineral/tranquillite/fifty
amount = 50
diff --git a/code/game/objects/items/weapons/storage/bags.dm b/code/game/objects/items/weapons/storage/bags.dm
index 5ace765ad391..a5a19c88d456 100644
--- a/code/game/objects/items/weapons/storage/bags.dm
+++ b/code/game/objects/items/weapons/storage/bags.dm
@@ -536,3 +536,19 @@
/obj/item/reagent_containers/glass/bottle, /obj/item/reagent_containers/iv_bag,
/obj/item/reagent_containers/hypospray/autoinjector/epinephrine)
resistance_flags = FLAMMABLE
+
+/*
+ * Mail bag
+ */
+
+/obj/item/storage/bag/mail
+ name = "mail bag"
+ desc = "A bag for envelopes, stamps, pens, and papers."
+ icon = 'icons/obj/bureaucracy.dmi'
+ icon_state = "mailbag"
+ item_state = "mailbag"
+ storage_slots = 14
+ max_combined_w_class = 28
+ w_class = WEIGHT_CLASS_TINY
+ can_hold = list(/obj/item/envelope, /obj/item/stamp, /obj/item/pen, /obj/item/paper)
+ resistance_flags = FLAMMABLE
diff --git a/code/game/objects/mail.dm b/code/game/objects/mail.dm
new file mode 100644
index 000000000000..6e6a2af8590b
--- /dev/null
+++ b/code/game/objects/mail.dm
@@ -0,0 +1,272 @@
+/obj/item/envelope
+ name = "broken letter"
+ desc = "We just got a letter, we just got a letter, we just got a letter -- I wonder who it's from?"
+ force = 0
+ throwforce = 0
+ w_class = WEIGHT_CLASS_SMALL
+ icon = 'icons/obj/bureaucracy.dmi'
+ icon_state = "mail_misc"
+ item_state = "paper"
+ drop_sound = 'sound/items/handling/paper_drop.ogg'
+ pickup_sound = 'sound/items/handling/paper_pickup.ogg'
+
+ var/list/possible_contents = list()
+ /// A list that contains the names of the jobs that can receive this type of letter. Only the base job has to be put in it, alternative titles have the same definition on the mind. Name of the job can be found in `mind.assigned_role`
+ var/list/job_list = list()
+ var/mob/living/recipient
+ var/has_been_scanned = FALSE
+
+/obj/item/envelope/suicide_act(mob/user)
+ user.visible_message("[user] is licking a sharp corner of the envelope. It looks like [user.p_theyre()] trying to commit suicide!")
+ playsound(loc, 'sound/effects/-adminhelp.ogg', 50, TRUE, -1)
+ return BRUTELOSS
+
+/obj/item/envelope/attack_self(mob/user)
+ if(!user?.mind)
+ return
+ if(user.real_name != recipient.real_name)
+ to_chat(user, "You don't want to open up another person's mail, that's an invasion of their privacy!")
+ return
+ if(do_after(user, 1 SECONDS, target = user) && !QDELETED(src))
+ to_chat(user, "You begin to open the envelope.")
+ playsound(loc, 'sound/items/poster_ripped.ogg', 50, TRUE)
+ user.unEquip(src)
+ for(var/obj/item/I in contents)
+ user.put_in_hands(I)
+ qdel(src)
+
+/obj/item/envelope/Initialize(mapload)
+ . = ..()
+ var/item = pick(possible_contents)
+ new item(src)
+ new /obj/item/stack/spacecash(src, rand(1, 50) * 5)
+ var/list/mind_copy = shuffle(SSticker.minds)
+ for(var/datum/mind/mail_attracted_people in mind_copy)
+ var/turf/T = get_turf(mail_attracted_people.current)
+ if(mail_attracted_people.offstation_role || !ishuman(mail_attracted_people.current) || is_admin_level(T.z))
+ continue
+ if(mail_attracted_people.assigned_role in job_list)
+ recipient = mail_attracted_people.current
+ name = "letter to [recipient.real_name]"
+ return
+ if(!admin_spawned)
+ log_debug("Failed to find a new name to assign to [src]!")
+ qdel(src)
+
+/obj/item/envelope/security
+ icon_state = "mail_sec"
+ possible_contents = list(/obj/item/reagent_containers/food/snacks/donut/sprinkles,
+ /obj/item/megaphone,
+ /obj/item/poster/random_official,
+ /obj/item/restraints/handcuffs/pinkcuffs,
+ /obj/item/restraints/legcuffs/bola/energy,
+ /obj/item/reagent_containers/food/drinks/coffee,
+ /obj/item/stock_parts/cell/super,
+ /obj/item/grenade/barrier/dropwall,
+ /obj/item/toy/figure/crew/detective,
+ /obj/item/toy/figure/crew/hos,
+ /obj/item/toy/figure/crew/secofficer)
+ job_list = list("Head of Security", "Security Officer", "Detective", "Warden")
+
+/obj/item/envelope/science
+ icon_state = "mail_sci"
+ possible_contents = list(/obj/item/analyzer,
+ /obj/item/assembly/signaler,
+ /obj/item/slime_extract/grey,
+ /obj/item/clothing/mask/gas,
+ /obj/item/reagent_containers/spray/cleaner,
+ /obj/item/clothing/glasses/regular,
+ /obj/item/stack/ore/diamond, // Jackpot
+ /obj/item/paicard,
+ /obj/item/toy/figure/crew/borg,
+ /obj/item/toy/figure/crew/geneticist,
+ /obj/item/toy/figure/crew/rd,
+ /obj/item/toy/figure/crew/roboticist,
+ /obj/item/toy/figure/crew/scientist)
+ job_list = list("Research Director", "Roboticist", "Geneticist", "Scientist")
+
+/obj/item/envelope/supply
+ icon_state = "mail_sup"
+ possible_contents = list(/obj/item/reagent_containers/hypospray/autoinjector/survival,
+ /obj/item/reagent_containers/food/drinks/bottle/absinthe/premium,
+ /obj/item/clothing/glasses/meson/gar,
+ /obj/item/stack/marker_beacon/ten,
+ /obj/item/stack/medical/splint,
+ /obj/item/pen/multi/fountain,
+ /obj/item/clothing/mask/cigarette/cigar,
+ /obj/item/stack/wrapping_paper,
+ /obj/item/toy/figure/crew/cargotech,
+ /obj/item/toy/figure/crew/qm,
+ /obj/item/toy/figure/crew/miner)
+ job_list = list("Quartermaster", "Cargo Technician", "Shaft Miner")
+
+/obj/item/envelope/medical
+ icon_state = "mail_med"
+ possible_contents = list(/obj/item/soap,
+ /obj/item/reagent_containers/glass/bottle/morphine,
+ /obj/item/reagent_containers/hypospray/safety,
+ /obj/item/reagent_containers/applicator/brute,
+ /obj/item/reagent_containers/applicator/burn,
+ /obj/item/clothing/glasses/sunglasses,
+ /obj/item/reagent_containers/food/snacks/fortunecookie,
+ /obj/item/scalpel/laser/laser1,
+ /obj/item/toy/figure/crew/cmo,
+ /obj/item/toy/figure/crew/chemist,
+ /obj/item/toy/figure/crew/geneticist,
+ /obj/item/toy/figure/crew/md,
+ /obj/item/toy/figure/crew/virologist)
+ job_list = list("Chief Medical Officer", "Medical Doctor", "Coroner", "Chemist", "Virologist", "Psychiatrist", "Paramedic")
+
+/obj/item/envelope/engineering
+ icon_state = "mail_eng"
+ possible_contents = list(/obj/item/airlock_electronics,
+ /obj/item/reagent_containers/food/drinks/cans/beer,
+ /obj/item/reagent_containers/food/snacks/candy/confectionery/nougat,
+ /obj/item/mod/module/storage/large_capacity,
+ /obj/item/weldingtool/hugetank,
+ /obj/item/geiger_counter,
+ /obj/item/rcd_ammo,
+ /obj/item/grenade/gas/oxygen,
+ /obj/item/toy/figure/crew/atmos,
+ /obj/item/toy/figure/crew/ce,
+ /obj/item/toy/figure/crew/engineer)
+ job_list = list("Chief Engineer", "Station Engineer", "Life Support Specialist")
+
+/obj/item/envelope/bread
+ icon_state = "mail_serv"
+ possible_contents = list(/obj/item/painter,
+ /obj/item/gun/energy/floragun,
+ /obj/item/reagent_containers/food/drinks/bottle/fernet,
+ /obj/item/whetstone,
+ /obj/item/soap/deluxe,
+ /obj/item/stack/tile/disco_light/thirty,
+ /obj/item/paicard,
+ /obj/item/gun/projectile/automatic/toy/pistol,
+ /obj/item/toy/figure/crew/bartender,
+ /obj/item/toy/figure/crew/botanist,
+ /obj/item/toy/figure/crew/chef,
+ /obj/item/toy/figure/crew/janitor,
+ /obj/item/toy/figure/crew/librarian)
+ job_list = list("Bartender", "Chef", "Botanist", "Janitor", "Barber", "Librarian", "Barber")
+
+/obj/item/envelope/circuses
+ icon_state = "mail_serv"
+ possible_contents = list(/obj/item/painter,
+ /obj/item/stack/sheet/mineral/tranquillite/ten,
+ /obj/item/stack/sheet/mineral/bananium/ten,
+ /obj/item/reagent_containers/food/drinks/bottle/bottleofnothing,
+ /obj/item/gun/throw/piecannon,
+ /obj/item/ammo_box/shotgun/confetti,
+ /obj/item/book/manual/wiki/sop_security, // They'll need this.
+ /obj/item/soulstone/anybody/purified/chaplain,
+ /obj/item/toy/figure/crew/clown,
+ /obj/item/toy/figure/crew/hop,
+ /obj/item/toy/figure/crew/chaplain,
+ /obj/item/toy/figure/crew/mime)
+ job_list = list("Clown", "Mime", "Head of Personnel", "Chaplain")
+
+
+/obj/item/envelope/command
+ icon_state = "mail_com"
+ possible_contents = list(/obj/item/flash,
+ /obj/item/storage/fancy/cigarettes/cigpack_robustgold,
+ /obj/item/poster/random_official,
+ /obj/item/book/manual/wiki/sop_command,
+ /obj/item/reagent_containers/food/pill/patch/synthflesh,
+ /obj/item/paper_bin/nanotrasen,
+ /obj/item/reagent_containers/food/snacks/spesslaw,
+ /obj/item/clothing/head/collectable/petehat,
+ /obj/item/toy/figure/crew/captain,
+ /obj/item/toy/figure/crew/lawyer,
+ /obj/item/toy/figure/crew/dsquad)
+ job_list = list("Captain", "Magistrate", "Nanotrasen Representative", "Blueshield", "Internal Affairs Agent")
+
+/obj/item/envelope/misc
+ possible_contents = list(/obj/item/clothing/under/misc/assistantformal,
+ /obj/item/clothing/under/syndicate/tacticool,
+ /obj/item/clothing/shoes/ducky,
+ /obj/item/toy/plushie/orange_fox/grump, // A grumpy plushie for a grumpy tider
+ /obj/item/multitool,
+ /obj/item/instrument/piano_synth,
+ /obj/item/toy/crayon/spraycan,
+ /obj/item/clothing/head/cakehat,
+ /obj/item/toy/figure/crew/assistant,
+ /obj/item/toy/figure/owl,
+ /obj/item/toy/figure/griffin)
+ job_list = list("Assistant", "Explorer")
+
+
+ /*//////////////////////\/
+ \/ \\ // \/
+ \/ \\ // \/
+ \/ \\ // \/
+ \/ \\ // \/
+ \/ \\// \/
+ \/ \/
+ \/ You've got: \/
+ \/ Mail \/
+ \/ \/
+ \/\\\\\\\\\\\\\\\\\\\\\\*/
+
+/obj/item/mail_scanner
+ name = "mail scanner"
+ desc = "Sponsored by Messaging and Intergalactic Letters, this device allows you to log mail deliveries in exchange for financial compensation."
+ force = 0
+ throwforce = 0
+ icon = 'icons/obj/device.dmi'
+ icon_state = "mail_scanner"
+ item_state = "mail_scanner"
+ lefthand_file = 'icons/mob/inhands/items/devices_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/items/devices_righthand.dmi'
+ flags = CONDUCT
+ slot_flags = SLOT_BELT
+ w_class = WEIGHT_CLASS_SMALL
+ origin_tech = "magnets=1"
+ /// The reference to the envelope that is currently stored in the mail scanner. It will be cleared upon confirming a correct delivery
+ var/obj/item/envelope/saved
+
+/obj/item/mail_scanner/examine(mob/user)
+ . = ..()
+ . += "Scan a letter to log it into the active database, then scan the person you wish to hand the letter to. Correctly scanning the recipient of the letter logged into the active database will add credits to the Supply budget."
+
+/obj/item/mail_scanner/attack()
+ return
+
+/obj/item/mail_scanner/afterattack(atom/A, mob/user)
+ if(istype(A, /obj/item/envelope))
+ var/obj/item/envelope/envelope = A
+ if(envelope.has_been_scanned)
+ to_chat(user, "This letter has already been logged to the active database!")
+ playsound(loc, 'sound/mail/maildenied.ogg', 50, TRUE)
+ return
+ to_chat(user, "You add [envelope] to the active database.")
+ playsound(loc, 'sound/mail/mailscanned.ogg', 50, TRUE)
+ saved = A
+ return
+ if(isliving(A))
+ var/mob/living/M = A
+ if(!saved)
+ to_chat(user, "Error: You have not logged mail to the mail scanner!")
+ playsound(loc, 'sound/mail/maildenied.ogg', 50, TRUE)
+ return
+
+ if(M.stat == DEAD)
+ to_chat(user, "Consent Verification failed: You can't deliver mail to a corpse!")
+ playsound(loc, 'sound/mail/maildenied.ogg', 50, TRUE)
+ return
+
+ if(M.real_name != saved.recipient.real_name)
+ to_chat(user, "'Identity Verification failed: Target is not an authorized recipient of this package!")
+ playsound(loc, 'sound/mail/maildenied.ogg', 50, TRUE)
+ return
+
+ if(!M.client)
+ to_chat(user, "Consent Verification failed: The scanner will not accept confirmation of orders from SSD people!")
+ playsound(loc, 'sound/mail/maildenied.ogg', 50, TRUE)
+ return
+
+ saved.has_been_scanned = TRUE
+ saved = null
+ to_chat(user, "Successful delivery acknowledged! [MAIL_DELIVERY_BONUS] credits added to Supply account!")
+ playsound(loc, 'sound/mail/mailapproved.ogg', 50, TRUE)
+ GLOB.station_money_database.credit_account(SSeconomy.cargo_account, MAIL_DELIVERY_BONUS, "Mail Delivery Compensation", "Messaging and Intergalactic Letters", supress_log = FALSE)
diff --git a/code/game/objects/structures/crates_lockers/crates.dm b/code/game/objects/structures/crates_lockers/crates.dm
index 035de1684763..6b6f51f1e17e 100644
--- a/code/game/objects/structures/crates_lockers/crates.dm
+++ b/code/game/objects/structures/crates_lockers/crates.dm
@@ -504,6 +504,31 @@
icon_opened = "electricalcrate_open"
icon_closed = "electricalcrate"
+/obj/structure/closet/crate/mail
+ name = "mail crate"
+ desc = "A plastic crate for mail delivery."
+ icon = 'icons/obj/bureaucracy.dmi'
+ icon_state = "mailsealed"
+ icon_opened = "mailopen"
+ icon_closed = "mailsealed"
+ material_drop = /obj/item/stack/sheet/plastic
+ material_drop_amount = 4
+ var/list/possible_contents = list(/obj/item/envelope/security,
+ /obj/item/envelope/science,
+ /obj/item/envelope/supply,
+ /obj/item/envelope/medical,
+ /obj/item/envelope/engineering,
+ /obj/item/envelope/bread,
+ /obj/item/envelope/circuses,
+ /obj/item/envelope/command,
+ /obj/item/envelope/misc)
+
+/obj/structure/closet/crate/mail/populate_contents()
+ . = ..()
+ for(var/i in 1 to rand(5, 10))
+ var/item = pick(possible_contents)
+ new item(src)
+
/obj/structure/closet/crate/tape/populate_contents()
if(prob(10))
new /obj/item/bikehorn/rubberducky(src)
diff --git a/code/modules/clothing/suits/misc_suits.dm b/code/modules/clothing/suits/misc_suits.dm
index cbc5490d86c3..a97ad4d73c85 100644
--- a/code/modules/clothing/suits/misc_suits.dm
+++ b/code/modules/clothing/suits/misc_suits.dm
@@ -501,7 +501,7 @@
name = "cargo winter coat"
icon_state = "wintercoat_cargo"
item_state = "coatcargo"
- allowed = list(/obj/item/flashlight, /obj/item/tank/internals/emergency_oxygen, /obj/item/toy, /obj/item/storage/fancy/cigarettes, /obj/item/lighter, /obj/item/rcs, /obj/item/clipboard)
+ allowed = list(/obj/item/flashlight, /obj/item/tank/internals/emergency_oxygen, /obj/item/toy, /obj/item/storage/fancy/cigarettes, /obj/item/lighter, /obj/item/rcs, /obj/item/clipboard, /obj/item/envelope, /obj/item/storage/bag/mail)
hoodtype = /obj/item/clothing/head/hooded/winterhood/cargo
/obj/item/clothing/head/hooded/winterhood/cargo
@@ -891,7 +891,7 @@
icon_state = "bombercargo"
item_state = "bombercargo"
ignore_suitadjust = 0
- allowed = list(/obj/item/rcs, /obj/item/clipboard, /obj/item/flashlight, /obj/item/tank/internals/emergency_oxygen, /obj/item/toy, /obj/item/lighter, /obj/item/storage/fancy/cigarettes)
+ allowed = list(/obj/item/rcs, /obj/item/clipboard, /obj/item/flashlight, /obj/item/tank/internals/emergency_oxygen, /obj/item/toy, /obj/item/lighter, /obj/item/storage/fancy/cigarettes, /obj/item/storage/bag/mail, /obj/item/envelope)
body_parts_covered = UPPER_TORSO | LOWER_TORSO | ARMS
cold_protection = UPPER_TORSO | LOWER_TORSO | ARMS
min_cold_protection_temperature = FIRE_SUIT_MIN_TEMP_PROTECT
diff --git a/code/modules/mining/machine_redemption.dm b/code/modules/mining/machine_redemption.dm
index df41f7948b02..41d77a681bbe 100644
--- a/code/modules/mining/machine_redemption.dm
+++ b/code/modules/mining/machine_redemption.dm
@@ -468,7 +468,7 @@
if(!(C.department in supply_consoles))
continue
if(!supply_consoles[C.department] || length(supply_consoles[C.department] - mats_in_stock))
- C.createMessage("Ore Redemption Machine", "New Minerals Available!", msg, 1) // RQ_NORMALPRIORITY
+ C.createMessage("Ore Redemption Machine", "New Minerals Available!", msg, RQ_NORMALPRIORITY)
/**
* Tries to insert the ID card held by the given user into the machine.
diff --git a/code/modules/research/designs/autolathe_designs.dm b/code/modules/research/designs/autolathe_designs.dm
index 49ccab63937c..eedcc0c05cd5 100644
--- a/code/modules/research/designs/autolathe_designs.dm
+++ b/code/modules/research/designs/autolathe_designs.dm
@@ -933,3 +933,11 @@
materials = list(MAT_METAL = 4000)
build_path = /obj/item/desk_bell
category = list("initial", "Miscellaneous")
+
+/datum/design/mailscanner
+ name = "Mail Scanner"
+ id = "mailscanner"
+ build_type = AUTOLATHE
+ materials = list(MAT_METAL = 1500, MAT_GLASS = 500)
+ build_path = /obj/item/mail_scanner
+ category = list("initial", "Miscellaneous")
diff --git a/code/modules/shuttle/supply.dm b/code/modules/shuttle/supply.dm
index f2dcf4d109b0..9e2f6c23a8f3 100644
--- a/code/modules/shuttle/supply.dm
+++ b/code/modules/shuttle/supply.dm
@@ -34,8 +34,8 @@
/obj/effect/hierophant,
/obj/item/warp_cube,
/obj/machinery/quantumpad,
- /obj/structure/extraction_point
- )
+ /obj/structure/extraction_point,
+ /obj/item/envelope)
if(A)
if(is_type_in_list(A, blacklist))
return TRUE
@@ -126,10 +126,12 @@
continue
if(istype(MA, /mob/dead))
continue
+ if(istype(MA, /obj/structure/closet/crate/mail))
+ continue
SSeconomy.sold_atoms += " [MA.name]"
// Must be in a crate (or a critter crate)!
- if(istype(MA,/obj/structure/closet/crate) || istype(MA,/obj/structure/closet/critter))
+ if(istype(MA, /obj/structure/closet/crate) || istype(MA, /obj/structure/closet/critter))
SSeconomy.sold_atoms += ":"
if(!length(MA.contents))
SSeconomy.sold_atoms += " (empty)"
diff --git a/code/modules/supply/supply_console.dm b/code/modules/supply/supply_console.dm
index 8d323258b5b1..863be663631c 100644
--- a/code/modules/supply/supply_console.dm
+++ b/code/modules/supply/supply_console.dm
@@ -410,7 +410,7 @@
if(is_public) // Public consoles cant move the shuttle. Dont allow exploiters.
return
if(SSshuttle.supply.canMove())
- to_chat(user, "For safety reasons the automated supply shuttle cannot transport live organisms, classified nuclear weaponry or homing beacons.")
+ to_chat(user, "For safety reasons the automated supply shuttle cannot transport live organisms, undelivered mail, classified nuclear weaponry or homing beacons.")
else if(SSshuttle.supply.getDockedId() == "supply_home")
SSshuttle.toggleShuttle("supply", "supply_home", "supply_away", 1)
investigate_log("| [key_name(user)] has sent the supply shuttle away. Shuttle contents: [SSeconomy.sold_atoms]", "cargo")
diff --git a/icons/mob/inhands/items/devices_lefthand.dmi b/icons/mob/inhands/items/devices_lefthand.dmi
index bf9c3154c62b..73f963c0eaa4 100644
Binary files a/icons/mob/inhands/items/devices_lefthand.dmi and b/icons/mob/inhands/items/devices_lefthand.dmi differ
diff --git a/icons/mob/inhands/items/devices_righthand.dmi b/icons/mob/inhands/items/devices_righthand.dmi
index 93a5d9296104..5ec4e6b3dc52 100644
Binary files a/icons/mob/inhands/items/devices_righthand.dmi and b/icons/mob/inhands/items/devices_righthand.dmi differ
diff --git a/icons/obj/bureaucracy.dmi b/icons/obj/bureaucracy.dmi
index 40d7e5ae7290..66b7a42a8d89 100644
Binary files a/icons/obj/bureaucracy.dmi and b/icons/obj/bureaucracy.dmi differ
diff --git a/icons/obj/device.dmi b/icons/obj/device.dmi
index aa2bd1e89259..de549408ec30 100644
Binary files a/icons/obj/device.dmi and b/icons/obj/device.dmi differ
diff --git a/paradise.dme b/paradise.dme
index e841908a0e5a..90f308fbd2f3 100644
--- a/paradise.dme
+++ b/paradise.dme
@@ -869,6 +869,7 @@
#include "code\game\objects\empulse.dm"
#include "code\game\objects\explosion.dm"
#include "code\game\objects\items.dm"
+#include "code\game\objects\mail.dm"
#include "code\game\objects\obj_defense.dm"
#include "code\game\objects\objs.dm"
#include "code\game\objects\structures.dm"
diff --git a/sound/mail/mailapproved.ogg b/sound/mail/mailapproved.ogg
new file mode 100644
index 000000000000..2f3135bc3c22
Binary files /dev/null and b/sound/mail/mailapproved.ogg differ
diff --git a/sound/mail/maildenied.ogg b/sound/mail/maildenied.ogg
new file mode 100644
index 000000000000..d8cdd0c107f8
Binary files /dev/null and b/sound/mail/maildenied.ogg differ
diff --git a/sound/mail/mailscanned.ogg b/sound/mail/mailscanned.ogg
new file mode 100644
index 000000000000..b2514370ba1f
Binary files /dev/null and b/sound/mail/mailscanned.ogg differ