Merge branch 'Ghommie-cit265' of https://github.com/Ghommie/Citadel-Station-13 into Ghommie-cit435

This commit is contained in:
Ghommie
2020-01-12 23:21:03 +01:00
2163 changed files with 249943 additions and 224792 deletions
+171 -171
View File
@@ -1,171 +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
/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
+222 -221
View File
@@ -1,222 +1,223 @@
#define CAMERA_PICTURE_SIZE_HARD_LIMIT 21
/obj/item/camera
name = "camera"
icon = 'icons/obj/items_and_weapons.dmi'
desc = "A polaroid camera."
icon_state = "camera"
item_state = "camera"
lefthand_file = 'icons/mob/inhands/misc/devices_lefthand.dmi'
righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi'
light_color = LIGHT_COLOR_WHITE
light_power = FLASH_LIGHT_POWER
w_class = WEIGHT_CLASS_SMALL
flags_1 = CONDUCT_1
slot_flags = ITEM_SLOT_NECK
materials = list(MAT_METAL = 50, MAT_GLASS = 150)
var/flash_enabled = TRUE
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
var/can_customise = TRUE
var/default_picture_name
/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)
. = ..()
. += "<span class='notice'>Alt-click to change its focusing, allowing you to set how big of an area it will capture.</span>"
/obj/item/camera/AltClick(mob/user)
if(!user.canUseTopic(src, BE_CLOSE))
return
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
picture_size_x = min(CLAMP(desired_x, picture_size_x_min, picture_size_x_max), CAMERA_PICTURE_SIZE_HARD_LIMIT)
picture_size_y = min(CLAMP(desired_y, picture_size_y_min, picture_size_y_max), CAMERA_PICTURE_SIZE_HARD_LIMIT)
/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)
. = ..()
. += "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
var/realcooldown = cooldown
var/mob/living/carbon/human/H = user
if (HAS_TRAIT(H, TRAIT_PHOTOGRAPHER))
realcooldown *= 0.5
addtimer(CALLBACK(src, .proc/cooldown), realcooldown)
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)
if(flash_enabled)
flash_lighting_fx(8, light_power, light_color)
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/customise = "No"
if(can_customise)
customise = alert(user, "Do you want to customize the photo?", "Customization", "Yes", "No")
if(customise == "Yes")
var/name1 = stripped_input(user, "Set a name for this photo, or leave blank. 32 characters max.", "Name", max_length = 32)
var/desc1 = stripped_input(user, "Set a description to add to photo, or leave blank. 128 characters max.", "Caption", max_length = 128)
var/caption = stripped_input(user, "Set a caption for this photo, or leave blank. 256 characters max.", "Caption", max_length = 256)
if(name1)
picture.picture_name = name1
if(desc1)
picture.picture_desc = "[desc1] - [picture.picture_desc]"
if(caption)
picture.caption = caption
else
if(default_picture_name)
picture.picture_name = default_picture_name
p.set_picture(picture, TRUE, TRUE)
if(CONFIG_GET(flag/picture_logging_camera))
#define CAMERA_PICTURE_SIZE_HARD_LIMIT 21
/obj/item/camera
name = "camera"
icon = 'icons/obj/items_and_weapons.dmi'
desc = "A polaroid camera."
icon_state = "camera"
item_state = "camera"
lefthand_file = 'icons/mob/inhands/misc/devices_lefthand.dmi'
righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi'
light_color = LIGHT_COLOR_WHITE
light_power = FLASH_LIGHT_POWER
w_class = WEIGHT_CLASS_SMALL
flags_1 = CONDUCT_1
slot_flags = ITEM_SLOT_NECK
materials = list(MAT_METAL = 50, MAT_GLASS = 150)
var/flash_enabled = TRUE
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
var/can_customise = TRUE
var/default_picture_name
/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)
. = ..()
. += "<span class='notice'>Alt-click to change its focusing, allowing you to set how big of an area it will capture.</span>"
/obj/item/camera/AltClick(mob/user)
. = ..()
if(!user.canUseTopic(src, BE_CLOSE))
return
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
picture_size_x = min(CLAMP(desired_x, picture_size_x_min, picture_size_x_max), CAMERA_PICTURE_SIZE_HARD_LIMIT)
picture_size_y = min(CLAMP(desired_y, picture_size_y_min, picture_size_y_max), CAMERA_PICTURE_SIZE_HARD_LIMIT)
return TRUE
/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)
. = ..()
. += "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
var/realcooldown = cooldown
var/mob/living/carbon/human/H = user
if (HAS_TRAIT(H, TRAIT_PHOTOGRAPHER))
realcooldown *= 0.5
addtimer(CALLBACK(src, .proc/cooldown), realcooldown)
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)
if(flash_enabled)
flash_lighting_fx(8, light_power, light_color)
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/customise = "No"
if(can_customise)
customise = alert(user, "Do you want to customize the photo?", "Customization", "Yes", "No")
if(customise == "Yes")
var/name1 = stripped_input(user, "Set a name for this photo, or leave blank. 32 characters max.", "Name", max_length = 32)
var/desc1 = stripped_input(user, "Set a description to add to photo, or leave blank. 128 characters max.", "Caption", max_length = 128)
var/caption = stripped_input(user, "Set a caption for this photo, or leave blank. 256 characters max.", "Caption", max_length = 256)
if(name1)
picture.picture_name = name1
if(desc1)
picture.picture_desc = "[desc1] - [picture.picture_desc]"
if(caption)
picture.caption = caption
else
if(default_picture_name)
picture.picture_name = default_picture_name
p.set_picture(picture, TRUE, TRUE)
if(CONFIG_GET(flag/picture_logging_camera))
picture.log_to_file()
@@ -1,89 +1,89 @@
/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)
if(img)
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
/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)
if(img)
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
+14 -14
View File
@@ -1,14 +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)
/*
* 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)
+21 -21
View File
@@ -1,21 +1,21 @@
/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
/obj/item/camera/spooky/family
name = "fancy camera"
desc = "A fancy camera that has the latest magnifier mods and double the film load! With a complex double lens set with holy water to be able to see the dead, at laest to the Chaplain..."
see_ghosts = CAMERA_SEE_GHOSTS_ORBIT
pictures_max = 30
pictures_left = 30
/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
/obj/item/camera/spooky/family
name = "fancy camera"
desc = "A fancy camera that has the latest magnifier mods and double the film load! With a complex double lens set with holy water to be able to see the dead, at laest to the Chaplain..."
see_ghosts = CAMERA_SEE_GHOSTS_ORBIT
pictures_max = 30
pictures_left = 30
@@ -1,99 +1,99 @@
/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"
flash_enabled = FALSE
/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>")
/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"
flash_enabled = FALSE
/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>")
+75 -75
View File
@@ -1,75 +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()
. = ..()
var/datum/component/storage/STR = GetComponent(/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"
/*
* 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()
. = ..()
var/datum/component/storage/STR = GetComponent(/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"
+163 -163
View File
@@ -1,163 +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)
return list()
return ..()
/obj/item/wallframe/picture/update_icon()
cut_overlays()
if(displayed)
add_overlay(image(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)
return list()
return ..()
/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(image(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)
// 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)
return list()
return ..()
/obj/item/wallframe/picture/update_icon()
cut_overlays()
if(displayed)
add_overlay(image(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)
return list()
return ..()
/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(image(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)
+92 -92
View File
@@ -1,92 +1,92 @@
/*
* 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
. += "<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)
/*
* 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(/datum/reagent/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
. += "<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)