mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-07-21 13:05:36 +01:00
[TM First] The Paintening: Part 2 of ? - NanoPaint: The Modular Computer Paint App (and related modular computer changes) (#95213)
This commit is contained in:
@@ -1,3 +1,88 @@
|
||||
GLOBAL_LIST_INIT(print_types, init_print_types())
|
||||
|
||||
/proc/init_print_types()
|
||||
var/list/print_types = list()
|
||||
for(var/obj/item/canvas/canvas_type as anything in typesof(/obj/item/canvas))
|
||||
var/width = canvas_type::width
|
||||
var/height = canvas_type::height
|
||||
LAZYADDASSOC(print_types, "[width]x[height]", list("[canvas_type]" = list(
|
||||
"displayText" = "Canvas ([width]x[height])",
|
||||
"typepath" = canvas_type,
|
||||
"width" = width,
|
||||
"height" = height,
|
||||
"check_callback" = CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(check_can_print_canvas)),
|
||||
"prepare_callback" = CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(prepare_canvas_from_file)),
|
||||
)))
|
||||
for(var/size in 1 to /obj/item/camera::picture_size_x_max)
|
||||
var/width = ICON_SIZE_X*(size*2-1)
|
||||
var/height = ICON_SIZE_Y*(size*2-1)
|
||||
LAZYADDASSOC(print_types, "[width]x[height]", list("[/obj/item/photo]" = list(
|
||||
"displayText" = "Photo Paper ([size*2-1]m focal length)",
|
||||
"typepath" = /obj/item/photo,
|
||||
"width" = width,
|
||||
"height" = height,
|
||||
"check_callback" = CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(check_can_print_photo)),
|
||||
"prepare_callback" = CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(prepare_photo_from_file)),
|
||||
)))
|
||||
return print_types
|
||||
|
||||
/proc/check_can_print_canvas(_typepath, _image_file, obj/item/modular_computer/computer, mob/user)
|
||||
if(!(computer.hardware_flag & PROGRAM_CONSOLE))
|
||||
to_chat(user, span_notice("Printing error: Canvas printing is only supported on stationary consoles."))
|
||||
return FALSE
|
||||
if(computer.stored_paper < CANVAS_PAPER_COST)
|
||||
to_chat(user, span_notice("Printing error: Your printer needs at least [CANVAS_PAPER_COST] paper to print a canvas."))
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/proc/prepare_canvas_from_file(obj/item/canvas/canvas, datum/computer_file/image/image_file, obj/item/modular_computer/computer, width, height, x, y)
|
||||
computer.stored_paper -= CANVAS_PAPER_COST
|
||||
if(istype(image_file.source_photo_or_painting, /datum/painting))
|
||||
var/datum/painting/source_painting = image_file.source_photo_or_painting
|
||||
var/icon/painting_icon = source_painting.get_icon()
|
||||
if(width == painting_icon.Width() && height == painting_icon.Height() && !x && !y)
|
||||
source_painting.fill_canvas(canvas)
|
||||
return
|
||||
var/datum/icon_transformer/transformer = new()
|
||||
var/temp_file = "tmp/[copytext(REF(image_file.stored_icon), 2, -1)].dmi"
|
||||
fcopy(image_file.stored_icon, temp_file)
|
||||
transformer.scale(width, height)
|
||||
transformer.blend_color("#ffffff", ICON_OVERLAY)
|
||||
transformer.blend_icon(uni_icon(temp_file, ""), ICON_OVERLAY, x+1, y+1)
|
||||
var/datum/universal_icon/blank = uni_icon('icons/blanks/32x32.dmi', "nothing", transform = transformer)
|
||||
canvas.fill_grid_from_icon(blank.to_icon())
|
||||
fdel(temp_file)
|
||||
canvas.painting_metadata.medium = "Digital Art"
|
||||
canvas.used = TRUE
|
||||
canvas.update_icon()
|
||||
|
||||
/proc/check_can_print_photo(_typepath, _image_file, obj/item/modular_computer/computer, mob/user)
|
||||
if(computer.stored_paper < PHOTO_PAPER_COST)
|
||||
to_chat(user, span_notice("Printing error: Your printer needs at least [PHOTO_PAPER_COST] paper to print a photo."))
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/proc/prepare_photo_from_file(obj/item/photo/photo, datum/computer_file/image/image_file, obj/item/modular_computer/computer, width, height, x, y)
|
||||
computer.stored_paper -= PHOTO_PAPER_COST
|
||||
var/icon/photo_image = image_file.stored_icon
|
||||
var/image_width = photo_image.Width()
|
||||
var/image_height = photo_image.Height()
|
||||
if(istype(image_file.source_photo_or_painting, /datum/picture) && width == image_width && height == image_height && !x && !y)
|
||||
var/datum/picture/source_photo = image_file.source_photo_or_painting
|
||||
photo.set_picture(source_photo, TRUE, TRUE)
|
||||
return
|
||||
var/datum/icon_transformer/transformer = new()
|
||||
var/temp_file = "tmp/[copytext(REF(photo_image), 2, -1)].dmi"
|
||||
fcopy(photo_image, temp_file)
|
||||
transformer.scale(width, height)
|
||||
transformer.blend_color("#ffffff", ICON_OVERLAY)
|
||||
transformer.blend_icon(uni_icon(temp_file, ""), ICON_OVERLAY, x+1, y+1)
|
||||
var/datum/universal_icon/blank = uni_icon('icons/blanks/32x32.dmi', "nothing", transform = transformer)
|
||||
var/datum/picture/new_photo = new(image_file.filename, desc = "A printout of a digital image.", image = blank.to_icon(), size_x = width, size_y = height, autogenerate_icon = TRUE, author_ckey_ = image_file.author_ckey)
|
||||
fdel(temp_file)
|
||||
image_file.source_photo_or_painting = new_photo
|
||||
photo.set_picture(new_photo, TRUE, TRUE)
|
||||
|
||||
/datum/computer_file/program/filemanager
|
||||
filename = "filemanager"
|
||||
filedesc = "File Manager"
|
||||
@@ -33,7 +118,7 @@
|
||||
var/datum/computer_file/file = computer.find_file_by_name(params["name"])
|
||||
if(!file)
|
||||
return
|
||||
var/newname = reject_bad_name(params["new_name"])
|
||||
var/newname = reject_bad_name(params["new_name"], allow_numbers = TRUE, cap_after_symbols = FALSE, cap_at_start = FALSE)
|
||||
if(!newname || newname != params["new_name"])
|
||||
playsound(computer, 'sound/machines/terminal/terminal_error.ogg', 25, FALSE)
|
||||
return
|
||||
@@ -45,7 +130,7 @@
|
||||
var/datum/computer_file/file = computer.find_file_by_name(params["name"], computer.inserted_disk)
|
||||
if(!file)
|
||||
return
|
||||
var/newname = reject_bad_name(params["new_name"])
|
||||
var/newname = reject_bad_name(params["new_name"], allow_numbers = TRUE, cap_after_symbols = FALSE, cap_at_start = FALSE)
|
||||
if(!newname || newname != params["new_name"])
|
||||
playsound(computer, 'sound/machines/terminal/terminal_error.ogg', 25, FALSE)
|
||||
return
|
||||
@@ -78,6 +163,51 @@
|
||||
if(!binary || !istype(binary))
|
||||
return
|
||||
binary.alert_silenced = !binary.alert_silenced
|
||||
if("PRG_print")
|
||||
var/datum/computer_file/image/picture = computer.find_file_by_name(params["name"])
|
||||
if(!istype(picture))
|
||||
return
|
||||
try_print(picture, params["width"], params["height"], params["offsetX"], params["offsetY"], params["typepath"], usr)
|
||||
if("PRG_usbprint")
|
||||
if(!computer.inserted_disk)
|
||||
return
|
||||
var/datum/computer_file/image/picture = computer.find_file_by_name(params["name"], computer.inserted_disk)
|
||||
if(!istype(picture))
|
||||
return
|
||||
try_print(picture, params["width"], params["height"], params["offsetX"], params["offsetY"], params["typepath"], usr)
|
||||
|
||||
/datum/computer_file/program/filemanager/proc/try_print(datum/computer_file/image/picture, width, height, offset_x, offset_y, typepath, mob/user)
|
||||
var/list/print_types_for_dimensions = GLOB.print_types["[width]x[height]"]
|
||||
if(!length(print_types_for_dimensions))
|
||||
return
|
||||
var/list/print_type = print_types_for_dimensions[typepath]
|
||||
if(!print_type)
|
||||
return
|
||||
var/image_width = picture.stored_icon.Width()
|
||||
var/image_height = picture.stored_icon.Height()
|
||||
var/min_offset_x = min(width - image_width, 0)
|
||||
var/max_offset_x = max(width - image_width, 0)
|
||||
var/min_offset_y = min(height - image_height, 0)
|
||||
var/max_offset_y = max(height - image_height, 0)
|
||||
if(!ISINRANGE(offset_x, min_offset_x, max_offset_x) || !ISINRANGE(offset_y, min_offset_y, max_offset_y))
|
||||
return
|
||||
typepath = text2path(typepath)
|
||||
var/datum/callback/check_callback = print_type["check_callback"]
|
||||
if(!check_callback.Invoke(typepath, picture, computer, user))
|
||||
return
|
||||
var/obj/item/printed_item = new typepath(computer.physical.drop_location())
|
||||
var/datum/callback/prepare_callback = print_type["prepare_callback"]
|
||||
prepare_callback.Invoke(printed_item, picture, computer, width, height, offset_x, offset_y)
|
||||
user?.put_in_hands(printed_item)
|
||||
playsound(computer.physical, 'sound/machines/printer.ogg', 100, TRUE)
|
||||
|
||||
/datum/computer_file/program/filemanager/ui_static_data(mob/user)
|
||||
var/list/print_types = list()
|
||||
for(var/dimensions in GLOB.print_types)
|
||||
var/list/types_for_dimensions = GLOB.print_types[dimensions]
|
||||
for(var/print_typepath in types_for_dimensions)
|
||||
print_types += list(types_for_dimensions[print_typepath])
|
||||
return list("printTypes" = print_types)
|
||||
|
||||
/datum/computer_file/program/filemanager/ui_data(mob/user)
|
||||
var/list/data = list()
|
||||
@@ -90,10 +220,20 @@
|
||||
for(var/datum/computer_file/F as anything in computer.stored_files)
|
||||
var/noisy = FALSE
|
||||
var/silenced = FALSE
|
||||
var/printable = FALSE
|
||||
var/image_width = 0
|
||||
var/image_height = 0
|
||||
var/image_ref
|
||||
var/datum/computer_file/program/binary = F
|
||||
if(istype(binary))
|
||||
noisy = binary.alert_able
|
||||
silenced = binary.alert_silenced
|
||||
var/datum/computer_file/image/picture_file = F
|
||||
if(istype(picture_file))
|
||||
printable = TRUE
|
||||
image_width = picture_file.stored_icon.Width()
|
||||
image_height = picture_file.stored_icon.Height()
|
||||
image_ref = picture_file.get_image_ref()
|
||||
files += list(list(
|
||||
"name" = F.filename,
|
||||
"type" = F.filetype,
|
||||
@@ -101,17 +241,35 @@
|
||||
"undeletable" = F.undeletable,
|
||||
"alert_able" = noisy,
|
||||
"alert_silenced" = silenced,
|
||||
"printable" = printable,
|
||||
"image_ref" = image_ref,
|
||||
"image_width" = image_width,
|
||||
"image_height" = image_height,
|
||||
))
|
||||
data["files"] = files
|
||||
if(computer.inserted_disk)
|
||||
data["usbconnected"] = TRUE
|
||||
var/list/usbfiles = list()
|
||||
for(var/datum/computer_file/F as anything in computer.inserted_disk.stored_files)
|
||||
var/printable = FALSE
|
||||
var/image_width = 0
|
||||
var/image_height = 0
|
||||
var/image_ref
|
||||
var/datum/computer_file/image/picture_file = F
|
||||
if(istype(picture_file))
|
||||
printable = TRUE
|
||||
image_width = picture_file.stored_icon.Width()
|
||||
image_height = picture_file.stored_icon.Height()
|
||||
image_ref = picture_file.get_image_ref()
|
||||
usbfiles += list(list(
|
||||
"name" = F.filename,
|
||||
"type" = F.filetype,
|
||||
"size" = F.size,
|
||||
"undeletable" = F.undeletable,
|
||||
"printable" = printable,
|
||||
"image_ref" = image_ref,
|
||||
"image_width" = image_width,
|
||||
"image_height" = image_height,
|
||||
))
|
||||
data["usbfiles"] = usbfiles
|
||||
|
||||
|
||||
@@ -14,8 +14,18 @@
|
||||
var/obj/item/camera/app/internal_camera
|
||||
/// Latest picture taken by the app.
|
||||
var/datum/picture/internal_picture
|
||||
/// A mutable_appearance of the latest picture, for getting an appeance reference for the UI.
|
||||
var/mutable_appearance/picture_appearance
|
||||
/// How many pictures were taken already, used for the camera's TGUI photo display
|
||||
var/picture_number = 1
|
||||
/// Can we edit the metadata of the latest picture?
|
||||
var/can_edit_metadata = TRUE
|
||||
/// The name we will give to the picture when we first save it
|
||||
var/current_picture_name
|
||||
/// The description we will give to the picture when we first save it
|
||||
var/current_picture_desc
|
||||
/// The caption we will give to the picture when we first save it
|
||||
var/current_picture_caption
|
||||
|
||||
// Special type of camera for this exact usecase to prevent harddels
|
||||
/obj/item/camera/app
|
||||
@@ -27,54 +37,134 @@
|
||||
. = ..()
|
||||
internal_camera = new(computer)
|
||||
internal_camera.print_picture_on_snap = FALSE
|
||||
RegisterSignal(internal_camera, COMSIG_CAMERA_IMAGE_CAPTURED, PROC_REF(save_picture))
|
||||
picture_appearance = new()
|
||||
RegisterSignal(internal_camera, COMSIG_CAMERA_IMAGE_CAPTURED, PROC_REF(on_image_captured))
|
||||
|
||||
/datum/computer_file/program/maintenance/camera/Destroy()
|
||||
QDEL_NULL(internal_camera)
|
||||
QDEL_NULL(internal_picture)
|
||||
internal_picture = null
|
||||
QDEL_NULL(picture_appearance)
|
||||
return ..()
|
||||
|
||||
|
||||
/datum/computer_file/program/maintenance/camera/tap(atom/tapped_atom, mob/living/user, list/modifiers)
|
||||
. = ..()
|
||||
take_picture(user, get_turf(tapped_atom))
|
||||
|
||||
QDEL_NULL(internal_picture)
|
||||
/datum/computer_file/program/maintenance/camera/on_made_active_program(user)
|
||||
RegisterSignal(computer, COMSIG_RANGED_ITEM_INTERACTING_WITH_ATOM_SECONDARY, PROC_REF(on_computer_ranged_interact))
|
||||
|
||||
/datum/computer_file/program/maintenance/camera/kill_program(mob/user)
|
||||
. = ..()
|
||||
UnregisterSignal(computer, COMSIG_RANGED_ITEM_INTERACTING_WITH_ATOM_SECONDARY)
|
||||
internal_picture = null
|
||||
|
||||
/datum/computer_file/program/maintenance/camera/background_program(mob/user)
|
||||
. = ..()
|
||||
UnregisterSignal(computer, COMSIG_RANGED_ITEM_INTERACTING_WITH_ATOM_SECONDARY)
|
||||
|
||||
/datum/computer_file/program/maintenance/camera/proc/on_computer_ranged_interact(_source, mob/user, atom/target, list/modifiers)
|
||||
SIGNAL_HANDLER
|
||||
take_picture(user, get_turf(target))
|
||||
|
||||
/datum/computer_file/program/maintenance/camera/proc/take_picture(mob/user, turf/target)
|
||||
if(internal_camera.blending)
|
||||
user.balloon_alert(user, "still blending!")
|
||||
return
|
||||
|
||||
var/turf/our_turf = get_turf(tapped_atom)
|
||||
var/spooky_camera = locate(/datum/computer_file/program/maintenance/spectre_meter) in computer.stored_files
|
||||
internal_camera.see_ghosts = spooky_camera ? CAMERA_SEE_GHOSTS_BASIC : CAMERA_NO_GHOSTS
|
||||
INVOKE_ASYNC(internal_camera, TYPE_PROC_REF(/obj/item/camera, captureimage), our_turf, user, internal_camera.picture_size_x + 1, internal_camera.picture_size_y + 1)
|
||||
INVOKE_ASYNC(internal_camera, TYPE_PROC_REF(/obj/item/camera, captureimage), target, user, internal_camera.picture_size_x - 1, internal_camera.picture_size_y - 1)
|
||||
|
||||
/datum/computer_file/program/maintenance/camera/proc/save_picture(cam, target, user, datum/picture/picture)
|
||||
/datum/computer_file/program/maintenance/camera/proc/on_image_captured(cam, target, user, datum/picture/picture)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
internal_picture = picture
|
||||
picture_appearance.icon = internal_picture.picture_image
|
||||
current_picture_name = null
|
||||
current_picture_desc = null
|
||||
current_picture_caption = null
|
||||
can_edit_metadata = TRUE
|
||||
picture_number++
|
||||
computer.save_photo(internal_picture.picture_image)
|
||||
|
||||
/datum/computer_file/program/maintenance/camera/proc/save_picture(mob/user)
|
||||
var/datum/computer_file/image/photo_file = new(
|
||||
internal_picture.picture_image,
|
||||
display_name = internal_picture.picture_name || "photo[picture_number]",
|
||||
source_photo_or_painting = internal_picture
|
||||
)
|
||||
if(computer.store_file(photo_file, user))
|
||||
return FALSE
|
||||
commit_metadata()
|
||||
return TRUE
|
||||
|
||||
/datum/computer_file/program/maintenance/camera/proc/print_picture(mob/user)
|
||||
if(computer.stored_paper < PHOTO_PAPER_COST)
|
||||
return
|
||||
commit_metadata()
|
||||
var/obj/item/photo/new_photo = new(computer.physical.drop_location())
|
||||
new_photo.set_picture(internal_picture, TRUE, TRUE)
|
||||
user?.put_in_hands(new_photo)
|
||||
playsound(computer.physical, 'sound/machines/printer.ogg', 100, TRUE)
|
||||
computer.stored_paper--
|
||||
computer.visible_message(span_notice("\The [computer] prints out a paper."))
|
||||
|
||||
/datum/computer_file/program/maintenance/camera/proc/commit_metadata()
|
||||
if(can_edit_metadata)
|
||||
internal_picture.picture_name = current_picture_name
|
||||
internal_picture.picture_desc = "[current_picture_desc] - [internal_picture.picture_desc]"
|
||||
internal_picture.caption = current_picture_caption
|
||||
can_edit_metadata = FALSE
|
||||
|
||||
/datum/computer_file/program/maintenance/camera/ui_static_data(mob/user)
|
||||
return list("maxNameLength" = 32, "maxDescLength" = 128, "maxCaptionLength" = 256, "printCost" = 1)
|
||||
|
||||
/datum/computer_file/program/maintenance/camera/ui_data(mob/user)
|
||||
var/list/data = list()
|
||||
|
||||
if(!isnull(internal_picture))
|
||||
user << browse_rsc(internal_picture.picture_image, "tmp_photo[picture_number].png")
|
||||
data["photo"] = "tmp_photo[picture_number].png"
|
||||
|
||||
data["paper_left"] = computer.stored_paper
|
||||
data["photo"] = REF(picture_appearance.appearance)
|
||||
data["canEditMetadata"] = can_edit_metadata
|
||||
data["name"] = current_picture_name
|
||||
data["desc"] = current_picture_desc
|
||||
data["caption"] = current_picture_caption
|
||||
data["storedPaper"] = computer.stored_paper
|
||||
data["size"] = internal_camera.picture_size_x
|
||||
data["minSize"] = internal_camera.picture_size_x_min
|
||||
data["maxSize"] = min(internal_camera.picture_size_x_max, CAMERA_PICTURE_SIZE_HARD_LIMIT)
|
||||
|
||||
return data
|
||||
|
||||
/datum/computer_file/program/maintenance/camera/ui_act(action, params, datum/tgui/ui, datum/ui_state/state)
|
||||
. = ..()
|
||||
switch(action)
|
||||
if("print_photo")
|
||||
if(computer.stored_paper <= 0)
|
||||
to_chat(ui.user, span_notice("Hardware error: Printer out of paper."))
|
||||
if("adjustSize")
|
||||
var/new_size = round(params["value"], 1)
|
||||
if(!ISINRANGE(new_size, internal_camera.picture_size_x_min, min(CAMERA_PICTURE_SIZE_HARD_LIMIT, internal_camera.picture_size_x_max)))
|
||||
return
|
||||
internal_camera.printpicture(usr, internal_picture)
|
||||
computer.stored_paper--
|
||||
computer.visible_message(span_notice("\The [computer] prints out a paper."))
|
||||
internal_camera.picture_size_x = new_size
|
||||
internal_camera.picture_size_y = new_size
|
||||
if("setName")
|
||||
if(!(internal_picture && can_edit_metadata))
|
||||
return
|
||||
current_picture_name = trim(params["value"], PREVENT_CHARACTER_TRIM_LOSS(32))
|
||||
if("setDesc")
|
||||
if(!(internal_picture && can_edit_metadata))
|
||||
return
|
||||
current_picture_desc = trim(params["value"], PREVENT_CHARACTER_TRIM_LOSS(128))
|
||||
if("setCaption")
|
||||
if(!(internal_picture && can_edit_metadata))
|
||||
return
|
||||
current_picture_caption = trim(params["value"], PREVENT_CHARACTER_TRIM_LOSS(256))
|
||||
if("savePhoto")
|
||||
if(!internal_picture)
|
||||
return
|
||||
save_picture(ui.user)
|
||||
if("printPhoto")
|
||||
if(!internal_picture)
|
||||
return
|
||||
print_picture(ui.user)
|
||||
return TRUE
|
||||
|
||||
/obj/item/circuit_component/mod_program/camera
|
||||
associated_program = /datum/computer_file/program/maintenance/camera
|
||||
@@ -82,6 +172,8 @@
|
||||
|
||||
///A target to take a picture of.
|
||||
var/datum/port/input/picture_target
|
||||
///The size of the photo to take.
|
||||
var/datum/port/input/picture_size
|
||||
///The photographed target
|
||||
var/datum/port/output/photographed
|
||||
/**
|
||||
@@ -94,6 +186,7 @@
|
||||
/obj/item/circuit_component/mod_program/camera/populate_ports()
|
||||
. = ..()
|
||||
picture_target = add_input_port("Picture Target", PORT_TYPE_ATOM)
|
||||
picture_size = add_input_port("Picture Size", PORT_TYPE_NUMBER)
|
||||
photographed = add_output_port("Photographed Entity", PORT_TYPE_ATOM)
|
||||
photo_taken = add_output_port("Photo Taken", PORT_TYPE_SIGNAL)
|
||||
|
||||
@@ -108,6 +201,8 @@
|
||||
return ..()
|
||||
|
||||
/obj/item/circuit_component/mod_program/camera/input_received(datum/port/input/port)
|
||||
if(!COMPONENT_TRIGGERED_BY(port, trigger_input))
|
||||
return
|
||||
var/atom/target = picture_target.value
|
||||
if(!target)
|
||||
var/turf/our_turf = get_location()
|
||||
@@ -117,9 +212,8 @@
|
||||
var/datum/computer_file/program/maintenance/camera/cam = associated_program
|
||||
if(!cam.internal_camera.can_target(target))
|
||||
return
|
||||
var/pic_size_x = cam.internal_camera.picture_size_x - 1
|
||||
var/pic_size_y = cam.internal_camera.picture_size_y - 1
|
||||
INVOKE_ASYNC(cam.internal_camera, TYPE_PROC_REF(/obj/item/camera, captureimage), target, null, pic_size_x, pic_size_y)
|
||||
var/pic_size = clamp(round(picture_size.value), 1, cam.internal_camera.picture_size_x_max)-1
|
||||
INVOKE_ASYNC(cam.internal_camera, TYPE_PROC_REF(/obj/item/camera, captureimage), target, null, pic_size, pic_size)
|
||||
|
||||
/obj/item/circuit_component/mod_program/camera/proc/on_image_captured(obj/item/camera/source, atom/target, mob/user)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
+14
-14
@@ -52,19 +52,19 @@
|
||||
/datum/computer_file/program/messenger/on_install()
|
||||
. = ..()
|
||||
RegisterSignal(computer, COMSIG_MODULAR_COMPUTER_FILE_STORE, PROC_REF(check_new_photo))
|
||||
RegisterSignal(computer, COMSIG_MODULAR_COMPUTER_FILE_DELETE, PROC_REF(check_photo_removed))
|
||||
RegisterSignal(computer, COMSIG_MODULAR_COMPUTER_FILE_DELETE, PROC_REF(check_image_removed))
|
||||
RegisterSignal(computer, COMSIG_MODULAR_PDA_IMPRINT_UPDATED, PROC_REF(on_imprint_added))
|
||||
RegisterSignal(computer, COMSIG_MODULAR_PDA_IMPRINT_RESET, PROC_REF(on_imprint_reset))
|
||||
|
||||
/datum/computer_file/program/messenger/proc/check_new_photo(sender, datum/computer_file/picture/storing_picture)
|
||||
/datum/computer_file/program/messenger/proc/check_new_photo(sender, datum/computer_file/image/storing_image)
|
||||
SIGNAL_HANDLER
|
||||
if(!istype(storing_picture))
|
||||
if(!istype(storing_image))
|
||||
return
|
||||
update_pictures_for_all()
|
||||
|
||||
/datum/computer_file/program/messenger/proc/check_photo_removed(sender, datum/computer_file/picture/photo_removed)
|
||||
/datum/computer_file/program/messenger/proc/check_image_removed(sender, datum/computer_file/image/image_removed)
|
||||
SIGNAL_HANDLER
|
||||
if(istype(photo_removed) && selected_image == photo_removed.picture_name)
|
||||
if(istype(image_removed) && selected_image == image_removed.image_name)
|
||||
selected_image = null
|
||||
|
||||
/datum/computer_file/program/messenger/proc/on_imprint_added(sender)
|
||||
@@ -109,12 +109,12 @@
|
||||
/datum/computer_file/program/messenger/proc/can_send_everyone_message()
|
||||
return COOLDOWN_FINISHED(src, last_text) && COOLDOWN_FINISHED(src, last_text_everyone)
|
||||
|
||||
/// Gets all currently relevant photo asset keys
|
||||
/// Gets all currently relevant image asset keys
|
||||
/datum/computer_file/program/messenger/proc/get_picture_assets()
|
||||
var/list/data = list()
|
||||
|
||||
for(var/datum/computer_file/picture/photo in computer.stored_files)
|
||||
data |= photo.picture_name
|
||||
for(var/datum/computer_file/image/image_file in computer.stored_files)
|
||||
data |= image_file.image_name
|
||||
|
||||
if(viewing_messages_of in saved_chats)
|
||||
var/datum/pda_chat/chat = LAZYACCESS(saved_chats, viewing_messages_of)
|
||||
@@ -309,12 +309,12 @@
|
||||
|
||||
var/photo_uid = text2num(params["uid"])
|
||||
|
||||
var/datum/computer_file/picture/selected_photo = computer.find_file_by_uid(photo_uid)
|
||||
var/datum/computer_file/image/selected_image_file = computer.find_file_by_uid(photo_uid)
|
||||
|
||||
if(!istype(selected_photo))
|
||||
if(!istype(selected_image_file))
|
||||
return FALSE
|
||||
|
||||
selected_image = selected_photo.picture_name
|
||||
selected_image = selected_image_file.image_name
|
||||
return TRUE
|
||||
|
||||
if("PDA_siliconSelectPhoto")
|
||||
@@ -367,10 +367,10 @@
|
||||
// silicons handle selecting photos a bit differently for now
|
||||
if(!issilicon(user))
|
||||
var/list/stored_photos = list()
|
||||
for(var/datum/computer_file/picture/photo_file in computer.stored_files)
|
||||
for(var/datum/computer_file/image/image_file in computer.stored_files)
|
||||
stored_photos += list(list(
|
||||
"uid" = photo_file.uid,
|
||||
"path" = SSassets.transport.get_asset_url(photo_file.picture_name)
|
||||
"uid" = image_file.uid,
|
||||
"path" = SSassets.transport.get_asset_url(image_file.image_name)
|
||||
))
|
||||
data["stored_photos"] = stored_photos
|
||||
data["selected_photo_path"] = !isnull(selected_image) ? SSassets.transport.get_asset_url(selected_image) : null
|
||||
|
||||
@@ -0,0 +1,313 @@
|
||||
#define PALETTE_SIZE 32
|
||||
#define SANE_PHOTO_EDITING_SIZE_LIMIT 96 // I really don't think the server can handle someone editing large images, but a photo taken with the default camera dimensions shouldn't be too awful.
|
||||
|
||||
GLOBAL_LIST_INIT(nanopaint_supported_filetypes, zebra_typecacheof(list(\
|
||||
/datum/computer_file/data/paint_project = /datum/computer_file/data/paint_project,\
|
||||
/datum/computer_file/image = /datum/computer_file/image,\
|
||||
)))
|
||||
|
||||
/datum/computer_file/program/nanopaint
|
||||
filename = "nanopaint"
|
||||
filedesc = "NanoPaint"
|
||||
downloader_category = PROGRAM_CATEGORY_DEVICE
|
||||
program_open_overlay = "generic"
|
||||
extended_desc = "Draw pictures on your device."
|
||||
tgui_id = "NtosNanopaint"
|
||||
program_icon = "paintbrush"
|
||||
size = 5
|
||||
can_run_on_flags = PROGRAM_ALL
|
||||
/// A weak reference to the data file containing the workspace currently being worked on.
|
||||
var/datum/weakref/backing_file
|
||||
/// The name of the file that was opened, in case we are trying to save a file that is no longer accessible.
|
||||
var/opened_file_name
|
||||
/// The typepath of the file that was opened, in case we are trying to save a file that is no longer accessible.
|
||||
var/datum/computer_file/opened_file_type
|
||||
var/datum/sprite_editor_workspace/current_workspace
|
||||
/// If the opened file is an unmodified photo or painting, this is a reference to it.
|
||||
var/source_photo_or_painting
|
||||
/// If we have modified this project, store whatever unmodified photo or painting we were made from here.
|
||||
var/source_on_undo_all
|
||||
/// The current color we are painting with
|
||||
var/current_color = "#ffffffff"
|
||||
/// A list of colors we have quick access to
|
||||
var/list/palette = list()
|
||||
/// UI data for a modal dialog to display
|
||||
var/list/dialog
|
||||
|
||||
/datum/computer_file/program/nanopaint/ui_static_data(mob/user)
|
||||
return list(
|
||||
"templateSizes" = GLOB.canvas_dimensions,
|
||||
"saveableTypes" = list(
|
||||
list(
|
||||
"displayText" = "NanoPaint Project (.[/datum/computer_file/data/paint_project::filetype])",
|
||||
"typepath" = /datum/computer_file/data/paint_project,
|
||||
"extension" = /datum/computer_file/data/paint_project::filetype,
|
||||
),
|
||||
list(
|
||||
"displayText" = "PNG Image (.[/datum/computer_file/image::filetype])",
|
||||
"typepath" = /datum/computer_file/image,
|
||||
"extension" = /datum/computer_file/image::filetype,
|
||||
),
|
||||
),
|
||||
"minSize" = 1,
|
||||
"maxSize" = SANE_PHOTO_EDITING_SIZE_LIMIT,
|
||||
)
|
||||
|
||||
/datum/computer_file/program/nanopaint/ui_data(mob/user)
|
||||
var/list/data = list()
|
||||
data["dialog"] = dialog
|
||||
var/list/editor_data = list()
|
||||
editor_data["serverSelectedColor"] = current_color
|
||||
editor_data["serverPalette"] = palette
|
||||
editor_data["maxServerColors"] = PALETTE_SIZE
|
||||
editor_data["onSelectServerColor"] = "onSelectColor"
|
||||
editor_data["onAddServerColor"] = "onAddPaletteColor"
|
||||
editor_data["onRemoveServerColor"] = "onRemovePaletteColor"
|
||||
if(current_workspace)
|
||||
editor_data += current_workspace.sprite_editor_ui_data()
|
||||
data["editorData"] = editor_data
|
||||
data["workspaceOpen"] = !!current_workspace
|
||||
data["diskInserted"] = !!computer.inserted_disk
|
||||
var/list/all_files = computer.get_files(TRUE)
|
||||
var/list/drive_files = list()
|
||||
var/list/disk_files = list()
|
||||
for(var/datum/computer_file/file as anything in all_files)
|
||||
var/base_supported_type = is_type_in_typecache(file, GLOB.nanopaint_supported_filetypes)
|
||||
if(!base_supported_type)
|
||||
continue
|
||||
var/list/file_data = list("name" = file.filename, "extension" = file.filetype, "uid" = file.uid, "baseType" = base_supported_type)
|
||||
if(file.computer)
|
||||
drive_files += list(file_data)
|
||||
else
|
||||
disk_files += list(file_data)
|
||||
data["driveFiles"] = drive_files
|
||||
data["diskFiles"] = disk_files
|
||||
return data
|
||||
|
||||
/datum/computer_file/program/nanopaint/proc/check_dialog(act, modal_type)
|
||||
return dialog && dialog["type"] == modal_type && (!act || dialog["action"] == act)
|
||||
|
||||
/datum/computer_file/program/nanopaint/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
|
||||
. = ..()
|
||||
var/mob/user = ui.user
|
||||
switch(action)
|
||||
if("spriteEditorCommand")
|
||||
if(!current_workspace)
|
||||
return
|
||||
var/command = params["command"]
|
||||
switch(command)
|
||||
if("transaction")
|
||||
current_workspace.new_transaction(params["transaction"])
|
||||
if(!source_on_undo_all && source_photo_or_painting)
|
||||
source_on_undo_all = source_photo_or_painting
|
||||
source_photo_or_painting = null
|
||||
if("toggleVisible")
|
||||
current_workspace.toggle_layer_visible(params["layer"])
|
||||
if("undo")
|
||||
current_workspace.undo()
|
||||
if(!length(current_workspace.undo_stack))
|
||||
source_photo_or_painting = source_on_undo_all
|
||||
source_on_undo_all = null
|
||||
if("redo")
|
||||
current_workspace.redo()
|
||||
if(!source_on_undo_all && source_photo_or_painting)
|
||||
source_on_undo_all = source_photo_or_painting
|
||||
source_photo_or_painting = null
|
||||
return TRUE
|
||||
if("onSelectColor")
|
||||
current_color = params["color"]
|
||||
return TRUE
|
||||
if("onAddPaletteColor")
|
||||
if(length(palette) >= PALETTE_SIZE)
|
||||
return
|
||||
palette += params["color"]
|
||||
if("onRemovePaletteColor")
|
||||
var/index = params["index"]
|
||||
palette.Cut(index, index+1)
|
||||
return TRUE
|
||||
if("closeDialog")
|
||||
dialog = null
|
||||
return TRUE
|
||||
if("newDialog")
|
||||
dialog = list("type" = "new")
|
||||
return TRUE
|
||||
if("new")
|
||||
if(!check_dialog(null, "new"))
|
||||
return
|
||||
var/width = params["width"]
|
||||
if(!ISINRANGE(width, 1, SANE_PHOTO_EDITING_SIZE_LIMIT))
|
||||
return
|
||||
var/height = params["height"]
|
||||
if(!ISINRANGE(height, 1, SANE_PHOTO_EDITING_SIZE_LIMIT))
|
||||
return
|
||||
dialog = null
|
||||
close_workspace()
|
||||
INVOKE_ASYNC(src, PROC_REF(new_workspace), width, height)
|
||||
return TRUE
|
||||
if("openDialog")
|
||||
dialog = list("type" = "select", "title" = "Open File", "confirmText" = "Open", "action" = "open")
|
||||
return TRUE
|
||||
if("open")
|
||||
if(!check_dialog("open", "select"))
|
||||
return
|
||||
dialog = null
|
||||
INVOKE_ASYNC(src, PROC_REF(open_file), user, params["uid"], params["onDisk"], params["name"], text2path(params["type"]))
|
||||
return TRUE
|
||||
if("save")
|
||||
var/datum/computer_file/actual_file = backing_file?.resolve()
|
||||
if(!actual_file)
|
||||
if(!opened_file_name)
|
||||
dialog = list("type" = "select", "title" = "Save As", "confirmText" = "Save", "action" = "saveAs")
|
||||
return TRUE
|
||||
actual_file = computer.find_file_by_full_name("[opened_file_name].[opened_file_type::filetype]")
|
||||
if(actual_file && actual_file.computer != computer && actual_file.disk_host != computer.inserted_disk)
|
||||
actual_file = null
|
||||
if(actual_file)
|
||||
INVOKE_ASYNC(src, PROC_REF(write_to_file), user, actual_file, actual_file.disk_host)
|
||||
else
|
||||
INVOKE_ASYNC(src, PROC_REF(save_file), user, opened_file_name, opened_file_type)
|
||||
return TRUE
|
||||
if("saveAsDialog")
|
||||
dialog = list("type" = "select", "title" = "Save As", "confirmText" = "Save", "action" = "saveAs")
|
||||
return TRUE
|
||||
if("saveAs", "overwrite")
|
||||
if(!check_dialog(action, action == "saveAs" ? "select" : "confirm"))
|
||||
return
|
||||
dialog = null
|
||||
var/uid = params["uid"]
|
||||
var/new_file_name = params["name"]
|
||||
var/saving_to_disk = params["onDisk"]
|
||||
var/datum/computer_file/new_file_type = text2path(params["typepath"])
|
||||
var/extension = new_file_type::filetype
|
||||
var/datum/computer_file/existing_file
|
||||
if(saving_to_disk)
|
||||
if(!computer.inserted_disk)
|
||||
dialog = list("type" = "error", "message" = "[new_file_name] - The disk has been removed.")
|
||||
return TRUE
|
||||
if(uid)
|
||||
existing_file = computer.find_file_by_uid(uid, computer.inserted_disk)
|
||||
else
|
||||
existing_file = computer.find_file_by_full_name("[new_file_name].[extension]", computer.inserted_disk)
|
||||
else
|
||||
if(uid)
|
||||
existing_file = computer.find_file_by_uid(uid)
|
||||
else
|
||||
existing_file = computer.find_file_by_full_name("[new_file_name].[extension]")
|
||||
if(existing_file)
|
||||
if(action == "saveAs")
|
||||
dialog = list(
|
||||
"type" = "confirm",
|
||||
"title" = "Confirm Save As",
|
||||
"message" = "[new_file_name] already exists. Do you want to overwrite this file?",
|
||||
"action" = "overwrite",
|
||||
"params" = list("uid" = uid,
|
||||
"name" = new_file_name,
|
||||
"onDisk" = saving_to_disk,
|
||||
"typepath" = new_file_type),
|
||||
)
|
||||
else
|
||||
INVOKE_ASYNC(src, PROC_REF(write_to_file), user, existing_file)
|
||||
return TRUE
|
||||
INVOKE_ASYNC(src, PROC_REF(save_file), user, new_file_name, new_file_type, saving_to_disk && computer.inserted_disk)
|
||||
return TRUE
|
||||
|
||||
/datum/computer_file/program/nanopaint/proc/new_workspace(width, height)
|
||||
current_workspace = new(width, height)
|
||||
|
||||
/datum/computer_file/program/nanopaint/proc/open_file(mob/user, uid, on_disk, file_name, datum/computer_file/file_type)
|
||||
var/datum/computer_file/file_being_opened
|
||||
var/full_file_name = file_name + file_type::filetype
|
||||
if(on_disk)
|
||||
if(!computer.inserted_disk)
|
||||
dialog = list("type" = "error", "message" = "[full_file_name] - The disk has been removed.")
|
||||
return
|
||||
if(uid)
|
||||
file_being_opened = computer.find_file_by_uid(uid, computer.inserted_disk)
|
||||
else
|
||||
file_being_opened = computer.find_file_by_full_name(full_file_name, computer.inserted_disk)
|
||||
else
|
||||
if(uid)
|
||||
file_being_opened = computer.find_file_by_uid(uid)
|
||||
else
|
||||
file_being_opened = computer.find_file_by_full_name(full_file_name)
|
||||
if(!file_being_opened)
|
||||
dialog = list("type" = "error", "message" = "[full_file_name] - The selected file could not be found")
|
||||
return
|
||||
var/base_supported_type = is_type_in_typecache(file_being_opened, GLOB.nanopaint_supported_filetypes)
|
||||
if(!base_supported_type)
|
||||
dialog = list("type" = "error", "message" = "[full_file_name] - Unsupported format")
|
||||
return
|
||||
close_workspace()
|
||||
switch(base_supported_type)
|
||||
if(/datum/computer_file/data/paint_project)
|
||||
var/datum/computer_file/data/paint_project/project_file = file_being_opened
|
||||
current_workspace = project_file.workspace.copy()
|
||||
backing_file = WEAKREF(file_being_opened)
|
||||
opened_file_name = project_file.filename
|
||||
source_photo_or_painting = project_file.source_photo_or_painting
|
||||
if(/datum/computer_file/image)
|
||||
var/datum/computer_file/image/image_file = file_being_opened
|
||||
var/icon/image = image_file.stored_icon
|
||||
var/image_width = image.Width()
|
||||
var/image_height = image.Height()
|
||||
if(image_width <= 0 || image_height <= 0)
|
||||
dialog = list("type" = "error", "message" = "[file_name] - Invalid dimensions")
|
||||
return
|
||||
if(image_width > SANE_PHOTO_EDITING_SIZE_LIMIT || image_height > SANE_PHOTO_EDITING_SIZE_LIMIT)
|
||||
dialog = list("type" = "error", "message" = "[file_name] - Too large")
|
||||
return
|
||||
current_workspace = new(image_width, image_height)
|
||||
fill_grid_from_icon(current_workspace.get_first_layer_pixel_data(), image)
|
||||
source_photo_or_painting = image_file.source_photo_or_painting
|
||||
opened_file_type = base_supported_type
|
||||
|
||||
/datum/computer_file/program/nanopaint/proc/write_to_file(mob/user, datum/computer_file/file)
|
||||
switch(file.type)
|
||||
if(/datum/computer_file/data/paint_project)
|
||||
var/datum/computer_file/data/paint_project/project_file = file
|
||||
project_file.workspace = current_workspace.copy()
|
||||
project_file.set_source(source_photo_or_painting)
|
||||
backing_file = WEAKREF(project_file)
|
||||
if(/datum/computer_file/image)
|
||||
var/datum/computer_file/image/image_file = file
|
||||
image_file.stored_icon = current_workspace.to_icon()
|
||||
image_file.image_name = null
|
||||
image_file.assign_path()
|
||||
image_file.ref_appearance = null
|
||||
image_file.assign_ref_appearance()
|
||||
image_file.set_source(source_photo_or_painting)
|
||||
if(!source_photo_or_painting)
|
||||
image_file.author_ckey = user.ckey
|
||||
message_admins("[ADMIN_LOOKUP(user)] has saved a custom image to [computer] as [file.filename].[file.filetype].")
|
||||
log_player_image_creation("[key_name(user)] has saved a custom image to [computer] as [file.filename].[file.filetype]", user, image_file.stored_icon)
|
||||
|
||||
/datum/computer_file/program/nanopaint/proc/save_file(mob/user, name, file_type, obj/item/disk/computer/target_disk)
|
||||
var/datum/computer_file/file = new file_type()
|
||||
file.filename = reject_bad_name(name, allow_numbers = TRUE, cap_after_symbols = FALSE, cap_at_start = FALSE)
|
||||
var/file_stored
|
||||
if(target_disk)
|
||||
file_stored = target_disk.add_file(file)
|
||||
else
|
||||
file_stored = computer.store_file(file)
|
||||
if(file_stored)
|
||||
write_to_file(user, file)
|
||||
else
|
||||
dialog = list("type" = "error", "message" = "[name] - Unable to save file")
|
||||
SStgui.update_uis(computer)
|
||||
|
||||
/datum/computer_file/program/nanopaint/proc/close_workspace()
|
||||
backing_file = null
|
||||
opened_file_name = null
|
||||
opened_file_type = null
|
||||
current_workspace = null
|
||||
source_photo_or_painting = null
|
||||
source_on_undo_all = null
|
||||
palette = list()
|
||||
current_color = "#ffffffff"
|
||||
|
||||
/datum/computer_file/program/nanopaint/kill_program(mob/user)
|
||||
close_workspace()
|
||||
return ..()
|
||||
|
||||
#undef SANE_PHOTO_EDITING_SIZE_LIMIT
|
||||
#undef PALETTE_SIZE
|
||||
@@ -1,8 +1,3 @@
|
||||
|
||||
///how much paper it takes from the printer to create a canvas.
|
||||
#define CANVAS_PAPER_COST 10
|
||||
|
||||
|
||||
/**
|
||||
* ## the art gallery viewer/printer!
|
||||
*
|
||||
@@ -57,8 +52,10 @@
|
||||
search_mode = search_mode == PAINTINGS_FILTER_SEARCH_TITLE ? PAINTINGS_FILTER_SEARCH_CREATOR : PAINTINGS_FILTER_SEARCH_TITLE
|
||||
generate_matching_paintings_list()
|
||||
. = TRUE
|
||||
if("select")
|
||||
if("print")
|
||||
print_painting(params["selected"])
|
||||
if("download")
|
||||
download_painting(params["selected"])
|
||||
|
||||
/datum/computer_file/program/portrait_printer/proc/generate_matching_paintings_list()
|
||||
matching_paintings = null
|
||||
@@ -72,34 +69,23 @@
|
||||
if(computer.stored_paper < CANVAS_PAPER_COST)
|
||||
to_chat(usr, span_notice("Printing error: Your printer needs at least [CANVAS_PAPER_COST] paper to print a canvas."))
|
||||
return
|
||||
computer.stored_paper -= CANVAS_PAPER_COST
|
||||
|
||||
//canvas printing!
|
||||
var/datum/painting/chosen_portrait = locate(selected_painting) in SSpersistent_paintings.paintings
|
||||
|
||||
var/png = "data/paintings/images/[chosen_portrait.md5].png"
|
||||
var/icon/art_icon = new(png)
|
||||
var/obj/item/canvas/printed_canvas
|
||||
var/art_width = art_icon.Width()
|
||||
var/art_height = art_icon.Height()
|
||||
for(var/canvas_type in typesof(/obj/item/canvas))
|
||||
printed_canvas = canvas_type
|
||||
if(initial(printed_canvas.width) == art_width && initial(printed_canvas.height) == art_height)
|
||||
printed_canvas = new canvas_type(get_turf(computer.physical))
|
||||
break
|
||||
printed_canvas = null
|
||||
if(!printed_canvas)
|
||||
var/obj/item/canvas/new_canvas = chosen_portrait.spawn_canvas(get_turf(computer.physical))
|
||||
if(!new_canvas)
|
||||
to_chat(usr, span_notice("Printing error: An unknown error has occurred."))
|
||||
return
|
||||
printed_canvas.painting_metadata = chosen_portrait
|
||||
printed_canvas.fill_grid_from_icon(art_icon)
|
||||
printed_canvas.generated_icon = art_icon
|
||||
printed_canvas.icon_generated = TRUE
|
||||
printed_canvas.finalized = TRUE
|
||||
printed_canvas.name = "painting - [chosen_portrait.title]"
|
||||
///this is a copy of something that is already in the database- it should not be able to be saved.
|
||||
printed_canvas.no_save = TRUE
|
||||
printed_canvas.update_icon()
|
||||
computer.stored_paper -= CANVAS_PAPER_COST
|
||||
to_chat(usr, span_notice("You have printed [chosen_portrait.title] onto a new canvas."))
|
||||
playsound(computer.physical, 'sound/items/poster/poster_being_created.ogg', 100, TRUE)
|
||||
playsound(computer.physical, 'sound/machines/printer.ogg', 100, TRUE)
|
||||
|
||||
#undef CANVAS_PAPER_COST
|
||||
/datum/computer_file/program/portrait_printer/proc/download_painting(selected_painting)
|
||||
var/datum/painting/chosen_portrait = locate(selected_painting) in SSpersistent_paintings.paintings
|
||||
var/icon/portrait_icon = chosen_portrait.get_icon()
|
||||
var/datum/computer_file/image/image_file = new(portrait_icon, display_name = chosen_portrait.title, source_photo_or_painting = chosen_portrait)
|
||||
if(!computer.store_file(image_file, usr))
|
||||
to_chat(usr, span_notice("Unable to download [chosen_portrait.title].[/datum/computer_file/image::filetype]."))
|
||||
return
|
||||
to_chat(usr, span_notice("Downloaded [chosen_portrait.title].[/datum/computer_file/image::filetype]."))
|
||||
|
||||
@@ -195,7 +195,7 @@ GLOBAL_LIST_EMPTY(virtual_pets_list)
|
||||
|
||||
if(isnull(photo))
|
||||
return
|
||||
computer.store_file(new /datum/computer_file/picture(photo))
|
||||
computer.store_file(new /datum/computer_file/image(photo.picture_image, display_name = photo.picture_name))
|
||||
|
||||
/datum/computer_file/program/virtual_pet/proc/set_hat_offsets(new_dir)
|
||||
var/direction_text = dir2text(new_dir)
|
||||
|
||||
Reference in New Issue
Block a user