diff --git a/baystation12.dme b/baystation12.dme index a820faad30..98fc485a45 100644 --- a/baystation12.dme +++ b/baystation12.dme @@ -1037,6 +1037,7 @@ #include "code\modules\mob\living\silicon\robot\laws.dm" #include "code\modules\mob\living\silicon\robot\life.dm" #include "code\modules\mob\living\silicon\robot\login.dm" +#include "code\modules\mob\living\silicon\robot\photos.dm" #include "code\modules\mob\living\silicon\robot\robot.dm" #include "code\modules\mob\living\silicon\robot\robot_damage.dm" #include "code\modules\mob\living\silicon\robot\robot_items.dm" @@ -1114,6 +1115,7 @@ #include "code\modules\paperwork\pen.dm" #include "code\modules\paperwork\photocopier.dm" #include "code\modules\paperwork\photography.dm" +#include "code\modules\paperwork\silicon_photography.dm" #include "code\modules\paperwork\stamps.dm" #include "code\modules\power\apc.dm" #include "code\modules\power\batteryrack.dm" diff --git a/code/_onclick/ai.dm b/code/_onclick/ai.dm index 204f39e69c..c6078c10ac 100644 --- a/code/_onclick/ai.dm +++ b/code/_onclick/ai.dm @@ -53,6 +53,11 @@ return next_move = world.time + 9 + if(aiCamera.in_camera_mode) + aiCamera.camera_mode_off() + aiCamera.captureimage(A, usr) + return + /* AI restrained() currently does nothing if(restrained()) diff --git a/code/_onclick/cyborg.dm b/code/_onclick/cyborg.dm index d86faacbd1..04682c76ad 100644 --- a/code/_onclick/cyborg.dm +++ b/code/_onclick/cyborg.dm @@ -37,6 +37,14 @@ face_atom(A) // change direction to face what you clicked on + if(aiCamera.in_camera_mode) + aiCamera.camera_mode_off() + if(is_component_functioning("camera")) + aiCamera.captureimage(A, usr) + else + src << "Your camera isn't functional." + return + /* cyborg restrained() currently does nothing if(restrained()) diff --git a/code/game/machinery/newscaster.dm b/code/game/machinery/newscaster.dm index b1783b1707..fba4927947 100644 --- a/code/game/machinery/newscaster.dm +++ b/code/game/machinery/newscaster.dm @@ -748,16 +748,27 @@ var/list/obj/machinery/newscaster/allCasters = list() //Global list that will co /obj/machinery/newscaster/proc/AttachPhoto(mob/user as mob) if(photo) - photo.loc = src.loc - user.put_in_inactive_hand(photo) + if(!issilicon(user)) + photo.loc = src.loc + user.put_in_inactive_hand(photo) photo = null if(istype(user.get_active_hand(), /obj/item/weapon/photo)) photo = user.get_active_hand() user.drop_item() photo.loc = src + else if(istype(user,/mob/living/silicon)) + var/mob/living/silicon/tempAI = user + var/obj/item/device/camera/siliconcam/camera = tempAI.aiCamera + if(!camera) + return + var/datum/picture/selection = camera.selectpicture() + if (!selection) + return - + var/obj/item/weapon/photo/P = new/obj/item/weapon/photo() + P.construct(selection) + photo = P //######################################################################################################################## diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm index a2b375d3be..0fe0ddc93d 100644 --- a/code/modules/mob/living/silicon/ai/ai.dm +++ b/code/modules/mob/living/silicon/ai/ai.dm @@ -91,6 +91,7 @@ var/list/ai_list = list() aiMulti = new(src) aiRadio = new(src) aiRadio.myAi = src + aiCamera = new/obj/item/device/camera/siliconcam/ai_camera(src) if (istype(loc, /turf)) verbs.Add(/mob/living/silicon/ai/proc/ai_call_shuttle,/mob/living/silicon/ai/proc/ai_camera_track, \ @@ -756,4 +757,4 @@ var/list/ai_list = list() src << "Accessing Subspace Transceiver control..." if (src.aiRadio) - src.aiRadio.interact(src) + src.aiRadio.interact(src) diff --git a/code/modules/mob/living/silicon/robot/drone/drone.dm b/code/modules/mob/living/silicon/robot/drone/drone.dm index 0ab4e42028..cd0cbc9e2c 100644 --- a/code/modules/mob/living/silicon/robot/drone/drone.dm +++ b/code/modules/mob/living/silicon/robot/drone/drone.dm @@ -61,6 +61,10 @@ flavor_text = "It's a tiny little repair drone. The casing is stamped with an NT logo and the subscript: 'NanoTrasen Recursive Repair Systems: Fixing Tomorrow's Problem, Today!'" updateicon() +/mob/living/silicon/robot/drone/init() + new/obj/item/device/camera/siliconcam/drone_camera(src) + playsound(src.loc, 'sound/machines/twobeep.ogg', 50, 0) + //Redefining some robot procs... /mob/living/silicon/robot/drone/updatename() real_name = "maintenance drone ([rand(100,999)])" diff --git a/code/modules/mob/living/silicon/robot/laws.dm b/code/modules/mob/living/silicon/robot/laws.dm index bff875a0d6..2d6a12e966 100644 --- a/code/modules/mob/living/silicon/robot/laws.dm +++ b/code/modules/mob/living/silicon/robot/laws.dm @@ -18,6 +18,7 @@ else lawsync() + photosync() src << "Laws synced with AI, be sure to note any changes." if(mind && mind.special_role == "traitor" && mind.original == src) src << "Remember, your AI does NOT share or know about your law 0." diff --git a/code/modules/mob/living/silicon/robot/photos.dm b/code/modules/mob/living/silicon/robot/photos.dm new file mode 100644 index 0000000000..fbcc3bf484 --- /dev/null +++ b/code/modules/mob/living/silicon/robot/photos.dm @@ -0,0 +1,13 @@ +/mob/living/silicon/robot/proc/photosync() + var/obj/item/device/camera/siliconcam/master_cam = connected_ai ? connected_ai.aiCamera : null + if (!master_cam) + return + + var/synced + synced = 0 + for(var/datum/picture/z in aiCamera.aipictures) + if (!(master_cam.aipictures.Find(z))) + aiCamera.printpicture(null, z) + synced = 1 + if(synced) + src << "Locally saved images synced with AI. Images were retained in local database in case of loss of connection with the AI." diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm index 273df70dd9..4daa0f5cf8 100644 --- a/code/modules/mob/living/silicon/robot/robot.dm +++ b/code/modules/mob/living/silicon/robot/robot.dm @@ -97,6 +97,7 @@ if(connected_ai) connected_ai.connected_robots += src lawsync() + photosync() lawupdate = 1 else lawupdate = 0 @@ -137,12 +138,11 @@ hud_list[IMPCHEM_HUD] = image('icons/mob/hud.dmi', src, "hudblank") hud_list[IMPTRACK_HUD] = image('icons/mob/hud.dmi', src, "hudblank") hud_list[SPECIALROLE_HUD] = image('icons/mob/hud.dmi', src, "hudblank") + init() - - if(istype(src,/mob/living/silicon/robot/drone)) - playsound(src.loc, 'sound/machines/twobeep.ogg', 50, 0) - else - playsound(loc, 'sound/voice/liveagain.ogg', 75, 1) +/mob/living/silicon/robot/proc/init() + new/obj/item/device/camera/siliconcam/robot_camera(src) + playsound(loc, 'sound/voice/liveagain.ogg', 75, 1) // setup the PDA and its name /mob/living/silicon/robot/proc/setup_PDA() diff --git a/code/modules/mob/living/silicon/robot/wires.dm b/code/modules/mob/living/silicon/robot/wires.dm index 10b6832fe4..9ed4320c84 100644 --- a/code/modules/mob/living/silicon/robot/wires.dm +++ b/code/modules/mob/living/silicon/robot/wires.dm @@ -74,6 +74,7 @@ if(BORG_WIRE_LAWCHECK) //Forces a law update if the borg is set to receive them. Since an update would happen when the borg checks its laws anyway, not much use, but eh if (src.lawupdate) src.lawsync() + src.photosync() if (BORG_WIRE_AI_CONTROL) //pulse the AI wire to make the borg reselect an AI if(!src.emagged) diff --git a/code/modules/mob/living/silicon/silicon.dm b/code/modules/mob/living/silicon/silicon.dm index 99adb65ed0..d730bd9b5f 100644 --- a/code/modules/mob/living/silicon/silicon.dm +++ b/code/modules/mob/living/silicon/silicon.dm @@ -13,6 +13,8 @@ var/speak_exclamation = "declares" var/speak_query = "queries" + var/obj/item/device/camera/siliconcam/aiCamera = null //photography + /mob/living/silicon/proc/show_laws() return diff --git a/code/modules/paperwork/photocopier.dm b/code/modules/paperwork/photocopier.dm index 7f006325e6..6963fe2f75 100644 --- a/code/modules/paperwork/photocopier.dm +++ b/code/modules/paperwork/photocopier.dm @@ -34,6 +34,8 @@ dat += "+

