diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index 442b27c3ab..5cae51bbb0 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -921,3 +921,45 @@ modules/mob/living/carbon/human/life.dm if you die, you will be zoomed out. /obj/item/proc/animal_consumed(var/mob/user) user.visible_message(SPAN_NOTICE("\The [user] finishes eating \the [src]."), SPAN_NOTICE("You finish eating \the [src].")) qdel(src) + + +/obj/item/proc/BlockInteraction(mob/living/user, silent, list/options) + var/static/list/default_options = list( + "type" = /mob/living, + "distance" = TRUE, + "incapacitation" = INCAPACITATION_DEFAULT, + "dexterity" = FALSE + ) + . = TRUE // A code per case is possible. For now, just TRUE if the interaction should be blocked. + if (QDELETED(user)) + return + if (!options) + options = default_options + var/test_type = options["type"] + if (test_type) + if (!istype(user, test_type)) + if (!silent) + to_chat(user, SPAN_WARNING("You're the wrong kind of mob to use \the [src].")) + return + var/test_distance = options["distance"] + if (test_distance > 0) + if (test_distance <= 1) + if (!Adjacent(user)) //Adjacency is more costly than simple distance, but common use. + if (!silent) + to_chat(user, SPAN_WARNING("You're not close enough to \the [src].")) + return + else if (get_dist(src, user) > test_distance) + if (!silent) + to_chat(user, SPAN_WARNING("You're not close enough to \the [src].")) + return + var/test_incapacitation = options["incapacitation"] + if (test_incapacitation) + if (user.incapacitated(test_incapacitation)) + if (!silent) + to_chat(user, SPAN_WARNING("You're in no condition to use \the [src].")) + return + var/test_dexterity = options["dexterity"] + if (test_dexterity) + if (!user.check_dexterity(test_dexterity, src, silent)) // Handles its own failure message. + return + return FALSE diff --git a/code/modules/paperwork/folders.dm b/code/modules/paperwork/folders.dm index 3c71bccdb5..c8c6c79889 100644 --- a/code/modules/paperwork/folders.dm +++ b/code/modules/paperwork/folders.dm @@ -7,85 +7,55 @@ pressure_resistance = 2 drop_sound = 'sound/items/drop/paper.ogg' pickup_sound = 'sound/items/pickup/paper.ogg' - var/list/obj/item/paper/initial_contents + + /// The items that spawn inside this folder, if anything. + var/list/obj/item/initial_contents /obj/item/folder/Initialize() . = ..() - for (var/obj/item/paper/paper as anything in initial_contents) - new paper (src) + if (length(initial_contents)) + for (var/obj/item/item as anything in initial_contents) + new item (src) + update_icon() -/obj/item/folder/blue - desc = "A blue folder." - icon_state = "folder_blue" - -/obj/item/folder/red - desc = "A red folder." - icon_state = "folder_red" - -/obj/item/folder/yellow - desc = "A yellow folder." - icon_state = "folder_yellow" - -/obj/item/folder/white - desc = "A white folder." - icon_state = "folder_white" - -/obj/item/folder/blue_captain - desc = "A blue folder with Site Manager markings." - icon_state = "folder_captain" - -/obj/item/folder/blue_hop - desc = "A blue folder with HoP markings." - icon_state = "folder_hop" - -/obj/item/folder/white_cmo - desc = "A white folder with CMO markings." - icon_state = "folder_cmo" - -/obj/item/folder/white_rd - desc = "A white folder with RD markings." - icon_state = "folder_rd" - -/obj/item/folder/white_rd/Initialize() - . = ..() - //add some memos - var/obj/item/paper/P = new() - P.name = "Memo RE: proper analysis procedure" - P.info = "
We keep test dummies in pens here for a reason" - src.contents += P - update_icon() - -/obj/item/folder/yellow_ce - desc = "A yellow folder with CE markings." - icon_state = "folder_ce" - -/obj/item/folder/red_hos - desc = "A red folder with HoS markings." - icon_state = "folder_hos" - /obj/item/folder/update_icon() cut_overlays() - if(contents.len) + if (length(contents)) add_overlay("folder_paper") - return -/obj/item/folder/attackby(obj/item/W as obj, mob/user as mob) - if(istype(W, /obj/item/paper) || istype(W, /obj/item/photo) || istype(W, /obj/item/paper_bundle)) - user.drop_item() - W.loc = src - to_chat(user, "You put the [W] into \the [src].") + +/obj/item/folder/attackby(obj/item/item, mob/living/user) + . = TRUE + if (istype(item, /obj/item/paper) || istype(item, /obj/item/photo) || istype(item, /obj/item/paper_bundle)) + if (!user.unEquip(item, target = src)) + return + user.visible_message( + SPAN_ITALIC("\The [user] puts \a [item] into \a [src]."), + SPAN_ITALIC("You put \the [item] into \the [src]."), + range = 5 + ) update_icon() - else if(istype(W, /obj/item/pen)) - var/n_name = sanitizeSafe(input(usr, "What would you like to label the folder?", "Folder Labelling", null) as text, MAX_NAME_LEN) - if((loc == usr && usr.stat == 0)) - name = "folder[(n_name ? text("- '[n_name]'") : null)]" - return + return + if (istype(item, /obj/item/pen)) + var/response = input(user, "Set new folder label:") as null | text + if (isnull(response)) + return + if (BlockInteraction(user)) + return + if (!response) + name = initial(name) + return + response = sanitizeSafe(response, MAX_NAME_LEN) + if (!response) + return + name = "folder - `[response]`" + return ..() -/obj/item/folder/attack_self(mob/user as mob) + +/obj/item/folder/attack_self(mob/living/user) var/dat = "[name]" - for(var/obj/item/paper/P in src) dat += "Remove Rename - [P.name]
" for(var/obj/item/photo/Ph in src) @@ -97,19 +67,17 @@ add_fingerprint(usr) return -/obj/item/folder/Topic(href, href_list) + +/obj/item/folder/Topic(href, list/href_list) ..() if((usr.stat || usr.restrained())) return - if(src.loc == usr) - if(href_list["remove"]) var/obj/item/P = locate(href_list["remove"]) if(P && (P.loc == src) && istype(P)) P.loc = usr.loc usr.put_in_hands(P) - else if(href_list["read"]) var/obj/item/paper/P = locate(href_list["read"]) if(P && (P.loc == src) && istype(P)) @@ -130,38 +98,92 @@ onclose(usr, "[P.name]") else if(href_list["rename"]) var/obj/item/O = locate(href_list["rename"]) - if(O && (O.loc == src)) if(istype(O, /obj/item/paper)) var/obj/item/paper/to_rename = O to_rename.rename() - else if(istype(O, /obj/item/photo)) var/obj/item/photo/to_rename = O to_rename.rename() - else if(istype(O, /obj/item/paper_bundle)) var/obj/item/paper_bundle/to_rename = O to_rename.rename() - - //Update everything attack_self(usr) update_icon() - return + + +/obj/item/folder/blue + desc = "A blue folder." + icon_state = "folder_blue" + + +/obj/item/folder/red + desc = "A red folder." + icon_state = "folder_red" + + +/obj/item/folder/yellow + desc = "A yellow folder." + icon_state = "folder_yellow" + + +/obj/item/folder/white + desc = "A white folder." + icon_state = "folder_white" + + +/obj/item/folder/blue_captain + desc = "A blue folder with Site Manager markings." + icon_state = "folder_captain" + + +/obj/item/folder/blue_hop + desc = "A blue folder with HoP markings." + icon_state = "folder_hop" + + +/obj/item/folder/white_cmo + desc = "A white folder with CMO markings." + icon_state = "folder_cmo" + + +/obj/item/folder/yellow_ce + desc = "A yellow folder with CE markings." + icon_state = "folder_ce" + + +/obj/item/folder/red_hos + desc = "A red folder with HoS markings." + icon_state = "folder_hos" + + +/obj/item/folder/white_rd + desc = "A white folder with RD markings." + icon_state = "folder_rd" + initial_contents = list( + /obj/item/paper/white_rd_folder + ) + + +/obj/item/paper/white_rd_folder + name = "Memo RE: proper analysis procedure" + info = {"\ +

