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.
/🆑
This commit is contained in:
_0Steven
2025-06-19 20:53:25 +02:00
committed by GitHub
parent c0c8ebe979
commit f324e6dd0c
2 changed files with 73 additions and 44 deletions
+62 -35
View File
@@ -159,26 +159,49 @@
disk = new_disk
return ITEM_INTERACT_SUCCESS
//user can be atom or mob
/obj/item/camera/proc/can_target(atom/target, mob/user)
/// Attempt to take an image, optionally given a user.
/obj/item/camera/proc/attempt_picture(atom/target, atom/user)
if(!can_target(target, user))
return FALSE
if(!photo_taken(target, user))
return FALSE
return TRUE
/// Check whether we can take a picture of the target, optionally given a user.
/obj/item/camera/proc/can_target(atom/target, atom/user)
if(!on || blending || !pictures_left)
return FALSE
var/turf/T = get_turf(target)
if(!T)
var/turf/target_turf = get_turf(target)
if(isnull(target_turf))
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(CONFIG_GET(string/default_view), user)))
return FALSE
else if(isliving(loc))
if(!(get_turf(target) in view(world.view, loc)))
return FALSE
else //user is an atom or null
if(!(get_turf(target) in view(world.view, user || src)))
if(isAI(user))
return can_ai_target(target_turf)
if(ismob(user))
return can_mob_target(target_turf, user)
if(isliving(loc))
if(!(target_turf in view(world.view, loc)))
return FALSE
return TRUE
// User is an atom or null
if(!(target_turf in view(world.view, user || src)))
return FALSE
return TRUE
/// Check whether an AI could take a picture of the target turf.
/obj/item/camera/proc/can_ai_target(turf/target_turf)
if(!GLOB.cameranet.checkTurfVis(target_turf))
return FALSE
return TRUE
/// Check whether a mob could take a picture of the target turf.
/obj/item/camera/proc/can_mob_target(turf/target_turf, mob/user)
var/user_view = user.client ? user.client.view : CONFIG_GET(string/default_view)
var/user_eye = user.client ? user.client.eye : user
if(!(target_turf in get_hear(user_view, user_eye)))
return FALSE
return TRUE
/obj/item/camera/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers)
@@ -201,11 +224,7 @@
disk.record.caller_name = recorded_mob.name
disk.record.set_caller_image(recorded_mob)
if(!can_target(interacting_with, user))
return ITEM_INTERACT_BLOCKING
if(!photo_taken(interacting_with, user))
return ITEM_INTERACT_BLOCKING
return ITEM_INTERACT_SUCCESS
return attempt_picture(interacting_with, user) ? ITEM_INTERACT_SUCCESS : ITEM_INTERACT_BLOCKING
/obj/item/camera/proc/photo_taken(atom/target, mob/user)
@@ -242,7 +261,6 @@
var/list/desc = list("This is a photo of an area of [size_x+1] meters by [size_y+1] meters.")
var/list/mobs_spotted = list()
var/list/dead_spotted = list()
var/ai_user = isAI(user)
var/list/seen
var/list/viewlist = user?.client ? getviewsize(user.client.view) : getviewsize(world.view)
var/viewr = max(viewlist[1], viewlist[2]) + max(size_x, size_y)
@@ -254,22 +272,33 @@
var/clone_area = SSmapping.request_turf_block_reservation(size_x * 2 + 1, size_y * 2 + 1, 1)
///list of human names taken on picture
var/list/names = list()
var/cameranet_user = isAI(user) || istype(viewc, /mob/eye/camera)
var/width = size_x * 2 + 1
var/height = size_y * 2 + 1
for(var/turf/placeholder as anything in CORNER_BLOCK_OFFSET(target_turf, width, height, -size_x, -size_y))
while(istype(placeholder, /turf/open/openspace)) //Multi-z photography
placeholder = GET_TURF_BELOW(placeholder)
if(!placeholder)
break
for(var/turf/seen_placeholder as anything in CORNER_BLOCK_OFFSET(target_turf, width, height, -size_x, -size_y))
if(isnull(seen_placeholder))
continue
if(placeholder && ((ai_user && GLOB.cameranet.checkTurfVis(placeholder)) || (placeholder in seen)))
turfs += placeholder
for(var/mob/M in placeholder)
mobs += M
if(locate(/obj/item/blueprints) in placeholder)
if(cameranet_user && !GLOB.cameranet.checkTurfVis(seen_placeholder))
continue
if(!cameranet_user && !(seen_placeholder in seen))
continue
//Multi-z photography
var/turf/target_placeholder = seen_placeholder
while(!isnull(target_placeholder))
turfs += target_placeholder
for(var/mob/mob_there in target_placeholder)
mobs += mob_there
if(locate(/obj/item/blueprints) in target_placeholder)
blueprints = TRUE
if(isopenspaceturf(target_placeholder) || istype(target_placeholder, /turf/open/floor/glass))
target_placeholder = GET_TURF_BELOW(target_placeholder)
else
break
// do this before picture is taken so we can reveal revenants for the photo
steal_souls(mobs)
@@ -398,8 +427,6 @@
target = locate(our_turf.x + picture_coord_x.value, our_turf.y + picture_coord_y.value, our_turf.z)
if(!target)
return
if(!camera.can_target(target))
return
INVOKE_ASYNC(camera, TYPE_PROC_REF(/obj/item/camera, captureimage), target, null, camera.picture_size_x - 1, camera.picture_size_y - 1)
INVOKE_ASYNC(camera, TYPE_PROC_REF(/obj/item/camera, attempt_picture), target)
#undef CAMERA_PICTURE_SIZE_HARD_LIMIT
@@ -1,14 +1,16 @@
/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(ismovable(A))
var/atom/movable/AM = A
step_x = AM.step_x
step_y = AM.step_y
. = ..()
/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))