Third time's the charm - Photography update: 7x7 cameras, photo logging with full metadata, persistent albums and wall frames! (#38944)

* Photography Update

* Pictures logged in their own /data/picture_logs folder rather than normal logs

* Pictures logged in their own /data/picture_logs folder rather than normal logs

* Photos broke, retrying

* Persistence stuff

* I'm almost done I promise!

* Persistence mostly working, compile, etc etc

* Persistence mostly working, compile, etc etc

* Remove something really not needed from the PR

* Prevents duplication

* default to off

* removes check tick

* increase slots in albums to 21

* Allows for singular loading

* Update camera_image_capturing.dm

* Addresses review

* Anturk

* Update camera.dm

* Update misc.dm

* Update datum.dm

* Update camera.dm
This commit is contained in:
kevinz000
2018-08-01 05:52:41 -07:00
committed by yogstation13-bot
parent 94d6155ed4
commit ffc9d1695a
47 changed files with 1240 additions and 825 deletions

View File

@@ -451,6 +451,9 @@ GLOBAL_LIST_INIT(pda_styles, list(MONO, VT, ORBITRON, SHARE))
//Filters
#define AMBIENT_OCCLUSION filter(type="drop_shadow", x=0, y=-2, size=4, border=4, color="#04080FAA")
#define STANDARD_GRAVITY 1 //Anything above this is high gravity, anything below no grav
#define GRAVITY_DAMAGE_TRESHOLD 3 //Starting with this value gravity will start to damage mobs
#define CAMERA_NO_GHOSTS 0
#define CAMERA_SEE_GHOSTS_BASIC 1
#define CAMERA_SEE_GHOSTS_ORBIT 2

View File

@@ -19,7 +19,7 @@
announcement += "<h1 class='alert'>[sender_override]</h1>"
if (title && length(title) > 0)
announcement += "<br><h2 class='alert'>[html_encode(title)]</h2>"
if(!sender_override)
if(title == "")
GLOB.news_network.SubmitArticle(text, "Central Command Update", "Station Announcements", null)
@@ -46,7 +46,7 @@
var/datum/comm_message/M = new
M.title = title
M.content = text
SScommunications.send_message(M)
/proc/minor_announce(message, title = "Attention:", alert)

10
code/__HELPERS/qdel.dm Normal file
View File

@@ -0,0 +1,10 @@
#define QDEL_IN(item, time) addtimer(CALLBACK(GLOBAL_PROC, .proc/qdel, item), time, TIMER_STOPPABLE)
#define QDEL_IN_CLIENT_TIME(item, time) addtimer(CALLBACK(GLOBAL_PROC, .proc/qdel, item), time, TIMER_STOPPABLE | TIMER_CLIENT_TIME)
#define QDEL_NULL(item) qdel(item); item = null
#define QDEL_LIST(L) if(L) { for(var/I in L) qdel(I); L.Cut(); }
#define QDEL_LIST_IN(L, time) addtimer(CALLBACK(GLOBAL_PROC, .proc/______qdel_list_wrapper, L), time, TIMER_STOPPABLE)
#define QDEL_LIST_ASSOC(L) if(L) { for(var/I in L) { qdel(L[I]); qdel(I); } L.Cut(); }
#define QDEL_LIST_ASSOC_VAL(L) if(L) { for(var/I in L) qdel(L[I]); L.Cut(); }
/proc/______qdel_list_wrapper(list/L) //the underscores are to encourage people not to use this directly.
QDEL_LIST(L)

View File

@@ -1293,19 +1293,6 @@ GLOBAL_REAL_VAR(list/stack_trace_storage)
#define RANDOM_COLOUR (rgb(rand(0,255),rand(0,255),rand(0,255)))
#define QDEL_IN(item, time) addtimer(CALLBACK(GLOBAL_PROC, .proc/qdel, item), time, TIMER_STOPPABLE)
#define QDEL_IN_CLIENT_TIME(item, time) addtimer(CALLBACK(GLOBAL_PROC, .proc/qdel, item), time, TIMER_STOPPABLE | TIMER_CLIENT_TIME)
#define QDEL_NULL(item) qdel(item); item = null
#define QDEL_LIST(L) if(L) { for(var/I in L) qdel(I); L.Cut(); }
#define QDEL_LIST_IN(L, time) addtimer(CALLBACK(GLOBAL_PROC, .proc/______qdel_list_wrapper, L), time, TIMER_STOPPABLE)
#define QDEL_LIST_ASSOC(L) if(L) { for(var/I in L) { qdel(L[I]); qdel(I); } L.Cut(); }
#define QDEL_LIST_ASSOC_VAL(L) if(L) { for(var/I in L) qdel(L[I]); L.Cut(); }
/proc/______qdel_list_wrapper(list/L) //the underscores are to encourage people not to use this directly.
QDEL_LIST(L)
/proc/random_nukecode()
var/val = rand(0, 99999)
var/str = "[val]"

View File

@@ -46,3 +46,13 @@ GLOBAL_LIST_EMPTY(adminlog)
GLOBAL_PROTECT(adminlog)
GLOBAL_LIST_EMPTY(active_turfs_startlist)
/////Picture logging
GLOBAL_VAR(picture_log_directory)
GLOBAL_PROTECT(picture_log_directory)
GLOBAL_VAR_INIT(picture_logging_id, 1)
GLOBAL_PROTECT(picture_logging_id)
GLOBAL_VAR(picture_logging_prefix)
GLOBAL_PROTECT(picture_logging_prefix)
/////

View File

@@ -135,10 +135,10 @@
return
if(isAI(usr))
var/mob/living/silicon/ai/AI = usr
AI.aicamera.toggle_camera_mode()
AI.aicamera.toggle_camera_mode(usr)
else if(iscyborg(usr))
var/mob/living/silicon/robot/R = usr
R.aicamera.toggle_camera_mode()
R.aicamera.toggle_camera_mode(usr)
/obj/screen/ai/image_view
name = "View Images"
@@ -149,10 +149,10 @@
return
if(isAI(usr))
var/mob/living/silicon/ai/AI = usr
AI.aicamera.viewpictures()
AI.aicamera.viewpictures(usr)
else if(iscyborg(usr))
var/mob/living/silicon/robot/R = usr
R.aicamera.viewpictures()
R.aicamera.viewpictures(usr)
/obj/screen/ai/sensors
name = "Sensor Augmentation"

View File

@@ -412,3 +412,7 @@
/datum/config_entry/string/default_view
config_entry_value = "15x15"
/datum/config_entry/flag/log_pictures
/datum/config_entry/flag/picture_logging_camera

View File

@@ -9,7 +9,7 @@ SUBSYSTEM_DEF(pathfinder)
/datum/controller/subsystem/pathfinder/Initialize()
space_type_cache = typecacheof(/turf/open/space)
mobs = new(10)
circuits = new(3)
circuits = new(3)
return ..()
/datum/flowcache

View File

@@ -15,6 +15,9 @@ SUBSYSTEM_DEF(persistence)
var/list/spawned_objects = list()
var/list/antag_rep = list()
var/list/antag_rep_change = list()
var/list/picture_logging_information = list()
var/list/obj/structure/sign/picture_frame/photo_frames
var/list/obj/item/storage/photo_album/photo_albums
/datum/controller/subsystem/persistence/Initialize()
LoadSatchels()
@@ -22,6 +25,7 @@ SUBSYSTEM_DEF(persistence)
LoadChiselMessages()
LoadTrophies()
LoadRecentModes()
LoadPhotoPersistence()
if(CONFIG_GET(flag/use_antag_rep))
LoadAntagReputation()
..()
@@ -194,15 +198,84 @@ SUBSYSTEM_DEF(persistence)
T.placer_key = chosen_trophy["placer_key"]
T.update_icon()
/datum/controller/subsystem/persistence/proc/CollectData()
CollectChiselMessages()
CollectSecretSatchels()
CollectTrophies()
CollectRoundtype()
SavePhotoPersistence() //THIS IS PERSISTENCE, NOT THE LOGGING PORTION.
if(CONFIG_GET(flag/use_antag_rep))
CollectAntagReputation()
/datum/controller/subsystem/persistence/proc/GetPhotoAlbums()
var/album_path = file("data/photo_albums.json")
if(fexists(album_path))
return json_decode(file2text(album_path))
/datum/controller/subsystem/persistence/proc/GetPhotoFrames()
var/frame_path = file("data/photo_frames.json")
if(fexists(frame_path))
return json_decode(file2text(frame_path))
/datum/controller/subsystem/persistence/proc/LoadPhotoPersistence()
var/album_path = file("data/photo_albums.json")
var/frame_path = file("data/photo_frames.json")
if(fexists(album_path))
var/list/json = json_decode(file2text(album_path))
if(json.len)
for(var/i in photo_albums)
var/obj/item/storage/photo_album/A = i
if(!A.persistence_id)
continue
if(json[A.persistence_id])
A.populate_from_id_list(json[A.persistence_id])
if(fexists(frame_path))
var/list/json = json_decode(file2text(frame_path))
if(json.len)
for(var/i in photo_frames)
var/obj/structure/sign/picture_frame/PF = i
if(!PF.persistence_id)
continue
if(json[PF.persistence_id])
PF.load_from_id(json[PF.persistence_id])
/datum/controller/subsystem/persistence/proc/SavePhotoPersistence()
var/album_path = file("data/photo_albums.json")
var/frame_path = file("data/photo_frames.json")
var/list/frame_json = list()
var/list/album_json = list()
if(fexists(album_path))
album_json = json_decode(file2text(album_path))
fdel(album_path)
for(var/i in photo_albums)
var/obj/item/storage/photo_album/A = i
if(!istype(A) || !A.persistence_id)
continue
var/list/L = A.get_picture_id_list()
album_json[A.persistence_id] = L
album_json = json_encode(album_json)
WRITE_FILE(album_path, album_json)
if(fexists(frame_path))
frame_json = json_decode(file2text(frame_path))
fdel(frame_path)
for(var/i in photo_frames)
var/obj/structure/sign/picture_frame/F = i
if(!istype(F) || !F.persistence_id)
continue
frame_json[F.persistence_id] = F.get_photo_id()
frame_json = json_encode(frame_json)
WRITE_FILE(frame_path, frame_json)
/datum/controller/subsystem/persistence/proc/CollectSecretSatchels()
satchel_blacklist = typecacheof(list(/obj/item/stack/tile/plasteel, /obj/item/crowbar))
var/list/satchels_to_add = list()

View File

@@ -214,13 +214,16 @@
if(!C)
C = H.client
var/image = get_id_photo(H, C, show_directions)
var/obj/item/photo/photo_front = new()
var/obj/item/photo/photo_side = new()
for(var/D in show_directions)
if(D == SOUTH)
photo_front.photocreate(null, icon(image, dir = D))
if(D == WEST || D == EAST)
photo_side.photocreate(null, icon(image, dir = D))
var/datum/picture/pf = new
var/datum/picture/ps = new
pf.picture_name = "[H]"
ps.picture_name = "[H]"
pf.picture_desc = "This is [H]."
ps.picture_desc = "This is [H]."
pf.picture_image = icon(image, dir = SOUTH)
ps.picture_image = icon(image, dir = WEST)
var/obj/item/photo/photo_front = new(null, pf)
var/obj/item/photo/photo_side = new(null, ps)
//These records should ~really~ be merged or something
//General Record

View File

@@ -84,3 +84,67 @@
/datum/proc/to_chat_check_changed_vars(target = world)
to_chat(target, txt_changed_vars())
#endif
//Return a LIST for serialize_datum to encode! Not the actual json!
/datum/proc/serialize_list(list/options)
CRASH("Attempted to serialize datum [src] of type [type] without serialize_list being implemented!")
//Accepts a LIST from deserialize_datum. Should return src or another datum.
/datum/proc/deserialize_list(json, list/options)
CRASH("Attempted to deserialize datum [src] of type [type] without deserialize_list being implemented!")
//Serializes into JSON. Does not encode type.
/datum/proc/serialize_json(list/options)
. = serialize_list(options)
if(!islist(.))
. = null
else
. = json_encode(.)
//Deserializes from JSON. Does not parse type.
/datum/proc/deserialize_json(list/input, list/options)
var/list/jsonlist = json_decode(input)
. = deserialize_list(jsonlist)
if(!istype(., /datum))
. = null
/proc/json_serialize_datum(datum/D, list/options)
if(!istype(D))
return
var/list/jsonlist = D.serialize_list(options)
if(islist(jsonlist))
jsonlist["DATUM_TYPE"] = D.type
return json_encode(jsonlist)
/proc/json_deserialize_datum(list/jsonlist, list/options, target_type, strict_target_type = FALSE)
if(!islist(jsonlist))
if(!istext(jsonlist))
CRASH("Invalid JSON")
return
jsonlist = json_decode(jsonlist)
if(!islist(jsonlist))
CRASH("Invalid JSON")
return
if(!jsonlist["DATUM_TYPE"])
return
if(!ispath(jsonlist["DATUM_TYPE"]))
if(!istext(jsonlist["DATUM_TYPE"]))
return
jsonlist["DATUM_TYPE"] = text2path(jsonlist["DATUM_TYPE"])
if(!ispath(jsonlist["DATUM_TYPE"]))
return
if(target_type)
if(!ispath(target_type))
return
if(strict_target_type)
if(target_type != jsonlist["DATUM_TYPE"])
return
else if(!ispath(jsonlist["DATUM_TYPE"], target_type))
return
var/typeofdatum = jsonlist["DATUM_TYPE"] //BYOND won't directly read if this is just put in the line below, and will instead runtime because it thinks you're trying to make a new list?
var/datum/D = new typeofdatum
var/datum/returned = D.deserialize_list(jsonlist, options)
if(!istype(returned, /datum))
qdel(D)
else
return returned

