Files
Paradise/code/modules/mob/camera/chunk.dm
Toastical 33ccc967cd Fix camera static offsets over transformed turf (#29643)
* apply reset_transform on camera static

* turns out I can just do this
2025-06-17 15:53:58 +00:00

174 lines
5.9 KiB
Plaintext

/// A 16x16 grid of the map with a list of turfs that can be seen, are visible and are dimmed.
/// Allows camera eyes to stream these chunks and know what they can and cannot see.
/datum/camerachunk
var/list/obscured_turfs = list()
var/list/visible_turfs = list()
var/list/obscured = list()
var/list/active_cameras = list()
var/list/inactive_cameras = list()
var/list/turfs = list()
var/list/seenby = list()
var/changed = FALSE
var/updating = FALSE
var/x = 0
var/y = 0
var/z = 0
/// Adds a camera to the chunk if it has not already been added.
/datum/camerachunk/proc/add_camera(obj/machinery/camera/cam)
if(active_cameras[cam] || inactive_cameras[cam])
return
if(cam.non_chunking_camera)
return
// Register all even though it is active/inactive. Won't get called incorrectly
RegisterSignal(cam, COMSIG_CAMERA_OFF, PROC_REF(deactivate_camera), TRUE)
RegisterSignal(cam, COMSIG_CAMERA_ON, PROC_REF(activate_camera), TRUE)
RegisterSignal(cam, COMSIG_PARENT_QDELETING, PROC_REF(remove_camera), TRUE)
RegisterSignal(cam, COMSIG_CAMERA_MOVED, PROC_REF(camera_moved), TRUE)
if(cam.can_use())
active_cameras[cam] = cam
else
inactive_cameras[cam] = cam
/// Handles each movement by a camera that has been added to the chunk.
/datum/camerachunk/proc/camera_moved(obj/machinery/camera/cam, atom/old_loc)
var/turf/T = get_turf(cam)
// Falls outside of the chunk view distance
if(T.x + CAMERA_VIEW_DISTANCE < x || T.x - CAMERA_VIEW_DISTANCE >= x + CAMERA_CHUNK_SIZE || T.y + CAMERA_VIEW_DISTANCE < y || T.y - CAMERA_VIEW_DISTANCE >= y + CAMERA_CHUNK_SIZE || T.z != z)
remove_camera(cam)
/// Removes a camera from the chunk.
/datum/camerachunk/proc/remove_camera(obj/machinery/camera/cam)
UnregisterSignal(cam, list(COMSIG_CAMERA_OFF, COMSIG_CAMERA_ON, COMSIG_PARENT_QDELETING, COMSIG_CAMERA_MOVED))
active_cameras -= cam
inactive_cameras -= cam
SScamera.queue(src)
/datum/camerachunk/proc/activate_camera(obj/machinery/camera/cam)
inactive_cameras -= cam
active_cameras += cam
SScamera.queue(src)
/datum/camerachunk/proc/deactivate_camera(obj/machinery/camera/cam)
inactive_cameras += cam
active_cameras -= cam
SScamera.queue(src)
/// Add a camera eye to the chunk, then update if changed.
/datum/camerachunk/proc/add(mob/camera/eye/eye, add_images = TRUE)
if(add_images)
var/client/client = eye.get_viewer_client()
if(client)
client.images += obscured
eye.visible_camera_chunks += src
seenby += eye
RegisterSignal(eye, COMSIG_PARENT_QDELETING, PROC_REF(eye_destroyed))
if(changed)
SScamera.queue(src)
/datum/camerachunk/proc/eye_destroyed(mob/camera/eye/eye)
remove(eye, FALSE)
/// Remove a camera eye from the chunk, then update if changed.
/datum/camerachunk/proc/remove(mob/camera/eye/eye, remove_images = TRUE)
if(remove_images)
var/client/client = eye.get_viewer_client()
if(client)
client.images -= obscured
eye.visible_camera_chunks -= src
seenby -= eye
UnregisterSignal(eye, COMSIG_PARENT_QDELETING)
/// Called when a chunk has changed. I.E: A wall was deleted.
/datum/camerachunk/proc/visibility_changed(turf/loc)
if(!visible_turfs[loc])
return
has_changed()
/// Updates the chunk, makes sure that it doesn't update too much. If the chunk isn't being watched it will
/// instead be flagged to update the next time a camera eye moves near it.
/datum/camerachunk/proc/has_changed(update_now = 0)
if(update_now)
update()
SScamera.remove_from_queue(src)
if(length(seenby))
SScamera.queue(src)
else
changed = TRUE
/// Gathers the visible turfs from cameras and puts them into the appropiate lists.
/datum/camerachunk/proc/update()
var/list/new_visible_turfs = list()
for(var/obj/machinery/camera/c as anything in active_cameras)
var/turf/point = locate(src.x + (CAMERA_CHUNK_SIZE / 2), src.y + (CAMERA_CHUNK_SIZE / 2), src.z)
var/turf/T = get_turf(c)
if(get_dist(point, T) > CAMERA_VIEW_DISTANCE + (CAMERA_CHUNK_SIZE / 2))
// Still needed for Ais who get created on Z level 1 on the spot of the new player
continue
for(var/turf/t in c.can_see())
if(turfs[t])
new_visible_turfs[t] = t
var/list/vis_added = new_visible_turfs - visible_turfs
var/list/vis_removed = visible_turfs - new_visible_turfs
visible_turfs = new_visible_turfs
obscured_turfs = turfs - new_visible_turfs
var/list/images_to_remove = list()
var/list/images_to_add = list()
for(var/turf/t as anything in vis_added)
if(t.obscured)
obscured -= t.obscured
images_to_remove += t.obscured
for(var/turf/t as anything in vis_removed)
if(!t.obscured)
t.obscured = image('icons/effects/cameravis.dmi', t, null, BYOND_LIGHTING_LAYER + 0.1)
t.obscured.appearance_flags = RESET_TRANSFORM
t.obscured.plane = BYOND_LIGHTING_PLANE + 1
obscured += t.obscured
images_to_add += t.obscured
for(var/mob/camera/eye/eye as anything in seenby)
var/client/client = eye.get_viewer_client()
if(client)
client.images -= images_to_remove
client.images += images_to_add
changed = FALSE
/// Create a new camera chunk, since the chunks are made as they are needed.
/datum/camerachunk/New(loc, x, y, z)
// 0xf = 15
x &= ~(CAMERA_CHUNK_SIZE - 1)
y &= ~(CAMERA_CHUNK_SIZE - 1)
src.x = x
src.y = y
src.z = z
var/half_chunk = CAMERA_CHUNK_SIZE / 2
for(var/obj/machinery/camera/c in urange(half_chunk + CAMERA_VIEW_DISTANCE, locate(x + half_chunk, y + half_chunk, z)))
add_camera(c)
for(var/turf/t in block(max(x, 1), max(y, 1), max(z, 1), min(x + CAMERA_CHUNK_SIZE - 1, world.maxx), min(y + CAMERA_CHUNK_SIZE - 1, world.maxy), z))
turfs[t] = t
for(var/obj/machinery/camera/c as anything in active_cameras)
for(var/turf/t in c.can_see())
if(turfs[t])
visible_turfs[t] = t
obscured_turfs = turfs - visible_turfs
for(var/turf/t as anything in obscured_turfs)
if(!t.obscured)
t.obscured = image('icons/effects/cameravis.dmi', t, "black", BYOND_LIGHTING_LAYER + 0.1)
t.obscured.appearance_flags = RESET_TRANSFORM
t.obscured.plane = BYOND_LIGHTING_PLANE + 1
obscured += t.obscured