Files
Bubberstation/code/modules/photography/camera/camera_image_capturing.dm
T
_0StevenandGitHub f324e6dd0c Allows taking pictures over advanced consoles or looking up/down, and fixes some multi-z photography issues. (#91632)
## About The Pull Request

I wanted to allow you to take pictures over advanced camera consoles for
heist planning style point reasons, and parity with the AI, and in the
process found a small pile of bugs.
So, this aims to fix a pile of those, and slightly clean up the code in
the process.

The biggest part of this change involves making the multi-z part of
photography code properly account for turfs below open or glass turfs,
rather than trying to skip to the bottom. This needs to be done because
objects can exist above those open turfs, most commonly
lattices/catwalks, but also nograv stuff.

...Parallax is its whole own other issue with how photography works
right now, so uh. Yeah, no.

Sadly I couldn't get this to work with regular camera consoles, and as I
found it to be an entirely different can of worms that would only
slightly touch this code I decided that would be best explored in a
separate pr.
## Why It's Good For The Game

I think it's cute to be able to take a picture of your screen and show
it off to people, maybe even combine it with a detective board for a
cool little planning thing. And the AI can take pictures over the
cameranet so, y'know.
Doing so while looking up/down was a side-effect, but really I think
it's fine to let you photograph it if you can see it.

Less multi-z jank is good 👍

### Actual Pictures:
<details>
  <summary>Unfold</summary>
  

![image](https://github.com/user-attachments/assets/322ba400-e72e-4e0e-b974-6ba60372103f)

![image](https://github.com/user-attachments/assets/aaa0ffd4-7313-44ea-bc73-ac55b4c2a51b)

![image](https://github.com/user-attachments/assets/f5647b02-60c4-4d46-a9a7-6f1541550e1d)

![image](https://github.com/user-attachments/assets/49498845-9fdc-4907-b660-96989c0b28fb)

![image](https://github.com/user-attachments/assets/d8c19118-84f2-4ab6-b8c0-0d5427ed9262)

![image](https://github.com/user-attachments/assets/1ade6619-4d7f-42ce-8063-041e8fe58dfb)

![image](https://github.com/user-attachments/assets/b5d14478-4ce0-4f5c-a9bf-1ef927103d85)

</details>

## Changelog
🆑
add: You can take pictures over advanced camera consoles.
add: You can take pictures while looking up or down.
fix: AIs taking pictures in multi-z shows the objects of your floor and
the ones below, instead of just the ones below.
fix: Humans taking pictures through holes in multi-z actually shows the
floor below, instead of pitch-black nothingness.
fix: Taking pictures of glass floors in multi-z actually shows the floor
below.
/🆑
2025-06-19 18:53:25 +00:00

155 lines
5.2 KiB
Plaintext

/obj/effect/appearance_clone
/obj/effect/appearance_clone/New(loc, atom/our_atom) //Intentionally not Initialize(), to make sure the clone assumes the intended appearance in time for the camera getFlatIcon.
if(!istype(our_atom))
return ..()
if(!isopenspaceturf(our_atom))
appearance = our_atom.appearance
dir = our_atom.dir
if(ismovable(our_atom))
var/atom/movable/our_movable = our_atom
step_x = our_movable.step_x
step_y = our_movable.step_y
return ..()
#define PHYSICAL_POSITION(atom) ((atom.y * ICON_SIZE_Y) + (atom.pixel_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/list/lighting = list()
var/skip_normal = FALSE
var/wipe_atoms = FALSE
var/mutable_appearance/backdrop = mutable_appearance('icons/hud/screen_gen.dmi', "flash")
backdrop.blend_mode = BLEND_OVERLAY
backdrop.color = "#292319"
if(istype(clone_area) && total_x == clone_area.width && total_y == clone_area.height && size_x >= 0 && size_y > 0)
var/turf/bottom_left = clone_area.bottom_left_turfs[1]
var/cloned_center_x = round(bottom_left.x + ((total_x - 1) / 2))
var/cloned_center_y = round(bottom_left.y + ((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, bottom_left.z)
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)
if(T.lighting_object)
var/obj/effect/appearance_clone/lighting_overlay = new(newT)
lighting_overlay.appearance = T.lighting_object.current_underlay
lighting_overlay.underlays += backdrop
lighting_overlay.blend_mode = BLEND_MULTIPLY
lighting += lighting_overlay
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, bottom_left.z)
if(!skip_normal)
for(var/i in turfs)
var/turf/T = i
atoms += T
if(T.lighting_object)
var/obj/effect/appearance_clone/lighting_overlay = new(T)
lighting_overlay.appearance = T.lighting_object.current_underlay
lighting_overlay.underlays += backdrop
lighting_overlay.blend_mode = BLEND_MULTIPLY
lighting += lighting_overlay
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/blanks/96x96.dmi', "nothing")
res.Scale(psize_x, psize_y)
atoms += lighting
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.plane > c.plane)
continue
if(c2.plane < c.plane)
break
var/c_position = PHYSICAL_POSITION(c)
var/c2_position = PHYSICAL_POSITION(c2)
// If you are above me, I layer above you
if(c2_position - 32 >= c_position)
break
// If I am above you you will always layer above me
if(c2_position <= c_position - 32)
continue
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
if(!skip_normal) //these are not clones
for(var/atom/A in sorted)
var/xo = (A.x - center.x) * ICON_SIZE_X + A.pixel_x + xcomp
var/yo = (A.y - center.y) * ICON_SIZE_Y + A.pixel_y + ycomp
if(ismovable(A))
var/atom/movable/AM = A
xo += AM.step_x
yo += AM.step_y
var/icon/img = getFlatIcon(A, no_anim = TRUE)
res.Blend(img, blendMode2iconMode(A.blend_mode), xo, yo)
CHECK_TICK
else
for(var/X in sorted) //these are clones
var/obj/effect/appearance_clone/clone = X
var/icon/img = getFlatIcon(clone, no_anim = TRUE)
if(!img)
CHECK_TICK
continue
// Center of the image in X
var/xo = (clone.x - center.x) * ICON_SIZE_X + clone.pixel_x + xcomp + clone.step_x
// Center of the image in Y
var/yo = (clone.y - center.y) * ICON_SIZE_Y + clone.pixel_y + ycomp + clone.step_y
if(clone.transform) // getFlatIcon doesn't give a snot about transforms.
var/datum/decompose_matrix/decompose = clone.transform.decompose()
// Scale in X, Y
if(decompose.scale_x != 1 || decompose.scale_y != 1)
var/base_w = img.Width()
var/base_h = img.Height()
// scale_x can be negative
img.Scale(base_w * abs(decompose.scale_x), base_h * decompose.scale_y)
if(decompose.scale_x < 0)
img.Flip(EAST)
xo -= base_w * (decompose.scale_x - SIGN(decompose.scale_x)) / 2 * SIGN(decompose.scale_x)
yo -= base_h * (decompose.scale_y - 1) / 2
// Rotation
if(decompose.rotation != 0)
img.Turn(decompose.rotation)
// Shift
xo += decompose.shift_x
yo += decompose.shift_y
res.Blend(img, blendMode2iconMode(clone.blend_mode), xo, yo)
CHECK_TICK
if(wipe_atoms)
QDEL_LIST(atoms)
else
QDEL_LIST(lighting)
return res
#undef PHYSICAL_POSITION