View File

@@ -148,12 +148,12 @@
/datum/objective_item/steal/blueprints/check_special_completion(obj/item/I)
if(istype(I, /obj/item/areaeditor/blueprints))
return 1
return TRUE
if(istype(I, /obj/item/photo))
var/obj/item/photo/P = I
if(P.blueprints) //if the blueprints are in frame
return 1
return 0
if(P.picture.has_blueprints) //if the blueprints are in frame
return TRUE
return FALSE
/datum/objective_item/steal/slime
name = "an unused sample of slime extract."

View File

@@ -108,10 +108,10 @@
if(active1 in GLOB.data_core.general)
if(istype(active1.fields["photo_front"], /obj/item/photo))
var/obj/item/photo/P1 = active1.fields["photo_front"]
user << browse_rsc(P1.img, "photo_front")
user << browse_rsc(P1.picture.picture_image, "photo_front")
if(istype(active1.fields["photo_side"], /obj/item/photo))
var/obj/item/photo/P2 = active1.fields["photo_side"]
user << browse_rsc(P2.img, "photo_side")
user << browse_rsc(P2.picture.picture_image, "photo_side")
dat += "<tr><td>Name:</td><td>[active1.fields["name"]]</td>"
dat += "<td><a href='?src=[REF(src)];field=show_photo_front'><img src=photo_front height=80 width=80 border=4></a></td>"
dat += "<td><a href='?src=[REF(src)];field=show_photo_side'><img src=photo_side height=80 width=80 border=4></a></td></tr>"

View File

@@ -183,10 +183,10 @@
if(istype(active1, /datum/data/record) && GLOB.data_core.general.Find(active1))
if(istype(active1.fields["photo_front"], /obj/item/photo))
var/obj/item/photo/P1 = active1.fields["photo_front"]
user << browse_rsc(P1.img, "photo_front")
user << browse_rsc(P1.picture.picture_image, "photo_front")
if(istype(active1.fields["photo_side"], /obj/item/photo))
var/obj/item/photo/P2 = active1.fields["photo_side"]
user << browse_rsc(P2.img, "photo_side")
user << browse_rsc(P2.picture.picture_image, "photo_side")
dat += {"<table><tr><td><table>
<tr><td>Name:</td><td><A href='?src=[REF(src)];choice=Edit Field;field=name'>&nbsp;[active1.fields["name"]]&nbsp;</A></td></tr>
<tr><td>ID:</td><td><A href='?src=[REF(src)];choice=Edit Field;field=id'>&nbsp;[active1.fields["id"]]&nbsp;</A></td></tr>
@@ -447,7 +447,7 @@ What a mess.*/
sleep(30)
if((istype(active1, /datum/data/record) && GLOB.data_core.general.Find(active1)))//make sure the record still exists.
var/obj/item/photo/photo = active1.fields["photo_front"]
new /obj/item/poster/wanted(src.loc, photo.img, wanted_name, info)
new /obj/item/poster/wanted(loc, photo.picture.picture_image, wanted_name, info)
printing = 0
//RECORD DELETE
@@ -612,7 +612,7 @@ What a mess.*/
if(photo)
qdel(active1.fields["photo_front"])
//Lets center it to a 32x32.
var/icon/I = photo.img
var/icon/I = photo.picture.picture_image
var/w = I.Width()
var/h = I.Height()
var/dw = w - 32
@@ -623,7 +623,7 @@ What a mess.*/
if(active1.fields["photo_front"])
if(istype(active1.fields["photo_front"], /obj/item/photo))
var/obj/item/photo/P = active1.fields["photo_front"]
print_photo(P.img, active1.fields["name"])
print_photo(P.picture.picture_image, active1.fields["name"])
if("show_photo_side")
if(active1.fields["photo_side"])
if(istype(active1.fields["photo_side"], /obj/item/photo))
@@ -634,7 +634,7 @@ What a mess.*/
if(photo)
qdel(active1.fields["photo_side"])
//Lets center it to a 32x32.
var/icon/I = photo.img
var/icon/I = photo.picture.picture_image
var/w = I.Width()
var/h = I.Height()
var/dw = w - 32
@@ -645,7 +645,7 @@ What a mess.*/
if(active1.fields["photo_side"])
if(istype(active1.fields["photo_side"], /obj/item/photo))
var/obj/item/photo/P = active1.fields["photo_side"]
print_photo(P.img, active1.fields["name"])
print_photo(P.picture.picture_image, active1.fields["name"])
if("mi_crim_add")
if(istype(active1, /datum/data/record))
var/t1 = stripped_input(usr, "Please input minor crime names:", "Secure. records", "", null)
@@ -760,10 +760,9 @@ What a mess.*/
var/obj/item/photo/P = null
if(issilicon(user))
var/mob/living/silicon/tempAI = user
var/datum/picture/selection = tempAI.GetPhoto()
var/datum/picture/selection = tempAI.GetPhoto(user)
if(selection)
P = new()
P.photocreate(selection.fields["icon"], selection.fields["img"], selection.fields["desc"])
P = new(null, selection)
else if(istype(user.get_active_held_item(), /obj/item/photo))
P = user.get_active_held_item()
return P
@@ -779,7 +778,7 @@ What a mess.*/
small_img.Scale(8, 8)
ic.Blend(small_img,ICON_OVERLAY, 13, 13)
P.icon = ic
P.img = temp
P.picture.picture_image = temp
P.desc = "The photo on file for [name]."
P.pixel_x = rand(-10, 10)
P.pixel_y = rand(-10, 10)

View File

@@ -118,17 +118,17 @@ GLOBAL_LIST_EMPTY(allCasters)
newChannel.is_admin_channel = adminChannel
network_channels += newChannel
/datum/newscaster/feed_network/proc/SubmitArticle(msg, author, channel_name, obj/item/photo/photo, adminMessage = 0, allow_comments = 1)
/datum/newscaster/feed_network/proc/SubmitArticle(msg, author, channel_name, datum/picture/picture, adminMessage = 0, allow_comments = 1)
var/datum/newscaster/feed_message/newMsg = new /datum/newscaster/feed_message
newMsg.author = author
newMsg.body = msg
newMsg.time_stamp = "[station_time_timestamp()]"
newMsg.is_admin_message = adminMessage
newMsg.locked = !allow_comments
if(photo)
newMsg.img = photo.img
newMsg.caption = photo.scribble
newMsg.photo_file = save_photo(photo.img)
if(picture)
newMsg.img = picture.picture_image
newMsg.caption = picture.caption
newMsg.photo_file = save_photo(picture.picture_image)
for(var/datum/newscaster/feed_channel/FC in network_channels)
if(FC.channel_name == channel_name)
FC.messages += newMsg
@@ -138,15 +138,15 @@ GLOBAL_LIST_EMPTY(allCasters)
lastAction ++
newMsg.creationTime = lastAction
/datum/newscaster/feed_network/proc/submitWanted(criminal, body, scanned_user, obj/item/photo/photo, adminMsg = 0, newMessage = 0)
/datum/newscaster/feed_network/proc/submitWanted(criminal, body, scanned_user, datum/picture/picture, adminMsg = 0, newMessage = 0)
wanted_issue.active = 1
wanted_issue.criminal = criminal
wanted_issue.body = body
wanted_issue.scannedUser = scanned_user
wanted_issue.isAdminMsg = adminMsg
if(photo)
wanted_issue.img = photo.img
wanted_issue.photo_file = save_photo(photo.img)
if(picture)
wanted_issue.img = picture.picture_image
wanted_issue.photo_file = save_photo(picture.picture_image)
if(newMessage)
for(var/obj/machinery/newscaster/N in GLOB.allCasters)
N.newsAlert()
@@ -197,7 +197,7 @@ GLOBAL_LIST_EMPTY(allCasters)
var/alert = FALSE
var/scanned_user = "Unknown"
var/msg = ""
var/obj/item/photo/photo = null
var/datum/picture/picture
var/channel_name = ""
var/c_locked=0
var/datum/newscaster/feed_channel/viewing_channel = null
@@ -221,7 +221,7 @@ GLOBAL_LIST_EMPTY(allCasters)
/obj/machinery/newscaster/Destroy()
GLOB.allCasters -= src
viewing_channel = null
photo = null
picture = null
return ..()
/obj/machinery/newscaster/update_icon()
@@ -312,7 +312,7 @@ GLOBAL_LIST_EMPTY(allCasters)
dat+="<HR><B><A href='?src=[REF(src)];set_channel_receiving=1'>Receiving Channel</A>:</B> [channel_name]<BR>"
dat+="<B>Message Author:</B> <FONT COLOR='green'>[scanned_user]</FONT><BR>"
dat+="<B><A href='?src=[REF(src)];set_new_message=1'>Message Body</A>:</B> <BR><font face=\"[PEN_FONT]\">[parsemarkdown(msg, user)]</font><BR>"
dat+="<B><A href='?src=[REF(src)];set_attachment=1'>Attach Photo</A>:</B> [(photo ? "Photo Attached" : "No Photo")]</BR>"
dat+="<B><A href='?src=[REF(src)];set_attachment=1'>Attach Photo</A>:</B> [(picture ? "Photo Attached" : "No Photo")]</BR>"
dat+="<B><A href='?src=[REF(src)];set_comment=1'>Comments [allow_comments ? "Enabled" : "Disabled"]</A></B><BR>"
dat+="<BR><A href='?src=[REF(src)];submit_new_message=1'>Submit</A><BR><BR><A href='?src=[REF(src)];setScreen=[0]'>Cancel</A><BR>"
if(4)
@@ -454,7 +454,7 @@ GLOBAL_LIST_EMPTY(allCasters)
dat+="<HR>"
dat+="<A href='?src=[REF(src)];set_wanted_name=1'>Criminal Name</A>: [channel_name] <BR>"
dat+="<A href='?src=[REF(src)];set_wanted_desc=1'>Description</A>: [msg] <BR>"
dat+="<A href='?src=[REF(src)];set_attachment=1'>Attach Photo</A>: [(photo ? "Photo Attached" : "No Photo")]</BR>"
dat+="<A href='?src=[REF(src)];set_attachment=1'>Attach Photo</A>: [(picture ? "Photo Attached" : "No Photo")]</BR>"
if(wanted_already)
dat+="<B>Wanted Issue created by:</B><FONT COLOR='green'>[GLOB.news_network.wanted_issue.scannedUser]</FONT><BR>"
else
@@ -561,7 +561,7 @@ GLOBAL_LIST_EMPTY(allCasters)
if(msg =="" || msg=="\[REDACTED\]" || scanned_user == "Unknown" || channel_name == "" )
screen=6
else
GLOB.news_network.SubmitArticle("<font face=\"[PEN_FONT]\">[parsemarkdown(msg, usr)]</font>", scanned_user, channel_name, photo, 0, allow_comments)
GLOB.news_network.SubmitArticle("<font face=\"[PEN_FONT]\">[parsemarkdown(msg, usr)]</font>", scanned_user, channel_name, picture, 0, allow_comments)
SSblackbox.record_feedback("amount", "newscaster_stories", 1)
screen=4
msg = ""
@@ -612,13 +612,13 @@ GLOBAL_LIST_EMPTY(allCasters)
if(choice=="Confirm")
scan_user(usr)
if(input_param==1) //If input_param == 1 we're submitting a new wanted issue. At 2 we're just editing an existing one.
GLOB.news_network.submitWanted(channel_name, msg, scanned_user, photo, 0 , 1)
GLOB.news_network.submitWanted(channel_name, msg, scanned_user, picture, 0 , 1)
screen = 15
else
if(GLOB.news_network.wanted_issue.isAdminMsg)
alert("The wanted issue has been distributed by a Nanotrasen higherup. You cannot edit it.","Ok")
return
GLOB.news_network.submitWanted(channel_name, msg, scanned_user, photo)
GLOB.news_network.submitWanted(channel_name, msg, scanned_user, picture)
screen = 19
updateUsrDialog()
else if(href_list["cancel_wanted"])
@@ -783,22 +783,11 @@ GLOBAL_LIST_EMPTY(allCasters)
take_damage(5, BRUTE, "melee")
/obj/machinery/newscaster/proc/AttachPhoto(mob/user)
var/obj/item/photo/photo = user.is_holding_item_of_type(/obj/item/photo)
if(photo)
if(!photo.sillynewscastervar)
photo.forceMove(drop_location())
if(!issilicon(user))
user.put_in_inactive_hand(photo)
else
qdel(photo)
photo = null
photo = user.is_holding_item_of_type(/obj/item/photo)
if(photo && !user.transferItemToLoc(photo, src))
photo = null
picture = photo.picture
if(issilicon(user))
var/list/nametemp = list()
var/find
var/datum/picture/selection
var/obj/item/camera/siliconcam/targetcam = null
var/obj/item/camera/siliconcam/targetcam
if(isAI(user))
var/mob/living/silicon/ai/R = user
targetcam = R.aicamera
@@ -810,21 +799,12 @@ GLOBAL_LIST_EMPTY(allCasters)
targetcam = R.aicamera
else
to_chat(user, "<span class='warning'>You cannot interface with silicon photo uploading!</span>")
if(targetcam.aipictures.len == 0)
if(!targetcam.stored.len)
to_chat(usr, "<span class='boldannounce'>No images saved</span>")
return
for(var/datum/picture/t in targetcam.aipictures)
nametemp += t.fields["name"]
find = input("Select image (numbered in order taken)") in nametemp
var/obj/item/photo/P = new/obj/item/photo()
for(var/datum/picture/q in targetcam.aipictures)
if(q.fields["name"] == find)
selection = q
break
P.photocreate(selection.fields["icon"], selection.fields["img"], selection.fields["desc"])
P.sillynewscastervar = 1
photo = P
qdel(P)
var/datum/picture/selection = targetcam.selectpicture(user)
if(selection)
picture = selection
/obj/machinery/newscaster/proc/scan_user(mob/living/user)
if(ishuman(user))

View File

@@ -141,7 +141,7 @@
break
// Del - Sender - Recepient - Message
// X - Al Green - Your Mom - WHAT UP!?
dat += "<tr><td width = '5%'><center><A href='?src=[REF(src)];delete_logs=[REF(pda)]' style='color: rgb(255,0,0)'>X</a></center></td><td width='15%'>[pda.sender]</td><td width='15%'>[pda.recipient]</td><td width='300px'>[pda.message][pda.photo ? " <a href='byond://?src=[REF(pda)];photo=1'>(Photo)</a>":""]</td></tr>"
dat += "<tr><td width = '5%'><center><A href='?src=[REF(src)];delete_logs=[REF(pda)]' style='color: rgb(255,0,0)'>X</a></center></td><td width='15%'>[pda.sender]</td><td width='15%'>[pda.recipient]</td><td width='300px'>[pda.message][pda.picture ? " <a href='byond://?src=[REF(pda)];photo=1'>(Photo)</a>":""]</td></tr>"
dat += "</table>"
//Hacking screen.
if(2)

View File

@@ -124,7 +124,7 @@
var/sender = "Unspecified"
var/recipient = "Unspecified"
var/message = "Blank" // transferred message
var/icon/photo // attached photo
var/datum/picture/picture // attached photo
/datum/data_pda_msg/New(param_rec, param_sender, param_message, param_photo)
if(param_rec)
@@ -134,17 +134,17 @@
if(param_message)
message = param_message
if(param_photo)
photo = param_photo
picture = param_photo
/datum/data_pda_msg/Topic(href,href_list)
..()
if(href_list["photo"])
var/mob/M = usr
M << browse_rsc(photo, "pda_photo.png")
M << browse_rsc(picture.picture_image, "pda_photo.png")
M << browse("<html><head><title>PDA Photo</title></head>" \
+ "<body style='overflow:hidden;margin:0;text-align:center'>" \
+ "<img src='pda_photo.png' width='192' style='-ms-interpolation-mode:nearest-neighbor' />" \
+ "</body></html>", "window=pdaphoto;size=192x192")
+ "</body></html>", "window=pdaphoto;size=[picture.psize_x]x[picture.psize_y]")
onclose(M, "pdaphoto")
/datum/data_rc_msg