We keep test dummies in pens here for a reason.

\ + "} /obj/item/folder/envelope abstract_type = /obj/item/folder/envelope name = "envelope" desc = "A thick envelope for holding documents securely." - icon_state = "envelope_sealed" + icon_state = "envelope_envelope_seal" - // When true, a confirmation is shown before opening. If accepted, it becomes false. - var/sealed = TRUE + // When falsy, the envelope can be opened. Otherwise, the seal name to show in examine. + var/envelope_seal = "original" /obj/item/folder/envelope/update_icon() - if (sealed) + if (envelope_seal) icon_state = "envelope_sealed" else if (length(contents)) icon_state = "envelope_full" @@ -171,59 +193,96 @@ /obj/item/folder/envelope/examine(mob/user) . = ..() - if (sealed) - . += "The seal is [SPAN_NOTICE("intact")]." + if (get_dist(src, user) > 3 && !isobserver(user)) + return + if (envelope_seal) + . += "It has an [SPAN_NOTICE("intact [envelope_seal] seal")]." else - . += "The seal is [SPAN_WARNING("broken")]." + . += "The seal is [SPAN_DANGER("broken")]." /obj/item/folder/envelope/attack_self(mob/living/user) - if (sealed) + if (envelope_seal) ConfirmBreakSeal(user) return ..() /obj/item/folder/envelope/attackby(obj/item/item, mob/living/user) - if (sealed && is_sharp(item)) + . = TRUE + if (envelope_seal) + if (!is_sharp(item)) + return ..() if (user.a_intent == I_HURT) user.visible_message( SPAN_WARNING("\The [user] slashes open \a [src] with \a [item]!"), SPAN_ITALIC("You slash open \the [src] with \the [item]!"), range = 5 ) - BreakSeal() - return TRUE + SetSeal(FALSE) + return ConfirmBreakSeal(user) - return TRUE - ..() + return + else if (istype(item, /obj/item/stamp)) + ConfirmCreateSeal(item, user) + return + return ..() + + +/obj/item/folder/envelope/proc/ConfirmCreateSeal(obj/item/stamp/stamp, mob/living/user) + PROTECTED_PROC(TRUE) + if (!istype(user)) + return + if (envelope_seal) + to_chat(user, SPAN_WARNING("\The [src] already has \a [envelope_seal] seal.")) + return + if (BlockInteraction(user)) + return + var/response = alert(user, "Create a new [stamp.name] seal?", "[src]", "Yes", "No") + if (response != "Yes") + return + if (BlockInteraction(user)) + return + if (envelope_seal) + to_chat(user, SPAN_WARNING("\The [src] already has \a [envelope_seal] seal.")) + return + user.visible_message( + SPAN_WARNING("\The [user] uses \a [stamp] to seal \a [src]."), + SPAN_ITALIC("You form a new seal on \the [src] with \the [stamp]."), + range = 5 + ) + SetSeal(stamp.authority_name) /obj/item/folder/envelope/proc/ConfirmBreakSeal(mob/living/user) PROTECTED_PROC(TRUE) if (!istype(user)) return - if (!Adjacent(user) || user.incapacitated()) - to_chat(user, SPAN_WARNING("You're in no condition to do that.")) + if (!envelope_seal) + to_chat(user, SPAN_WARNING("\The [src] is already open.")) + return + if (BlockInteraction(user)) return var/response = alert(user, "Break the seal?", "[src]", "Yes", "No") if (response != "Yes") return - if (!Adjacent(user) || user.incapacitated()) - to_chat(user, SPAN_WARNING("You're in no condition to do that.")) + if (BlockInteraction(user)) + return + if (!envelope_seal) + to_chat(user, SPAN_WARNING("\The [src] is already open.")) return user.visible_message( SPAN_WARNING("\The [user] breaks the seal on \a [src]."), SPAN_ITALIC("You break the seal on \the [src]."), range = 5 ) - BreakSeal() + SetSeal(FALSE) -/// Forces the envelope to be open. Internal / pcall. -/obj/item/folder/envelope/proc/BreakSeal() +/// Sets the seal name or breaks it, updating the envelope's icon. +/obj/item/folder/envelope/proc/SetSeal(value) PROTECTED_PROC(TRUE) - sealed = FALSE + envelope_seal = value update_icon() diff --git a/code/modules/paperwork/stamps.dm b/code/modules/paperwork/stamps.dm index 538e2f915c..028fd5c980 100644 --- a/code/modules/paperwork/stamps.dm +++ b/code/modules/paperwork/stamps.dm @@ -15,102 +15,165 @@ drop_sound = 'sound/items/drop/device.ogg' pickup_sound = 'sound/items/pickup/device.ogg' + /// The authority this stamp represents. Used where the full object name would be inappropriate. + var/authority_name = "" + + /// Any trailing posessiveness for authority_name. + var/authority_suffix = "" + + +/obj/item/stamp/Initialize() + . = ..() + GenerateName() + + +/obj/item/stamp/proc/GenerateName() + if (!authority_name) + name = initial(name) + return + var/first_char = copytext_char(authority_name, 1, 2) + if (lowertext(first_char) != first_char) + name = "\improper [authority_name][authority_suffix] [initial(name)]" + else + name = "[authority_name][authority_suffix] [initial(name)]" + + /obj/item/stamp/captain - name = "site manager's rubber stamp" icon_state = "stamp-cap" + authority_name = "site manager" + authority_suffix = "'s" + /obj/item/stamp/hop - name = "head of personnel's rubber stamp" icon_state = "stamp-hop" + authority_name = "head of personnel" + authority_suffix = "'s" + /obj/item/stamp/hos - name = "head of security's rubber stamp" icon_state = "stamp-hos" + authority_name = "head of security" + authority_suffix = "'s" + /obj/item/stamp/ward - name = "warden's rubber stamp" icon_state = "stamp-ward" + authority_name = "warden" + authority_suffix = "'s" + /obj/item/stamp/ce - name = "chief engineer's rubber stamp" icon_state = "stamp-ce" + authority_name = "chief engineer" + authority_suffix = "'s" + /obj/item/stamp/rd - name = "research director's rubber stamp" icon_state = "stamp-rd" + authority_name = "research director" + authority_suffix = "'s" + /obj/item/stamp/cmo - name = "chief medical officer's rubber stamp" icon_state = "stamp-cmo" + authority_name = "chief medical officer" + authority_suffix = "'s" + /obj/item/stamp/denied - name = "\improper DENIED rubber stamp" icon_state = "stamp-deny" attack_verb = list("DENIED") + authority_name = "DENIED" + /obj/item/stamp/accepted - name = "\improper ACCEPTED rubber stamp" icon_state = "stamp-ok" + attack_verb = list("ACCEPTED") + authority_name = "ACCEPTED" + /obj/item/stamp/clown - name = "clown's rubber stamp" icon_state = "stamp-clown" + authority_name = "clown" + authority_suffix = "'s" + /obj/item/stamp/internalaffairs - name = "internal affairs rubber stamp" icon_state = "stamp-intaff" + authority_name = "internal affairs" + /obj/item/stamp/centcomm - name = "\improper CentCom rubber stamp" icon_state = "stamp-cent" + authority_name = "CentCom" + /obj/item/stamp/qm - name = "quartermaster's rubber stamp" icon_state = "stamp-qm" + authority_name = "quartermaster" + authority_suffix = "'s" + /obj/item/stamp/cargo - name = "cargo rubber stamp" icon_state = "stamp-cargo" + authority_name = "cargo" + /obj/item/stamp/solgov - name = "\improper Sol Government rubber stamp" icon_state = "stamp-sg" + authority_name = "Sol Government" + /obj/item/stamp/solgovlogo - name = "\improper Sol Government logo stamp" icon_state = "stamp-sol" + name = "logo stamp" + authority_name = "Sol Government" + /obj/item/stamp/einstein - name = "\improper Einstein Engines rubber stamp" icon_state = "stamp-einstein" + authority_name = "Eintstein Engines" + /obj/item/stamp/hephaestus - name = "\improper Hephaestus Industries rubber stamp" icon_state = "stamp-heph" + authority_name = "Hephaestus Industries" + /obj/item/stamp/zeng_hu - name = "\improper Zeng-Hu Pharmaceuticals rubber stamp" icon_state = "stamp-zenghu" + authority_name = "Zeng-Hu Pharmaceuticals" -// Syndicate stamp to forge documents. -/obj/item/stamp/chameleon/attack_self(mob/user as mob) - var/list/stamp_types = typesof(/obj/item/stamp) - src.type // Get all stamp types except our own - var/list/stamps = list() +/obj/item/stamp/chameleon + var/static/list/chameleon_stamps - // Generate them into a list - for(var/stamp_type in stamp_types) - var/obj/item/stamp/S = new stamp_type - stamps[capitalize(S.name)] = S - var/list/show_stamps = list("EXIT" = null) + sortList(stamps) // the list that will be shown to the user to pick from +/obj/item/stamp/chameleon/Initialize() + . = ..() + if (chameleon_stamps) + return + chameleon_stamps = list() + for (var/obj/item/stamp/stamp as anything in (typesof(/obj/item/stamp) - type)) + stamp = new stamp + chameleon_stamps[stamp.name] = list(stamp.icon_state, stamp.authority_name, stamp.authority_suffix) + chameleon_stamps = sortList(chameleon_stamps) - var/input_stamp = input(user, "Choose a stamp to disguise as.", "Choose a stamp.") in show_stamps - if(user && (src in user.contents)) // Er, how necessary is this in attack_self? +/obj/item/stamp/chameleon/attack_self(mob/living/user) + UpdateChameleon(user) - var/obj/item/stamp/chosen_stamp = stamps[capitalize(input_stamp)] - if(chosen_stamp) - name = chosen_stamp.name - icon_state = chosen_stamp.icon_state +/obj/item/stamp/chameleon/proc/UpdateChameleon(mob/living/user) + if (BlockInteraction(user)) + return + var/response = input(user, "Select a stamp to copy:") as null | anything in chameleon_stamps + if (!response || !(response in chameleon_stamps)) + return + if (BlockInteraction(user)) + return + var/list/stamp = chameleon_stamps[response] + name = response + icon_state = stamp[1] + authority_name = stamp[2] + authority_suffix = stamp[3]