diff --git a/code/__DEFINES/economy_defines.dm b/code/__DEFINES/economy_defines.dm
index bb08ac5c54e..dc4a53f50de 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 81850bfc16a..544e499beea 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 ab3f1dd6e67..8f316ecc674 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 35de41472a8..a229a3ab80c 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 a646f082bb6..dd7c0e4cd87 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 1f380ef73d4..914ae3b18f7 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 4783089c03b..015f595f634 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 eaef20e4fa9..d77defb864c 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 db575db584a..b6a634ce373 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 85359257e12..ee5c879d8f6 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 5ace765ad39..a5a19c88d45 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 00000000000..6e6a2af8590
--- /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 035de168476..6b6f51f1e17 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 cbc5490d86c..a97ad4d73c8 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 df41f7948b0..41d77a681bb 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 49ccab63937..eedcc0c05cd 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 f2dcf4d109b..9e2f6c23a8f 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 8d323258b5b..863be663631 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 bf9c3154c62..73f963c0eaa 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 93a5d929610..5ec4e6b3dc5 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 40d7e5ae729..66b7a42a8d8 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 aa2bd1e8925..de549408ec3 100644
Binary files a/icons/obj/device.dmi and b/icons/obj/device.dmi differ
diff --git a/paradise.dme b/paradise.dme
index e841908a0e5..90f308fbd2f 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 00000000000..2f3135bc3c2
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 00000000000..d8cdd0c107f
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 00000000000..b2514370ba1
Binary files /dev/null and b/sound/mail/mailscanned.ogg differ