Merge branch 'Ghommie-cit265' of https://github.com/Ghommie/Citadel-Station-13 into Ghommie-cit435
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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>")
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user