From 5938853bbc8bb2b2dc08aaf272bedac5d6b3161c Mon Sep 17 00:00:00 2001 From: Lexanx <61974560+Lexanx@users.noreply.github.com> Date: Sun, 4 Aug 2024 12:40:16 +0300 Subject: [PATCH] =?UTF-8?q?=D0=9A=D0=B0=D0=BC=D0=B5=D1=80=D1=8B=20=D0=9F?= =?UTF-8?q?=D0=BB=D0=B0=D0=BD=D1=88=D0=B5=D1=82=D1=8B,=20DNG=20=D1=84?= =?UTF-8?q?=D0=B0=D0=B9=D0=BB=D1=8B.=20=20(#2520)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- code/_helpers/icons.dm | 330 ++++++++++-------- code/game/objects/items/weapons/cards_ids.dm | 6 +- .../preference_setup/general/05_preview.dm | 2 +- code/modules/mob/examinations.dm | 14 + .../programs/generic/file_browser.dm | 24 +- .../file_system/reports/crew_record.dm | 10 +- .../hardware/nano_printer.dm | 13 + .../modular_computers/ntos/components.dm | 10 + code/modules/modular_computers/ntos/ui.dm | 14 +- code/modules/paperwork/photography.dm | 188 +++++----- code/modules/species/station/machine.dm | 2 + icons/blanks/32x32.dmi | Bin 0 -> 216 bytes icons/blanks/96x96.dmi | Bin 0 -> 222 bytes mods/RnD/_RnD.dme | 1 + mods/RnD/code/camera.dm | 42 +++ mods/RnD/code/design.dm | 21 ++ nano/templates/file_manager.tmpl | 8 +- nano/templates/layout_modern.tmpl | 3 + 18 files changed, 442 insertions(+), 246 deletions(-) create mode 100644 icons/blanks/32x32.dmi create mode 100644 icons/blanks/96x96.dmi create mode 100644 mods/RnD/code/camera.dm diff --git a/code/_helpers/icons.dm b/code/_helpers/icons.dm index 51744660aaf49..7aae8ebc172ae 100644 --- a/code/_helpers/icons.dm +++ b/code/_helpers/icons.dm @@ -418,157 +418,168 @@ Get flat icon by DarkCampainger. As it says on the tin, will return an icon with as a single icon. Useful for when you want to manipulate an icon via the above as overlays are not normally included. The _flatIcons list is a cache for generated icon files. */ +//[SIERRA-EDIT] +/proc/getFlatIcon(image/appearance, defdir, deficon, defstate, defblend, start = TRUE, no_anim = FALSE) + // Loop through the underlays, then overlays, sorting them into the layers list + #define PROCESS_OVERLAYS_OR_UNDERLAYS(flat, process, base_layer) \ + for (var/i in 1 to LAZYLEN(process)) { \ + var/image/current = process[i]; \ + if (!current) { \ + continue; \ + } \ + if (current.plane != FLOAT_PLANE && current.plane != appearance.plane) { \ + continue; \ + } \ + var/current_layer = current.layer; \ + if (current_layer < 0) { \ + if (current_layer <= -1000) { \ + return flat; \ + } \ + current_layer = base_layer + appearance.layer + current_layer / 1000; \ + } \ + for (var/index_to_compare_to in 1 to LAZYLEN(layers)) { \ + var/compare_to = layers[index_to_compare_to]; \ + if (current_layer < layers[compare_to]) { \ + layers.Insert(index_to_compare_to, current); \ + break; \ + } \ + } \ + layers[current] = current_layer; \ + } + + var/static/icon/flat_template = icon('icons/blanks/32x32.dmi', "nothing") + + if(!appearance || appearance.alpha <= 0) + return icon(flat_template) + + if(start) + if(!defdir) + defdir = appearance.dir + if(!deficon) + deficon = appearance.icon + if(!defstate) + defstate = appearance.icon_state + if(!defblend) + defblend = appearance.blend_mode + + var/curicon = appearance.icon || deficon + var/curstate = appearance.icon_state || defstate + var/curdir = (!appearance.dir || appearance.dir == SOUTH) ? defdir : appearance.dir + + var/render_icon = curicon + + if (render_icon) + var/curstates = icon_states(curicon) + if(!(curstate in curstates)) + if ("" in curstates) + curstate = "" + else + render_icon = FALSE + + var/base_icon_dir //We'll use this to get the icon state to display if not null BUT NOT pass it to overlays as the dir we have + + //Try to remove/optimize this section ASAP, CPU hog. + //Determines if there's directionals. + if(render_icon && curdir != SOUTH) + if ( + !length(icon_states(icon(curicon, curstate, NORTH))) \ + && !length(icon_states(icon(curicon, curstate, EAST))) \ + && !length(icon_states(icon(curicon, curstate, WEST))) \ + ) + base_icon_dir = SOUTH + + if(!base_icon_dir) + base_icon_dir = curdir + + var/curblend = appearance.blend_mode || defblend + + if(LAZYLEN(appearance.overlays) || LAZYLEN(appearance.underlays)) + var/icon/flat = icon(flat_template) + // Layers will be a sorted list of icons/overlays, based on the order in which they are displayed + var/list/layers = list() + var/image/copy + // Add the atom's icon itself, without pixel_x/y offsets. + if(render_icon) + copy = image(icon=curicon, icon_state=curstate, layer=appearance.layer, dir=base_icon_dir) + copy.color = appearance.color + copy.alpha = appearance.alpha + copy.blend_mode = curblend + layers[copy] = appearance.layer + + PROCESS_OVERLAYS_OR_UNDERLAYS(flat, appearance.underlays, 0) + PROCESS_OVERLAYS_OR_UNDERLAYS(flat, appearance.overlays, 1) + + var/icon/add // Icon of overlay being added + + var/flatX1 = 1 + var/flatX2 = flat.Width() + var/flatY1 = 1 + var/flatY2 = flat.Height() + + var/addX1 = 0 + var/addX2 = 0 + var/addY1 = 0 + var/addY2 = 0 + + for(var/image/layer_image as anything in layers) + if(layer_image.alpha == 0) + continue -/proc/getFlatIcon(image/A, defdir=2, deficon=null, defstate="", defblend=BLEND_DEFAULT, always_use_defdir = 0) - RETURN_TYPE(/icon) - // We start with a blank canvas, otherwise some icon procs crash silently - var/icon/flat = icon('icons/effects/effects.dmi', "icon_state"="nothing") // Final flattened icon - if(!A) - return flat - if(A.alpha <= 0) - return flat - var/noIcon = FALSE - - var/curicon - if(A.icon) - curicon = A.icon - else - curicon = deficon + if(layer_image == copy) // 'layer_image' is an /image based on the object being flattened. + curblend = BLEND_OVERLAY + add = icon(layer_image.icon, layer_image.icon_state, base_icon_dir) + else // 'I' is an appearance object. + add = getFlatIcon(image(layer_image), curdir, curicon, curstate, curblend, FALSE, no_anim) + if(!add) + continue - if(!curicon) - noIcon = TRUE // Do not render this object. + // Find the new dimensions of the flat icon to fit the added overlay + addX1 = min(flatX1, layer_image.pixel_x + 1) + addX2 = max(flatX2, layer_image.pixel_x + add.Width()) + addY1 = min(flatY1, layer_image.pixel_y + 1) + addY2 = max(flatY2, layer_image.pixel_y + add.Height()) - var/curstate - if(A.icon_state) - curstate = A.icon_state - else - curstate = defstate + if(addX1!=flatX1 || addX2!=flatX2 || addY1!=flatY1 || addY2!=flatY2) + // Resize the flattened icon so the new icon fits + flat.Crop(addX1-flatX1+1, addY1-flatY1+1, addX2-flatX1+1, addY2-flatY1+1) + flatX1=addX1;flatX2=addX2 + flatY1=addY1;flatY2=addY2 - if(!noIcon && !(curstate in icon_states(curicon))) - if("" in icon_states(curicon)) - curstate = "" - else - noIcon = TRUE // Do not render this object. + // Blend the overlay into the flattened icon + flat.Blend(add, blendMode2iconMode(curblend), layer_image.pixel_x + 2 - flatX1, layer_image.pixel_y + 2 - flatY1) - var/curdir - if(A.dir != 2 && !always_use_defdir) - curdir = A.dir - else - curdir = defdir + if(appearance.color) + if(islist(appearance.color)) + flat.MapColors(arglist(appearance.color)) + else + flat.Blend(appearance.color, ICON_MULTIPLY) - var/curblend - if(A.blend_mode == BLEND_DEFAULT) - curblend = defblend - else - curblend = A.blend_mode - - // Layers will be a sorted list of icons/overlays, based on the order in which they are displayed - var/list/layers = list() - var/image/copy - // Add the atom's icon itself, without pixel_x/y offsets. - if(!noIcon) - copy = image(icon=curicon, icon_state=curstate, layer=A.layer, dir=curdir) - copy.color = A.color - copy.alpha = A.alpha - copy.blend_mode = curblend - layers[copy] = A.layer + if(appearance.alpha < 255) + flat.Blend(rgb(255, 255, 255, appearance.alpha), ICON_MULTIPLY) - // Loop through the underlays, then overlays, sorting them into the layers list - var/list/process = A.underlays // Current list being processed - var/pSet=0 // Which list is being processed: 0 = underlays, 1 = overlays - var/curIndex=1 // index of 'current' in list being processed - var/current // Current overlay being sorted - var/currentLayer // Calculated layer that overlay appears on (special case for FLOAT_LAYER) - var/compare // The overlay 'add' is being compared against - var/cmpIndex // The index in the layers list of 'compare' - while(TRUE) - if(curIndex<=length(process)) - current = process[curIndex] - if(current) - currentLayer = current:layer - if(currentLayer<0) // Special case for FLY_LAYER - if(currentLayer <= -1000) return flat - if(pSet == 0) // Underlay - currentLayer = A.layer+currentLayer/1000 - else // Overlay - currentLayer = A.layer+(1000+currentLayer)/1000 - - // Sort add into layers list - for(cmpIndex=1,cmpIndex<=length(layers),cmpIndex++) - compare = layers[cmpIndex] - if(currentLayer < layers[compare]) // Associated value is the calculated layer - layers.Insert(cmpIndex,current) - layers[current] = currentLayer - break - if(cmpIndex>length(layers)) // Reached end of list without inserting - layers[current]=currentLayer // Place at end - - curIndex++ - else if(pSet == 0) // Switch to overlays - curIndex = 1 - pSet = 1 - process = A.overlays - else // All done - break - - var/icon/add // Icon of overlay being added - - // Current dimensions of flattened icon - var/flatX1=1 - var/flatX2=flat.Width() - var/flatY1=1 - var/flatY2=flat.Height() - // Dimensions of overlay being added - var/addX1 - var/addX2 - var/addY1 - var/addY2 - - for(var/I in layers) - - if(I:plane == EMISSIVE_PLANE) //Just replace this with whatever it is TG is doing these days sometime. Getflaticon breaks emissives - continue - - if(I:alpha == 0) - continue - - if(I == copy) // 'I' is an /image based on the object being flattened. - curblend = BLEND_OVERLAY - add = icon(I:icon, I:icon_state, I:dir) - else // 'I' is an appearance object. - if(istype(A,/obj/machinery/atmospherics) && (I in A.underlays)) - var/image/Im = I - add = getFlatIcon(new/image(I), Im.dir, curicon, curstate, curblend, 1) - else - add = getFlatIcon(new/image(I), curdir, curicon, curstate, curblend, always_use_defdir) - - // Find the new dimensions of the flat icon to fit the added overlay - addX1 = min(flatX1, I:pixel_x+1) - addX2 = max(flatX2, I:pixel_x+add.Width()) - addY1 = min(flatY1, I:pixel_y+1) - addY2 = max(flatY2, I:pixel_y+add.Height()) - - if(addX1!=flatX1 || addX2!=flatX2 || addY1!=flatY1 || addY2!=flatY2) - // Resize the flattened icon so the new icon fits - flat.Crop(addX1-flatX1+1, addY1-flatY1+1, addX2-flatX1+1, addY2-flatY1+1) - flatX1=addX1;flatX2=addX2 - flatY1=addY1;flatY2=addY2 - var/iconmode - if(I in A.overlays) - iconmode = ICON_OVERLAY - else if(I in A.underlays) - iconmode = ICON_UNDERLAY + if(no_anim) + //Clean up repeated frames + var/icon/cleaned = new /icon() + cleaned.Insert(flat, "", SOUTH, 1, 0) + return cleaned else - iconmode = blendMode2iconMode(curblend) - // Blend the overlay into the flattened icon - flat.Blend(add, iconmode, I:pixel_x + 2 - flatX1, I:pixel_y + 2 - flatY1) + return icon(flat, "", SOUTH) + else if (render_icon) // There's no overlays. + var/icon/final_icon = icon(icon(curicon, curstate, base_icon_dir), "", SOUTH, no_anim ? TRUE : null) - if(A.color) - flat.Blend(A.color, ICON_MULTIPLY) - if(A.alpha < 255) - flat.Blend(rgb(255, 255, 255, A.alpha), ICON_MULTIPLY) + if (appearance.alpha < 255) + final_icon.Blend(rgb(255,255,255, appearance.alpha), ICON_MULTIPLY) - return icon(flat, "", SOUTH) + if (appearance.color) + if (islist(appearance.color)) + final_icon.MapColors(arglist(appearance.color)) + else + final_icon.Blend(appearance.color, ICON_MULTIPLY) + + return final_icon + + #undef PROCESS_OVERLAYS_OR_UNDERLAYS +//[/SIERRA-EDIT] /proc/getIconMask(atom/A)//By yours truly. Creates a dynamic mask for a mob/whatever. /N RETURN_TYPE(/icon) @@ -646,7 +657,7 @@ The _flatIcons list is a cache for generated icon files. for(var/i = 1; gap + i <= length(result); i++) var/atom/l = result[i] //Fucking hate var/atom/r = result[gap+i] //how lists work here - if(l.plane > r.plane || (l.plane == r.plane && l.layer > r.layer)) //no "result[i].layer" for me + if(l.layer > r.layer) //no "result[i].layer" for me //[SIERRA-EDIT] result.Swap(i, gap + i) swapped = 1 return result @@ -656,6 +667,7 @@ arguments tx, ty, tz are target coordinates (requred), range defines render dist cap_mode is capturing mode (optional), user is capturing mob (requred only wehen cap_mode = CAPTURE_MODE_REGULAR), lighting determines lighting capturing (optional), suppress_errors suppreses errors and continues to capture (optional). */ +//[SIERRA-EDIT] /proc/generate_image(tx as num, ty as num, tz as num, range as num, cap_mode = CAPTURE_MODE_PARTIAL, mob/living/user, lighting = 1, suppress_errors = 1) RETURN_TYPE(/icon) var/list/turfstocapture = list() @@ -674,17 +686,30 @@ lighting determines lighting capturing (optional), suppress_errors suppreses err //Capture includes non-existan turfs if(!suppress_errors) return + + return generate_image_from_turfs(locate(tx, ty, tz), turfstocapture, range, cap_mode, user, lighting) + +/proc/generate_image_from_turfs(turf/topleft, list/turf/turfstocapture, range as num, cap_mode = CAPTURE_MODE_PARTIAL, mob/living/user, lighting = TRUE) + var/tx = topleft.x + var/ty = topleft.y //Lines below determine what objects will be rendered var/list/atoms = list() + var/list/render_lighting = list() for(var/turf/T in turfstocapture) - atoms.Add(T) + atoms += T for(var/atom/A in T) - if(istype(A, /atom/movable/lighting_overlay) && lighting) //Special case for lighting - atoms.Add(A) + + if(istype(A, /atom/movable/lighting_overlay)) //Special case for lighting + if(lighting) + render_lighting.Add(A) continue - if(A.invisibility) continue - atoms.Add(A) - //Lines below actually render all colected data + + if(A.invisibility) + continue + + atoms += A + + //Lines below actually render all collected data atoms = sort_atoms_by_layer(atoms) var/icon/cap = icon('icons/effects/96x96.dmi', "") cap.Scale(range*32, range*32) @@ -699,4 +724,13 @@ lighting determines lighting capturing (optional), suppress_errors suppreses err var/yoff = (A.y - ty) * 32 cap.Blend(img, blendMode2iconMode(A.blend_mode), A.pixel_x + xoff, A.pixel_y + yoff) + if(lighting) + for(var/atom/movable/lighting_overlay/lighting_overlay as anything in render_lighting) + var/icon/lighting_overlay_icon = getFlatIcon(lighting_overlay) + var/x_offset = (lighting_overlay.x - tx) * world.icon_size + var/y_offset = (lighting_overlay.y - ty) * world.icon_size + cap.Blend(lighting_overlay_icon, ICON_MULTIPLY, lighting_overlay.pixel_x + x_offset, lighting_overlay.pixel_y + y_offset) + return cap + +//[/SIERRA-EDIT] diff --git a/code/game/objects/items/weapons/cards_ids.dm b/code/game/objects/items/weapons/cards_ids.dm index f7d95b297afd1..39197584a6586 100644 --- a/code/game/objects/items/weapons/cards_ids.dm +++ b/code/game/objects/items/weapons/cards_ids.dm @@ -294,8 +294,10 @@ var/global/const/NO_EMAG_ACT = -50 /obj/item/card/id/proc/set_id_photo(mob/M) M.ImmediateOverlayUpdate() - front = getFlatIcon(M, SOUTH, always_use_defdir = 1) - side = getFlatIcon(M, WEST, always_use_defdir = 1) +//[SIERRA-EDIT] + front = getFlatIcon(M, SOUTH) + side = getFlatIcon(M, WEST) +//[/SIERRA-EDIT] /mob/proc/set_id_info(obj/item/card/id/id_card) id_card.age = 0 diff --git a/code/modules/client/preference_setup/general/05_preview.dm b/code/modules/client/preference_setup/general/05_preview.dm index 1bd2ef4900a1f..d71e6774bf8da 100644 --- a/code/modules/client/preference_setup/general/05_preview.dm +++ b/code/modules/client/preference_setup/general/05_preview.dm @@ -116,7 +116,7 @@ // last_built_icon.Blend(getFlatIcon(mannequin, NORTH, always_use_defdir = TRUE), ICON_OVERLAY, 25, 17) // CHECK_TICK mannequin.dir = man_dir - last_built_icon.Blend(getFlatIcon(mannequin, man_dir, always_use_defdir = TRUE), ICON_OVERLAY, 25, 3) + last_built_icon.Blend(getFlatIcon(mannequin, man_dir), ICON_OVERLAY, 25, 3) //[SIERRA-EDIT] preview_icon = new (last_built_icon) var/scale = client.get_preference_value(/datum/client_preference/preview_scale) switch (scale) diff --git a/code/modules/mob/examinations.dm b/code/modules/mob/examinations.dm index bf500a35d38c6..f7a12cea24dcc 100644 --- a/code/modules/mob/examinations.dm +++ b/code/modules/mob/examinations.dm @@ -28,6 +28,20 @@ crash_with("Improper /examine() override: [log_info_line(A)]") if (!A.LateExamine(user, distance, is_adjacent)) crash_with("Improper /LateExamine() override: [log_info_line(A)]") +//[SIERRA-ADD] + if(user.is_species(SPECIES_IPC)) + ecscamera(user, A) + + +/proc/ecscamera(mob/living/carbon/human/user, atom/A) + if(istype(user, /mob/living/carbon/human)) + var/mob/living/carbon/human/M = user + if(M.internal_organs_by_name[BP_EXONET]) + var/obj/item/organ/internal/ecs/ecs = M.internal_organs_by_name[BP_EXONET] + if(ecs.computer.in_camera_mode) + ecs.computer.hard_drive.create_file(ecs.computer.camera.captureimagecomputer(A, usr)) + to_chat(usr, SPAN_NOTICE("You took a photo of \the [A].")) +//[/SIERRA-ADD] /mob/proc/ForensicsExamination(atom/A, distance, is_adjacent) if (!(get_skill_value(SKILL_FORENSICS) >= SKILL_EXPERIENCED && distance <= (get_skill_value(SKILL_FORENSICS) - SKILL_TRAINED))) diff --git a/code/modules/modular_computers/file_system/programs/generic/file_browser.dm b/code/modules/modular_computers/file_system/programs/generic/file_browser.dm index eb3395399f5a4..a87a393778d83 100644 --- a/code/modules/modular_computers/file_system/programs/generic/file_browser.dm +++ b/code/modules/modular_computers/file_system/programs/generic/file_browser.dm @@ -84,11 +84,17 @@ if(!open_file) return var/datum/computer_file/data/F = computer.get_file(open_file) - if(!istype(F)) - return - if(!computer.print_paper(F.generate_file_data(),F.filename,F.papertype, F.metadata)) - error = "Hardware error: Unable to print the file." - return +//[SIERRA-EDIT] + var/datum/computer_file/binary/photo/P = computer.get_file(open_file) + if(istype(F)) + if(!computer.print_paper(F.generate_file_data(),F.filename,F.papertype, F.metadata)) + error = "Hardware error: Unable to print the file." + return + if(istype(P)) + if(!computer.print_photo(P.photo, P.filename)) + error = "Hardware error: Unable to print the photo." + return +//[/SIERRA-EDIT] if(.) SSnano.update_uis(NM) @@ -107,7 +113,13 @@ data["error"] = "I/O ERROR: Unable to access hard drive." else var/datum/computer_file/data/F = PRG.computer.get_file(PRG.open_file) - if(!istype(F)) + //[SIERRA-ADD] - MODPACK RND + var/datum/computer_file/binary/photo/P = PRG.computer.get_file(PRG.open_file) + if(istype(P)) + data["filename"] = "[P.filename].[P.filetype]" + data["photodata"] = P.generate_photo_data(user, P.photo) + //[/SIERRA-ADD] - MODPACK RND + else if(!istype(F)) data["error"] = "I/O ERROR: Unable to open file." else data["filedata"] = F.generate_file_data(user) diff --git a/code/modules/modular_computers/file_system/reports/crew_record.dm b/code/modules/modular_computers/file_system/reports/crew_record.dm index 60faad5304c93..b11394bb97af8 100644 --- a/code/modules/modular_computers/file_system/reports/crew_record.dm +++ b/code/modules/modular_computers/file_system/reports/crew_record.dm @@ -25,12 +25,14 @@ GLOBAL_VAR_INIT(arrest_security_status, "Arrest") /datum/computer_file/report/crew_record/proc/load_from_mob(mob/living/carbon/human/H) if(istype(H)) H.ImmediateOverlayUpdate() - photo_front = getFlatIcon(H, SOUTH, always_use_defdir = 1) - photo_side = getFlatIcon(H, WEST, always_use_defdir = 1) +//[SIERRA-EDIT] + photo_front = getFlatIcon(H, SOUTH) + photo_side = getFlatIcon(H, WEST) else var/mob/living/carbon/human/dummy = new() - photo_front = getFlatIcon(dummy, SOUTH, always_use_defdir = 1) - photo_side = getFlatIcon(dummy, WEST, always_use_defdir = 1) + photo_front = getFlatIcon(dummy, SOUTH) + photo_side = getFlatIcon(dummy, WEST) +//[/SIERRA-EDIT] qdel(dummy) // Add honorifics, etc. diff --git a/code/modules/modular_computers/hardware/nano_printer.dm b/code/modules/modular_computers/hardware/nano_printer.dm index e2a26634711a2..1ace7b7eea109 100644 --- a/code/modules/modular_computers/hardware/nano_printer.dm +++ b/code/modules/modular_computers/hardware/nano_printer.dm @@ -30,6 +30,19 @@ T.visible_message(SPAN_NOTICE("\The [src] prints out a paper.")) return TRUE +//[SIERRA-ADD] +/obj/item/stock_parts/computer/nano_printer/proc/print_picture() + . = FALSE + if(printer_ready()) + last_print = world.time + var/turf/T = get_turf(src) + stored_paper -= 5 + playsound(T, "sound/machines/dotprinter.ogg", 30) + T.visible_message(SPAN_NOTICE("\The [src] prints out a photo.")) + //just to spend paper, real photos are printerd /datum/extension/interactive/ntos/proc/print_photo(content, title) + return TRUE +//[/SIERRA-ADD] + /obj/item/stock_parts/computer/nano_printer/proc/printer_ready() if(!stored_paper) return FALSE diff --git a/code/modules/modular_computers/ntos/components.dm b/code/modules/modular_computers/ntos/components.dm index 0fdb95be1cb14..1e297b6b51c0d 100644 --- a/code/modules/modular_computers/ntos/components.dm +++ b/code/modules/modular_computers/ntos/components.dm @@ -19,6 +19,16 @@ if(printer) return printer.print_text(content, title) +//[SIERRA-ADD] +/datum/extension/interactive/ntos/proc/print_photo(photo, name) + var/obj/item/stock_parts/computer/nano_printer/printer = get_component(PART_PRINTER) + if(printer) + var/obj/item/modular_computer/c = holder + var/user = usr + c.camera.printpicturecomputer(user, photo) + return printer.print_picture() +//[/SIERRA-ADD] + /// Returns the network tag that other computers trying to reach it would see. /datum/extension/interactive/ntos/proc/get_network_tag_incoming() var/obj/item/stock_parts/computer/network_card/network_card = get_component(PART_NETWORK) diff --git a/code/modules/modular_computers/ntos/ui.dm b/code/modules/modular_computers/ntos/ui.dm index e013b9c6f740e..2b2a90ee978bd 100644 --- a/code/modules/modular_computers/ntos/ui.dm +++ b/code/modules/modular_computers/ntos/ui.dm @@ -37,7 +37,12 @@ data["updating"] = updating data["update_progress"] = update_progress data["updates"] = updates - +//[SIERRA-ADD] + var/obj/item/modular_computer/c = holder + if(istype(c)) + if(c.in_camera_mode) + data["in_camera_mode"] = 1 +//[/SIERRA-ADD] ui = SSnano.try_update_ui(user, src, ui_key, ui, data, force_open) if (!ui) ui = new(user, src, ui_key, "laptop_mainscreen.tmpl", "NTOS Main Menu ", 400, 500) @@ -87,6 +92,13 @@ if( href_list["PC_minimize"] ) minimize_program(usr) return TOPIC_HANDLED +//[SIERRA-ADD] + var/obj/item/modular_computer/c = holder + if(istype(c)) + if( href_list["PC_camera"] ) + camera() + return TOPIC_HANDLED +//[/SIERRA-ADD] if( href_list["PC_killprogram"] ) var/datum/computer_file/program/P = get_file(href_list["PC_killprogram"]) diff --git a/code/modules/paperwork/photography.dm b/code/modules/paperwork/photography.dm index 871f78a3cf223..34dd28e82dca3 100644 --- a/code/modules/paperwork/photography.dm +++ b/code/modules/paperwork/photography.dm @@ -33,7 +33,7 @@ var/global/photo_count = 0 var/id var/icon/img //Big photo image var/scribble //Scribble on the back. - var/image/tiny + var/icon/tiny var/photo_size = 3 /obj/item/photo/Initialize() @@ -43,21 +43,6 @@ var/global/photo_count = 0 /obj/item/photo/attack_self(mob/user as mob) examinate(user, src) -/obj/item/photo/on_update_icon() - ClearOverlays() - var/scale = 8/(photo_size*32) - var/image/small_img = image(img) - small_img.SetTransform(scale = scale) - small_img.pixel_x = -32*(photo_size-1)/2 - 3 - small_img.pixel_y = -32*(photo_size-1)/2 - AddOverlays(small_img) - - tiny = image(img) - tiny.SetTransform(scale = 0.5 * scale) - tiny.underlays += image('icons/obj/bureaucracy.dmi',"photo") - tiny.pixel_x = -32*(photo_size-1)/2 - 3 - tiny.pixel_y = -32*(photo_size-1)/2 + 3 - /obj/item/photo/use_tool(obj/item/item, mob/living/user, list/click_params) if(istype(item, /obj/item/pen)) var/txt = sanitize(input(user, "What would you like to write on the back?", "Photo Writing", null) as text, 128) @@ -145,91 +130,103 @@ var/global/photo_count = 0 icon = 'icons/obj/tools/photography.dmi' desc = "A polaroid camera." icon_state = "camera" - item_state = "electropack" - w_class = ITEM_SIZE_SMALL - obj_flags = OBJ_FLAG_CONDUCTIBLE slot_flags = SLOT_BELT matter = list(MATERIAL_ALUMINIUM = 1000, MATERIAL_PLASTIC = 750) + var/black_white = FALSE var/pictures_max = 10 var/pictures_left = 10 var/on = 1 var/icon_on = "camera" var/icon_off = "camera_off" var/size = 3 -/obj/item/device/camera/on_update_icon() - var/datum/extension/base_icon_state/bis = get_extension(src, /datum/extension/base_icon_state) - if(on) - icon_state = "[bis.base_icon_state]" - else - icon_state = "[bis.base_icon_state]_off" -/obj/item/device/camera/Initialize() - set_extension(src, /datum/extension/base_icon_state, icon_state) - update_icon() - . = ..() +//[SIERRA-EDIT] + var/Flash = FALSE + /obj/item/device/camera/verb/change_size() set name = "Set Photo Focus" set category = "Object" + set src in usr + var/nsize = input("Photo Size","Pick a size of resulting photo.") as null|anything in list(1,3,5,7) if(nsize) size = nsize - to_chat(usr, SPAN_NOTICE("Camera will now take [size]x[size] photos.")) + to_chat(usr, "Camera will now take [size]x[size] photos.") + +/obj/item/device/camera/verb/set_flash() + set name = "Set Flash" + set category = "Object" + set src in usr + + Flash = !Flash + to_chat(usr, "You switch the camera's flash [Flash ? "on" : "off"].") + /obj/item/device/camera/attack_self(mob/user as mob) on = !on - update_icon() + if(on) + src.icon_state = icon_on + else + src.icon_state = icon_off to_chat(user, "You switch the camera [on ? "on" : "off"].") return - /obj/item/device/camera/use_tool(obj/item/tool, mob/user, list/click_params) - // Camera Film - Add film - if (istype(tool, /obj/item/device/camera_film)) - if (pictures_left) - USE_FEEDBACK_FAILURE("\The [src] already has film in it.") + if(istype(tool, /obj/item/device/camera_film)) + if(pictures_left) + to_chat(user, "[src] still has some film in it!") return TRUE - pictures_left = pictures_max - user.visible_message( - SPAN_NOTICE("\The [user] adds \a [tool] to \a [src]."), - SPAN_NOTICE("You add \the [tool] to \the [src]."), - range = 2 - ) + to_chat(user, "You insert [tool] into [src].") + user.drop_from_inventory(tool, get_turf(src)) qdel(tool) + pictures_left = pictures_max return TRUE - return ..() +/obj/item/device/camera/AltClick(mob/user) + change_size() /obj/item/device/camera/proc/get_mobs(turf/the_turf as turf) var/mob_detail for(var/mob/living/carbon/A in the_turf) if(A.invisibility) continue var/holding = null - var/list/held_items = A.GetAllHeld() - if (length(held_items)) - holding = "They are holding [english_list(A.GetAllHeld())]" + if(A.l_hand || A.r_hand) + if(A.l_hand) holding = "They are holding \a [A.l_hand]" + if(A.r_hand) + if(holding) + holding += " and \a [A.r_hand]" + else + holding = "They are holding \a [A.r_hand]" if(!mob_detail) - mob_detail = "You can see [A] on the photo[(A.health / A.maxHealth) < 0.75 ? " - [A] looks hurt":""].[holding ? " [holding]":"."]. " + mob_detail = "You can see [A] in the photo[A:health < 75 ? " - [A] looks hurt":""].[holding ? " [holding]":"."]. " else - mob_detail += "You can also see [A] on the photo[(A.health / A.maxHealth)< 0.75 ? " - [A] looks hurt":""].[holding ? " [holding]":"."]." + mob_detail += "You can also see [A] in the photo[A:health < 75 ? " - [A] looks hurt":""].[holding ? " [holding]":"."]." return mob_detail /obj/item/device/camera/afterattack(atom/target as mob|obj|turf|area, mob/user as mob, flag) - if(!on || !pictures_left || ismob(target.loc)) return - captureimage(target, user, flag) + if(!on || ismob(target.loc)) return + if(pictures_left > 0) + captureimage(target, user, flag) + pictures_left-- - playsound(loc, pick('sound/items/polaroid1.ogg', 'sound/items/polaroid2.ogg'), 75, 1, -3) + if(Flash) + set_light(4, 2, light_color) + addtimer(new Callback(src, PROC_REF(finish)), 5) - pictures_left-- - to_chat(user, SPAN_NOTICE("[pictures_left] photos left.")) + playsound(loc, pick('sound/items/polaroid1.ogg', 'sound/items/polaroid2.ogg'), 75, 1, -3) + to_chat(user, "[pictures_left] photos left.") + icon_state = icon_off on = 0 - update_icon() + addtimer(new Callback(src, PROC_REF(ready_to_use)), 2 SECONDS) + + +/obj/item/device/camera/proc/ready_to_use() + icon_state = icon_on + on = 1 -/obj/item/device/camera/examine(mob/user) - . = ..() - to_chat(user, "It has [pictures_left] photo\s left.") //Proc for capturing check /mob/living/proc/can_capture_turf(turf/T) @@ -240,50 +237,76 @@ var/global/photo_count = 0 return can_see /obj/item/device/camera/proc/captureimage(atom/target, mob/living/user, flag) - var/x_c = target.x - (size-1)/2 - var/y_c = target.y + (size-1)/2 - var/z_c = target.z - var/mobs = "" - for(var/i = 1 to size) - for(var/j = 1 to size) - var/turf/T = locate(x_c, y_c, z_c) - if(user.can_capture_turf(T)) - mobs += get_mobs(T) - x_c++ - y_c-- - x_c = x_c - size - - var/obj/item/photo/p = createpicture(target, user, mobs, flag) + var/obj/item/photo/p = createpicture(get_turf(target), user, flag) printpicture(user, p) -/obj/item/device/camera/proc/createpicture(atom/target, mob/user, mobs, flag) +/obj/item/device/camera/proc/finish() + set_light(0) + +/obj/item/device/camera/proc/createpicture(atom/target, mob/living/user, flag) + var/mobs = "" + var/list/turfs = list() + + FOR_DVIEW(var/turf/T, size, target, INVISIBILITY_LIGHTING) + if (user.can_capture_turf(T)) + mobs += get_mobs(T) + turfs += T + + END_FOR_DVIEW + var/x_c = target.x - (size-1)/2 var/y_c = target.y - (size-1)/2 var/z_c = target.z - var/icon/photoimage = generate_image(x_c, y_c, z_c, size, CAPTURE_MODE_REGULAR, user, 0) + var/icon/photoimage + + var/turf/topleft = locate(x_c, y_c, z_c) + if (!topleft) + return null + + if(Flash) + photoimage = generate_image_from_turfs(topleft, turfs, size, CAPTURE_MODE_REGULAR, user, lighting = FALSE) + else + photoimage = generate_image_from_turfs(topleft, turfs, size, CAPTURE_MODE_REGULAR, user, lighting = TRUE) + + var/icon/small_img = icon(photoimage) + var/icon/tiny_img = icon(photoimage) + var/icon/ic = icon('icons/obj/tools/photography.dmi',"photo") + var/icon/pc = icon('icons/obj/bureaucracy.dmi', "photo") + small_img.Scale(8, 8) + tiny_img.Scale(4, 4) + ic.Blend(small_img,ICON_OVERLAY, 10, 13) + pc.Blend(tiny_img,ICON_OVERLAY, 12, 19) var/obj/item/photo/p = new() + p.name = "photo" + p.icon = ic + p.tiny = pc p.img = photoimage + if(black_white) + p.img.MapColors(rgb(77,77,77), rgb(150,150,150), rgb(28,28,28), rgb(0,0,0)) + p.tiny.MapColors(rgb(77,77,77), rgb(150,150,150), rgb(28,28,28), rgb(0,0,0)) p.desc = mobs + p.pixel_x = rand(-10, 10) + p.pixel_y = rand(-10, 10) p.photo_size = size - p.update_icon() return p /obj/item/device/camera/proc/printpicture(mob/user, obj/item/photo/p) - if(!user.put_in_inactive_hand(p)) - p.dropInto(loc) + p.forceMove(user.loc) + if(!user.get_inactive_hand()) + user.put_in_inactive_hand(p) /obj/item/photo/proc/copy(copy_id = 0) var/obj/item/photo/p = new/obj/item/photo() - p.SetName(name) // Do this first, manually, to make sure listeners are alerted properly. - p.appearance = appearance - - p.tiny = new - p.tiny.appearance = tiny.appearance + p.name = name + p.icon = icon(icon, icon_state) + p.tiny = icon(tiny) p.img = icon(img) - + p.desc = desc + p.pixel_x = pixel_x + p.pixel_y = pixel_y p.photo_size = photo_size p.scribble = scribble @@ -291,3 +314,4 @@ var/global/photo_count = 0 p.id = id return p +//[/SIERRA-EDIT] diff --git a/code/modules/species/station/machine.dm b/code/modules/species/station/machine.dm index 8f0d9ebfa902e..9c2df0fffef5e 100644 --- a/code/modules/species/station/machine.dm +++ b/code/modules/species/station/machine.dm @@ -38,6 +38,8 @@ spawn_flags = SPECIES_CAN_JOIN | SPECIES_IS_WHITELISTED | SPECIES_NO_FBP_CONSTRUCTION appearance_flags = SPECIES_APPEARANCE_HAS_UNDERWEAR | SPECIES_APPEARANCE_HAS_EYE_COLOR //IPCs can wear undies too :( + var/in_camera_mode = 0 //special fot ECS photos [SIERRA-ADD] + blood_color = "#1f181f" flesh_color = "#575757" diff --git a/icons/blanks/32x32.dmi b/icons/blanks/32x32.dmi new file mode 100644 index 0000000000000000000000000000000000000000..6c4f2b33e0fee9d3f99ff3febc0760cfd078aed2 GIT binary patch literal 216 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnL3?x0byx0z;m;-!5Tn`*LkmkKF1;}MA3GxeO zaCmkj4amu^3W+FjNi9w;$}A|!%+F(BsF)KRR!~&>{Y!Ac$FEPcymhtCojD)8A=Kca z@q&JF>8>?&JF>8>?r978O6lM^IZ7bl4HG(F^GV0pm6sL^WE Q0hDF%boFyt=akR{025G63;+NC literal 0 HcmV?d00001 diff --git a/mods/RnD/_RnD.dme b/mods/RnD/_RnD.dme index 48896794bcf2b..c53f9809c21e4 100644 --- a/mods/RnD/_RnD.dme +++ b/mods/RnD/_RnD.dme @@ -14,6 +14,7 @@ #include "code/tech_robotics.dm" #include "code/binary.dm" #include "code/design.dm" +#include "code/camera.dm" #include "code/sciefolder.dm" #include "code/misc.dm" diff --git a/mods/RnD/code/camera.dm b/mods/RnD/code/camera.dm new file mode 100644 index 0000000000000..14a3f92791eb0 --- /dev/null +++ b/mods/RnD/code/camera.dm @@ -0,0 +1,42 @@ +/obj/item/device/camera/computer + name = "device camera" + var/photo_num = 0 + +/obj/item/modular_computer + var/obj/item/device/camera/computer/camera = new /obj/item/device/camera/computer + var/in_camera_mode = 0 + + +/obj/item/modular_computer/afterattack(atom/target as mob|obj|turf|area, mob/user as mob, flag) + . = ..() + if(in_camera_mode) + hard_drive.create_file(camera.captureimagecomputer(target, usr)) + to_chat(usr, SPAN_NOTICE("You took a photo of \the [target].")) + in_camera_mode = 0 + + +/obj/item/device/camera/computer/proc/captureimagecomputer(atom/target, mob/living/user, flag) + set_light(3, 3, light_color) + var/obj/item/photo/p = createpicture(get_turf(target), user, flag) + addtimer(new Callback(src, PROC_REF(finish)), 5) + var/datum/computer_file/binary/photo/file = new + file.photo = p + file.set_filename(++photo_num) + file.assetname = "[rand(0,999)][rand(0,999)][rand(0,999)].png" + register_asset(file.assetname, p.img) + return file + + +/obj/item/device/camera/computer/proc/printpicturecomputer(mob/user, obj/item/photo/p) + var/obj/item/photo/newp = new(get_turf(src), p) + newp = p.copy(p.id) + user.put_in_hands(newp) + + + +/datum/extension/interactive/ntos/proc/camera() + var/obj/item/modular_computer/c = holder + if(c.in_camera_mode) + c.in_camera_mode = 0 + else + c.in_camera_mode = 1 diff --git a/mods/RnD/code/design.dm b/mods/RnD/code/design.dm index 13d2962009700..835e2ce8a3d04 100644 --- a/mods/RnD/code/design.dm +++ b/mods/RnD/code/design.dm @@ -24,6 +24,26 @@ data["filename"] = filename return data + +/datum/computer_file/binary/photo + filetype = "DNG" + size = 4 + var/obj/item/photo/photo + var/assetname + +/datum/computer_file/binary/photo/clone() + var/datum/computer_file/binary/photo/F = ..() + F.photo = photo + F.assetname = assetname + return F + +/datum/computer_file/binary/photo/proc/set_filename(new_name) + filename = sanitizeFileName("photo [new_name]") + +/datum/computer_file/binary/photo/proc/generate_photo_data(mob/user, photo) + send_asset(user.client, assetname) + return "
" + /datum/computer_file/binary/sci filetype = "SF" // Science Folded size = 1 @@ -37,3 +57,4 @@ var/datum/computer_file/binary/sci/F = ..() F.uniquekey = uniquekey return F + diff --git a/nano/templates/file_manager.tmpl b/nano/templates/file_manager.tmpl index f8a1f38af92de..cdd090900cb00 100644 --- a/nano/templates/file_manager.tmpl +++ b/nano/templates/file_manager.tmpl @@ -11,7 +11,12 @@ {{:helper.link('EDIT', null, { "PRG_edit" : 1 })}} {{:helper.link('PRINT', null, { "PRG_printfile" : 1 })}}
- {{:data.filedata}} + {{if data.photodata}} + {{:data.photodata}} + {{/if}} + {{if data.filedata}} + {{:data.filedata}} + {{/if}} {{else}}

Available files (local):

@@ -54,5 +59,4 @@ {{/if}} {{:helper.link('NEW DATA FILE', null, { "PRG_newtextfile" : 1 })}} {{/if}} - {{/if}} diff --git a/nano/templates/layout_modern.tmpl b/nano/templates/layout_modern.tmpl index 25754cb926a28..a745878aa9421 100644 --- a/nano/templates/layout_modern.tmpl +++ b/nano/templates/layout_modern.tmpl @@ -34,6 +34,9 @@
+ {{if !data.PC_showexitprogram}} + {{:helper.link('', 'image', {'PC_camera' : 1}, null, data.in_camera_mode ? 'yellowButton' : null)}} + {{/if}} {{if data.PC_showexitprogram}} {{:helper.link('', 'minusthick', {'PC_minimize' : 1})}} {{:helper.link('', 'closethick', {'PC_exit' : 1})}}