View File

@@ -72,7 +72,7 @@ GLOBAL_LIST_EMPTY(PDAs)
var/obj/item/paicard/pai = null // A slot for a personal AI device
var/icon/photo //Scanned photo
var/datum/picture/picture //Scanned photo
var/list/contained_item = list(/obj/item/pen, /obj/item/toy/crayon, /obj/item/lipstick, /obj/item/flashlight/pen, /obj/item/clothing/mask/cigarette)
var/obj/item/inserted_item //Used for pen, crayon, and lipstick insertion or removal. Same as above.
@@ -631,8 +631,8 @@ GLOBAL_LIST_EMPTY(PDAs)
"message" = message,
"targets" = string_targets
))
if (photo)
signal.data["photo"] = photo
if (picture)
signal.data["photo"] = picture
signal.send_to_receivers()
// If it didn't reach, note that fact
@@ -652,7 +652,7 @@ GLOBAL_LIST_EMPTY(PDAs)
log_talk(user, "[key_name(user)] (PDA: [initial(name)]) sent \"[message]\" to [target_text]", LOGPDA)
to_chat(user, "<span class='info'>Message sent to [target_text]: \"[message]\"</span>")
// Reset the photo
photo = null
picture = null
last_text = world.time
if (everyone)
last_everyone = world.time
@@ -821,7 +821,7 @@ GLOBAL_LIST_EMPTY(PDAs)
update_icon()
else if(istype(C, /obj/item/photo))
var/obj/item/photo/P = C
photo = P.img
picture = P.picture
to_chat(user, "<span class='notice'>You scan \the [C].</span>")
else
return ..()
@@ -936,11 +936,11 @@ GLOBAL_LIST_EMPTY(PDAs)
var/selected = plist[c]
if(aicamera.aipictures.len>0)
if(aicamera.stored.len)
var/add_photo = input(user,"Do you want to attach a photo?","Photo","No") as null|anything in list("Yes","No")
if(add_photo=="Yes")
var/datum/picture/Pic = aicamera.selectpicture(aicamera)
aiPDA.photo = Pic.fields["img"]
var/datum/picture/Pic = aicamera.selectpicture(user)
aiPDA.picture = Pic
if(incapacitated())
return

View File

@@ -19,4 +19,5 @@
new /obj/item/export_scanner(src)
new /obj/item/door_remote/quartermaster(src)
new /obj/item/circuitboard/machine/techfab/department/cargo(src)
new /obj/item/storage/photo_album/QM(src)
new /obj/item/circuitboard/machine/ore_silo(src)

View File

@@ -29,6 +29,7 @@
new /obj/item/inducer(src)
new /obj/item/circuitboard/machine/techfab/department/engineering(src)
new /obj/item/extinguisher/advanced(src)
new /obj/item/storage/photo_album/CE(src)
/obj/structure/closet/secure_closet/engineering_electrical
name = "electrical supplies locker"

View File

@@ -76,6 +76,7 @@
new /obj/item/pet_carrier(src)
new /obj/item/wallframe/defib_mount(src)
new /obj/item/circuitboard/machine/techfab/department/medical(src)
new /obj/item/storage/photo_album/CMO(src)
/obj/structure/closet/secure_closet/animal
name = "animal control"

View File

@@ -25,3 +25,4 @@
new /obj/item/laser_pointer(src)
new /obj/item/door_remote/research_director(src)
new /obj/item/circuitboard/machine/techfab/department/science(src)
new /obj/item/storage/photo_album/RD(src)

View File

@@ -32,6 +32,7 @@
new /obj/item/gun/energy/e_gun(src)
new /obj/item/door_remote/captain(src)
new /obj/item/card/id/captains_spare(src)
new /obj/item/storage/photo_album/Captain(src)
/obj/structure/closet/secure_closet/hop
name = "\proper head of personnel's locker"
@@ -58,6 +59,7 @@
new /obj/item/pet_carrier(src)
new /obj/item/door_remote/civillian(src)
new /obj/item/circuitboard/machine/techfab/department/service(src)
new /obj/item/storage/photo_album/HoP(src)
/obj/structure/closet/secure_closet/hos
name = "\proper head of security's locker"
@@ -90,6 +92,7 @@
new /obj/item/flashlight/seclite(src)
new /obj/item/pinpointer/nuke(src)
new /obj/item/circuitboard/machine/techfab/department/security(src)
new /obj/item/storage/photo_album/HoS(src)
/obj/structure/closet/secure_closet/warden
name = "\proper warden's locker"

View File

@@ -79,13 +79,24 @@ GLOBAL_PROTECT(security_mode)
/world/proc/SetupLogs()
var/override_dir = params[OVERRIDE_LOG_DIRECTORY_PARAMETER]
if(!override_dir)
GLOB.log_directory = "data/logs/[time2text(world.realtime, "YYYY/MM/DD")]/round-"
var/realtime = world.realtime
var/texttime = time2text(realtime, "YYYY/MM/DD")
GLOB.log_directory = "data/logs/[texttime]/round-"
GLOB.picture_logging_prefix = "L_[time2text(realtime, "YYYYMMDD")]_"
GLOB.picture_log_directory = "data/picture_logs/[texttime]/round-"
if(GLOB.round_id)
GLOB.log_directory += "[GLOB.round_id]"
GLOB.picture_logging_prefix += "R_[GLOB.round_id]_"
GLOB.picture_log_directory += "[GLOB.round_id]"
else
GLOB.log_directory += "[replacetext(time_stamp(), ":", ".")]"
var/timestamp = replacetext(time_stamp(), ":", ".")
GLOB.log_directory += "[timestamp]"
GLOB.picture_log_directory += "[timestamp]"
GLOB.picture_logging_prefix += "T_[timestamp]_"
else
GLOB.log_directory = "data/logs/[override_dir]"
GLOB.picture_logging_prefix = "O_[override_dir]_"
GLOB.picture_log_directory = "data/picture_logs/[override_dir]"
GLOB.world_game_log = "[GLOB.log_directory]/game.log"
GLOB.world_attack_log = "[GLOB.log_directory]/attack.log"

View File

@@ -48,7 +48,7 @@ GLOBAL_LIST_INIT(admin_verbs_debug_mapping, list(
/client/proc/show_line_profiling,
/client/proc/create_mapping_job_icons,
/client/proc/debug_z_levels,
/client/proc/place_ruin,
/client/proc/place_ruin
))
/obj/effect/debugging/mapfix_marker

View File

@@ -99,8 +99,9 @@
dat += "<h3> Experiment </h3>"
if(occupant)
var/obj/item/photo/P = new
P.photocreate(null, icon(dissection_icon(occupant), dir = SOUTH))
user << browse_rsc(P.img, "dissection_img")
P.picture = new
P.picture.picture_image = icon(dissection_icon(occupant), dir = SOUTH)
user << browse_rsc(P.picture.picture_image, "dissection_img")
dat += "<table><tr><td>"
dat += "<img src=dissection_img height=80 width=80>" //Avert your eyes
dat += "</td><td>"

View File

@@ -3,6 +3,8 @@
//Yes, I'm sorry.
/datum/turf_reservation
var/list/reserved_turfs = list()
var/width = 0
var/height = 0
var/bottom_left_coords[3]
var/top_right_coords[3]
var/wipe_reservation_on_release = TRUE
@@ -59,6 +61,8 @@
SSmapping.unused_turfs["[T.z]"] -= T
SSmapping.used_turfs[T] = src
T.ChangeTurf(turf_type, turf_type)
src.width = width
src.height = height
return TRUE
/datum/turf_reservation/New()

View File

@@ -136,6 +136,10 @@ GLOBAL_VAR_INIT(observer_default_invisibility, INVISIBILITY_OBSERVER)
grant_all_languages()
/mob/dead/observer/get_photo_description(obj/item/camera/camera)
if(!invisibility || camera.see_ghosts)
return "You can also see a g-g-g-g-ghooooost!"
/mob/dead/observer/narsie_act()
var/old_color = color
color = "#960000"

View File

@@ -104,7 +104,6 @@
msg += "[t_He] look[p_s()] very happy.\n"
if(MOOD_LEVEL_HAPPY4 to INFINITY)
msg += "[t_He] look[p_s()] ecstatic.\n"
msg += "*---------*</span>"
to_chat(user, msg)

View File

