mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-29 16:18:01 +01:00
## 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>        </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. /🆑
155 lines
5.2 KiB
Plaintext
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
|