From 762779f3f227bfbd32b098c5ff33e57ead779ad0 Mon Sep 17 00:00:00 2001 From: John Willard <53777086+JohnFulpWillard@users.noreply.github.com> Date: Wed, 27 Mar 2024 00:58:01 -0400 Subject: [PATCH] Adds a photobooth (#82105) ## About The Pull Request Adds a photobooth machine to the HoP line which can be used to update your security records photo. It updates the records of the person's name, which means hiding your identity and wearing an ID will let you change other people's photos. If you aren't hiding your identity and are wearing someone else's ID, it won't update at all because it will be unable to find your record. There's 2 variants of this machine, one at the HoP line that's tied behind Law Office access (so Lawyers have an extra thing they can help out with if no one's available, but maybe it would be better behind Library access since Curators are our "photographer" role?), and the Security one that requires Security access. The Security one has a special feature that it adds a height chart behind the player. ![image](https://github.com/tgstation/tgstation/assets/53777086/c4eb0661-d752-4052-8006-2898af78c528) This machine is used by either right-clicking on it or by pressing a button, so the HoP can do it remotely from their line (except on Tram where it's in the library), and after a few delays it'll update your records automatically to how you look like. Emagging the machine will remove its access restrictions but every time it updates your photos it will spam camera flashing and blind you. Sprites Open ![image](https://github.com/tgstation/tgstation/assets/53777086/d5e97d84-46d8-40b5-995f-c0ef5c7f18ac) Closed ![image](https://github.com/tgstation/tgstation/assets/53777086/520e88ed-4630-40d8-9039-4c6dd85a9872) Security version on the left, has a red tint on it ![image](https://github.com/tgstation/tgstation/assets/53777086/ef71a092-8d1b-4776-bdb4-6718308967c7) Video demonstration (old sprites): https://github.com/tgstation/tgstation/assets/53777086/a2e59f08-2d58-4f5b-b081-e137c7606d35 ## Why It's Good For The Game Current security records has no way to change your photo ID, something that was a feature before it was moved to TGUI. The only alternative is to fully delete your record and make a new one with a photo and the same name, but this shares a major issue with the HTML UI: You're immediately sold out by the fact you have a background. No matter how well you try to cover your tracks, any security officer looking at records will see you are the only photo on the manifest that has a background and even a camera in your hand, which makes it impossible to get away with it unless security essentially takes pity on you. This opens up the ability to fully mask yourself in records, finally. This is also better even for non antagonists because you can now properly update your photos to match what you look like in the event of an appearance change or even just a job change, which makes it a benefit for HoPs who likes to keep records as up-to-date as possible. ## Changelog :cl: JohnFulpWillard, Twox add: Added a new photobooth machine to the HoP's line. fix: Things checking for access now checks your off hand, too. /:cl: --------- Co-authored-by: Ghom <42542238+Ghommie@users.noreply.github.com> --- _maps/map_files/Birdshot/birdshot.dmm | 29 +-- .../map_files/Deltastation/DeltaStation2.dmm | 32 +-- .../map_files/IceBoxStation/IceBoxStation.dmm | 48 ++-- _maps/map_files/MetaStation/MetaStation.dmm | 113 ++++----- _maps/map_files/NorthStar/north_star.dmm | 22 +- _maps/map_files/tramstation/tramstation.dmm | 2 + code/controllers/subsystem/ticker.dm | 3 +- code/datums/records/manifest.dm | 18 +- code/datums/records/record.dm | 42 +++- code/game/machinery/buttons.dm | 21 +- .../machinery/computer/records/records.dm | 29 +-- .../machinery/computer/records/security.dm | 16 +- code/game/machinery/photobooth.dm | 228 ++++++++++++++++++ .../machines/machine_circuitboards.dm | 14 +- code/modules/jobs/access.dm | 2 +- code/modules/jobs/job_types/prisoner.dm | 12 +- code/modules/mob/inventory.dm | 2 +- .../research/designs/machine_designs.dm | 20 ++ code/modules/research/techweb/all_nodes.dm | 2 + icons/obj/machines/photobooth.dmi | Bin 0 -> 3425 bytes tgstation.dme | 1 + 21 files changed, 482 insertions(+), 174 deletions(-) create mode 100644 code/game/machinery/photobooth.dm create mode 100644 icons/obj/machines/photobooth.dmi diff --git a/_maps/map_files/Birdshot/birdshot.dmm b/_maps/map_files/Birdshot/birdshot.dmm index 7b7fd7a2eb3..dfdc0d2a761 100644 --- a/_maps/map_files/Birdshot/birdshot.dmm +++ b/_maps/map_files/Birdshot/birdshot.dmm @@ -21117,6 +21117,9 @@ /obj/machinery/door/window/left/directional/west{ name = "Reception Window" }, +/obj/structure/desk_bell{ + pixel_x = 7 + }, /turf/open/floor/iron/textured_large, /area/station/command/heads_quarters/hop) "hBA" = ( @@ -22368,20 +22371,9 @@ /turf/open/floor/iron/dark, /area/station/medical/medbay/aft) "ibq" = ( -/obj/structure/table/wood, -/obj/structure/desk_bell{ - pixel_x = 7 - }, -/obj/item/paper_bin{ - pixel_x = -4; - pixel_y = 4 - }, -/obj/item/pen{ - pixel_x = -4; - pixel_y = 3 - }, /obj/machinery/bluespace_vendor/directional/north, /obj/machinery/light/small/directional/west, +/obj/machinery/photobooth, /turf/open/floor/iron/textured_large, /area/station/hallway/primary/central/fore) "ibv" = ( @@ -26278,8 +26270,12 @@ }, /obj/machinery/button/flasher{ id = "hopflash"; - pixel_x = -23; - pixel_y = 11 + pixel_x = -24; + pixel_y = 6 + }, +/obj/machinery/button/photobooth{ + pixel_y = -6; + pixel_x = -24 }, /turf/open/floor/iron/dark/textured_corner, /area/station/command/heads_quarters/hop) @@ -39342,6 +39338,7 @@ dir = 4 }, /obj/effect/decal/cleanable/dirt, +/obj/effect/turf_decal/delivery, /turf/open/floor/iron/half, /area/station/hallway/primary/central/fore) "nZh" = ( @@ -65647,15 +65644,13 @@ /obj/effect/turf_decal/tile/dark_red/half/contrasted{ dir = 8 }, -/obj/structure/chair{ - pixel_y = -2 - }, /obj/machinery/computer/security/telescreen/interrogation{ dir = 8; name = "isolation room monitor"; network = list("isolation"); pixel_x = 30 }, +/obj/machinery/photobooth/security, /turf/open/floor/iron, /area/station/security/execution/transfer) "wvn" = ( diff --git a/_maps/map_files/Deltastation/DeltaStation2.dmm b/_maps/map_files/Deltastation/DeltaStation2.dmm index 56237c8fea2..d38200e7a71 100644 --- a/_maps/map_files/Deltastation/DeltaStation2.dmm +++ b/_maps/map_files/Deltastation/DeltaStation2.dmm @@ -19010,7 +19010,9 @@ /obj/effect/turf_decal/stripes/line{ dir = 8 }, -/obj/item/folder/blue, +/obj/structure/desk_bell{ + pixel_x = 7 + }, /turf/open/floor/iron, /area/station/command/heads_quarters/hop) "eIy" = ( @@ -49305,10 +49307,8 @@ req_access = list("kitchen") }, /obj/machinery/button/ticket_machine{ - pixel_y = 22 - }, -/obj/item/radio/intercom/directional/north{ - pixel_y = 30 + pixel_y = 22; + pixel_x = -6 }, /obj/machinery/button/door/directional/west{ id = "hopblast"; @@ -49323,6 +49323,10 @@ req_access = list("hop") }, /obj/effect/turf_decal/tile/neutral/fourcorners, +/obj/machinery/button/photobooth{ + pixel_y = 22; + pixel_x = 6 + }, /turf/open/floor/iron/dark, /area/station/command/heads_quarters/hop) "mlW" = ( @@ -59837,6 +59841,7 @@ /obj/structure/table/wood, /obj/item/papercutter, /obj/item/paper/fluff/ids_for_dummies, +/obj/item/radio/intercom/directional/east, /turf/open/floor/wood, /area/station/command/heads_quarters/hop) "paQ" = ( @@ -66038,11 +66043,9 @@ /turf/open/floor/iron, /area/station/engineering/main) "qyK" = ( -/obj/structure/table/reinforced, -/obj/item/paper_bin, -/obj/item/pen, /obj/machinery/light/small/directional/west, /obj/effect/turf_decal/tile/neutral/fourcorners, +/obj/machinery/photobooth/security, /turf/open/floor/iron/dark, /area/station/security/execution/transfer) "qyX" = ( @@ -67100,24 +67103,13 @@ /turf/open/floor/iron/dark, /area/station/security/courtroom) "qLa" = ( -/obj/structure/table, -/obj/item/paper_bin{ - pixel_x = -6; - pixel_y = 5 - }, -/obj/item/pen{ - pixel_x = -6; - pixel_y = 5 - }, /obj/machinery/flasher/directional/east{ id = "hopflash"; pixel_x = 0; pixel_y = 26 }, -/obj/structure/desk_bell{ - pixel_x = 7 - }, /obj/effect/turf_decal/tile/neutral/fourcorners, +/obj/machinery/photobooth, /turf/open/floor/iron/dark, /area/station/hallway/primary/central/fore) "qLg" = ( diff --git a/_maps/map_files/IceBoxStation/IceBoxStation.dmm b/_maps/map_files/IceBoxStation/IceBoxStation.dmm index 35700dbf5ea..00f1dbce54c 100644 --- a/_maps/map_files/IceBoxStation/IceBoxStation.dmm +++ b/_maps/map_files/IceBoxStation/IceBoxStation.dmm @@ -20624,6 +20624,10 @@ dir = 8 }, /obj/machinery/light/small/directional/west, +/obj/machinery/requests_console/directional/west, +/obj/effect/mapping_helpers/requests_console/assistance, +/obj/effect/mapping_helpers/requests_console/announcement, +/obj/effect/mapping_helpers/requests_console/information, /turf/open/floor/iron, /area/station/command/heads_quarters/hop) "gnL" = ( @@ -25917,7 +25921,6 @@ /turf/open/floor/plating/snowed/icemoon, /area/icemoon/underground/explored) "hYW" = ( -/obj/machinery/door/firedoor, /obj/effect/turf_decal/tile/red{ dir = 4 }, @@ -25980,11 +25983,12 @@ /turf/open/floor/iron, /area/station/cargo/storage) "iaS" = ( -/obj/machinery/modular_computer/preset/id, -/obj/item/paper/fluff/ids_for_dummies, /obj/effect/turf_decal/tile/blue/half/contrasted{ dir = 8 }, +/obj/structure/chair/office{ + dir = 8 + }, /turf/open/floor/iron, /area/station/command/heads_quarters/hop) "iaT" = ( @@ -59014,9 +59018,6 @@ /turf/open/floor/iron/dark/smooth_large, /area/station/security/processing) "rYk" = ( -/obj/structure/chair/office{ - dir = 8 - }, /obj/machinery/button/flasher{ id = "hopflash"; pixel_x = 8; @@ -59041,6 +59042,10 @@ /obj/effect/turf_decal/tile/blue/anticorner/contrasted{ dir = 8 }, +/obj/machinery/modular_computer/preset/id{ + dir = 1 + }, +/obj/item/paper/fluff/ids_for_dummies, /turf/open/floor/iron, /area/station/command/heads_quarters/hop) "rYq" = ( @@ -69573,13 +69578,9 @@ /turf/open/floor/iron, /area/station/security/courtroom) "vry" = ( -/obj/machinery/requests_console/directional/south{ - department = "Head of Personnel's Desk"; - name = "Head of Personnel's Requests Console" +/obj/machinery/button/photobooth{ + pixel_y = -26 }, -/obj/effect/mapping_helpers/requests_console/announcement, -/obj/effect/mapping_helpers/requests_console/information, -/obj/effect/mapping_helpers/requests_console/assistance, /turf/open/floor/iron, /area/station/command/heads_quarters/hop) "vrC" = ( @@ -70882,6 +70883,10 @@ /obj/machinery/holopad, /turf/open/floor/iron/kitchen/diagonal, /area/station/service/kitchen) +"vMz" = ( +/obj/machinery/photobooth, +/turf/open/floor/iron, +/area/station/hallway/primary/central) "vMA" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -75533,10 +75538,7 @@ req_access = list("brig"); specialfunctions = 4 }, -/obj/item/paper/crumpled{ - default_raw_text = "Remember! Corporate spent a lot of money to create this state of the art fashion show. If we EVER even so much as HEAR a rumor that a news crew or corporate rep is coming by, this place needs to be in TIP TOP condition. It's all of our asses (and our pensions) if it's not."; - name = "Crumpled Memo" - }, +/obj/machinery/photobooth/security, /turf/open/floor/iron/smooth, /area/station/security/execution/transfer) "xgg" = ( @@ -169937,10 +169939,10 @@ thA thA thA thA -xMq psb scw psb +iDt thA thA xMq @@ -170194,10 +170196,10 @@ thA thA thA iDt -iDt scw iDt scw +iDt thA thA thA @@ -170451,10 +170453,10 @@ thA thA iDt iDt -iDt psb scw psb +iDt thA thA thA @@ -235216,10 +235218,10 @@ qrJ uLC xzh dnq -dnq qcu bDR hYW +mpy bep ylU dnq @@ -235474,9 +235476,9 @@ nqU rcE rcE rcE -rcE hVc jII +jII dnq ylU dnq @@ -235731,8 +235733,8 @@ ydT iIA fSC iIA -iIA cvg +vMz jII okb ylU @@ -235988,8 +235990,8 @@ cpm cpm cpm egY -egY wwn +jfc cpm pzb cRF diff --git a/_maps/map_files/MetaStation/MetaStation.dmm b/_maps/map_files/MetaStation/MetaStation.dmm index 2192d36a613..053d7dc0cbc 100644 --- a/_maps/map_files/MetaStation/MetaStation.dmm +++ b/_maps/map_files/MetaStation/MetaStation.dmm @@ -561,12 +561,6 @@ /area/station/ai_monitored/turret_protected/ai_upload) "akT" = ( /obj/structure/table, -/obj/item/storage/box/bodybags{ - pixel_x = 4; - pixel_y = 2 - }, -/obj/item/pen, -/obj/item/storage/box/prisoner, /obj/effect/turf_decal/trimline/red/filled/line{ dir = 9 }, @@ -575,6 +569,10 @@ network = list("ss13","prison") }, /obj/machinery/light/directional/south, +/obj/item/storage/box/prisoner{ + pixel_y = 8 + }, +/obj/item/storage/box/prisoner, /turf/open/floor/iron, /area/station/security/execution/transfer) "akZ" = ( @@ -4111,12 +4109,29 @@ /area/station/maintenance/aft/lesser) "buw" = ( /obj/structure/table, -/obj/item/clothing/gloves/color/orange, -/obj/item/restraints/handcuffs, -/obj/item/reagent_containers/spray/pepper, /obj/effect/turf_decal/tile/red/half/contrasted{ dir = 4 }, +/obj/item/clothing/under/rank/prisoner{ + pixel_x = 8; + pixel_y = 5 + }, +/obj/item/clothing/shoes/sneakers/orange{ + pixel_x = -6; + pixel_y = 10 + }, +/obj/item/clothing/shoes/sneakers/orange{ + pixel_x = -6; + pixel_y = 4 + }, +/obj/item/clothing/shoes/sneakers/orange{ + pixel_x = -6; + pixel_y = -2 + }, +/obj/item/clothing/shoes/sneakers/orange{ + pixel_x = -6; + pixel_y = -8 + }, /turf/open/floor/iron, /area/station/security/execution/transfer) "buE" = ( @@ -23906,29 +23921,12 @@ "iJx" = ( /obj/structure/cable, /obj/structure/table, -/obj/item/clothing/shoes/sneakers/orange{ - pixel_x = -6; - pixel_y = 10 - }, -/obj/item/clothing/shoes/sneakers/orange{ - pixel_x = -6; - pixel_y = -2 - }, -/obj/item/clothing/shoes/sneakers/orange{ - pixel_x = -6; - pixel_y = 4 - }, -/obj/item/clothing/shoes/sneakers/orange{ - pixel_x = -6; - pixel_y = -8 - }, -/obj/item/clothing/under/rank/prisoner{ - pixel_x = 8; - pixel_y = 5 - }, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/effect/turf_decal/tile/red/anticorner/contrasted, +/obj/item/clothing/gloves/color/orange, +/obj/item/restraints/handcuffs, +/obj/item/reagent_containers/spray/pepper, /turf/open/floor/iron, /area/station/security/execution/transfer) "iJC" = ( @@ -30248,14 +30246,22 @@ /turf/open/floor/iron, /area/station/hallway/primary/central) "kQz" = ( -/obj/structure/table, -/obj/item/storage/box/prisoner{ - pixel_y = 8 - }, -/obj/item/storage/box/prisoner, /obj/effect/turf_decal/tile/red/half/contrasted{ dir = 4 }, +/obj/structure/table, +/obj/item/clothing/under/rank/prisoner/skirt{ + pixel_x = 9; + pixel_y = 5 + }, +/obj/item/clothing/under/rank/prisoner{ + pixel_x = -2; + pixel_y = 5 + }, +/obj/item/clothing/under/rank/prisoner/skirt{ + pixel_x = -13; + pixel_y = 5 + }, /turf/open/floor/iron, /area/station/security/execution/transfer) "kQP" = ( @@ -44736,7 +44742,7 @@ "qaN" = ( /obj/structure/table/wood, /obj/machinery/button/ticket_machine{ - pixel_x = 32 + pixel_x = 38 }, /obj/machinery/light_switch/directional/south{ pixel_x = 6; @@ -44763,6 +44769,13 @@ pixel_y = -34; req_access = list("hop") }, +/obj/item/pen{ + pixel_x = -4; + pixel_y = 3 + }, +/obj/machinery/button/photobooth{ + pixel_x = 26 + }, /turf/open/floor/wood, /area/station/command/heads_quarters/hop) "qaP" = ( @@ -55075,6 +55088,9 @@ name = "Privacy Shutters" }, /obj/structure/cable, +/obj/structure/desk_bell{ + pixel_x = 7 + }, /turf/open/floor/iron, /area/station/command/heads_quarters/hop) "tIx" = ( @@ -57418,20 +57434,9 @@ /turf/open/floor/iron, /area/station/engineering/break_room) "uwC" = ( -/obj/structure/table, -/obj/item/paper_bin{ - pixel_x = -4; - pixel_y = 4 - }, -/obj/item/pen{ - pixel_x = -4; - pixel_y = 3 - }, /obj/effect/turf_decal/delivery, /obj/machinery/bluespace_vendor/directional/east, -/obj/structure/desk_bell{ - pixel_x = 7 - }, +/obj/machinery/photobooth, /turf/open/floor/iron, /area/station/hallway/secondary/command) "uwK" = ( @@ -57909,25 +57914,13 @@ /turf/open/floor/iron, /area/station/engineering/break_room) "uFK" = ( -/obj/structure/table, -/obj/item/clothing/under/rank/prisoner/skirt{ - pixel_x = -13; - pixel_y = 5 - }, -/obj/item/clothing/under/rank/prisoner/skirt{ - pixel_x = 9; - pixel_y = 5 - }, -/obj/item/clothing/under/rank/prisoner{ - pixel_x = -2; - pixel_y = 5 - }, /obj/structure/cable, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/effect/turf_decal/tile/red/anticorner/contrasted{ dir = 8 }, +/obj/machinery/photobooth/security, /turf/open/floor/iron, /area/station/security/execution/transfer) "uFQ" = ( diff --git a/_maps/map_files/NorthStar/north_star.dmm b/_maps/map_files/NorthStar/north_star.dmm index 9af6d5baf06..16d9d96dec3 100644 --- a/_maps/map_files/NorthStar/north_star.dmm +++ b/_maps/map_files/NorthStar/north_star.dmm @@ -14966,9 +14966,8 @@ /turf/open/floor/engine, /area/station/maintenance/floor1/port/aft) "dNU" = ( -/obj/structure/table, -/obj/item/paper_bin, -/obj/item/pen, +/obj/machinery/photobooth, +/obj/effect/turf_decal/delivery, /turf/open/floor/iron/dark, /area/station/hallway/floor4/fore) "dOa" = ( @@ -54431,6 +54430,14 @@ }, /turf/open/floor/iron/dark, /area/station/service/hydroponics) +"nVN" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/effect/turf_decal/tile/red/diagonal_edge, +/obj/structure/cable, +/obj/machinery/photobooth/security, +/turf/open/floor/iron/dark, +/area/station/security/prison/visit) "nVP" = ( /obj/structure/cable/multilayer/multiz, /turf/open/floor/plating, @@ -78876,7 +78883,8 @@ "uqF" = ( /obj/structure/table, /obj/machinery/button/ticket_machine{ - pixel_x = -32 + pixel_x = -24; + pixel_y = -6 }, /obj/item/stamp/head/hop{ pixel_x = 8; @@ -78886,6 +78894,10 @@ pixel_x = -4; pixel_y = 3 }, +/obj/machinery/button/photobooth{ + pixel_y = 6; + pixel_x = -24 + }, /turf/open/floor/iron/dark, /area/station/command/heads_quarters/hop) "uqT" = ( @@ -326677,7 +326689,7 @@ gDx iIb ftZ tBk -qTA +nVN wKi akt rbr diff --git a/_maps/map_files/tramstation/tramstation.dmm b/_maps/map_files/tramstation/tramstation.dmm index 03f46d2d840..cac4e19df12 100644 --- a/_maps/map_files/tramstation/tramstation.dmm +++ b/_maps/map_files/tramstation/tramstation.dmm @@ -604,6 +604,7 @@ /obj/effect/turf_decal/trimline/red/filled/line{ dir = 4 }, +/obj/machinery/photobooth/security, /turf/open/floor/iron, /area/station/security/execution/transfer) "acj" = ( @@ -38858,6 +38859,7 @@ /area/station/command/heads_quarters/hop) "mHZ" = ( /obj/item/radio/intercom/directional/east, +/obj/machinery/photobooth, /turf/open/floor/iron/grimy, /area/station/service/library/lounge) "mId" = ( diff --git a/code/controllers/subsystem/ticker.dm b/code/controllers/subsystem/ticker.dm index e3d9be937e2..45085c0a6f8 100644 --- a/code/controllers/subsystem/ticker.dm +++ b/code/controllers/subsystem/ticker.dm @@ -464,8 +464,7 @@ SUBSYSTEM_DEF(ticker) /datum/controller/subsystem/ticker/proc/transfer_characters() var/list/livings = list() - for(var/i in GLOB.new_player_list) - var/mob/dead/new_player/player = i + for(var/mob/dead/new_player/player as anything in GLOB.new_player_list) var/mob/living = player.transfer_character() if(living) qdel(player) diff --git a/code/datums/records/manifest.dm b/code/datums/records/manifest.dm index 146f0b85845..fc2bb30ce8d 100644 --- a/code/datums/records/manifest.dm +++ b/code/datums/records/manifest.dm @@ -154,8 +154,6 @@ GLOBAL_DATUM_INIT(manifest, /datum/manifest, new) quirk_notes = person.get_quirk_string(TRUE, CAT_QUIRK_NOTES), ) - return - /// Edits the rank and trim of the found record. /datum/manifest/proc/modify(name, assignment, trim) var/datum/record/crew/target = find_record(name) @@ -165,6 +163,22 @@ GLOBAL_DATUM_INIT(manifest, /datum/manifest, new) target.rank = assignment target.trim = trim +/** + * Using the name to find the record, and person in reference to the body, we recreate photos for the manifest (and records). + * Args: + * - name - The name of the record we're looking for, which should be the name of the person. + * - person - The mob we're taking pictures of to update the records. + * - add_height_chart - If we should add a height chart to the background of the photo. + */ +/datum/manifest/proc/change_pictures(name, mob/living/person, add_height_chart = FALSE) + var/datum/record/crew/target = find_record(name) + if(!target) + return FALSE + + target.character_appearance = new(person.appearance) + target.recreate_manifest_photos(add_height_chart) + return TRUE + /datum/manifest/ui_state(mob/user) return GLOB.always_state diff --git a/code/datums/records/record.dm b/code/datums/records/record.dm index 167f227fc3a..4c40a85d5fc 100644 --- a/code/datums/records/record.dm +++ b/code/datums/records/record.dm @@ -84,6 +84,9 @@ /// Current arrest status var/wanted_status = WANTED_NONE + ///Photo used for records, which we store here so we don't have to constantly make more of. + var/list/obj/item/photo/record_photos + /datum/record/crew/New( age = 18, blood_type = "?", @@ -120,6 +123,7 @@ /datum/record/crew/Destroy() GLOB.manifest.general -= src + QDEL_LAZYLIST(record_photos) return ..() /** @@ -170,6 +174,20 @@ /datum/record/crew/proc/get_side_photo() return get_photo("photo_side", WEST) +/// A helper proc to recreate all photos of a character from the record. +/datum/record/crew/proc/recreate_manifest_photos(add_height_chart) + delete_photos("photo_front") + make_photo("photo_front", SOUTH, add_height_chart) + delete_photos("photo_side") + make_photo("photo_side", WEST, add_height_chart) + +///Deletes the existing photo for field_name +/datum/record/crew/proc/delete_photos(field_name) + var/obj/item/photo/existing_photo = LAZYACCESS(record_photos, field_name) + if(existing_photo) + qdel(existing_photo) + LAZYREMOVE(record_photos, field_name) + /** * You shouldn't be calling this directly, use `get_front_photo()` or `get_side_photo()` * instead. @@ -188,18 +206,29 @@ * Returns an empty `/icon` if there was no `character_appearance` entry in the `fields` list, * returns the generated/cached photo otherwise. */ -/datum/record/crew/proc/get_photo(field_name, orientation) +/datum/record/crew/proc/get_photo(field_name, orientation = SOUTH) if(!field_name) return - if(!character_appearance) return new /icon() + var/obj/item/photo/existing_photo = LAZYACCESS(record_photos, field_name) + if(!existing_photo) + existing_photo = make_photo(field_name, orientation) + return existing_photo +/** + * make_photo + * + * Called if the person doesn't already have a photo, this will make a photo of the person, + * then make a picture out of it, then finally create a new photo. + */ +/datum/record/crew/proc/make_photo(field_name, orientation, add_height_chart) var/icon/picture_image if(!isicon(character_appearance)) var/mutable_appearance/appearance = character_appearance appearance.setDir(orientation) - + if(add_height_chart) + appearance.underlays += mutable_appearance('icons/obj/machines/photobooth.dmi', "height_chart", alpha = 125, appearance_flags = RESET_ALPHA|RESET_COLOR|RESET_TRANSFORM) picture_image = getFlatIcon(appearance) else picture_image = character_appearance @@ -209,9 +238,10 @@ picture.picture_desc = "This is [name]." picture.picture_image = picture_image - var/obj/item/photo/photo = new(null, picture) - field_name = photo - return photo + var/obj/item/photo/new_photo = new(null, picture) + + LAZYSET(record_photos, field_name, new_photo) + return new_photo /// Returns a paper printout of the current record's crime data. /datum/record/crew/proc/get_rapsheet(alias, header = "Rapsheet", description = "No further details.") diff --git a/code/game/machinery/buttons.dm b/code/game/machinery/buttons.dm index 552afbb4ed8..32e56e2fca9 100644 --- a/code/game/machinery/buttons.dm +++ b/code/game/machinery/buttons.dm @@ -4,17 +4,7 @@ icon = 'icons/obj/machines/wallmounts.dmi' base_icon_state = "button" icon_state = "button" - ///Icon suffix for the skin of the front pannel that is added to base_icon_state - var/skin = "" - ///Whether it is possible to change the panel skin - var/can_alter_skin = TRUE power_channel = AREA_USAGE_ENVIRON - var/obj/item/assembly/device - var/obj/item/electronics/airlock/board - var/device_type = null - var/id = null - var/initialized_button = 0 - var/silicon_access_disabled = FALSE light_power = 0.5 // Minimums, we want the button to glow if it has a mask, not light an area light_range = 1.5 light_color = LIGHT_COLOR_VIVID_GREEN @@ -22,6 +12,17 @@ idle_power_usage = BASE_MACHINE_IDLE_CONSUMPTION * 0.02 resistance_flags = LAVA_PROOF | FIRE_PROOF interaction_flags_machine = parent_type::interaction_flags_machine | INTERACT_MACHINE_OPEN + ///Icon suffix for the skin of the front pannel that is added to base_icon_state + var/skin = "" + ///Whether it is possible to change the panel skin + var/can_alter_skin = TRUE + + var/obj/item/assembly/device + var/obj/item/electronics/airlock/board + var/device_type = null + var/id = null + var/initialized_button = 0 + var/silicon_access_disabled = FALSE /obj/machinery/button/indestructible resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF diff --git a/code/game/machinery/computer/records/records.dm b/code/game/machinery/computer/records/records.dm index c3aba60e4c6..f0b360ff1c8 100644 --- a/code/game/machinery/computer/records/records.dm +++ b/code/game/machinery/computer/records/records.dm @@ -28,6 +28,7 @@ . = ..() if(.) return + var/mob/user = ui.user var/datum/record/crew/target if(params["crew_ref"]) @@ -41,7 +42,7 @@ return FALSE var/value = trim(params["value"], MAX_BROADCAST_LEN) - investigate_log("[key_name(usr)] changed the field: \"[field]\" with value: \"[target.vars[field]]\" to new value: \"[value || "Unknown"]\"", INVESTIGATE_RECORDS) + investigate_log("[key_name(user)] changed the field: \"[field]\" with value: \"[target.vars[field]]\" to new value: \"[value || "Unknown"]\"", INVESTIGATE_RECORDS) target.vars[field] = value || "Unknown" return TRUE @@ -51,23 +52,23 @@ return FALSE // Don't let people off station futz with the station network. if(!is_station_level(z)) - balloon_alert(usr, "out of range!") + balloon_alert(user, "out of range!") return TRUE expunge_record_info(target) - balloon_alert(usr, "record expunged") + balloon_alert(user, "record expunged") playsound(src, 'sound/machines/terminal_eject.ogg', 70, TRUE) - investigate_log("[key_name(usr)] expunged the record of [target.name].", INVESTIGATE_RECORDS) + investigate_log("[key_name(user)] expunged the record of [target.name].", INVESTIGATE_RECORDS) return TRUE if("login") - authenticated = secure_login(usr) - investigate_log("[key_name(usr)] [authenticated ? "successfully logged" : "failed to log"] into the [src].", INVESTIGATE_RECORDS) + authenticated = secure_login(user) + investigate_log("[key_name(user)] [authenticated ? "successfully logged" : "failed to log"] into the [src].", INVESTIGATE_RECORDS) return TRUE if("logout") - balloon_alert(usr, "logged out") + balloon_alert(user, "logged out") playsound(src, 'sound/machines/terminal_off.ogg', 70, TRUE) authenticated = FALSE @@ -76,22 +77,22 @@ if("purge_records") // Don't let people off station futz with the station network. if(!is_station_level(z)) - balloon_alert(usr, "out of range!") + balloon_alert(user, "out of range!") return TRUE ui.close() - balloon_alert(usr, "purging records...") + balloon_alert(user, "purging records...") playsound(src, 'sound/machines/terminal_alert.ogg', 70, TRUE) - if(do_after(usr, 5 SECONDS)) + if(do_after(user, 5 SECONDS)) for(var/datum/record/crew/entry in GLOB.manifest.general) expunge_record_info(entry) - balloon_alert(usr, "records purged") + balloon_alert(user, "records purged") playsound(src, 'sound/machines/terminal_off.ogg', 70, TRUE) - investigate_log("[key_name(usr)] purged all records.", INVESTIGATE_RECORDS) + investigate_log("[key_name(user)] purged all records.", INVESTIGATE_RECORDS) else - balloon_alert(usr, "interrupted!") + balloon_alert(user, "interrupted!") return TRUE @@ -100,7 +101,7 @@ return FALSE playsound(src, "sound/machines/terminal_button0[rand(1, 8)].ogg", 50, TRUE) - update_preview(usr, params["assigned_view"], target) + update_preview(user, params["assigned_view"], target) return TRUE return FALSE diff --git a/code/game/machinery/computer/records/security.dm b/code/game/machinery/computer/records/security.dm index 6e44d112b4b..0797b4d1890 100644 --- a/code/game/machinery/computer/records/security.dm +++ b/code/game/machinery/computer/records/security.dm @@ -147,6 +147,8 @@ if(.) return + var/mob/user = ui.user + var/datum/record/crew/target if(params["crew_ref"]) target = locate(params["crew_ref"]) in GLOB.manifest.general @@ -155,29 +157,29 @@ switch(action) if("add_crime") - add_crime(usr, target, params) + add_crime(user, target, params) return TRUE if("delete_record") - investigate_log("[usr] deleted record: \"[target]\".", INVESTIGATE_RECORDS) + investigate_log("[user] deleted record: \"[target]\".", INVESTIGATE_RECORDS) qdel(target) return TRUE if("edit_crime") - edit_crime(usr, target, params) + edit_crime(user, target, params) return TRUE if("invalidate_crime") - invalidate_crime(usr, target, params) + invalidate_crime(user, target, params) return TRUE if("print_record") - print_record(usr, target, params) + print_record(user, target, params) return TRUE if("set_note") var/note = trim(params["note"], MAX_MESSAGE_LEN) - investigate_log("[usr] has changed the security note of record: \"[target]\" from \"[target.security_note]\" to \"[note]\".") + investigate_log("[user] has changed the security note of record: \"[target]\" from \"[target.security_note]\" to \"[note]\".") target.security_note = note return TRUE @@ -318,7 +320,7 @@ /// Handles printing records via UI. Takes the params from UI_act. /obj/machinery/computer/records/security/proc/print_record(mob/user, datum/record/crew/target, list/params) if(printing) - balloon_alert(usr, "printer busy") + balloon_alert(user, "printer busy") playsound(src, 'sound/machines/terminal_error.ogg', 100, TRUE) return FALSE diff --git a/code/game/machinery/photobooth.dm b/code/game/machinery/photobooth.dm new file mode 100644 index 00000000000..d7775974269 --- /dev/null +++ b/code/game/machinery/photobooth.dm @@ -0,0 +1,228 @@ +/** + * Photobooth + * A machine used to change occupant's security record photos, working similarly to a + * camera, but doesn't give any physical photo to the user. + * Links to buttons for remote control. + */ +/obj/machinery/photobooth + name = "photobooth" + desc = "A machine with some drapes and a camera, used to update security record photos. Requires Law Office access to use." + icon = 'icons/obj/machines/photobooth.dmi' + icon_state = "booth_open" + base_icon_state = "booth" + state_open = TRUE + circuit = /obj/item/circuitboard/machine/photobooth + light_system = OVERLAY_LIGHT_DIRECTIONAL //Used as a flash here. + light_range = 6 + light_color = COLOR_WHITE + light_power = FLASH_LIGHT_POWER + light_on = FALSE + interaction_flags_atom = INTERACT_ATOM_ATTACK_HAND + req_one_access = list(ACCESS_LAWYER, ACCESS_SECURITY) + ///Boolean on whether we should add a height chart to the underlays of the people we take photos of. + var/add_height_chart = FALSE + ///Boolean on whether the machine is currently busy taking someone's pictures, so you can't start taking pictures while it's working. + var/taking_pictures = FALSE + ///The ID of the photobooth, used to connect it to a button. + var/button_id = "photobooth_machine_default" + +/** + * Security photobooth + * Adds a height chart in the background, used for people you want to evidently stick out as prisoners. + * Good for people you plan on putting in the permabrig. + */ +/obj/machinery/photobooth/security + name = "security photobooth" + desc = "A machine with some drapes and a camera, used to update security record photos. Requires Security access to use, and adds a height chart to the person." + circuit = /obj/item/circuitboard/machine/photobooth/security + req_one_access = list(ACCESS_SECURITY) + color = COLOR_LIGHT_GRAYISH_RED + add_height_chart = TRUE + +/obj/machinery/photobooth/Initialize(mapload) + . = ..() + register_context() + +/obj/machinery/photobooth/interact(mob/living/user, list/modifiers) + . = ..() + if(taking_pictures) + balloon_alert(user, "machine busy!") + return + if(state_open) + close_machine() + else + open_machine() + +/obj/machinery/photobooth/attack_hand_secondary(mob/user, list/modifiers) + if(taking_pictures) + balloon_alert(user, "machine busy!") + return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN + if(occupant) + if(allowed(user)) + start_taking_pictures() + else + balloon_alert(user, "access denied!") + return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN + return ..() + +/obj/machinery/photobooth/add_context(atom/source, list/context, obj/item/held_item, mob/user) + if(machine_stat & (BROKEN|NOPOWER) || !isnull(held_item)) + return NONE + + context[SCREENTIP_CONTEXT_LMB] = "[state_open ? "Close" : "Open"] Machine" + if(occupant) + context[SCREENTIP_CONTEXT_RMB] = "Take Pictures" + + return CONTEXTUAL_SCREENTIP_SET + +/obj/machinery/photobooth/close_machine(mob/user, density_to_set = TRUE) + if(panel_open) + balloon_alert(user, "close panel first!") + return + playsound(src, 'sound/effects/curtain.ogg', 50, TRUE) + return ..() + +/obj/machinery/photobooth/open_machine(drop = TRUE, density_to_set = FALSE) + playsound(src, 'sound/effects/curtain.ogg', 50, TRUE) + return ..() + +/obj/machinery/photobooth/update_icon_state() + . = ..() + if(machine_stat & (BROKEN|NOPOWER)) + icon_state = "[base_icon_state]_off" + else if(state_open) + icon_state = "[base_icon_state]_open" + else + icon_state = "[base_icon_state]_closed" + +/obj/machinery/photobooth/update_overlays() + . = ..() + if((machine_stat & MAINT) || panel_open) + . += "[base_icon_state]_panel" + +/obj/machinery/photobooth/screwdriver_act(mob/living/user, obj/item/tool) + if(!has_buckled_mobs() && default_deconstruction_screwdriver(user, icon_state, icon_state, tool)) + update_appearance(UPDATE_ICON) + return ITEM_INTERACT_SUCCESS + return ..() + +/obj/machinery/photobooth/crowbar_act(mob/living/user, obj/item/tool) + if(default_deconstruction_crowbar(tool)) + return ITEM_INTERACT_SUCCESS + return ..() + +/obj/machinery/photobooth/emag_act(mob/user, obj/item/card/emag/emag_card) + if(obj_flags & EMAGGED) + return FALSE + req_access = list() //in case someone sets this to something + req_one_access = list() + balloon_alert(user, "beeps softly") + obj_flags |= EMAGGED + return TRUE + +/** + * Handles the effects of taking pictures of the user, calling finish_taking_pictures + * to actually update the records. + */ +/obj/machinery/photobooth/proc/start_taking_pictures() + taking_pictures = TRUE + if(obj_flags & EMAGGED) + var/mob/living/carbon/carbon_occupant = occupant + for(var/i in 1 to 5) //play a ton of sounds to mimic it blinding you + playsound(src, pick('sound/items/polaroid1.ogg', 'sound/items/polaroid2.ogg'), 75, TRUE) + if(carbon_occupant) + carbon_occupant.flash_act(5) + sleep(0.2 SECONDS) + if(carbon_occupant) + carbon_occupant.emote("scream") + finish_taking_pictures() + return + if(!do_after(occupant, 2 SECONDS, src, timed_action_flags = IGNORE_HELD_ITEM)) //gives them time to put their hand items away. + taking_pictures = FALSE + return + playsound(src, 'sound/items/polaroid1.ogg', 75, TRUE) + flash() + if(!do_after(occupant, 3 SECONDS, src, timed_action_flags = IGNORE_HELD_ITEM)) + taking_pictures = FALSE + return + playsound(src, 'sound/items/polaroid2.ogg', 75, TRUE) + flash() + if(!do_after(occupant, 2 SECONDS, src, timed_action_flags = IGNORE_HELD_ITEM)) + taking_pictures = FALSE + return + finish_taking_pictures() + +///Updates the records (if possible), giving feedback, and spitting the user out if all's well. +/obj/machinery/photobooth/proc/finish_taking_pictures() + taking_pictures = FALSE + if(!GLOB.manifest.change_pictures(occupant.name, occupant, add_height_chart = add_height_chart)) + balloon_alert(occupant, "record not found!") + return + balloon_alert(occupant, "records updated") + open_machine() + +///Mimicing the camera, gives a flash effect by turning the light on and calling flash_end. +/obj/machinery/photobooth/proc/flash() + set_light_on(TRUE) + addtimer(CALLBACK(src, PROC_REF(flash_end)), FLASH_LIGHT_DURATION, TIMER_OVERRIDE|TIMER_UNIQUE) + +///Called by a timer to turn the light off to end the flash effect. +/obj/machinery/photobooth/proc/flash_end() + set_light_on(FALSE) + + +/obj/machinery/button/photobooth + name = "photobooth control button" + desc = "Operates the photobooth from a distance, allowing people to update their security record photos." + device_type = /obj/item/assembly/control/photobooth_control + req_one_access = list(ACCESS_SECURITY, ACCESS_LAWYER) + id = "photobooth_machine_default" + +/obj/machinery/button/photobooth/Initialize(mapload) + . = ..() + if(device) + var/obj/item/assembly/control/photobooth_control/ours = device + ours.id = id + +/obj/machinery/button/photobooth/multitool_act(mob/living/user, obj/item/multitool/tool) + . = ..() + if(tool.buffer && !istype(tool.buffer, /obj/machinery/photobooth)) + return + var/obj/item/assembly/control/photobooth_control/controller = device + controller.booth_machine_ref = WEAKREF(tool.buffer) + id = null + controller.id = null + balloon_alert(user, "linked to [tool.buffer]") + +/obj/item/assembly/control/photobooth_control + name = "photobooth controller" + desc = "A remote controller for the HoP's photobooth." + ///Weakref to the photobooth we're connected to. + var/datum/weakref/booth_machine_ref + +/obj/item/assembly/control/photobooth_control/Initialize(mapload) + ..() + return INITIALIZE_HINT_LATELOAD + +/obj/item/assembly/control/photobooth_control/LateInitialize() + find_machine() + +/// Locate the photobooth we're linked via ID +/obj/item/assembly/control/photobooth_control/proc/find_machine() + for(var/obj/machinery/photobooth/booth as anything in SSmachines.get_machines_by_type_and_subtypes(/obj/machinery/photobooth)) + if(booth.button_id == id) + booth_machine_ref = WEAKREF(booth) + if(booth_machine_ref) + return TRUE + return FALSE + +/obj/item/assembly/control/photobooth_control/activate(mob/activator) + if(!booth_machine_ref) + return + var/obj/machinery/photobooth/machine = booth_machine_ref.resolve() + if(!machine) + return + if(machine.taking_pictures) + balloon_alert(activator, "machine busy!") + return + machine.start_taking_pictures() diff --git a/code/game/objects/items/circuitboards/machines/machine_circuitboards.dm b/code/game/objects/items/circuitboards/machines/machine_circuitboards.dm index 2be7daca2bd..addfbc233ef 100644 --- a/code/game/objects/items/circuitboards/machines/machine_circuitboards.dm +++ b/code/game/objects/items/circuitboards/machines/machine_circuitboards.dm @@ -79,7 +79,6 @@ req_components = list(/datum/stock_part/capacitor = 1) needs_anchored = FALSE - /obj/item/circuitboard/machine/telecomms/broadcaster name = "Subspace Broadcaster" greyscale_colors = CIRCUIT_COLOR_ENGINEERING @@ -1113,6 +1112,19 @@ build_path = /obj/machinery/rnd/production/techfab/department/security //Service +/obj/item/circuitboard/machine/photobooth + name = "Photobooth" + greyscale_colors = CIRCUIT_COLOR_SERVICE + build_path = /obj/machinery/photobooth + req_components = list( + /datum/stock_part/matter_bin = 1, + /datum/stock_part/servo = 1, + ) + +/obj/item/circuitboard/machine/photobooth/security + name = "Security Photobooth" + greyscale_colors = CIRCUIT_COLOR_SECURITY + build_path = /obj/machinery/photobooth/security /obj/item/circuitboard/machine/biogenerator name = "Biogenerator" diff --git a/code/modules/jobs/access.dm b/code/modules/jobs/access.dm index 859f83bf35a..2055080710e 100644 --- a/code/modules/jobs/access.dm +++ b/code/modules/jobs/access.dm @@ -32,7 +32,7 @@ else if(SEND_SIGNAL(accessor, COMSIG_MOB_TRIED_ACCESS, src) & ACCESS_ALLOWED) return TRUE //If the mob is holding a valid ID, we let them in. get_active_held_item() is on the mob level, so no need to copypasta everywhere. - else if(check_access(accessor.get_active_held_item())) + else if(check_access(accessor.get_active_held_item()) || check_access(accessor.get_inactive_held_item())) return TRUE else if(ishuman(accessor)) var/mob/living/carbon/human/human_accessor = accessor diff --git a/code/modules/jobs/job_types/prisoner.dm b/code/modules/jobs/job_types/prisoner.dm index 28b697a794f..fd6f0f939d6 100644 --- a/code/modules/jobs/job_types/prisoner.dm +++ b/code/modules/jobs/job_types/prisoner.dm @@ -27,23 +27,25 @@ /datum/job/prisoner/New() . = ..() - RegisterSignal(SSdcs, COMSIG_GLOB_CREWMEMBER_JOINED, PROC_REF(add_pref_crime)) + RegisterSignal(SSdcs, COMSIG_GLOB_CREWMEMBER_JOINED, PROC_REF(handle_prisoner_joining)) -/datum/job/prisoner/proc/add_pref_crime(datum/source, mob/living/crewmember, rank) +/datum/job/prisoner/proc/handle_prisoner_joining(datum/source, mob/living/crewmember, rank) SIGNAL_HANDLER if(rank != title) return //not a prisoner var/crime_name = crewmember.client?.prefs?.read_preference(/datum/preference/choiced/prisoner_crime) if(!crime_name) - return - if(crime_name == "Random") + stack_trace("[crewmember] joined as a Prisoner without having a prisoner crime set.") + crime_name = pick(assoc_to_keys(GLOB.prisoner_crimes)) + else if(crime_name == "Random") crime_name = pick(assoc_to_keys(GLOB.prisoner_crimes)) var/datum/prisoner_crime/crime = GLOB.prisoner_crimes[crime_name] - var/datum/record/crew/target_record = find_record(crewmember.real_name) var/datum/crime/past_crime = new(crime.name, crime.desc, "Central Command", "Indefinite.") + var/datum/record/crew/target_record = find_record(crewmember.real_name) target_record.crimes += past_crime + target_record.recreate_manifest_photos(add_height_chart = TRUE) to_chat(crewmember, span_warning("You are imprisoned for \"[crime_name]\".")) crewmember.add_mob_memory(/datum/memory/key/permabrig_crimes, crimes = crime_name) diff --git a/code/modules/mob/inventory.dm b/code/modules/mob/inventory.dm index 15f45694c29..bc44e651868 100644 --- a/code/modules/mob/inventory.dm +++ b/code/modules/mob/inventory.dm @@ -1,4 +1,4 @@ -//These procs handle putting s tuff in your hands +//These procs handle putting stuff in your hands //as they handle all relevant stuff like adding it to the player's screen and updating their overlays. ///Returns the thing we're currently holding diff --git a/code/modules/research/designs/machine_designs.dm b/code/modules/research/designs/machine_designs.dm index a73b4459333..65dd75142a2 100644 --- a/code/modules/research/designs/machine_designs.dm +++ b/code/modules/research/designs/machine_designs.dm @@ -318,6 +318,26 @@ ) departmental_flags = DEPARTMENT_BITFLAG_SECURITY +/datum/design/board/photobooth + name = "Photobooth Board" + desc = "The circuit board for a photobooth." + id = "photobooth" + build_path = /obj/item/circuitboard/machine/photobooth + category = list( + RND_CATEGORY_MACHINE + RND_SUBCATEGORY_MACHINE_SERVICE + ) + departmental_flags = DEPARTMENT_BITFLAG_SERVICE + +/datum/design/board/security_photobooth + name = "Security Photobooth Board" + desc = "The circuit board for a security photobooth." + id = "security_photobooth" + build_path = /obj/item/circuitboard/machine/photobooth/security + category = list( + RND_CATEGORY_MACHINE + RND_SUBCATEGORY_MACHINE_SECURITY + ) + departmental_flags = DEPARTMENT_BITFLAG_SECURITY + /datum/design/board/biogenerator name = "Biogenerator Board" desc = "The circuit board for a biogenerator." diff --git a/code/modules/research/techweb/all_nodes.dm b/code/modules/research/techweb/all_nodes.dm index 76e5085c525..8e0fdc95a74 100644 --- a/code/modules/research/techweb/all_nodes.dm +++ b/code/modules/research/techweb/all_nodes.dm @@ -1204,8 +1204,10 @@ "idcard", "libraryconsole", "mining", + "photobooth", "rdcamera", "seccamera", + "security_photobooth", ) research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2000) diff --git a/icons/obj/machines/photobooth.dmi b/icons/obj/machines/photobooth.dmi new file mode 100644 index 0000000000000000000000000000000000000000..c4e812bdf47f166909113294492c9b934dd709f5 GIT binary patch literal 3425 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 Dc&NjZ literal 0 HcmV?d00001 diff --git a/tgstation.dme b/tgstation.dme index 8552083073a..0bb4356e926 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -1977,6 +1977,7 @@ #include "code\game\machinery\navbeacon.dm" #include "code\game\machinery\nebula_shielding.dm" #include "code\game\machinery\PDApainter.dm" +#include "code\game\machinery\photobooth.dm" #include "code\game\machinery\prisongate.dm" #include "code\game\machinery\prisonlabor.dm" #include "code\game\machinery\quantum_pad.dm"