Files
Paradise/code/game/objects/mail.dm
Alan 991e53368f Rework how mail envelopes are generated. (#32322)
* Rework how mail envelopes are generated.

* Add newline to departments_defines.dm

* Apply suggestion from CRUNCH review.

Co-authored-by: CRUNCH <143041327+CRUNCH-Borg@users.noreply.github.com>
Signed-off-by: Alan <alfalfascout@users.noreply.github.com>

---------

Signed-off-by: Alan <alfalfascout@users.noreply.github.com>
Co-authored-by: CRUNCH <143041327+CRUNCH-Borg@users.noreply.github.com>
2026-07-24 19:34:36 +00:00

329 lines
12 KiB
Plaintext

/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?"
icon = 'icons/obj/bureaucracy.dmi'
icon_state = "mail_misc"
inhand_icon_state = "paper"
w_class = WEIGHT_CLASS_SMALL
drop_sound = 'sound/items/handling/paper_drop.ogg'
pickup_sound = 'sound/items/handling/paper_pickup.ogg'
new_attack_chain = TRUE
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()
/// The real name required to open the letter
var/recipient
/// The job of the recipient
var/recipient_job
var/has_been_scanned = FALSE
/obj/item/envelope/suicide_act(mob/user)
user.visible_message(SPAN_SUICIDE("[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/activate_self(mob/user)
if(!user?.mind)
return ..()
if(user.real_name != recipient)
to_chat(user, SPAN_WARNING("You don't want to open up another person's mail, that's an invasion of their privacy!"))
return ITEM_INTERACT_COMPLETE
if(do_after(user, 1 SECONDS, target = user) && !QDELETED(src))
to_chat(user, SPAN_NOTICE("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)
return ITEM_INTERACT_COMPLETE
/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.real_name
name = "letter to [recipient]"
recipient_job = lowertext(mail_attracted_people.assigned_role)
return
if(!admin_spawned)
log_debug("Failed to find a new name to assign to [src]!")
qdel(src)
/obj/item/envelope/examine(mob/user)
. = ..()
. += "This letter is addressed to [recipient], the [recipient_job]."
/obj/item/envelope/security
icon_state = "mail_sec"
possible_contents = list(/obj/item/food/donut/sprinkles,
/obj/item/megaphone,
/obj/item/clothing/mask/whistle,
/obj/item/poster/random_official,
/obj/item/restraints/handcuffs/pinkcuffs,
/obj/item/restraints/legcuffs/bola/energy,
/obj/item/reagent_containers/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,
/obj/item/storage/box/scratch_cards)
job_list = DEP_MAIL_LIST_SECURITY
/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,
/obj/item/storage/box/scratch_cards)
job_list = DEP_MAIL_LIST_SCIENCE
/obj/item/envelope/supply
icon_state = "mail_sup"
possible_contents = list(/obj/item/reagent_containers/hypospray/autoinjector/survival,
/obj/item/reagent_containers/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/explorer,
/obj/item/toy/figure/crew/qm,
/obj/item/toy/figure/crew/miner,
/obj/item/storage/box/scratch_cards)
job_list = DEP_MAIL_LIST_SUPPLY
/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/food/fortunecookie,
/obj/item/scalpel/laser,
/obj/item/surgical_drapes,
/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,
/obj/item/storage/box/scratch_cards)
job_list = DEP_MAIL_LIST_MEDICAL
/obj/item/envelope/engineering
icon_state = "mail_eng"
possible_contents = list(/obj/item/airlock_electronics,
/obj/item/reagent_containers/drinks/bottle/beer,
/obj/item/food/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,
/obj/item/storage/box/scratch_cards)
job_list = DEP_MAIL_LIST_ENGINEERING
/obj/item/envelope/bread
icon_state = "mail_serv"
possible_contents = list(/obj/item/painter,
/obj/item/gun/energy/floragun,
/obj/item/reagent_containers/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,
/obj/item/storage/box/scratch_cards)
job_list = DEP_MAIL_LIST_BREAD
/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/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,
/obj/item/storage/box/scratch_cards)
job_list = DEP_MAIL_LIST_SERVICE
/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/patch/synthflesh,
/obj/item/paper_bin/nanotrasen,
/obj/item/food/spesslaw,
/obj/item/clothing/head/collectable/petehat,
/obj/item/toy/figure/crew/captain,
/obj/item/toy/figure/crew/iaa,
/obj/item/toy/figure/crew/dsquad,
/obj/item/storage/box/scratch_cards)
job_list = DEP_MAIL_LIST_COMMAND
/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,
/obj/item/storage/box/scratch_cards)
job_list = DEP_MAIL_LIST_MISC
/*//////////////////////\/
\/ \\ // \/
\/ \\ // \/
\/ \\ // \/
\/ \\ // \/
\/ \\// \/
\/ \/
\/ You've got: \/
\/ Mail \/
\/ \/
\/\\\\\\\\\\\\\\\\\\\\\\*/
/obj/item/mail_scanner
name = "mail scanner"
desc = "A portable mail scanner, this device allows you to log mail deliveries in exchange for financial compensation."
icon = 'icons/obj/device.dmi'
icon_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 = ITEM_SLOT_BELT
w_class = WEIGHT_CLASS_SMALL
origin_tech = "magnets=1"
materials = list(MAT_METAL = 1500, MAT_GLASS = 500)
/// 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
/// How far away can the scanner scan mail or people
var/scanner_range = 7
new_attack_chain = TRUE
/obj/item/mail_scanner/examine(mob/user)
. = ..()
. += SPAN_NOTICE("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/ranged_interact_with_atom(atom/target, mob/living/user, list/modifiers)
if(get_dist(target, user) > scanner_range)
to_chat(user, SPAN_WARNING("The scanner doesn't reach that far!"))
return ITEM_INTERACT_COMPLETE
if(istype(target, /obj/item/envelope))
scan_envelope(target, user)
return ITEM_INTERACT_COMPLETE
if(isliving(target))
scan_recipient(target, user)
return ITEM_INTERACT_COMPLETE
return ..()
/obj/item/mail_scanner/interact_with_atom(atom/target, mob/living/user, list/modifiers)
if(istype(target, /obj/item/envelope))
scan_envelope(target, user)
return ITEM_INTERACT_COMPLETE
if(isliving(target))
scan_recipient(target, user)
return ITEM_INTERACT_COMPLETE
return ..()
/obj/item/mail_scanner/proc/scan_envelope(obj/item/envelope/envelope, mob/user)
if(!istype(envelope))
return FALSE
if(envelope.has_been_scanned)
to_chat(user, SPAN_WARNING("This letter has already been logged to the active database!"))
playsound(loc, 'sound/mail/maildenied.ogg', 50, TRUE)
return FALSE
to_chat(user, SPAN_NOTICE("You add [envelope] to the active database."))
playsound(loc, 'sound/mail/mailscanned.ogg', 50, TRUE)
saved = envelope
SSblackbox.record_feedback("amount", "successful_mail_scan", 1)
add_fingerprint(user)
return TRUE
/obj/item/mail_scanner/proc/scan_recipient(mob/living/recipient, mob/user)
if(!istype(recipient))
return FALSE
if(!saved)
to_chat(user, SPAN_WARNING("Error: You have not logged mail to the mail scanner!"))
playsound(loc, 'sound/mail/maildenied.ogg', 50, TRUE)
return FALSE
if(recipient.stat == DEAD)
to_chat(user, SPAN_WARNING("Consent Verification failed: You can't deliver mail to a corpse!"))
playsound(loc, 'sound/mail/maildenied.ogg', 50, TRUE)
return FALSE
if(recipient.real_name != saved.recipient)
to_chat(user, SPAN_WARNING("'Identity Verification failed: Target is not an authorized recipient of this package!"))
playsound(loc, 'sound/mail/maildenied.ogg', 50, TRUE)
return FALSE
if(!recipient.client)
to_chat(user, SPAN_WARNING("Consent Verification failed: The scanner will not accept confirmation of orders from SSD people!"))
playsound(loc, 'sound/mail/maildenied.ogg', 50, TRUE)
return FALSE
saved.has_been_scanned = TRUE
saved = null
to_chat(user, SPAN_NOTICE("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", "Nanotrasen Mail and Interstellar Logistics", supress_log = FALSE)
SSblackbox.record_feedback("amount", "successful_mail_delivery", 1)
add_fingerprint(user)
return TRUE