@@ -150,6 +150,22 @@
if(prob(I.block_chance*2))
return 1
/mob/living/get_photo_description(obj/item/camera/camera)
var/list/mob_details = list()
var/list/holding = list()
var/len = length(held_items)
if(len)
for(var/obj/item/I in held_items)
if(!holding.len)
holding += "They are holding \a [I]"
else if(held_items.Find(I) == len)
holding += ", and \a [I]."
else
holding += ", \a [I]"
holding += "."
mob_details += "You can also see [src] on the photo[health < (maxHealth * 0.75) ? ", looking a bit hurt":""][holding ? ". [holding.Join("")]":"."]."
return mob_details.Join("")
//Called when we bump onto an obj
/mob/living/proc/ObjBump(obj/O)
return

View File

@@ -78,4 +78,5 @@
temp = master.supplied[index]
if (length(temp) > 0)
laws.supplied[index] = temp
return
picturesync()

View File

@@ -1170,4 +1170,12 @@
lawsync()
lawupdate = 1
return TRUE
picturesync()
return FALSE
/mob/living/silicon/robot/proc/picturesync()
if(connected_ai && connected_ai.aicamera && aicamera)
for(var/i in aicamera.stored)
connected_ai.aicamera.stored[i] = TRUE
for(var/i in connected_ai.aicamera.stored)
aicamera.stored[i] = TRUE

View File

@@ -385,9 +385,9 @@
add_sensors()
to_chat(src, "Sensor overlay activated.")
/mob/living/silicon/proc/GetPhoto()
/mob/living/silicon/proc/GetPhoto(mob/user)
if (aicamera)
return aicamera.selectpicture(aicamera)
return aicamera.selectpicture(user)
/mob/living/silicon/update_transform()
var/matrix/ntransform = matrix(transform) //aka transform.Copy()

View File

@@ -69,6 +69,9 @@
to_chat(usr, t)
/mob/proc/get_photo_description(obj/item/camera/camera)
return "a ... thing?"
/mob/proc/show_message(msg, type, alt_msg, alt_type)//Message, type of message (1 or 2), alternative message, alt message type (1 or 2)
if(!client)

View File

@@ -94,30 +94,7 @@
else if(photocopy)
for(var/i = 0, i < copies, i++)
if(toner >= 5 && !busy && photocopy) //Was set to = 0, but if there was say 3 toner left and this ran, you would get -2 which would be weird for ink
var/obj/item/photo/p = new /obj/item/photo (loc)
var/icon/I = icon(photocopy.icon, photocopy.icon_state)
var/icon/img = icon(photocopy.img)
if(greytoggle == "Greyscale")
if(toner > 10) //plenty of toner, go straight greyscale
I.MapColors(rgb(77,77,77), rgb(150,150,150), rgb(28,28,28), rgb(0,0,0)) //I'm not sure how expensive this is, but given the many limitations of photocopying, it shouldn't be an issue.
img.MapColors(rgb(77,77,77), rgb(150,150,150), rgb(28,28,28), rgb(0,0,0))
else //not much toner left, lighten the photo
I.MapColors(rgb(77,77,77), rgb(150,150,150), rgb(28,28,28), rgb(100,100,100))
img.MapColors(rgb(77,77,77), rgb(150,150,150), rgb(28,28,28), rgb(100,100,100))
toner -= 5 //photos use a lot of ink!
else if(greytoggle == "Color")
if(toner >= 10)
toner -= 10 //Color photos use even more ink!
else
continue
p.icon = I
p.img = img
p.name = photocopy.name
p.desc = photocopy.desc
p.scribble = photocopy.scribble
p.pixel_x = rand(-10, 10)
p.pixel_y = rand(-10, 10)
p.blueprints = photocopy.blueprints //a copy of a picture is still good enough for the syndicate
new /obj/item/photo (loc, photocopy.picture.Copy(greytoggle == "Greyscale"? TRUE : FALSE))
busy = TRUE
sleep(15)
busy = FALSE
@@ -155,15 +132,10 @@
else
break
var/obj/item/photo/p = new /obj/item/photo (loc)
p.desc = "You see [ass]'s ass on the photo."
p.pixel_x = rand(-10, 10)
p.pixel_y = rand(-10, 10)
p.img = temp_img
var/icon/small_img = icon(temp_img) //Icon() is needed or else temp_img will be rescaled too >.>
var/icon/ic = icon('icons/obj/items_and_weapons.dmi',"photo")
small_img.Scale(8, 8)
ic.Blend(small_img,ICON_OVERLAY, 13, 13)
p.icon = ic
p.picture = new(null, "You see [ass]'s ass on the photo.", temp_img)
p.update_icon()
toner -= 5
busy = TRUE
sleep(15)
@@ -196,29 +168,14 @@
if(!isAI(usr))
return
if(toner >= 5 && !busy)
var/list/nametemp = list()
var/find
var/datum/picture/selection
var/mob/living/silicon/ai/tempAI = usr
if(tempAI.aicamera.aipictures.len == 0)
if(tempAI.aicamera.stored.len == 0)
to_chat(usr, "<span class='boldannounce'>No images saved</span>")
return
for(var/datum/picture/t in tempAI.aicamera.aipictures)
nametemp += t.fields["name"]
find = input("Select image (numbered in order taken)") in nametemp
var/obj/item/photo/p = new /obj/item/photo (loc)
for(var/datum/picture/q in tempAI.aicamera.aipictures)
if(q.fields["name"] == find)
selection = q
break
var/icon/I = selection.fields["icon"]
var/icon/img = selection.fields["img"]
p.icon = I
p.img = img
p.desc = selection.fields["desc"]
p.blueprints = selection.fields["blueprints"]
p.pixel_x = rand(-10, 10)
p.pixel_y = rand(-10, 10)
var/datum/picture/selection = tempAI.aicamera.selectpicture(usr)
var/obj/item/photo = new(loc, selection)
photo.pixel_x = rand(-10, 10)
photo.pixel_y = rand(-10, 10)
toner -= 5 //AI prints color pictures only, thus they can do it more efficiently
busy = TRUE
sleep(15)

View File