" else if(toner) dat += "Please insert paper to copy.

" + if(istype(user,/mob/living/silicon)) + dat += "Print photo from database

" dat += "Current toner level: [toner]" if(!toner) dat +="
Please insert a new toner cartridge!" @@ -112,6 +114,27 @@ if(copies < maxcopies) copies++ updateUsrDialog() + else if(href_list["aipic"]) + if(!istype(usr,/mob/living/silicon)) return + if(toner >= 5) + var/mob/living/silicon/tempAI = usr + var/obj/item/device/camera/siliconcam/camera = tempAI.aiCamera + + if(!camera) + return + var/datum/picture/selection = camera.selectpicture() + if (!selection) + return + + var/obj/item/weapon/photo/p = new /obj/item/weapon/photo (src.loc) + p.construct(selection) + if (p.desc == "") + p.desc += "Copied by [tempAI.name]" + else + p.desc += " - Copied by [tempAI.name]" + toner -= 5 + sleep(15) + updateUsrDialog() attackby(obj/item/O as obj, mob/user as mob) if(istype(O, /obj/item/weapon/paper)) diff --git a/code/modules/paperwork/photography.dm b/code/modules/paperwork/photography.dm index cbc119eaf5..fe2326c21b 100644 --- a/code/modules/paperwork/photography.dm +++ b/code/modules/paperwork/photography.dm @@ -219,10 +219,33 @@ mob_detail += "You can also see [A] on the photo[A:health < 75 ? " - [A] looks hurt":""].[holding ? " [holding]":"."]." return mob_detail - /obj/item/device/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) + playsound(loc, pick('sound/items/polaroid1.ogg', 'sound/items/polaroid2.ogg'), 75, 1, -3) + + pictures_left-- + desc = "A polaroid camera. It has [pictures_left] photos left." + user << "[pictures_left] photos left." + icon_state = icon_off + on = 0 + spawn(64) + icon_state = icon_on + on = 1 + +/obj/item/device/camera/proc/can_capture_turf(turf/T, mob/user) + var/mob/dummy = new(T) //Go go visibility check dummy + var/viewer = user + if(user.client) //To make shooting through security cameras possible + viewer = user.client.eye + var/can_see = (dummy in viewers(world.view, viewer)) != null + + dummy.loc = null + dummy = null //Alas, nameless creature //garbage collect it instead + return can_see + +/obj/item/device/camera/proc/captureimage(atom/target, mob/user, flag) var/x_c = target.x - 1 var/y_c = target.y + 1 var/z_c = target.z @@ -233,25 +256,19 @@ for(var/i = 1; i <= 3; i++) for(var/j = 1; j <= 3; j++) var/turf/T = locate(x_c, y_c, z_c) - var/mob/dummy = new(T) //Go go visibility check dummy - var/viewer = user - if(user.client) //To make shooting through security cameras possible - viewer = user.client.eye - if(dummy in viewers(world.view, viewer)) + if(can_capture_turf(T, user)) turfs.Add(T) - mobs += get_mobs(T) - dummy.loc = null - dummy = null //Alas, nameless creature //garbage collect it instead + mobs += get_mobs(T) x_c++ y_c-- x_c = x_c - 3 + var/datum/picture/P = createpicture(target, user, turfs, mobs, flag) + printpicture(user, P) + +/obj/item/device/camera/proc/createpicture(atom/target, mob/user, list/turfs, mobs, flag) var/icon/photoimage = get_icon(turfs, target) - var/obj/item/weapon/photo/P = new/obj/item/weapon/photo() - P.loc = user.loc - if(!user.get_inactive_hand()) - user.put_in_inactive_hand(P) var/icon/small_img = icon(photoimage) var/icon/tiny_img = icon(photoimage) var/icon/ic = icon('icons/obj/items.dmi',"photo") @@ -260,19 +277,29 @@ tiny_img.Scale(4, 4) ic.Blend(small_img,ICON_OVERLAY, 10, 13) pc.Blend(tiny_img,ICON_OVERLAY, 12, 19) - P.icon = ic - P.tiny = pc - P.img = photoimage - P.desc = mobs - P.pixel_x = rand(-10, 10) - P.pixel_y = rand(-10, 10) - playsound(loc, pick('sound/items/polaroid1.ogg', 'sound/items/polaroid2.ogg'), 75, 1, -3) - pictures_left-- - desc = "A polaroid camera. It has [pictures_left] photos left." - user << "[pictures_left] photos left." - icon_state = icon_off - on = 0 - spawn(64) - icon_state = icon_on - on = 1 \ No newline at end of file + var/datum/picture/P = new() + P.fields["author"] = user + P.fields["icon"] = ic + P.fields["tiny"] = pc + P.fields["img"] = photoimage + P.fields["desc"] = mobs + P.fields["pixel_x"] = rand(-10, 10) + P.fields["pixel_y"] = rand(-10, 10) + + return P + +/obj/item/device/camera/proc/printpicture(mob/user, var/datum/picture/P) + var/obj/item/weapon/photo/Photo = new/obj/item/weapon/photo() + Photo.loc = user.loc + if(!user.get_inactive_hand()) + user.put_in_inactive_hand(Photo) + Photo.construct(P) + +/obj/item/weapon/photo/proc/construct(var/datum/picture/P) + icon = P.fields["icon"] + tiny = P.fields["tiny"] + img = P.fields["img"] + desc = P.fields["desc"] + pixel_x = P.fields["pixel_x"] + pixel_y = P.fields["pixel_y"] diff --git a/code/modules/paperwork/silicon_photography.dm b/code/modules/paperwork/silicon_photography.dm new file mode 100644 index 0000000000..4fbe953d72 --- /dev/null +++ b/code/modules/paperwork/silicon_photography.dm @@ -0,0 +1,162 @@ +/************** +* AI-specific * +**************/ +/datum/picture + var/name = "image" + var/list/fields = list() + +/obj/item/device/camera/siliconcam + var/in_camera_mode = 0 + var/photos_taken = 0 + var/list/aipictures = list() + +/obj/item/device/camera/siliconcam/ai_camera //camera AI can take pictures with + name = "AI photo camera" + +/obj/item/device/camera/siliconcam/robot_camera //camera cyborgs can take pictures with + name = "Cyborg photo camera" + +/obj/item/device/camera/siliconcam/drone_camera //currently doesn't offer the verbs, thus cannot be used + name = "Drone photo camera" + +/obj/item/device/camera/siliconcam/proc/injectaialbum(var/datum/picture/P, var/sufix = "") //stores image information to a list similar to that of the datacore + photos_taken++ + P.fields["name"] = "Image [photos_taken][sufix]" + aipictures += P + +/obj/item/device/camera/siliconcam/proc/injectmasteralbum(var/datum/picture/P) //stores image information to a list similar to that of the datacore + var/mob/living/silicon/robot/C = src.loc + if(C.connected_ai) + var/mob/A = P.fields["author"] + C.connected_ai.aiCamera.injectaialbum(P, " (taken by [A.name])") + C.connected_ai << "Image recorded and saved by [name]" + usr << "Image recorded and saved to remote database" //feedback to the Cyborg player that the picture was taken + else + injectaialbum(P) + usr << "Image recorded" + +/obj/item/device/camera/siliconcam/proc/selectpicture(obj/item/device/camera/siliconcam/cam) + if(!cam) + cam = getsource() + + var/list/nametemp = list() + var/find + if(cam.aipictures.len == 0) + usr << "No images saved" + return + for(var/datum/picture/t in cam.aipictures) + nametemp += t.fields["name"] + find = input("Select image (numbered in order taken)") in nametemp + + for(var/datum/picture/q in cam.aipictures) + if(q.fields["name"] == find) + return q + +/obj/item/device/camera/siliconcam/proc/viewpictures() + var/datum/picture/selection = selectpicture() + + if(!selection) + return + + var/obj/item/weapon/photo/P = new/obj/item/weapon/photo() + P.construct(selection) + P.show(usr) + usr << P.desc + + // TG uses a special garbage collector.. qdel(P) + del(P) //so 10 thousand pictures items are not left in memory should an AI take them and then view them all. + +/obj/item/device/camera/siliconcam/proc/deletepicture(obj/item/device/camera/siliconcam/cam) + var/datum/picture/selection = selectpicture(cam) + + if(!selection) + return + + cam.aipictures -= selection + usr << "Image deleted" + +/obj/item/device/camera/siliconcam/ai_camera/can_capture_turf(turf/T, mob/user) + var/mob/living/silicon/ai = user + return ai.TurfAdjacent(T) + +/obj/item/device/camera/siliconcam/proc/toggle_camera_mode() + if(in_camera_mode) + camera_mode_off() + else + camera_mode_on() + +/obj/item/device/camera/siliconcam/proc/camera_mode_off() + src.in_camera_mode = 0 + usr << "Camera Mode deactivated" + +/obj/item/device/camera/siliconcam/proc/camera_mode_on() + src.in_camera_mode = 1 + usr << "Camera Mode activated" + +/obj/item/device/camera/siliconcam/ai_camera/printpicture(mob/user, datum/picture/P) + injectaialbum(P) + usr << "Image recorded" + +/obj/item/device/camera/siliconcam/robot_camera/printpicture(mob/user, datum/picture/P) + injectmasteralbum(P) + +/obj/item/device/camera/siliconcam/ai_camera/verb/take_image() + set category = "AI Commands" + set name = "Take Image" + set desc = "Takes an image" + set src in usr + + toggle_camera_mode() + +/obj/item/device/camera/siliconcam/ai_camera/verb/view_images() + set category = "AI Commands" + set name = "View Images" + set desc = "View images" + set src in usr + + viewpictures() + +/obj/item/device/camera/siliconcam/ai_camera/verb/delete_images() + set category = "AI Commands" + set name = "Delete Image" + set desc = "Delete image" + set src in usr + + deletepicture() + +/obj/item/device/camera/siliconcam/robot_camera/verb/take_image() + set category ="Robot Commands" + set name = "Take Image" + set desc = "Takes an image" + set src in usr + + toggle_camera_mode() + +/obj/item/device/camera/siliconcam/robot_camera/verb/view_images() + set category ="Robot Commands" + set name = "View Images" + set desc = "View images" + set src in usr + + viewpictures() + +/obj/item/device/camera/siliconcam/robot_camera/verb/delete_images() + set category = "Robot Commands" + set name = "Delete Image" + set desc = "Delete a local image" + set src in usr + + // Explicitly only allow deletion from the local camera + deletepicture(src) + +obj/item/device/camera/siliconcam/proc/getsource() + if(istype(src.loc, /mob/living/silicon/ai)) + return src + + var/mob/living/silicon/robot/C = src.loc + var/obj/item/device/camera/siliconcam/Cinfo + if(C.connected_ai) + Cinfo = C.connected_ai.aiCamera + else + Cinfo = src + return Cinfo