Files
Aurora.3/code/modules/paperwork/photography.dm
T
Casper3667andGitHub 7a99813dd0 The Case Dossier program (#22703)
This adds the case dossier program. A program intended for investigators
to use for their cases, with the ability to record evidence, photos,
documents, people and their notes on a case. The rest of security is
able to view cases in the program, but not edit them.
Additionally, the forensic skill was added to evidence bags and photos,
allowing a person to add the time, place and the person using them,
mainly intended to be used with the case dossier program.
Fibers and prints can now be gathered from objects inside evidence bags,
to avoid contamination.
Also fixed some bugs with taking photos of certain objects, such as
airlocks.


https://github.com/user-attachments/assets/92a880e9-9032-46d4-88c0-43d45b64f979
2026-06-19 04:36:07 +00:00

365 lines
10 KiB
Plaintext

/* Photography!
* Contains:
* * Camera
* * Camera Film
* * Photos
* * Photo Albums
*/
/*******
* film *
*******/
/obj/item/camera_film
name = "film cartridge"
icon = 'icons/obj/bureaucracy.dmi'
desc = "A camera film cartridge. Insert it into a camera to reload it."
icon_state = "film"
item_state = "electropack"
w_class = WEIGHT_CLASS_TINY
/obj/item/camera_film/taj_film
name = "film canister"
icon = 'icons/obj/tajara_items.dmi'
desc = "A rolle of 35mm film intended for cameras of Tajaran make."
icon_state = "taj_film"
/********
* photo *
********/
GLOBAL_VAR_INIT(photo_count, 1)
/obj/item/photo
name = "photo"
desc = "An archaic means of visual preservation, kept alive as kitschy memorabilia by paparazzi, conspiracy theorists and teenage girls."
icon = 'icons/obj/bureaucracy.dmi'
icon_state = "photo"
item_state = "paper"
w_class = WEIGHT_CLASS_TINY
/// Who and/or what's in the picture.
var/picture_desc
/// The photo's id
var/id
/// Big photo image
var/icon/img
/// Scribble on the back.
var/scribble
/// The small size of the photo used for the icon
var/icon/tiny
/// The size of the photo
var/photo_size = 3
/// The time the photo was taken
var/time_taken
/// The place the photo was taken
var/location_taken
/// Who took the photo
var/taken_by
/// The photo's name/caption
var/caption
drop_sound = 'sound/items/drop/paper.ogg'
pickup_sound = 'sound/items/pickup/paper.ogg'
/obj/item/photo/feedback_hints(mob/user, distance, is_adjacent)
. += ..()
if(distance <= 1 || in_slide_projector(user))
show(user)
. += SPAN_NOTICE("[picture_desc]")
else
. += SPAN_NOTICE("You are too far away to discern its contents.")
/obj/item/photo/New()
. = ..()
id = GLOB.photo_count++
/obj/item/photo/attack_self(mob/user as mob)
examinate(user, src)
/obj/item/photo/attackby(obj/item/attacking_item, mob/user)
if(attacking_item.tool_behaviour == TOOL_PEN)
var/txt = sanitize( tgui_input_text(user, "What would you like to write on the back?", "Photo Writing", max_length = 128), 128 )
if((loc == user || Adjacent(user)) && user.stat == 0)
scribble = txt
..()
/obj/item/photo/proc/show(mob/user as mob)
send_rsc(user, img, "tmp_photo_[id].png")
var/dat = "<html><head><title>[name]</title></head>" \
+ "<body style='overflow:hidden;margin:0;text-align:center'>" \
+ "<img src='tmp_photo_[id].png' width='[64*photo_size]' style='-ms-interpolation-mode:nearest-neighbor' />" \
+ "[scribble ? "<br>Written on the back:<br><i>[scribble]</i>" : ""]" \
+ "</body></html>"
show_browser(user, HTML_SKELETON(dat), "window=book;size=[64*photo_size]x[scribble ? 400 : 64*photo_size]")
onclose(user, "[name]")
return
/obj/item/photo/verb/rename()
set name = "Rename Photo"
set category = "Object"
set src in usr
if(use_check_and_message(usr, USE_ALLOW_NON_ADJACENT))
return
var/n_name = sanitizeSafe(input(usr, "What would you like to label the photo?", "Photo Labelling", null) as text, MAX_NAME_LEN)
if(use_check_and_message(usr, USE_ALLOW_NON_ADJACENT))
return
var/atom/surface_atom = recursive_loc_turf_check(src, 3, usr)
if(surface_atom == usr || surface_atom.Adjacent(usr))
if(n_name)
name = "[initial(name)] ([n_name])"
caption = n_name
else
name = initial(name)
caption = null
add_fingerprint(usr)
/**************
* photo album *
**************/
/obj/item/storage/photo_album
name = "photo album"
icon = 'icons/obj/bureaucracy.dmi'
icon_state = "album"
item_state = "briefcase"
can_hold = list(/obj/item/photo)
/obj/item/storage/photo_album/mouse_drop_dragged(atom/over, mob/user, src_location, over_location, params)
if((istype(user, /mob/living/carbon/human)))
var/mob/M = user
if(!( istype(over, /atom/movable/screen) ))
return ..()
playsound(loc, SFX_RUSTLE, 50, 1, -5)
if((!( M.restrained() ) && !( M.stat ) && M.back == src))
switch(over.name)
if("right hand")
M.u_equip(src)
M.equip_to_slot_if_possible(src, slot_r_hand)
if("left hand")
M.u_equip(src)
M.equip_to_slot_if_possible(src, slot_l_hand)
add_fingerprint(user)
return
if(over == user && in_range(src, user) || user.contents.Find(src))
if(user.s_active)
user.s_active.close(user)
show_to(user)
return
return
/*********
* camera *
*********/
/obj/item/camera
name = "camera"
icon = 'icons/obj/bureaucracy.dmi'
desc = "A polaroid camera."
icon_state = "camera"
item_state = "electropack"
w_class = WEIGHT_CLASS_SMALL
obj_flags = OBJ_FLAG_CONDUCTABLE
slot_flags = SLOT_BELT
matter = list(DEFAULT_WALL_MATERIAL = 2000)
var/black_white = FALSE
var/pictures_max = 10
var/pictures_left = 10
var/on = 1
var/icon_on = "camera"
var/icon_off = "camera_off"
var/size = 3
/obj/item/camera/feedback_hints(mob/user, distance, is_adjacent)
. += ..()
if(is_adjacent)
. += SPAN_NOTICE("It has <b>[pictures_left] photos</b> left.")
/obj/item/camera/verb/change_size()
set name = "Set Photo Focus"
set category = "Object"
set src in usr
var/nsize = input("Photo Size","Pick a size of resulting photo.") as null|anything in list(1,3,5,7)
if(nsize)
size = nsize
to_chat(usr, SPAN_NOTICE("Camera will now take [size]x[size] photos."))
/obj/item/camera/attack(mob/living/target_mob, mob/living/user, target_zone)
return
/obj/item/camera/attack_self(mob/user as mob)
on = !on
if(on)
src.icon_state = icon_on
else
src.icon_state = icon_off
to_chat(user, "You switch the camera [on ? "on" : "off"].")
return
/obj/item/camera/attackby(obj/item/attacking_item, mob/user)
if(istype(attacking_item, /obj/item/camera_film))
if(pictures_left)
to_chat(user, SPAN_NOTICE("[src] still has some film in it!"))
return TRUE
to_chat(user, SPAN_NOTICE("You insert [attacking_item] into [src]."))
user.drop_from_inventory(attacking_item, get_turf(src))
qdel(attacking_item)
pictures_left = pictures_max
return TRUE
return ..()
/obj/item/camera/AltClick(var/mob/user)
change_size()
/obj/item/camera/proc/get_mobs(turf/the_turf as turf)
var/mob_detail
for(var/mob/living/carbon/A in the_turf)
if(A.invisibility) continue
var/holding = null
if(A.l_hand || A.r_hand)
if(A.l_hand) holding = "They are holding \a [A.l_hand]"
if(A.r_hand)
if(holding)
holding += " and \a [A.r_hand]"
else
holding = "They are holding \a [A.r_hand]"
if(!mob_detail)
mob_detail = "You can see [A] in the photo[A.health < 75 ? " - [A] looks hurt":""].[holding ? " [holding]":"."]. "
else
mob_detail += "You can also see [A] in the photo[A.health < 75 ? " - [A] looks hurt":""].[holding ? " [holding]":"."]."
return mob_detail
/obj/item/camera/afterattack(atom/target as mob|obj|turf|area, mob/user as mob, flag)
if(!on || !pictures_left || ismob(target.loc)) return
captureimage(target, user, flag)
do_photo_sound()
pictures_left--
to_chat(user, SPAN_NOTICE("[pictures_left] photos left."))
icon_state = icon_off
on = 0
spawn(64)
icon_state = icon_on
on = 1
/obj/item/camera/proc/do_photo_sound()
playsound(loc, SFX_PRINT, 75, 1, -3)
/obj/item/camera/detective
name = "detectives camera"
desc = "A one use - polaroid camera."
pictures_left = 30
// Detective cameras have build in data recording, so no need for a skill check
/obj/item/camera/detective/record_data(mob/living/user, obj/item/photo/pic)
pic.time_taken = worldtime2text()
pic.location_taken = get_area_display_name(get_area(user))
pic.taken_by = user.name
//Proc for capturing check
/mob/living/proc/can_capture_turf(turf/T)
return TRUE // DVIEW will do sanity checks, we've got no special checks.
/obj/item/camera/proc/captureimage(atom/target, mob/living/user, flag)
var/obj/item/photo/p = createpicture(get_turf(target), user, flag)
printpicture(user, p)
/obj/item/camera/proc/createpicture(atom/target, mob/living/user, flag)
var/mobs = ""
var/list/turfs = list()
FOR_DVIEW(var/turf/T, size, target, INVISIBILITY_LIGHTING)
if (user.can_capture_turf(T))
mobs += get_mobs(T)
turfs += T
END_FOR_DVIEW
var/x_c = target.x - (size-1)/2
var/y_c = target.y - (size-1)/2
var/z_c = target.z
var/turf/topleft = locate(x_c, y_c, z_c)
if (!topleft)
return null
var/icon/photoimage = generate_image_from_turfs(topleft, turfs, size, CAPTURE_MODE_REGULAR, user)
var/icon/small_img = icon(photoimage)
var/icon/tiny_img = icon(photoimage)
var/icon/ic = icon('icons/obj/bureaucracy.dmi',"photo")
var/icon/pc = icon('icons/obj/bureaucracy.dmi', "photo")
small_img.Scale(8, 8)
tiny_img.Scale(4, 4)
ic.Blend(small_img,ICON_OVERLAY, 10, 13)
pc.Blend(tiny_img,ICON_OVERLAY, 12, 19)
var/obj/item/photo/p = new()
p.name = "photo"
p.icon = ic
p.tiny = pc
p.img = photoimage
if(black_white)
p.img.MapColors(rgb(77,77,77), rgb(150,150,150), rgb(28,28,28), rgb(0,0,0))
p.tiny.MapColors(rgb(77,77,77), rgb(150,150,150), rgb(28,28,28), rgb(0,0,0))
p.picture_desc = mobs
p.pixel_x = rand(-10, 10)
p.pixel_y = rand(-10, 10)
p.photo_size = size
record_data(user, p)
return p
/// Data recorded on photos.
/obj/item/camera/proc/record_data(mob/living/user, obj/item/photo/pic)
var/forensic = GET_SKILL_LEVEL(user, FORENSICS_SKILL_COMPONENT)
forensic = forensic ? forensic : SKILL_LEVEL_TRAINED
// Trained people would know to add this data
if(forensic >= SKILL_LEVEL_FAMILIAR)
pic.time_taken = worldtime2text()
pic.location_taken = get_area_display_name(get_area(user))
pic.taken_by = user.name
/obj/item/camera/proc/printpicture(mob/user, obj/item/photo/p)
p.forceMove(user.loc)
if(!user.get_inactive_hand())
user.put_in_inactive_hand(p)
/obj/item/photo/proc/copy(var/copy_id = 0)
var/obj/item/photo/p = new/obj/item/photo()
p.name = name
p.icon = icon(icon, icon_state)
p.tiny = icon(tiny)
p.img = icon(img)
p.picture_desc = picture_desc
p.pixel_x = pixel_x
p.pixel_y = pixel_y
p.photo_size = photo_size
p.scribble = scribble
if(copy_id)
p.id = id
return p
/obj/item/camera/adhomai
name = "adhomian camera"
icon = 'icons/obj/tajara_items.dmi'
desc = "A slightly antiquated camera with a large flash bulb. Still popular with Tajara all over Adhomai."
icon_state = "taj_camera_on"
item_state = "taj_camera"
slot_flags = SLOT_MASK
black_white = TRUE
icon_on = "taj_camera_on"
icon_off = "taj_camera_off"
/obj/item/camera/adhomai/do_photo_sound()
flick("taj_camera_flash", src)
playsound(loc, 'sound/items/camerabulb.ogg', 75, 1, -3)