@@ -1,662 +0,0 @@
/* Photography!
* Contains:
* Camera
* Camera Film
* Photos
* Photo Albums
* Picture Frames
* AI Photography
*/
/*
* Film
*/
/obj/item/camera_film
name = "film cartridge"
icon = 'icons/obj/items_and_weapons.dmi'
desc = "A camera film cartridge. Insert it into a camera to reload it."
icon_state = "film"
item_state = "electropack"
lefthand_file = 'icons/mob/inhands/misc/devices_lefthand.dmi'
righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi'
w_class = WEIGHT_CLASS_TINY
resistance_flags = FLAMMABLE
materials = list(MAT_METAL = 10, MAT_GLASS = 10)
/*
* Photo
*/
/obj/item/photo
name = "photo"
icon = 'icons/obj/items_and_weapons.dmi'
icon_state = "photo"
item_state = "paper"
w_class = WEIGHT_CLASS_TINY
resistance_flags = FLAMMABLE
max_integrity = 50
grind_results = list("iodine" = 4)
var/icon/img //Big photo image
var/scribble //Scribble on the back.
var/blueprints = 0 //Does it include the blueprints?
var/sillynewscastervar //Photo objects with this set to 1 will not be ejected by a newscaster. Only gets set to 1 if a silicon puts one of their images into a newscaster
/obj/item/photo/suicide_act(mob/living/carbon/user)
user.visible_message("<span class='suicide'>[user] is taking one last look at \the [src]! It looks like [user.p_theyre()] giving in to death!</span>")//when you wanna look at photo of waifu one last time before you die...
if (user.gender == MALE)
playsound(user, 'sound/voice/human/manlaugh1.ogg', 50, 1)//EVERY TIME I DO IT MAKES ME LAUGH
else if (user.gender == FEMALE)
playsound(user, 'sound/voice/human/womanlaugh.ogg', 50, 1)
return OXYLOSS
/obj/item/photo/attack_self(mob/user)
user.examinate(src)
/obj/item/photo/attackby(obj/item/P, mob/user, params)
if(istype(P, /obj/item/pen) || istype(P, /obj/item/toy/crayon))
if(!user.is_literate())
to_chat(user, "<span class='notice'>You scribble illegibly on [src]!</span>")
return
var/txt = sanitize(input(user, "What would you like to write on the back?", "Photo Writing", null) as text)
txt = copytext(txt, 1, 128)
if(user.canUseTopic(src, BE_CLOSE))
scribble = txt
..()
/obj/item/photo/examine(mob/user)
..()
if(in_range(src, user))
show(user)
else
to_chat(user, "<span class='warning'>You need to get closer to get a good look at this photo!</span>")
/obj/item/photo/proc/show(mob/user)
user << browse_rsc(img, "tmp_photo.png")
user << browse("<html><head><title>[name]</title></head>" \
+ "<body style='overflow:hidden;margin:0;text-align:center'>" \
+ "<img src='tmp_photo.png' width='192' style='-ms-interpolation-mode:nearest-neighbor' />" \
+ "[scribble ? "<br>Written on the back:<br><i>[scribble]</i>" : ""]"\
+ "</body></html>", "window=book;size=192x[scribble ? 400 : 192]")
onclose(user, "[name]")
/obj/item/photo/verb/rename()
set name = "Rename photo"
set category = "Object"
set src in usr
var/n_name = copytext(sanitize(input(usr, "What would you like to label the photo?", "Photo Labelling", null) as text), 1, MAX_NAME_LEN)
//loc.loc check is for making possible renaming photos in clipboards
if((loc == usr || loc.loc && loc.loc == usr) && usr.stat == CONSCIOUS && usr.canmove && !usr.restrained())
name = "photo[(n_name ? text("- '[n_name]'") : null)]"
add_fingerprint(usr)
/obj/item/photo/proc/photocreate(inicon, inimg, indesc, inblueprints)
icon = inicon
img = inimg
desc = indesc
blueprints = inblueprints
/*
* Photo album
*/
/obj/item/storage/photo_album
name = "photo album"
icon = 'icons/obj/items_and_weapons.dmi'
icon_state = "album"
item_state = "briefcase"
lefthand_file = 'icons/mob/inhands/equipment/briefcase_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/briefcase_righthand.dmi'
resistance_flags = FLAMMABLE
/obj/item/storage/photo_album/Initialize()
. = ..()
GET_COMPONENT(STR, /datum/component/storage)
STR.can_hold = typecacheof(list(/obj/item/photo))
/*
* Camera
*/
/obj/item/camera
name = "camera"
icon = 'icons/obj/items_and_weapons.dmi'
desc = "A polaroid camera."
icon_state = "camera"
item_state = "electropack"
lefthand_file = 'icons/mob/inhands/misc/devices_lefthand.dmi'
righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi'
w_class = WEIGHT_CLASS_SMALL
flags_1 = CONDUCT_1
slot_flags = ITEM_SLOT_BELT
materials = list(MAT_METAL = 50, MAT_GLASS = 150)
var/pictures_max = 10
var/pictures_left = 10
var/on = TRUE
var/blueprints = 0 //are blueprints visible in the current photo being created?
var/list/aipictures = list() //Allows for storage of pictures taken by AI, in a similar manner the datacore stores info. Keeping this here allows us to share some procs w/ regualar camera
var/see_ghosts = 0 //for the spoop of it
var/obj/item/disk/holodisk/disk
/obj/item/camera/CheckParts(list/parts_list)
..()
var/obj/item/camera/C = locate(/obj/item/camera) in contents
if(C)
pictures_max = C.pictures_max
pictures_left = C.pictures_left
visible_message("[C] has been imbued with godlike power!")
qdel(C)
/obj/item/camera/spooky
name = "camera obscura"
desc = "A polaroid camera, some say it can see ghosts!"
see_ghosts = 1
/obj/item/camera/detective
name = "Detective's camera"
desc = "A polaroid camera with extra capacity for crime investigations."
pictures_max = 30
pictures_left = 30
/obj/item/camera/siliconcam //camera AI can take pictures with
name = "silicon photo camera"
var/in_camera_mode = 0
/obj/item/camera/siliconcam/ai_camera //camera AI can take pictures with
name = "AI photo camera"
/obj/item/camera/siliconcam/robot_camera //camera cyborgs can take pictures with.. needs it's own because of verb CATEGORY >.>
name = "Cyborg photo camera"
/obj/item/camera/siliconcam/robot_camera/verb/borgprinting()
set category ="Robot Commands"
set name = "Print Image"
set src in usr
if(usr.stat == DEAD)
return //won't work if dead
borgprint()
/obj/item/camera/attack(mob/living/carbon/human/M, mob/user)
return
/obj/item/camera/attackby(obj/item/I, mob/user, params)
if(istype(I, /obj/item/camera_film))
if(pictures_left)
to_chat(user, "<span class='notice'>[src] still has some film in it!</span>")
return
if(!user.temporarilyRemoveItemFromInventory(I))
return
to_chat(user, "<span class='notice'>You insert [I] into [src].</span>")
qdel(I)
pictures_left = pictures_max
return
if(istype(I, /obj/item/disk/holodisk))
if (!disk)
if(!user.transferItemToLoc(I, src))
to_chat(user, "<span class='warning'>[I] is stuck to your hand!</span>")
return TRUE
to_chat(user, "<span class='notice'>You slide [I] into the back of [src].</span>")
disk = I
else
to_chat(user, "<span class='warning'>There's already a disk inside [src].</span>")
return TRUE //no afterattack
..()
/obj/item/camera/attack_self(mob/user)
if(!disk)
return
to_chat(user, "<span class='notice'>You eject [disk] out the back of [src].</span>")
user.put_in_hands(disk)
disk = null
/obj/item/camera/examine(mob/user)
..()
to_chat(user, "It has [pictures_left] photo\s left.")
/obj/item/camera/proc/camera_get_icon(list/turfs, turf/center)
var/list/atoms = list()
for(var/turf/T in turfs)
atoms.Add(T)
for(var/atom/movable/A in T)
if(A.invisibility)
if(see_ghosts && isobserver(A))
var/mob/dead/observer/O = A
if(O.orbiting) //so you dont see ghosts following people like antags, etc.
continue
else
continue
atoms.Add(A)
var/list/sorted = sortTim(atoms,/proc/cmp_atom_layer_asc)
var/icon/res = icon('icons/effects/96x96.dmi', "")
for(var/atom/A in sorted)
var/icon/img = getFlatIcon(A, no_anim = TRUE)
if(isliving(A))
var/mob/living/L = A
if(L.lying)
img.Turn(L.lying)
var/offX = world.icon_size * (A.x - center.x) + A.pixel_x + 33
var/offY = world.icon_size * (A.y - center.y) + A.pixel_y + 33
if(ismovableatom(A))
var/atom/movable/AM = A
offX += AM.step_x
offY += AM.step_y
res.Blend(img, blendMode2iconMode(A.blend_mode), offX, offY)
if(istype(A, /obj/item/areaeditor/blueprints))
blueprints = 1
for(var/turf/T in turfs)
var/area/A = T.loc
if(A.icon_state)//There's actually something to blend in.
res.Blend(getFlatIcon(A,no_anim = TRUE), blendMode2iconMode(A.blend_mode), world.icon_size * (T.x - center.x) + 33, world.icon_size * (T.y - center.y) + 33)
return res
/obj/item/camera/proc/camera_get_mobs(turf/the_turf)
var/mob_detail
for(var/mob/M in the_turf)
if(M.invisibility)
if(see_ghosts && isobserver(M))
var/mob/dead/observer/O = M
if(O.orbiting)
continue
if(!mob_detail)
mob_detail = "You can see a g-g-g-g-ghooooost! "
else
mob_detail += "You can also see a g-g-g-g-ghooooost!"
else
continue
var/list/holding = list()
if(isliving(M))
var/mob/living/L = M
for(var/obj/item/I in L.held_items)
if(!holding)
holding += "[L.p_theyre(TRUE)] holding \a [I]"
else
holding += " and \a [I]"
holding = holding.Join()
if(!mob_detail)
mob_detail = "You can see [L] on the photo[L.health < (L.maxHealth * 0.75) ? " - [L] looks hurt":""].[holding ? " [holding]":"."]. "
else
mob_detail += "You can also see [L] on the photo[L.health < (L.maxHealth * 0.75) ? " - [L] looks hurt":""].[holding ? " [holding]":"."]."
return mob_detail
/obj/item/camera/proc/captureimage(atom/target, mob/user, flag) //Proc for both regular and AI-based camera to take the image
var/mobs = ""
var/isAi = isAI(user)
var/list/seen
if(!isAi) //crappy check, but without it AI photos would be subject to line of sight from the AI Eye object. Made the best of it by moving the sec camera check inside
if(user.client) //To make shooting through security cameras possible
seen = get_hear(world.view, user.client.eye) //To make shooting through security cameras possible
else
seen = get_hear(world.view, user)
else
seen = get_hear(world.view, target)
var/list/turfs = list()
for(var/turf/T in range(1, target))
if(T in seen)
if(isAi && !GLOB.cameranet.checkTurfVis(T))
continue
else
turfs += T
mobs += camera_get_mobs(T)
var/icon/temp = icon('icons/effects/96x96.dmi',"")
temp.Blend("#000", ICON_OVERLAY)
temp.Blend(camera_get_icon(turfs, target), ICON_OVERLAY)
if(!issilicon(user))
printpicture(user, temp, mobs, flag)
else
aipicture(user, temp, mobs, isAi, blueprints)
/obj/item/camera/proc/printpicture(mob/user, icon/temp, mobs, flag) //Normal camera proc for creating photos
var/obj/item/photo/P = new/obj/item/photo(get_turf(src))
if(in_range(src, user)) //needed because of TK
user.put_in_hands(P)
var/icon/small_img = icon(temp)
var/icon/ic = icon('icons/obj/items_and_weapons.dmi',"photo")
small_img.Scale(8, 8)
ic.Blend(small_img,ICON_OVERLAY, 13, 13)
P.icon = ic
P.img = temp
P.desc = mobs
P.pixel_x = rand(-10, 10)
P.pixel_y = rand(-10, 10)
if(blueprints)
P.blueprints = 1
blueprints = 0
/obj/item/camera/proc/aipicture(mob/user, icon/temp, mobs, isAi) //instead of printing a picture like a regular camera would, we do this instead for the AI
var/icon/small_img = icon(temp)
var/icon/ic = icon('icons/obj/items_and_weapons.dmi',"photo")
small_img.Scale(8, 8)
ic.Blend(small_img,ICON_OVERLAY, 13, 13)
var/icon = ic
var/img = temp
var/desc = mobs
var/pixel_x = rand(-10, 10)
var/pixel_y = rand(-10, 10)
var/injectblueprints = 1
if(blueprints)
injectblueprints = 1
blueprints = 0
if(isAi)
injectaialbum(icon, img, desc, pixel_x, pixel_y, injectblueprints)
else
injectmasteralbum(icon, img, desc, pixel_x, pixel_y, injectblueprints)
/datum/picture
var/name = "image"
var/list/fields = list()
/obj/item/camera/proc/injectaialbum(icon, img, desc, pixel_x, pixel_y, blueprintsinject) //stores image information to a list similar to that of the datacore
var/numberer = 1
for(var/datum/picture in src.aipictures)
numberer++
var/datum/picture/P = new()
P.fields["name"] = "Image [numberer] (taken by [src.loc.name])"
P.fields["icon"] = icon
P.fields["img"] = img
P.fields["desc"] = desc
P.fields["pixel_x"] = pixel_x
P.fields["pixel_y"] = pixel_y
P.fields["blueprints"] = blueprintsinject
aipictures += P
to_chat(usr, "<span class='unconscious'>Image recorded</span>") //feedback to the AI player that the picture was taken
/obj/item/camera/proc/injectmasteralbum(icon, img, desc, pixel_x, pixel_y, blueprintsinject) //stores image information to a list similar to that of the datacore
var/numberer = 1
var/mob/living/silicon/robot/C = src.loc
if(C.connected_ai)
for(var/datum/picture in C.connected_ai.aicamera.aipictures)
numberer++
var/datum/picture/P = new()
P.fields["name"] = "Image [numberer] (taken by [src.loc.name])"
P.fields["icon"] = icon
P.fields["img"] = img
P.fields["desc"] = desc
P.fields["pixel_x"] = pixel_x
P.fields["pixel_y"] = pixel_y
P.fields["blueprints"] = blueprintsinject
C.connected_ai.aicamera.aipictures += P
to_chat(usr, "<span class='unconscious'>Image recorded and saved to remote database</span>") //feedback to the Cyborg player that the picture was taken
else
injectaialbum(icon, img, desc, pixel_x, pixel_y, blueprintsinject)
/obj/item/camera/siliconcam/proc/selectpicture(obj/item/camera/siliconcam/targetloc)
var/list/nametemp = list()
var/find
if(targetloc.aipictures.len == 0)
to_chat(usr, "<span class='boldannounce'>No images saved</span>")
return
for(var/datum/picture/t in targetloc.aipictures)
nametemp += t.fields["name"]
find = input("Select image (numbered in order taken)") in nametemp
for(var/datum/picture/q in targetloc.aipictures)
if(q.fields["name"] == find)
return q
/obj/item/camera/siliconcam/proc/viewpichelper(obj/item/camera/siliconcam/targetloc)
var/obj/item/photo/P = new/obj/item/photo()
var/datum/picture/selection = selectpicture(targetloc)
if(selection)
P.photocreate(selection.fields["icon"], selection.fields["img"], selection.fields["desc"])
P.pixel_x = selection.fields["pixel_x"]
P.pixel_y = selection.fields["pixel_y"]
P.show(usr)
to_chat(usr, P.desc)
qdel(P) //so 10 thousand picture items are not left in memory should an AI take them and then view them all
/obj/item/camera/siliconcam/proc/viewpictures(user)
if(iscyborg(user)) // Cyborg
var/mob/living/silicon/robot/C = src.loc
var/obj/item/camera/siliconcam/Cinfo
if(C.connected_ai)
Cinfo = C.connected_ai.aicamera
viewpichelper(Cinfo)
else
Cinfo = C.aicamera
viewpichelper(Cinfo)
else // AI
var/Ainfo = src
viewpichelper(Ainfo)
/obj/item/camera/afterattack(atom/target, mob/user, flag)
. = ..()
if(!on || !pictures_left || !isturf(target.loc))
return
if (disk)
if(ismob(target))
if (disk.record)
QDEL_NULL(disk.record)
disk.record = new
var/mob/M = target
disk.record.caller_name = M.name
disk.record.set_caller_image(M)
else
return
else
captureimage(target, user, flag)
pictures_left--
to_chat(user, "<span class='notice'>[pictures_left] photos left.</span>")
playsound(loc, pick('sound/items/polaroid1.ogg', 'sound/items/polaroid2.ogg'), 75, 1, -3)
icon_state = "camera_off"
on = FALSE
addtimer(CALLBACK(src, .proc/cooldown), 64)
/obj/item/camera/proc/cooldown()
set waitfor = FALSE
icon_state = "camera"
on = TRUE
/obj/item/camera/siliconcam/proc/toggle_camera_mode()
if(in_camera_mode)
camera_mode_off()
else
camera_mode_on()
/obj/item/camera/siliconcam/proc/camera_mode_off()
src.in_camera_mode = 0
to_chat(usr, "<B>Camera Mode deactivated</B>")
/obj/item/camera/siliconcam/proc/camera_mode_on()
src.in_camera_mode = 1
to_chat(usr, "<B>Camera Mode activated</B>")
/obj/item/camera/siliconcam/robot_camera/proc/borgprint()
var/list/nametemp = list()
var/find
var/datum/picture/selection
var/mob/living/silicon/robot/C = src.loc
var/obj/item/camera/siliconcam/targetcam = null
if(C.toner < 20)
to_chat(usr, "Insufficent toner to print image.")
return
if(C.connected_ai)
targetcam = C.connected_ai.aicamera
else
targetcam = C.aicamera
if(targetcam.aipictures.len == 0)
to_chat(usr, "<span class='userdanger'>No images saved</span>")
return
for(var/datum/picture/t in targetcam.aipictures)
nametemp += t.fields["name"]
find = input("Select image (numbered in order taken)") in nametemp
for(var/datum/picture/q in targetcam.aipictures)
if(q.fields["name"] == find)
selection = q
break
var/obj/item/photo/p = new /obj/item/photo(C.loc)
p.photocreate(selection.fields["icon"], selection.fields["img"], selection.fields["desc"], selection.fields["blueprints"])
p.pixel_x = rand(-10, 10)
p.pixel_y = rand(-10, 10)
C.toner -= 20 //Cyborgs are very ineffeicient at printing an image
visible_message("[C.name] spits out a photograph from a narrow slot on its chassis.")
to_chat(usr, "<span class='notice'>You print a photograph.</span>")
// Picture frames
/obj/item/wallframe/picture
name = "picture frame"
desc = "The perfect showcase for your favorite deathtrap memories."
icon = 'icons/obj/decals.dmi'
materials = list()
flags_1 = 0
icon_state = "frame-empty"
result_path = /obj/structure/sign/picture_frame
var/obj/item/photo/displayed
/obj/item/wallframe/picture/attackby(obj/item/I, mob/user)
if(istype(I, /obj/item/photo))
if(!displayed)
if(!user.transferItemToLoc(I, src))
return
displayed = I
update_icon()
else
to_chat(user, "<span class=notice>\The [src] already contains a photo.</span>")
..()
//ATTACK HAND IGNORING PARENT RETURN VALUE
/obj/item/wallframe/picture/attack_hand(mob/user)
if(user.get_inactive_held_item() != src)
..()
return
if(contents.len)
var/obj/item/I = pick(contents)
user.put_in_hands(I)
to_chat(user, "<span class='notice'>You carefully remove the photo from \the [src].</span>")
displayed = null
update_icon()
return ..()
/obj/item/wallframe/picture/attack_self(mob/user)
user.examinate(src)
/obj/item/wallframe/picture/examine(mob/user)
if(user.is_holding(src) && displayed)
displayed.show(user)
else
..()
/obj/item/wallframe/picture/update_icon()
cut_overlays()
if(displayed)
add_overlay(getFlatIcon(displayed))
/obj/item/wallframe/picture/after_attach(obj/O)
..()
var/obj/structure/sign/picture_frame/PF = O
PF.copy_overlays(src)
if(displayed)
PF.framed = displayed
if(contents.len)
var/obj/item/I = pick(contents)
I.forceMove(PF)
/obj/structure/sign/picture_frame
name = "picture frame"
desc = "Every time you look it makes you laugh."
icon = 'icons/obj/decals.dmi'
icon_state = "frame-empty"
var/obj/item/photo/framed
/obj/structure/sign/picture_frame/New(loc, dir, building)
..()
if(dir)
setDir(dir)
if(building)
pixel_x = (dir & 3)? 0 : (dir == 4 ? -30 : 30)
pixel_y = (dir & 3)? (dir ==1 ? -30 : 30) : 0
/obj/structure/sign/picture_frame/examine(mob/user)
if(in_range(src, user) && framed)
framed.show(user)
else
..()
/obj/structure/sign/picture_frame/attackby(obj/item/I, mob/user, params)
if(istype(I, /obj/item/screwdriver) || istype(I, /obj/item/wrench))
to_chat(user, "<span class='notice'>You start unsecuring [name]...</span>")
if(I.use_tool(src, user, 30, volume=50))
playsound(loc, 'sound/items/deconstruct.ogg', 50, 1)
to_chat(user, "<span class='notice'>You unsecure [name].</span>")
deconstruct()
return
else if(istype(I, /obj/item/photo))
if(!framed)
var/obj/item/photo/P = I
if(!user.transferItemToLoc(P, src))
return
framed = P
update_icon()
else
to_chat(user, "<span class=notice>\The [src] already contains a photo.</span>")
..()
/obj/structure/sign/picture_frame/attack_hand(mob/user)
. = ..()
if(.)
return
if(framed)
framed.show(user)
/obj/structure/sign/picture_frame/update_icon()
cut_overlays()
if(framed)
add_overlay(getFlatIcon(framed))
/obj/structure/sign/picture_frame/deconstruct(disassembled = TRUE)
if(!(flags_1 & NODECONSTRUCT_1))
var/obj/item/wallframe/picture/F = new /obj/item/wallframe/picture(loc)
if(framed)
F.displayed = framed
framed = null
if(contents.len)
var/obj/item/I = pick(contents)
I.forceMove(F)
F.update_icon()
qdel(src)

View File

@@ -0,0 +1,171 @@
/datum/picture
var/picture_name = "picture"
var/picture_desc = "This is a picture."
var/caption
var/icon/picture_image
var/icon/picture_icon
var/psize_x = 96
var/psize_y = 96
var/has_blueprints = FALSE
var/logpath //If the picture has been logged this is the path.
var/id //this var is NOT protected because the worst you can do with this that you couldn't do otherwise is overwrite photos, and photos aren't going to be used as attack logs/investigations anytime soon.
/datum/picture/New(name, desc, image, icon, size_x, size_y, bp, caption_, autogenerate_icon)
if(!isnull(name))
picture_name = name
if(!isnull(desc))
picture_desc = desc
if(!isnull(image))
picture_image = image
if(!isnull(icon))
picture_icon = icon
if(!isnull(psize_x))
psize_x = size_x
if(!isnull(psize_y))
psize_y = size_y
if(!isnull(bp))
has_blueprints = bp
if(!isnull(caption_))
caption = caption_
if(autogenerate_icon && !picture_icon && picture_image)
regenerate_small_icon()
/datum/picture/proc/get_small_icon()
if(!picture_icon)
regenerate_small_icon()
return picture_icon
/datum/picture/proc/regenerate_small_icon()
if(!picture_image)
return
var/icon/small_img = icon(picture_image)
var/icon/ic = icon('icons/obj/items_and_weapons.dmi', "photo")
small_img.Scale(8, 8)
ic.Blend(small_img,ICON_OVERLAY, 13, 13)
picture_icon = ic
/datum/picture/serialize_list(list/options)
. = list()
.["id"] = id
.["desc"] = picture_desc
.["name"] = picture_name
.["caption"] = caption
.["pixel_size_x"] = psize_x
.["pixel_size_y"] = psize_y
.["blueprints"] = has_blueprints
.["logpath"] = logpath
/datum/picture/deserialize_list(list/input, list/options)
if(!input["logpath"] || !fexists(input["logpath"]) || !input["id"] || !input["pixel_size_x"] || !input["pixel_size_y"])
return
picture_image = icon(file(input["logpath"]))
logpath = input["logpath"]
id = input["id"]
psize_x = input["pixel_size_x"]
psize_y = input["pixel_size_y"]
if(input["blueprints"])
has_blueprints = input["blueprints"]
if(input["caption"])
caption = input["caption"]
if(input["desc"])
picture_desc = input["desc"]
if(input["name"])
picture_name = input["name"]
return src
/proc/load_photo_from_disk(id, location)
var/datum/picture/P = load_picture_from_disk(id)
if(istype(P))
var/obj/item/photo/p = new(location, P)
return p
/proc/load_picture_from_disk(id)
var/pathstring = log_path_from_picture_ID(id)
if(!pathstring)
return
var/path = file(pathstring)
if(!fexists(path))
return
var/dir_index = findlasttext(pathstring, "/")
var/dir = copytext(pathstring, 1, dir_index)
var/json_path = file("[dir]/metadata.json")
if(!fexists(json_path))
return
var/list/json = json_decode(file2text(json_path))
if(!json[id])
return
var/datum/picture/P = new
P.deserialize_json(json[id])
return P
/proc/log_path_from_picture_ID(id)
if(!istext(id))
return
. = "data/picture_logs/"
var/list/data = splittext(id, "_")
if(data.len < 3)
return null
var/mode = data[1]
switch(mode)
if("L")
if(data.len < 5)
return null
var/timestamp = data[2]
var/year = copytext(timestamp, 1, 5)
var/month = copytext(timestamp, 5, 7)
var/day = copytext(timestamp, 7, 9)
var/round = data[4]
. += "[year]/[month]/[day]/round-[round]"
if("O")
var/list/path = data.Copy(2, data.len)
. += path.Join("")
else
return null
var/n = data[data.len]
. += "/[n].png"
//BE VERY CAREFUL WITH THIS PROC, TO AVOID DUPLICATION.
/datum/picture/proc/log_to_file()
if(!picture_image)
return
if(!CONFIG_GET(flag/log_pictures))
return
if(logpath)
return //we're already logged
var/number = GLOB.picture_logging_id++
var/finalpath = "[GLOB.picture_log_directory]/[number].png"
fcopy(icon(picture_image, dir = SOUTH, frame = 1), finalpath)
logpath = finalpath
id = "[GLOB.picture_logging_prefix][number]"
var/jsonpath = "[GLOB.picture_log_directory]/metadata.json"
jsonpath = file(jsonpath)
var/list/json
if(fexists(jsonpath))
json = json_decode(file2text(jsonpath))
fdel(jsonpath)
else
json = list()
json[id] = serialize_json()
WRITE_FILE(jsonpath, json_encode(json))
/datum/picture/proc/Copy(greyscale = FALSE, cropx = 0, cropy = 0)
var/datum/picture/P = new
P.picture_name = picture_name
P.picture_desc = picture_desc
if(picture_image)
P.picture_image = icon(picture_image) //Copy, not reference.
if(picture_icon)
P.picture_icon = icon(picture_icon)
P.psize_x = psize_x - cropx * 2
P.psize_y = psize_y - cropy * 2
P.has_blueprints = has_blueprints
if(greyscale)
if(picture_image)
P.picture_image.MapColors(rgb(77,77,77), rgb(150,150,150), rgb(28,28,28), rgb(0,0,0))
if(picture_icon)
P.picture_icon.MapColors(rgb(77,77,77), rgb(150,150,150), rgb(28,28,28), rgb(0,0,0))
if(cropx || cropy)
if(picture_image)
P.picture_image.Crop(cropx, cropy, psize_x - cropx, psize_y - cropy)
P.regenerate_small_icon()
return P

View File

@@ -0,0 +1,202 @@
#define CAMERA_PICTURE_SIZE_HARD_LIMIT 21
/obj/item/camera
name = "camera"
icon = 'icons/obj/items_and_weapons.dmi'
desc = "A polaroid camera. <span class='boldnotice'>Alt click to change its focusing, allowing you to set how big of an area it will capture!</span>"
icon_state = "camera"
item_state = "electropack"
lefthand_file = 'icons/mob/inhands/misc/devices_lefthand.dmi'
righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi'
w_class = WEIGHT_CLASS_SMALL
flags_1 = CONDUCT_1
slot_flags = ITEM_SLOT_BELT
materials = list(MAT_METAL = 50, MAT_GLASS = 150)
var/state_on = "camera"
var/state_off = "camera_off"
var/pictures_max = 10
var/pictures_left = 10
var/on = TRUE
var/cooldown = 64
var/blending = FALSE //lets not take pictures while the previous is still processing!
var/see_ghosts = CAMERA_NO_GHOSTS //for the spoop of it
var/obj/item/disk/holodisk/disk
var/sound/custom_sound
var/silent = FALSE
var/picture_size_x = 2
var/picture_size_y = 2
var/picture_size_x_min = 1
var/picture_size_y_min = 1
var/picture_size_x_max = 4
var/picture_size_y_max = 4
/obj/item/camera/attack_self(mob/user)
if(!disk)
return
to_chat(user, "<span class='notice'>You eject [disk] out the back of [src].</span>")
user.put_in_hands(disk)
disk = null
/obj/item/camera/AltClick(mob/user)
var/desired_x = input(user, "How high do you want the camera to shoot, between [picture_size_x_min] and [picture_size_x_max]?", "Zoom", picture_size_x) as num
var/desired_y = input(user, "How wide do you want the camera to shoot, between [picture_size_y_min] and [picture_size_y_max]?", "Zoom", picture_size_y) as num
desired_x = min(CLAMP(desired_x, picture_size_x_min, picture_size_x_max), CAMERA_PICTURE_SIZE_HARD_LIMIT)
desired_y = min(CLAMP(desired_y, picture_size_y_min, picture_size_y_max), CAMERA_PICTURE_SIZE_HARD_LIMIT)
if(user.canUseTopic(src))
picture_size_x = desired_x
picture_size_y = desired_y
/obj/item/camera/attack(mob/living/carbon/human/M, mob/user)
return
/obj/item/camera/attackby(obj/item/I, mob/user, params)
if(istype(I, /obj/item/camera_film))
if(pictures_left)
to_chat(user, "<span class='notice'>[src] still has some film in it!</span>")
return
if(!user.temporarilyRemoveItemFromInventory(I))
return
to_chat(user, "<span class='notice'>You insert [I] into [src].</span>")
qdel(I)
pictures_left = pictures_max
return
if(istype(I, /obj/item/disk/holodisk))
if (!disk)
if(!user.transferItemToLoc(I, src))
to_chat(user, "<span class='warning'>[I] is stuck to your hand!</span>")
return TRUE
to_chat(user, "<span class='notice'>You slide [I] into the back of [src].</span>")
disk = I
else
to_chat(user, "<span class='warning'>There's already a disk inside [src].</span>")
return TRUE //no afterattack
..()
/obj/item/camera/examine(mob/user)
..()
to_chat(user, "It has [pictures_left] photos left.")
//user can be atom or mob
/obj/item/camera/proc/can_target(atom/target, mob/user, prox_flag)
if(!on || blending || !pictures_left)
return FALSE
var/turf/T = get_turf(target)
if(!T)
return FALSE
if(istype(user))
if(isAI(user) && !GLOB.cameranet.checkTurfVis(T))
return FALSE
else if(user.client && !(get_turf(target) in get_hear(user.client.view, user)))
return FALSE
else if(!(get_turf(target) in get_hear(world.view, user)))
return FALSE
else //user is an atom
if(!(get_turf(target) in view(world.view, user)))
return FALSE
return TRUE
/obj/item/camera/afterattack(atom/target, mob/user, flag)
if (disk)
if(ismob(target))
if (disk.record)
QDEL_NULL(disk.record)
disk.record = new
var/mob/M = target
disk.record.caller_name = M.name
disk.record.set_caller_image(M)
else
to_chat(user, "<span class='warning'>Invalid holodisk target.</span>")
return
if(!can_target(target, user, flag))
return
on = FALSE
addtimer(CALLBACK(src, .proc/cooldown), cooldown)
icon_state = state_off
INVOKE_ASYNC(src, .proc/captureimage, target, user, flag, picture_size_x - 1, picture_size_y - 1)
/obj/item/camera/proc/cooldown()
UNTIL(!blending)
icon_state = state_on
on = TRUE
/obj/item/camera/proc/show_picture(mob/user, datum/picture/selection)
var/obj/item/photo/P = new(src, selection)
P.show(user)
to_chat(user, P.desc)
qdel(P)
/obj/item/camera/proc/captureimage(atom/target, mob/user, flag, size_x = 1, size_y = 1)
blending = TRUE
var/turf/target_turf = get_turf(target)
if(!isturf(target_turf))
blending = FALSE
return FALSE
size_x = CLAMP(size_x, 0, CAMERA_PICTURE_SIZE_HARD_LIMIT)
size_y = CLAMP(size_y, 0, CAMERA_PICTURE_SIZE_HARD_LIMIT)
var/list/desc = list("This is a photo of an area of [size_x+1] meters by [size_y+1] meters.")
var/ai_user = isAI(user)
var/list/seen
var/list/viewlist = (user && user.client)? getviewsize(user.client.view) : getviewsize(world.view)
var/viewr = max(viewlist[1], viewlist[2]) + max(size_x, size_y)
var/viewc = user.client? user.client.eye : target
seen = get_hear(viewr, viewc)
var/list/turfs = list()
var/list/mobs = list()
var/blueprints = FALSE
var/clone_area = SSmapping.RequestBlockReservation(size_x * 2 + 1, size_y * 2 + 1)
for(var/turf/T in block(locate(target_turf.x - size_x, target_turf.y - size_y, target_turf.z), locate(target_turf.x + size_x, target_turf.y + size_y, target_turf.z)))
if((ai_user && GLOB.cameranet.checkTurfVis(T)) || T in seen)
turfs += T
for(var/mob/M in T)
mobs += M
if(locate(/obj/item/areaeditor/blueprints) in T)
blueprints = TRUE
for(var/i in mobs)
var/mob/M = i
desc += M.get_photo_description(src)
var/psize_x = (size_x * 2 + 1) * world.icon_size
var/psize_y = (size_y * 2 + 1) * world.icon_size
var/get_icon = camera_get_icon(turfs, target_turf, psize_x, psize_y, clone_area, size_x, size_y, (size_x * 2 + 1), (size_y * 2 + 1))
qdel(clone_area)
var/icon/temp = icon('icons/effects/96x96.dmi',"")
temp.Blend("#000", ICON_OVERLAY)
temp.Scale(psize_x, psize_y)
temp.Blend(get_icon, ICON_OVERLAY)
var/datum/picture/P = new("picture", desc.Join(" "), temp, null, psize_x, psize_y, blueprints)
after_picture(user, P, flag)
blending = FALSE
/obj/item/camera/proc/after_picture(mob/user, datum/picture/picture, proximity_flag)
printpicture(user, picture)
/obj/item/camera/proc/printpicture(mob/user, datum/picture/picture) //Normal camera proc for creating photos
var/obj/item/photo/p = new(get_turf(src), picture)
if(in_range(src, user)) //needed because of TK
user.put_in_hands(p)
pictures_left--
to_chat(user, "<span class='notice'>[pictures_left] photos left.</span>")
var/customize = alert(user, "Do you want to customize the photo?", "Customization", "Yes", "No")
if(customize == "Yes")
var/name1 = input(user, "Set a name for this photo, or leave blank. 32 characters max.", "Name") as text|null
var/desc1 = input(user, "Set a description to add to photo, or leave blank. 128 characters max.", "Caption") as text|null
var/caption = input(user, "Set a caption for this photo, or leave blank. 256 characters max.", "Caption") as text|null
if(name1)
name1 = copytext(name1, 1, 33)
picture.picture_name = name1
if(desc1)
desc1 = copytext(desc1, 1, 129)
picture.picture_desc = "[desc1] - [picture.picture_desc]"
if(caption)
caption = copytext(caption, 1, 257)
picture.caption = caption
p.set_picture(picture, TRUE, TRUE)
if(CONFIG_GET(flag/picture_logging_camera))
picture.log_to_file()

View File

@@ -0,0 +1,88 @@
/obj/effect/appearance_clone
/obj/effect/appearance_clone/New(loc, atom/A) //Intentionally not Initialize(), to make sure the clone assumes the intended appearance in time for the camera getFlatIcon.
if(istype(A))
appearance = A.appearance
dir = A.dir
if(ismovableatom(A))
var/atom/movable/AM = A
step_x = AM.step_x
step_y = AM.step_y
. = ..()
/obj/item/camera/proc/camera_get_icon(list/turfs, turf/center, psize_x = 96, psize_y = 96, datum/turf_reservation/clone_area, size_x, size_y, total_x, total_y)
var/list/atoms = list()
var/skip_normal = FALSE
var/wipe_atoms = FALSE
if(istype(clone_area) && total_x == clone_area.width && total_y == clone_area.height && size_x >= 0 && size_y > 0)
var/cloned_center_x = round(clone_area.bottom_left_coords[1] + ((total_x - 1) / 2))
var/cloned_center_y = round(clone_area.bottom_left_coords[2] + ((total_y - 1) / 2))
for(var/t in turfs)
var/turf/T = t
var/offset_x = T.x - center.x
var/offset_y = T.y - center.y
var/turf/newT = locate(cloned_center_x + offset_x, cloned_center_y + offset_y, clone_area.bottom_left_coords[3])
if(!(newT in clone_area.reserved_turfs)) //sanity check so we don't overwrite other areas somehow
continue
atoms += new /obj/effect/appearance_clone(newT, T)
if(T.loc.icon_state)
atoms += new /obj/effect/appearance_clone(newT, T.loc)
for(var/i in T.contents)
var/atom/A = i
if(!A.invisibility || (see_ghosts && isobserver(A)))
atoms += new /obj/effect/appearance_clone(newT, A)
skip_normal = TRUE
wipe_atoms = TRUE
center = locate(cloned_center_x, cloned_center_y, clone_area.bottom_left_coords[3])
if(!skip_normal)
for(var/i in turfs)
var/turf/T = i
atoms += T
for(var/atom/movable/A in T)
if(A.invisibility)
if(!(see_ghosts && isobserver(A)))
continue
atoms += A
CHECK_TICK
var/icon/res = icon('icons/effects/96x96.dmi', "")
res.Scale(psize_x, psize_y)
var/list/sorted = list()
var/j
for(var/i in 1 to atoms.len)
var/atom/c = atoms[i]
for(j = sorted.len, j > 0, --j)
var/atom/c2 = sorted[j]
if(c2.layer <= c.layer)
break
sorted.Insert(j+1, c)
CHECK_TICK
var/xcomp = FLOOR(psize_x / 2, 1) - 15
var/ycomp = FLOOR(psize_y / 2, 1) - 15
for(var/atom/A in sorted)
var/xo = (A.x - center.x) * world.icon_size + A.pixel_x + xcomp
var/yo = (A.y - center.y) * world.icon_size + A.pixel_y + ycomp
if(ismovableatom(A))
var/atom/movable/AM = A
xo += AM.step_x
yo += AM.step_y
var/icon/img = getFlatIcon(A)
res.Blend(img, blendMode2iconMode(A.blend_mode), xo, yo)
CHECK_TICK
if(!silent)
if(istype(custom_sound)) //This is where the camera actually finishes its exposure.
playsound(loc, custom_sound, 75, 1, -3)
else
playsound(loc, pick('sound/items/polaroid1.ogg', 'sound/items/polaroid2.ogg'), 75, 1, -3)
if(wipe_atoms)
QDEL_LIST(atoms)
return res

View File

@@ -0,0 +1,14 @@
/*
* Film
*/
/obj/item/camera_film
name = "film cartridge"
icon = 'icons/obj/items_and_weapons.dmi'
desc = "A camera film cartridge. Insert it into a camera to reload it."
icon_state = "film"
item_state = "electropack"
lefthand_file = 'icons/mob/inhands/misc/devices_lefthand.dmi'
righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi'
w_class = WEIGHT_CLASS_TINY
resistance_flags = FLAMMABLE
materials = list(MAT_METAL = 10, MAT_GLASS = 10)

View File

@@ -0,0 +1,14 @@
/obj/item/camera/spooky
name = "camera obscura"
desc = "A polaroid camera, some say it can see ghosts!"
see_ghosts = CAMERA_SEE_GHOSTS_BASIC
/obj/item/camera/spooky/badmin
desc = "A polaroid camera, some say it can see ghosts! It seems to have an extra magnifier on the end."
see_ghosts = CAMERA_SEE_GHOSTS_ORBIT
/obj/item/camera/detective
name = "Detective's camera"
desc = "A polaroid camera with extra capacity for crime investigations."
pictures_max = 30
pictures_left = 30

View File

@@ -0,0 +1,98 @@
/obj/item/camera/siliconcam
name = "silicon photo camera"
var/in_camera_mode = FALSE
var/list/datum/picture/stored = list()
/obj/item/camera/siliconcam/ai_camera
name = "AI photo camera"
/obj/item/camera/siliconcam/proc/toggle_camera_mode(mob/user)
if(in_camera_mode)
camera_mode_off(user)
else
camera_mode_on(user)
/obj/item/camera/siliconcam/proc/camera_mode_off(mob/user)
in_camera_mode = FALSE
to_chat(user, "<B>Camera Mode deactivated</B>")
/obj/item/camera/siliconcam/proc/camera_mode_on(mob/user)
in_camera_mode = TRUE
to_chat(user, "<B>Camera Mode activated</B>")
/obj/item/camera/siliconcam/proc/selectpicture(mob/user)
var/list/nametemp = list()
var/find
if(!stored.len)
to_chat(usr, "<span class='boldannounce'>No images saved</span>")
return
var/list/temp = list()
for(var/i in stored)
var/datum/picture/p = i
nametemp += p.picture_name
temp[p.picture_name] = p
find = input(user, "Select image") in nametemp|null
if(!find)
return
return temp[find]
/obj/item/camera/siliconcam/proc/viewpictures(mob/user)
var/datum/picture/selection = selectpicture(user)
if(istype(selection))
show_picture(user, selection)
/obj/item/camera/siliconcam/ai_camera/after_picture(mob/user, datum/picture/picture, proximity_flag)
var/number = stored.len
picture.picture_name = "Image [number] (taken by [loc.name])"
stored[picture] = TRUE
to_chat(usr, "<span class='unconscious'>Image recorded</span>")
/obj/item/camera/siliconcam/robot_camera
name = "Cyborg photo camera"
var/printcost = 2
/obj/item/camera/siliconcam/robot_camera/after_picture(mob/user, datum/picture/picture, proximity_flag)
var/mob/living/silicon/robot/C = loc
if(istype(C) && istype(C.connected_ai))
var/number = C.connected_ai.aicamera.stored.len
picture.picture_name = "Image [number] (taken by [loc.name])"
C.connected_ai.aicamera.stored[picture] = TRUE
to_chat(usr, "<span class='unconscious'>Image recorded and saved to remote database</span>")
else
var/number = stored.len
picture.picture_name = "Image [number] (taken by [loc.name])"
stored[picture] = TRUE
to_chat(usr, "<span class='unconscious'>Image recorded and saved to local storage. Upload will happen automatically if unit is lawsynced.</span>")
/obj/item/camera/siliconcam/robot_camera/selectpicture(mob/user)
var/mob/living/silicon/robot/R = loc
if(istype(R) && R.connected_ai)
R.picturesync()
return R.connected_ai.aicamera.selectpicture(user)
else
return ..()
/obj/item/camera/siliconcam/robot_camera/verb/borgprinting()
set category ="Robot Commands"
set name = "Print Image"
set src in usr
if(usr.stat == DEAD)
return
borgprint(usr)
/obj/item/camera/siliconcam/robot_camera/proc/borgprint(mob/user)
var/mob/living/silicon/robot/C = loc
if(!istype(C) || C.toner < 20)
to_chat(user, "<span class='warning'>Insufficent toner to print image.</span>")
return
var/datum/picture/selection = selectpicture(user)
if(!istype(selection))
to_chat(user, "<span class='warning'>Invalid Image.</span>")
return
var/obj/item/photo/p = new /obj/item/photo(C.loc, selection)
p.pixel_x = rand(-10, 10)
p.pixel_y = rand(-10, 10)
C.toner -= printcost //All fun allowed.
visible_message("[C.name] spits out a photograph from a narrow slot on its chassis.")
to_chat(usr, "<span class='notice'>You print a photograph.</span>")

View File

@@ -0,0 +1,75 @@
/*
* Photo album
*/
/obj/item/storage/photo_album
name = "photo album"
icon = 'icons/obj/items_and_weapons.dmi'
icon_state = "album"
item_state = "briefcase"
lefthand_file = 'icons/mob/inhands/equipment/briefcase_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/briefcase_righthand.dmi'
resistance_flags = FLAMMABLE
var/persistence_id
/obj/item/storage/photo_album/Initialize()
. = ..()
GET_COMPONENT(STR, /datum/component/storage)
STR.can_hold = typecacheof(list(/obj/item/photo))
STR.max_combined_w_class = 42
STR.max_items = 21
LAZYADD(SSpersistence.photo_albums, src)
/obj/item/storage/photo_album/Destroy()
LAZYREMOVE(SSpersistence.photo_albums, src)
return ..()
/obj/item/storage/photo_album/proc/get_picture_id_list()
var/list/L = list()
for(var/i in contents)
if(istype(i, /obj/item/photo))
L += i
if(!L.len)
return
. = list()
for(var/i in L)
var/obj/item/photo/P = i
if(!istype(P.picture))
continue
. |= P.picture.id
//Manual loading, DO NOT USE FOR HARDCODED/MAPPED IN ALBUMS. This is for if an album needs to be loaded mid-round from an ID.
/obj/item/storage/photo_album/proc/persistence_load()
var/list/data = SSpersistence.GetPhotoAlbums()
if(data[persistence_id])
populate_from_id_list(data[persistence_id])
/obj/item/storage/photo_album/proc/populate_from_id_list(list/ids)
var/list/current_ids = get_picture_id_list()
for(var/i in ids)
if(i in current_ids)
continue
var/obj/item/photo/P = load_photo_from_disk(i)
if(istype(P))
if(!SEND_SIGNAL(src, COMSIG_TRY_STORAGE_INSERT, P, null, TRUE, TRUE))
qdel(P)
/obj/item/storage/photo_album/HoS
persistence_id = "HoS"
/obj/item/storage/photo_album/RD
persistence_id = "RD"
/obj/item/storage/photo_album/HoP
persistence_id = "HoP"
/obj/item/storage/photo_album/Captain
persistence_id = "Captain"
/obj/item/storage/photo_album/CMO
persistence_id = "CMO"
/obj/item/storage/photo_album/QM
persistence_id = "QM"
/obj/item/storage/photo_album/CE
persistence_id = "CE"

View File

@@ -0,0 +1,163 @@
// Picture frames
/obj/item/wallframe/picture
name = "picture frame"
desc = "The perfect showcase for your favorite deathtrap memories."
icon = 'icons/obj/decals.dmi'
materials = list()
flags_1 = 0
icon_state = "frame-empty"
result_path = /obj/structure/sign/picture_frame
var/obj/item/photo/displayed
/obj/item/wallframe/picture/attackby(obj/item/I, mob/user)
if(istype(I, /obj/item/photo))
if(!displayed)
if(!user.transferItemToLoc(I, src))
return
displayed = I
update_icon()
else
to_chat(user, "<span class=notice>\The [src] already contains a photo.</span>")
..()
//ATTACK HAND IGNORING PARENT RETURN VALUE
/obj/item/wallframe/picture/attack_hand(mob/user)
if(user.get_inactive_held_item() != src)
..()
return
if(contents.len)
var/obj/item/I = pick(contents)
user.put_in_hands(I)
to_chat(user, "<span class='notice'>You carefully remove the photo from \the [src].</span>")
displayed = null
update_icon()
return ..()
/obj/item/wallframe/picture/attack_self(mob/user)
user.examinate(src)
/obj/item/wallframe/picture/examine(mob/user)
if(user.is_holding(src) && displayed)
displayed.show(user)
else
..()
/obj/item/wallframe/picture/update_icon()
cut_overlays()
if(displayed)
add_overlay(getFlatIcon(displayed))
/obj/item/wallframe/picture/after_attach(obj/O)
..()
var/obj/structure/sign/picture_frame/PF = O
PF.copy_overlays(src)
if(displayed)
PF.framed = displayed
if(contents.len)
var/obj/item/I = pick(contents)
I.forceMove(PF)
/obj/structure/sign/picture_frame
name = "picture frame"
desc = "Every time you look it makes you laugh."
icon = 'icons/obj/decals.dmi'
icon_state = "frame-empty"
var/obj/item/photo/framed
var/persistence_id
var/can_decon = TRUE
#define FRAME_DEFINE(id) /obj/structure/sign/picture_frame/##id/persistence_id = #id
//Put default persistent frame defines here!
#undef FRAME_DEFINE
/obj/structure/sign/picture_frame/Initialize(mapload, dir, building)
. = ..()
LAZYADD(SSpersistence.photo_frames, src)
if(dir)
setDir(dir)
if(building)
pixel_x = (dir & 3)? 0 : (dir == 4 ? -30 : 30)
pixel_y = (dir & 3)? (dir ==1 ? -30 : 30) : 0
/obj/structure/sign/picture_frame/Destroy()
LAZYREMOVE(SSpersistence.photo_frames, src)
return ..()
/obj/structure/sign/picture_frame/proc/get_photo_id()
if(istype(framed) && istype(framed.picture))
return framed.picture.id
//Manual loading, DO NOT USE FOR HARDCODED/MAPPED IN ALBUMS. This is for if an album needs to be loaded mid-round from an ID.
/obj/structure/sign/picture_frame/proc/persistence_load()
var/list/data = SSpersistence.GetPhotoFrames()
if(data[persistence_id])
load_from_id(data[persistence_id])
/obj/structure/sign/picture_frame/proc/load_from_id(id)
var/obj/item/photo/P = load_photo_from_disk(id)
if(istype(P))
if(istype(framed))
framed.forceMove(drop_location())
else
qdel(framed)
framed = P
update_icon()
/obj/structure/sign/picture_frame/examine(mob/user)
if(in_range(src, user) && framed)
framed.show(user)
else
..()
/obj/structure/sign/picture_frame/attackby(obj/item/I, mob/user, params)
if(can_decon && (istype(I, /obj/item/screwdriver) || istype(I, /obj/item/wrench)))
to_chat(user, "<span class='notice'>You start unsecuring [name]...</span>")
if(I.use_tool(src, user, 30, volume=50))
playsound(loc, 'sound/items/deconstruct.ogg', 50, 1)
to_chat(user, "<span class='notice'>You unsecure [name].</span>")
deconstruct()
else if(istype(I, /obj/item/wirecutters) && framed)
framed.forceMove(drop_location())
framed = null
user.visible_message("<span class='warning'>[user] cuts away [framed] from [src]!</span>")
return
else if(istype(I, /obj/item/photo))
if(!framed)
var/obj/item/photo/P = I
if(!user.transferItemToLoc(P, src))
return
framed = P
update_icon()
else
to_chat(user, "<span class=notice>\The [src] already contains a photo.</span>")
..()
/obj/structure/sign/picture_frame/attack_hand(mob/user)
. = ..()
if(.)
return
if(framed)
framed.show(user)
/obj/structure/sign/picture_frame/update_icon()
cut_overlays()
if(framed)
add_overlay(getFlatIcon(framed))
/obj/structure/sign/picture_frame/deconstruct(disassembled = TRUE)
if(!(flags_1 & NODECONSTRUCT_1))
var/obj/item/wallframe/picture/F = new /obj/item/wallframe/picture(loc)
if(framed)
F.displayed = framed
framed = null
if(contents.len)
var/obj/item/I = pick(contents)
I.forceMove(F)
F.update_icon()
qdel(src)

View File

@@ -0,0 +1,93 @@
/*
* Photo
*/
/obj/item/photo
name = "photo"
icon = 'icons/obj/items_and_weapons.dmi'
icon_state = "photo"
item_state = "paper"
w_class = WEIGHT_CLASS_TINY
resistance_flags = FLAMMABLE
max_integrity = 50
grind_results = list("iodine" = 4)
var/datum/picture/picture
var/scribble //Scribble on the back.
/obj/item/photo/Initialize(mapload, datum/picture/P, datum_name = TRUE, datum_desc = TRUE)
set_picture(P, datum_name, datum_desc, TRUE)
return ..()
/obj/item/photo/proc/set_picture(datum/picture/P, setname, setdesc, name_override = FALSE)
if(!istype(P))
return
picture = P
update_icon()
if(P.caption)
scribble = P.caption
if(setname && P.picture_name)
if(name_override)
name = P.picture_name
else
name = "photo - [P.picture_name]"
if(setdesc && P.picture_desc)
desc = P.picture_desc
/obj/item/photo/update_icon()
if(!istype(picture) || !picture.picture_image)
return
var/icon/I = picture.get_small_icon()
if(I)
icon = I
/obj/item/photo/suicide_act(mob/living/carbon/user)
user.visible_message("<span class='suicide'>[user] is taking one last look at \the [src]! It looks like [user.p_theyre()] giving in to death!</span>")//when you wanna look at photo of waifu one last time before you die...
if (user.gender == MALE)
playsound(user, 'sound/voice/human/manlaugh1.ogg', 50, 1)//EVERY TIME I DO IT MAKES ME LAUGH
else if (user.gender == FEMALE)
playsound(user, 'sound/voice/human/womanlaugh.ogg', 50, 1)
return OXYLOSS
/obj/item/photo/attack_self(mob/user)
user.examinate(src)
/obj/item/photo/attackby(obj/item/P, mob/user, params)
if(istype(P, /obj/item/pen) || istype(P, /obj/item/toy/crayon))
if(!user.is_literate())
to_chat(user, "<span class='notice'>You scribble illegibly on [src]!</span>")
return
var/txt = sanitize(input(user, "What would you like to write on the back?", "Photo Writing", null) as text)
txt = copytext(txt, 1, 128)
if(user.canUseTopic(src, BE_CLOSE))
scribble = txt
..()
/obj/item/photo/examine(mob/user)
..()
if(in_range(src, user))
show(user)
else
to_chat(user, "<span class='warning'>You need to get closer to get a good look at this photo!</span>")
/obj/item/photo/proc/show(mob/user)
if(!istype(picture) || !picture.picture_image)
to_chat(user, "<span class='warning'>[src] seems to be blank...</span>")
return
user << browse_rsc(picture.picture_image, "tmp_photo.png")
user << browse("<html><head><title>[name]</title></head>" \
+ "<body style='overflow:hidden;margin:0;text-align:center'>" \
+ "<img src='tmp_photo.png' width='480' style='-ms-interpolation-mode:nearest-neighbor' />" \
+ "[scribble ? "<br>Written on the back:<br><i>[scribble]</i>" : ""]"\
+ "</body></html>", "window=photo_showing;size=480x608")
onclose(user, "[name]")
/obj/item/photo/verb/rename()
set name = "Rename photo"
set category = "Object"
set src in usr
var/n_name = copytext(sanitize(input(usr, "What would you like to label the photo?", "Photo Labelling", null) as text), 1, MAX_NAME_LEN)
//loc.loc check is for making possible renaming photos in clipboards
if((loc == usr || loc.loc && loc.loc == usr) && usr.stat == CONSCIOUS && usr.canmove && !usr.restrained())
name = "photo[(n_name ? text("- '[n_name]'") : null)]"
add_fingerprint(usr)

View File

@@ -131,6 +131,12 @@ LOG_MANIFEST
## As an example of how this could be """useful""" look towards Poly (https://twitter.com/Poly_the_Parrot)
# LOG_TWITTER
## Enable logging pictures
# LOG_PICTURES
##Log camera pictures - Must have picture logging enabled
PICTURE_LOGGING_CAMERA
## period of time in seconds for players to be considered inactive
# INACTIVITY_PERIOD 300

View File

@@ -116,6 +116,7 @@
#include "code\__HELPERS\names.dm"
#include "code\__HELPERS\priority_announce.dm"
#include "code\__HELPERS\pronouns.dm"
#include "code\__HELPERS\qdel.dm"
#include "code\__HELPERS\radiation.dm"
#include "code\__HELPERS\radio.dm"
#include "code\__HELPERS\roundend.dm"
@@ -2163,8 +2164,16 @@
#include "code\modules\paperwork\paperplane.dm"
#include "code\modules\paperwork\pen.dm"
#include "code\modules\paperwork\photocopier.dm"
#include "code\modules\paperwork\photography.dm"
#include "code\modules\paperwork\stamps.dm"
#include "code\modules\photography\_pictures.dm"
#include "code\modules\photography\camera\camera.dm"
#include "code\modules\photography\camera\camera_image_capturing.dm"
#include "code\modules\photography\camera\film.dm"
#include "code\modules\photography\camera\other.dm"
#include "code\modules\photography\camera\silicon_camera.dm"
#include "code\modules\photography\photos\album.dm"
#include "code\modules\photography\photos\frame.dm"
#include "code\modules\photography\photos\photo.dm"
#include "code\modules\power\apc.dm"
#include "code\modules\power\cable.dm"
#include "code\modules\power\cell.dm"