diff --git a/code/__DEFINES/subsystems.dm b/code/__DEFINES/subsystems.dm
index a002b4bef93..9521983acd8 100644
--- a/code/__DEFINES/subsystems.dm
+++ b/code/__DEFINES/subsystems.dm
@@ -158,6 +158,7 @@
#define FIRE_PRIORITY_PARALLAX 65
#define FIRE_PRIORITY_INSTRUMENTS 80
#define FIRE_PRIORITY_FLUIDS 80
+#define FIRE_PRIORITY_CAMERAS 85
#define FIRE_PRIORITY_PRIORITY_EFFECTS 90
#define FIRE_PRIORITY_MOBS 100
#define FIRE_PRIORITY_TGUI 110
diff --git a/code/__HELPERS/spatial_info.dm b/code/__HELPERS/spatial_info.dm
index a8100f6a7a0..1135c93b055 100644
--- a/code/__HELPERS/spatial_info.dm
+++ b/code/__HELPERS/spatial_info.dm
@@ -403,6 +403,15 @@
. = view(range, source)
source.luminosity = lum
+/// get_hear that only gets turfs so we can use as_anything
+/proc/get_hear_turfs(range, atom/source)
+ var/lum = source.luminosity
+ source.luminosity = 6
+ . = list()
+ for(var/turf/turf in view(range, source))
+ . += turf
+ source.luminosity = lum
+
///Returns the open turf next to the center in a specific direction
/proc/get_open_turf_in_dir(atom/center, dir)
var/turf/open/get_turf = get_step(center, dir)
diff --git a/code/_onclick/ai.dm b/code/_onclick/ai.dm
index 1f5c943d454..394fe4cd206 100644
--- a/code/_onclick/ai.dm
+++ b/code/_onclick/ai.dm
@@ -294,4 +294,4 @@
//
/mob/living/silicon/ai/TurfAdjacent(turf/target_turf)
- return (GLOB.cameranet && GLOB.cameranet.checkTurfVis(target_turf))
+ return (SScameras.is_visible_by_cameras(target_turf))
diff --git a/code/controllers/subsystem/cameras.dm b/code/controllers/subsystem/cameras.dm
new file mode 100644
index 00000000000..5f5e9a2ec1b
--- /dev/null
+++ b/code/controllers/subsystem/cameras.dm
@@ -0,0 +1,296 @@
+/// Manages the security cameras and camera chunks
+SUBSYSTEM_DEF(cameras)
+ name = "Cameras"
+ flags = SS_BACKGROUND
+ priority = FIRE_PRIORITY_CAMERAS
+ runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
+ wait = 2 MINUTES
+ dependencies = list(
+ // Required to get plane offset for static images
+ /datum/controller/subsystem/mapping,
+ )
+
+ /// The cameras on the map, no matter if they work or not.
+ /// Updated in obj/machinery/camera.dm in Initialize() and Destroy().
+ var/list/obj/machinery/camera/cameras = list()
+ /// The chunks of the map, mapping the areas that the cameras can see.
+ var/list/chunks = list()
+ /// Chunks that must be updated
+ var/list/chunks_to_update = list()
+ /// List of images cloned by all chunk static images put onto turfs cameras cant see
+ /// Indexed by the plane offset to use
+ var/list/image/obscured_images = list()
+ /// Primarily for debugging, outright prevents all camera chunk updates
+ var/disable_camera_updates = FALSE
+ /// Tracks current subsystem run
+ var/list/current_run = list()
+
+/datum/controller/subsystem/cameras/Initialize()
+ update_offsets(SSmapping.max_plane_offset)
+ RegisterSignal(SSmapping, COMSIG_PLANE_OFFSET_INCREASE, PROC_REF(on_offset_growth))
+ return SS_INIT_SUCCESS
+
+/datum/controller/subsystem/cameras/fire(resumed = FALSE)
+ if(!resumed)
+ src.current_run = chunks_to_update.Copy()
+ chunks_to_update = list()
+
+ var/list/current_run = src.current_run
+ while(current_run.len)
+ var/datum/camerachunk/chunk = current_run[current_run.len]
+ chunk.force_update(only_if_necessary = TRUE) // Forces an update if necessary
+ current_run.len--
+ if(MC_TICK_CHECK)
+ break
+
+/datum/controller/subsystem/cameras/stat_entry(msg)
+ msg = "Cams: [length(cameras)] | Chunks: [length(chunks)] | Updating: [length(chunks_to_update)]"
+ return ..()
+
+/// Updates the images for new plane offsets
+/datum/controller/subsystem/cameras/proc/update_offsets(new_offset)
+ for(var/i in length(obscured_images) to new_offset)
+ var/image/obscured = new('icons/effects/cameravis.dmi')
+ SET_PLANE_W_SCALAR(obscured, CAMERA_STATIC_PLANE, i)
+ obscured.appearance_flags = RESET_TRANSFORM | RESET_ALPHA | RESET_COLOR | KEEP_APART
+ obscured.override = TRUE
+ obscured_images += obscured
+
+/datum/controller/subsystem/cameras/proc/on_offset_growth(datum/source, old_offset, new_offset)
+ SIGNAL_HANDLER
+ update_offsets(new_offset)
+
+/// Checks if a chunk has been generated in x, y, z.
+/datum/controller/subsystem/cameras/proc/get_camera_chunk(x, y, z)
+ x = GET_CHUNK_COORD(x)
+ y = GET_CHUNK_COORD(y)
+ if(GET_LOWEST_STACK_OFFSET(z) != 0)
+ var/turf/lowest = get_lowest_turf(locate(x, y, z))
+ return chunks["[x],[y],[lowest.z]"]
+
+ return chunks["[x],[y],[z]"]
+
+// Returns the chunk in the x, y, z.
+// If there is no chunk, it creates a new chunk and returns that.
+/datum/controller/subsystem/cameras/proc/generate_chunk(x, y, z)
+ x = GET_CHUNK_COORD(x)
+ y = GET_CHUNK_COORD(y)
+ var/turf/lowest = get_lowest_turf(locate(x, y, z))
+ var/key = "[x],[y],[lowest.z]"
+ . = chunks[key]
+ if(!.)
+ . = new /datum/camerachunk(x, y, lowest.z)
+ chunks[key] = .
+
+/// Updates what the camera eye can see.
+/// It is recommended you use this when a camera eye moves or its location is set.
+/datum/controller/subsystem/cameras/proc/update_eye_chunk(mob/eye/camera/eye)
+ var/list/visibleChunks = list()
+ //Get the eye's turf in case its located in an object like a mecha
+ var/turf/eye_turf = get_turf(eye)
+ if(eye.loc)
+ var/static_range = eye.static_visibility_range
+ var/x1 = max(1, eye_turf.x - static_range)
+ var/y1 = max(1, eye_turf.y - static_range)
+ var/x2 = min(world.maxx, eye_turf.x + static_range)
+ var/y2 = min(world.maxy, eye_turf.y + static_range)
+
+ for(var/x = x1; x <= x2; x += CHUNK_SIZE)
+ for(var/y = y1; y <= y2; y += CHUNK_SIZE)
+ visibleChunks |= generate_chunk(x, y, eye_turf.z)
+
+ var/list/remove = eye.visibleCameraChunks - visibleChunks
+ var/list/add = visibleChunks - eye.visibleCameraChunks
+
+ for(var/datum/camerachunk/chunk as anything in remove)
+ chunk.remove(eye)
+
+ for(var/datum/camerachunk/chunk as anything in add)
+ chunk.add(eye)
+
+/// Used in [/proc/major_chunk_change] - indicates the camera should be removed from the chunk list.
+#define REMOVE_CAMERA 0
+/// Used in [/proc/major_chunk_change] - indicates the camera should be added to the chunk list.
+#define ADD_CAMERA 1
+/// Used in [/proc/major_chunk_change] - indicates the chunk should be updated without adding/removing a camera.
+#define IGNORE_CAMERA 2
+
+/// Updates the chunks that the turf is located in. Use this when obstacles are destroyed or when doors open.
+/datum/controller/subsystem/cameras/proc/update_visibility(atom/relevant_atom)
+ if(!SSticker)
+ return
+ major_chunk_change(relevant_atom, IGNORE_CAMERA)
+
+/// Removes a camera from a chunk.
+/datum/controller/subsystem/cameras/proc/remove_camera_from_chunk(obj/machinery/camera/old_cam)
+ major_chunk_change(old_cam, REMOVE_CAMERA)
+
+/// Add a camera to a chunk.
+/datum/controller/subsystem/cameras/proc/add_camera_to_chunk(obj/machinery/camera/new_cam)
+ if(new_cam.can_use())
+ major_chunk_change(new_cam, ADD_CAMERA)
+
+/**
+ * Used for Cyborg/mecha cameras. Since portable cameras can be in ANY chunk.
+ * update_delay_buffer is passed all the way to queue_update() from their camera updates on movement
+ * to change the time between static updates.
+*/
+/datum/controller/subsystem/cameras/proc/update_portable_camera(obj/machinery/camera/updating_camera, update_delay_buffer)
+ if(updating_camera.can_use())
+ major_chunk_change(updating_camera, ADD_CAMERA, update_delay_buffer)
+
+/**
+ * Never access this proc directly!!!!
+ * This will update the chunk and all the surrounding chunks.
+ * It will also add the atom to the cameras list if you set the choice to 1.
+ * Setting the choice to 0 will remove the camera from the chunks.
+ * If you want to update the chunks around an object, without adding/removing a camera, use choice 2.
+ * update_delay_buffer is passed all the way to queue_update() from portable camera updates on movement
+ * to change the time between static updates.
+ */
+/datum/controller/subsystem/cameras/proc/major_chunk_change(atom/center_or_camera, choice = IGNORE_CAMERA, update_delay_buffer = 0)
+ PROTECTED_PROC(TRUE)
+
+ if(QDELETED(center_or_camera) && choice == ADD_CAMERA)
+ CRASH("Tried to add a qdeleting camera to the net")
+
+ var/turf/chunk_turf = get_turf(center_or_camera)
+ if(isnull(chunk_turf))
+ return
+
+ var/x1 = max(1, chunk_turf.x - (CHUNK_SIZE / 2))
+ var/y1 = max(1, chunk_turf.y - (CHUNK_SIZE / 2))
+ var/x2 = min(world.maxx, chunk_turf.x + (CHUNK_SIZE / 2))
+ var/y2 = min(world.maxy, chunk_turf.y + (CHUNK_SIZE / 2))
+ for(var/x = x1; x <= x2; x += CHUNK_SIZE)
+ for(var/y = y1; y <= y2; y += CHUNK_SIZE)
+ var/datum/camerachunk/chunk = get_camera_chunk(x, y, chunk_turf.z)
+ if(isnull(chunk))
+ continue
+ if(choice == REMOVE_CAMERA)
+ // Remove the camera.
+ chunk.cameras[chunk_turf.z] -= center_or_camera
+ if(choice == ADD_CAMERA)
+ // You can't have the same camera in the list twice.
+ chunk.cameras[chunk_turf.z] |= center_or_camera
+ chunk.queue_update(center_or_camera, update_delay_buffer)
+
+/// A faster, turf only version of [/datum/controller/subsystem/cameras/proc/major_chunk_change]
+/// For use in sensitive code, be careful with it
+/datum/controller/subsystem/cameras/proc/bare_major_chunk_change(turf/changed)
+ var/x1 = max(1, changed.x - (CHUNK_SIZE / 2))
+ var/y1 = max(1, changed.y - (CHUNK_SIZE / 2))
+ var/x2 = min(world.maxx, changed.x + (CHUNK_SIZE / 2))
+ var/y2 = min(world.maxy, changed.y + (CHUNK_SIZE / 2))
+ for(var/x = x1; x <= x2; x += CHUNK_SIZE)
+ for(var/y = y1; y <= y2; y += CHUNK_SIZE)
+ var/datum/camerachunk/chunk = get_camera_chunk(x, y, changed.z)
+ chunk?.queue_update(changed, 0)
+
+/// Will check if an atom is on a viewable turf.
+/// Returns TRUE if the atom is visible by any camera, FALSE otherwise.
+/datum/controller/subsystem/cameras/proc/is_visible_by_cameras(atom/target)
+ return turf_visible_by_cameras(get_turf(target))
+
+/// Checks if the passed turf is visible by any camera.
+/// Returns TRUE if the turf is visible by any camera, FALSE otherwise.
+/datum/controller/subsystem/cameras/proc/turf_visible_by_cameras(turf/position)
+ PRIVATE_PROC(TRUE)
+ if(isnull(position))
+ return FALSE
+ var/datum/camerachunk/chunk = generate_chunk(position.x, position.y, position.z)
+ if(isnull(chunk))
+ return FALSE
+ chunk.force_update(only_if_necessary = TRUE) // Update NOW if necessary
+ if(chunk.visibleTurfs[position])
+ return TRUE
+ return FALSE
+
+/// Gets the camera chunk the passed turf is in.
+/// Returns the chunk if it exists and is visible, null otherwise.
+/datum/controller/subsystem/cameras/proc/get_turf_camera_chunk(turf/position)
+ RETURN_TYPE(/datum/camerachunk)
+ var/datum/camerachunk/chunk = generate_chunk(position.x, position.y, position.z)
+ if(!chunk)
+ return null
+ chunk.force_update(only_if_necessary = TRUE) // Update NOW if necessary
+ if(chunk.visibleTurfs[position])
+ return chunk
+ return null
+
+/// Returns list of available cameras, ready to use for UIs displaying list of them
+/// The format is: list("name" = "camera.c_tag", ref = REF(camera))
+/datum/controller/subsystem/cameras/proc/get_available_cameras_data(list/networks_available, list/z_levels_available)
+ var/list/available_cameras_data = list()
+ for(var/obj/machinery/camera/camera as anything in get_filtered_and_sorted_cameras(networks_available, z_levels_available))
+ available_cameras_data += list(list(
+ name = camera.c_tag,
+ ref = REF(camera),
+ ))
+
+ return available_cameras_data
+
+/**
+ * get_available_camera_by_tag_list
+ *
+ * Builds a list of all available cameras that can be seen to networks_available and in z_levels_available.
+ * Entries are stored in `c_tag[camera.can_use() ? null : " (Deactivated)"]` => `camera` format
+ * Args:
+ * networks_available - List of networks that we use to see which cameras are visible to it.
+ * z_levels_available - List of z levels to filter camera by. If empty, all z levels are considered valid.
+ * sort_by_ctag - If the resulting list should be sorted by `c_tag`.
+ */
+/datum/controller/subsystem/cameras/proc/get_available_camera_by_tag_list(list/networks_available, list/z_levels_available)
+ var/list/available_cameras_by_tag = list()
+ for(var/obj/machinery/camera/camera as anything in get_filtered_and_sorted_cameras(networks_available, z_levels_available))
+ available_cameras_by_tag["[camera.c_tag][camera.can_use() ? null : " (Deactivated)"]"] = camera
+
+ return available_cameras_by_tag
+
+/// Returns list of all cameras that passed `is_camera_available` filter and sorted by `cmp_camera_ctag_asc`
+/datum/controller/subsystem/cameras/proc/get_filtered_and_sorted_cameras(list/networks_available, list/z_levels_available)
+ PRIVATE_PROC(TRUE)
+
+ var/list/filtered_cameras = list()
+ for(var/obj/machinery/camera/camera as anything in cameras)
+ if(!is_camera_available(camera, networks_available, z_levels_available))
+ continue
+
+ filtered_cameras += camera
+
+ return sortTim(filtered_cameras, GLOBAL_PROC_REF(cmp_camera_ctag_asc))
+
+/// Checks if the `camera_to_check` meets the requirements of availability.
+/datum/controller/subsystem/cameras/proc/is_camera_available(obj/machinery/camera/camera_to_check, list/networks_available, list/z_levels_available)
+ PRIVATE_PROC(TRUE)
+
+ if(!camera_to_check.c_tag)
+ return FALSE
+
+ if(length(z_levels_available) && !(camera_to_check.z in z_levels_available))
+ return FALSE
+
+ return length(camera_to_check.network & networks_available) > 0
+
+#undef ADD_CAMERA
+#undef REMOVE_CAMERA
+#undef IGNORE_CAMERA
+
+/obj/effect/overlay/camera_static
+ name = "static"
+ icon = null
+ icon_state = null
+ anchored = TRUE // should only appear in vis_contents, but to be safe
+ appearance_flags = RESET_TRANSFORM | TILE_BOUND | LONG_GLIDE
+ // this combination makes the static block clicks to everything below it,
+ // without appearing in the right-click menu for non-AI clients
+ mouse_opacity = MOUSE_OPACITY_ICON
+ invisibility = INVISIBILITY_ABSTRACT
+
+ plane = CAMERA_STATIC_PLANE
+
+ADMIN_VERB(pause_camera_updates, R_ADMIN, "Toggle Camera Updates", "Stop security cameras from updating, meaning what they see now is what they will see forever.", ADMIN_CATEGORY_DEBUG)
+ SScameras.disable_camera_updates = !SScameras.disable_camera_updates
+ log_admin("[key_name_admin(user)] [SScameras.disable_camera_updates ? "disabled" : "enabled"] camera updates.")
+ message_admins("Admin [key_name_admin(user)] has [SScameras.disable_camera_updates ? "disabled" : "enabled"] camera updates.")
+ BLACKBOX_LOG_ADMIN_VERB("Toggle Camera Updates")
diff --git a/code/controllers/subsystem/statpanel.dm b/code/controllers/subsystem/statpanel.dm
index d7d4b38793d..53e724b4543 100644
--- a/code/controllers/subsystem/statpanel.dm
+++ b/code/controllers/subsystem/statpanel.dm
@@ -220,7 +220,6 @@ SUBSYSTEM_DEF(statpanels)
#endif
for(var/datum/controller/subsystem/sub_system as anything in Master.subsystems)
mc_data[++mc_data.len] = list("\[[sub_system.state_letter()]][sub_system.name]", sub_system.stat_entry(), text_ref(sub_system))
- mc_data[++mc_data.len] = list("Camera Net", "Cameras: [GLOB.cameranet.cameras.len] | Chunks: [GLOB.cameranet.chunks.len]", text_ref(GLOB.cameranet))
///immediately update the active statpanel tab of the target client
/datum/controller/subsystem/statpanels/proc/immediate_send_stat_data(client/target)
diff --git a/code/datums/components/simple_bodycam.dm b/code/datums/components/simple_bodycam.dm
index 9d653f38a78..1723a7a0a5a 100644
--- a/code/datums/components/simple_bodycam.dm
+++ b/code/datums/components/simple_bodycam.dm
@@ -56,7 +56,7 @@
do_update_cam()
/datum/component/simple_bodycam/proc/do_update_cam()
- GLOB.cameranet.updatePortableCamera(bodycam, camera_update_time)
+ SScameras.update_portable_camera(bodycam, camera_update_time)
/datum/component/simple_bodycam/proc/rotate_cam(datum/source, old_dir, new_dir)
SIGNAL_HANDLER
diff --git a/code/game/machinery/_machinery.dm b/code/game/machinery/_machinery.dm
index 2023722ea35..6abdd7900ed 100644
--- a/code/game/machinery/_machinery.dm
+++ b/code/game/machinery/_machinery.dm
@@ -738,7 +738,7 @@
var/mob/user = ui.user
add_fingerprint(user)
update_last_used(user)
- if(isAI(user) && !GLOB.cameranet.checkTurfVis(get_turf(src))) //We check if they're an AI specifically here, so borgs/adminghosts/human wand can still access off-camera stuff.
+ if(isAI(user) && !SScameras.is_visible_by_cameras(get_turf(src))) //We check if they're an AI specifically here, so borgs/adminghosts/human wand can still access off-camera stuff.
to_chat(user, span_warning("You can no longer connect to this device!"))
return FALSE
return ..()
diff --git a/code/game/machinery/camera/camera.dm b/code/game/machinery/camera/camera.dm
index 6d214d6781b..f323ddc30ac 100644
--- a/code/game/machinery/camera/camera.dm
+++ b/code/game/machinery/camera/camera.dm
@@ -110,12 +110,12 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/camera/xray, 0)
network -= network_name
network += LOWER_TEXT(network_name)
- GLOB.cameranet.cameras += src
+ SScameras.cameras += src
myarea = get_room_area()
if(camera_enabled)
- GLOB.cameranet.addCamera(src)
+ SScameras.add_camera_to_chunk(src)
LAZYADD(myarea.cameras, src)
#ifdef MAP_TEST
update_appearance()
@@ -133,8 +133,8 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/camera/xray, 0)
/obj/machinery/camera/Destroy(force)
if(can_use())
toggle_cam(null, 0) //kick anyone viewing out and remove from the camera chunks
- GLOB.cameranet.removeCamera(src)
- GLOB.cameranet.cameras -= src
+ SScameras.remove_camera_from_chunk(src)
+ SScameras.cameras -= src
cancelCameraAlarm()
if(isarea(myarea))
LAZYREMOVE(myarea.cameras, src)
@@ -219,7 +219,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/camera/xray, 0)
if(!prob(150 / severity))
return
network = list()
- GLOB.cameranet.removeCamera(src)
+ SScameras.remove_camera_from_chunk(src)
set_machine_stat(machine_stat | EMPED)
set_light(0)
emped++ //Increase the number of consecutive EMP's
@@ -246,7 +246,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/camera/xray, 0)
set_machine_stat(machine_stat & ~EMPED)
update_appearance()
if(can_use())
- GLOB.cameranet.addCamera(src)
+ SScameras.add_camera_to_chunk(src)
emped = 0 //Resets the consecutive EMP count
addtimer(CALLBACK(src, PROC_REF(cancelCameraAlarm)), 10 SECONDS)
@@ -259,7 +259,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/camera/xray, 0)
/obj/machinery/camera/proc/setViewRange(num = 7)
src.view_range = num
- GLOB.cameranet.updateVisibility(src, 0)
+ SScameras.update_visibility(src)
/obj/machinery/camera/proc/shock(mob/living/user)
if(!istype(user))
@@ -336,7 +336,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/camera/xray, 0)
/obj/machinery/camera/proc/toggle_cam(mob/user, displaymessage = TRUE)
camera_enabled = !camera_enabled
if(can_use())
- GLOB.cameranet.addCamera(src)
+ SScameras.add_camera_to_chunk(src)
if (isturf(loc))
myarea = get_area(src)
LAZYADD(myarea.cameras, src)
@@ -344,12 +344,10 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/camera/xray, 0)
myarea = null
else
set_light(0)
- GLOB.cameranet.removeCamera(src)
+ SScameras.remove_camera_from_chunk(src)
if (isarea(myarea))
LAZYREMOVE(myarea.cameras, src)
// We are not guarenteed that the camera will be on a turf. account for that
- var/turf/our_turf = get_turf(src)
- GLOB.cameranet.updateChunk(our_turf.x, our_turf.y, our_turf.z)
var/change_msg = "deactivates"
if(camera_enabled)
change_msg = "reactivates"
@@ -389,6 +387,8 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/camera/xray, 0)
return FALSE
return TRUE
+/// Returns a list of turfs in this camera's view.
+/// This includes turfs that are "obscured by darkness" from the camera's POV.
/obj/machinery/camera/proc/can_see()
var/list/see = null
var/turf/pos = get_turf(src)
@@ -397,26 +397,22 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/camera/xray, 0)
var/check_higher = directly_above && istransparentturf(directly_above) && (pos != get_highest_turf(pos))
if(isXRay())
- see = range(view_range, pos)
+ see = RANGE_TURFS(view_range, pos)
else
- see = get_hear(view_range, pos)
+ see = get_hear_turfs(view_range, pos)
+
if(check_lower || check_higher)
// Haha datum var access KILL ME
- for(var/turf/seen in see)
+ for(var/turf/seen as anything in see)
if(check_lower)
- var/turf/visible = seen
- while(visible && istransparentturf(visible))
- var/turf/below = GET_TURF_BELOW(visible)
- for(var/turf/adjacent in range(1, below))
- see += adjacent
- see += adjacent.contents
- visible = below
+ var/turf/below = GET_TURF_BELOW(seen)
+ while(below && istransparentturf(below))
+ see += RANGE_TURFS(1, below)
+ below = GET_TURF_BELOW(below)
if(check_higher)
var/turf/above = GET_TURF_ABOVE(seen)
while(above && istransparentturf(above))
- for(var/turf/adjacent in range(1, above))
- see += adjacent
- see += adjacent.contents
+ see += RANGE_TURFS(1, above)
above = GET_TURF_ABOVE(above)
return see
diff --git a/code/game/machinery/camera/presets.dm b/code/game/machinery/camera/presets.dm
index 89af3fc1192..7383db15f66 100644
--- a/code/game/machinery/camera/presets.dm
+++ b/code/game/machinery/camera/presets.dm
@@ -185,6 +185,7 @@
camera_upgrade_bitflags |= CAMERA_UPGRADE_XRAY
update_appearance()
+ SScameras.update_visibility(src)
/obj/machinery/camera/proc/removeXRay(ignore_malf_upgrades)
if(!ignore_malf_upgrades) //don't downgrade it if malf software is forced onto it.
diff --git a/code/game/machinery/computer/camera.dm b/code/game/machinery/computer/camera.dm
index 8725bda22f5..f33cff0c7ea 100644
--- a/code/game/machinery/computer/camera.dm
+++ b/code/game/machinery/computer/camera.dm
@@ -89,7 +89,7 @@
var/list/data = list()
data["network"] = network
data["mapRef"] = cam_screen.assigned_map
- data["cameras"] = GLOB.cameranet.get_available_cameras_data(network)
+ data["cameras"] = SScameras.get_available_cameras_data(network)
return data
/obj/machinery/computer/security/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
@@ -99,7 +99,7 @@
if(action == "switch_camera")
active_camera?.on_stop_watching(src)
- var/obj/machinery/camera/selected_camera = locate(params["camera"]) in GLOB.cameranet.cameras
+ var/obj/machinery/camera/selected_camera = locate(params["camera"]) in SScameras.cameras
active_camera = selected_camera
if(isnull(active_camera))
diff --git a/code/game/machinery/computer/camera_advanced.dm b/code/game/machinery/computer/camera_advanced.dm
index bb034b1a322..c364afa9ec3 100644
--- a/code/game/machinery/computer/camera_advanced.dm
+++ b/code/game/machinery/computer/camera_advanced.dm
@@ -183,10 +183,10 @@
else
camera_location = myturf
else
- if((!consider_zlock || (myturf.z in z_lock)) && GLOB.cameranet.checkTurfVis(myturf))
+ if((!consider_zlock || (myturf.z in z_lock)) && SScameras.is_visible_by_cameras(myturf))
camera_location = myturf
else
- for(var/obj/machinery/camera/C as anything in GLOB.cameranet.cameras)
+ for(var/obj/machinery/camera/C as anything in SScameras.cameras)
if(!C.can_use() || consider_zlock && !(C.z in z_lock))
continue
var/list/network_overlap = networks & C.network
@@ -229,7 +229,7 @@
var/mob/eye/camera/remote/remote_eye = owner.remote_control
var/obj/machinery/computer/camera_advanced/origin = remote_eye.origin_ref.resolve()
- var/list/cameras_by_tag = GLOB.cameranet.get_available_camera_by_tag_list(origin.networks, origin.z_lock)
+ var/list/cameras_by_tag = SScameras.get_available_camera_by_tag_list(origin.networks, origin.z_lock)
playsound(origin, 'sound/machines/terminal/terminal_prompt.ogg', 25, FALSE)
var/camera = tgui_input_list(usr, "Camera to view", "Cameras", cameras_by_tag)
@@ -403,7 +403,7 @@
var/turf/eye_turf = get_turf(source)
if(!eye_turf)
return
- if(!GLOB.cameranet.checkTurfVis(eye_turf))
+ if(!SScameras.is_visible_by_cameras(eye_turf))
return
eye_x.set_output(source.x)
eye_y.set_output(source.y)
@@ -490,7 +490,7 @@
var/turf/target_turf = get_turf(target)
if(!target_turf)
return
- if(!GLOB.cameranet.checkTurfVis(target_turf))
+ if(!SScameras.is_visible_by_cameras(target_turf))
return
if(TIMER_COOLDOWN_RUNNING(parent.shell, COOLDOWN_CIRCUIT_TARGET_INTERCEPT))
return
diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm
index aea2ac558f7..65ce845a44b 100644
--- a/code/game/machinery/doors/airlock.dm
+++ b/code/game/machinery/doors/airlock.dm
@@ -1356,10 +1356,9 @@
set_airlock_state(AIRLOCK_OPENING, animated = TRUE, force_type = forced)
var/transparent_delay = animation_segment_delay(AIRLOCK_OPENING_TRANSPARENT)
sleep(transparent_delay)
- set_opacity(0)
+ set_opacity(FALSE)
if(multi_tile)
filler.set_opacity(FALSE)
- update_freelook_sight()
var/passable_delay = animation_segment_delay(AIRLOCK_OPENING_PASSABLE) - transparent_delay
sleep(passable_delay)
set_density(FALSE)
@@ -1446,7 +1445,6 @@
set_opacity(TRUE)
if(multi_tile)
filler.set_opacity(TRUE)
- update_freelook_sight()
var/close_delay = animation_segment_delay(AIRLOCK_CLOSING_FINISHED) - unpassable_delay - opaque_delay
sleep(close_delay)
set_airlock_state(AIRLOCK_CLOSED, animated = FALSE)
diff --git a/code/game/machinery/doors/door.dm b/code/game/machinery/doors/door.dm
index 697de876023..daecb318bb8 100644
--- a/code/game/machinery/doors/door.dm
+++ b/code/game/machinery/doors/door.dm
@@ -98,7 +98,6 @@
set_bounds()
set_filler()
update_overlays()
- update_freelook_sight()
air_update_turf(TRUE, TRUE)
register_context()
if(elevator_mode)
@@ -163,7 +162,6 @@
layer = initial(layer)
/obj/machinery/door/Destroy()
- update_freelook_sight()
if(elevator_mode)
GLOB.elevator_doors -= src
if(spark_system)
@@ -508,7 +506,7 @@
operating = TRUE
use_energy(active_power_usage)
run_animation(DOOR_OPENING_ANIMATION)
- set_opacity(0)
+ set_opacity(FALSE)
var/passable_delay = animation_segment_delay(DOOR_OPENING_PASSABLE)
SLEEP_NOT_DEL(passable_delay)
set_density(FALSE)
@@ -517,10 +515,9 @@
SLEEP_NOT_DEL(open_delay)
layer = initial(layer)
update_appearance()
- set_opacity(0)
+ set_opacity(FALSE)
operating = FALSE
air_update_turf(TRUE, FALSE)
- update_freelook_sight()
if(autoclose)
autoclose_in(DOOR_CLOSE_WAIT)
return TRUE
@@ -556,10 +553,9 @@
SLEEP_NOT_DEL(close_delay)
update_appearance()
if(visible && !glass)
- set_opacity(1)
+ set_opacity(TRUE)
operating = FALSE
air_update_turf(TRUE, TRUE)
- update_freelook_sight()
if(!can_crush)
return TRUE
@@ -618,10 +614,6 @@
/obj/machinery/door/proc/hasPower()
return !(machine_stat & NOPOWER)
-/obj/machinery/door/proc/update_freelook_sight()
- if(!glass && GLOB.cameranet)
- GLOB.cameranet.updateVisibility(src, 0)
-
/obj/machinery/door/block_superconductivity() // All non-glass airlocks block heat, this is intended.
if(opacity || heat_proof)
return 1
diff --git a/code/game/machinery/doors/windowdoor.dm b/code/game/machinery/doors/windowdoor.dm
index 8ede2092a4c..7579756590f 100644
--- a/code/game/machinery/doors/windowdoor.dm
+++ b/code/game/machinery/doors/windowdoor.dm
@@ -256,7 +256,6 @@
var/open_delay = animation_segment_delay(DOOR_OPENING_FINISHED) - passable_delay
sleep(open_delay)
air_update_turf(TRUE, FALSE)
- update_freelook_sight()
if(operating == 1) //emag again
operating = FALSE
@@ -299,7 +298,6 @@
sleep(unpassable_delay)
set_density(TRUE)
air_update_turf(TRUE, TRUE)
- update_freelook_sight()
var/close_delay = animation_segment_delay(DOOR_CLOSING_FINISHED) - unpassable_delay
sleep(close_delay)
@@ -549,8 +547,8 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/door/window/brigdoor/security/holding
operating = TRUE
set_density(FALSE)
+ set_opacity(FALSE)
air_update_turf(TRUE, FALSE)
- update_freelook_sight()
operating = FALSE
update_appearance()
@@ -564,8 +562,8 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/door/window/brigdoor/security/holding
operating = TRUE
set_density(TRUE)
+ set_opacity(TRUE)
air_update_turf(TRUE, TRUE)
- update_freelook_sight()
operating = FALSE
update_appearance()
diff --git a/code/game/objects/effects/effect_system/effect_system.dm b/code/game/objects/effects/effect_system/effect_system.dm
index 6ddd65f12cf..73ffc52a2bd 100644
--- a/code/game/objects/effects/effect_system/effect_system.dm
+++ b/code/game/objects/effects/effect_system/effect_system.dm
@@ -12,14 +12,6 @@ would spawn and follow the beaker, even if it is carried or thrown.
pass_flags = PASSTABLE | PASSGRILLE
anchored = TRUE
-/obj/effect/particle_effect/Initialize(mapload)
- . = ..()
- GLOB.cameranet.updateVisibility(src)
-
-/obj/effect/particle_effect/Destroy()
- GLOB.cameranet.updateVisibility(src)
- return ..()
-
// Prevents effects from getting registered for SSnewtonian_movement
/obj/effect/particle_effect/newtonian_move(inertia_angle, instant = FALSE, start_delay = 0, drift_force = 0, controlled_cap = null)
return TRUE
diff --git a/code/game/objects/items/devices/multitool.dm b/code/game/objects/items/devices/multitool.dm
index e7244491423..626c42d65a4 100644
--- a/code/game/objects/items/devices/multitool.dm
+++ b/code/game/objects/items/devices/multitool.dm
@@ -295,10 +295,10 @@
for(var/x = x1; x <= x2; x += CHUNK_SIZE)
for(var/y = y1; y <= y2; y += CHUNK_SIZE)
- var/datum/camerachunk/chunk = GLOB.cameranet.getCameraChunk(x, y, epicenter.z)
+ var/datum/camerachunk/chunk = SScameras.generate_chunk(x, y, epicenter.z)
// removing cameras in build mode didnt affect it and i guess it needs an AI eye to update so we have to do this manually
// unless we only want to see static in a jank manner only if an eye updates it
- chunk?.update() // UPDATE THE FUCK NOW
+ chunk?.force_update(only_if_necessary = FALSE) // UPDATE THE FUCK NOW
. |= chunk
/obj/item/multitool/ai_detect/proc/cleanup_static()
diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm
index 10b500824e5..cc7cf0f2ccd 100644
--- a/code/game/objects/objs.dm
+++ b/code/game/objects/objs.dm
@@ -64,10 +64,14 @@ GLOBAL_LIST_EMPTY(objects_by_id_tag)
if (id_tag)
GLOB.objects_by_id_tag[id_tag] = src
+ if(opacity)
+ SScameras.update_visibility(src)
/obj/Destroy(force)
if(!ismachinery(src))
STOP_PROCESSING(SSobj, src) // TODO: Have a processing bitflag to reduce on unnecessary loops through the processing lists
+ if(opacity)
+ SScameras.update_visibility(src)
SStgui.close_uis(src)
GLOB.objects_by_id_tag -= id_tag
. = ..()
diff --git a/code/game/objects/structures.dm b/code/game/objects/structures.dm
index 3e7e13c1df2..9d4442238ac 100644
--- a/code/game/objects/structures.dm
+++ b/code/game/objects/structures.dm
@@ -23,10 +23,8 @@
if(smoothing_flags & USES_SMOOTHING)
QUEUE_SMOOTH(src)
QUEUE_SMOOTH_NEIGHBORS(src)
- GLOB.cameranet.updateVisibility(src)
/obj/structure/Destroy(force)
- GLOB.cameranet.updateVisibility(src)
if(smoothing_flags & USES_SMOOTHING)
QUEUE_SMOOTH_NEIGHBORS(src)
return ..()
diff --git a/code/game/turfs/change_turf.dm b/code/game/turfs/change_turf.dm
index 347a3574769..49ebdb577a9 100644
--- a/code/game/turfs/change_turf.dm
+++ b/code/game/turfs/change_turf.dm
@@ -193,7 +193,7 @@ GLOBAL_LIST_INIT(blacklisted_automated_baseturfs, typecacheof(list(
space_tile.enable_starlight()
if(old_opacity != opacity && SSticker)
- GLOB.cameranet.bareMajorChunkChange(src)
+ SScameras.bare_major_chunk_change(src)
// We will only run this logic if the tile is not on the prime z layer, since we use area overlays to cover that
if(SSmapping.z_level_to_plane_offset[z])
diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm
index 0243b866d82..53f75673d87 100644
--- a/code/game/turfs/turf.dm
+++ b/code/game/turfs/turf.dm
@@ -553,9 +553,6 @@ GLOBAL_LIST_EMPTY(station_turfs)
/turf/proc/can_lay_cable()
return can_have_cabling() && underfloor_accessibility >= UNDERFLOOR_INTERACTABLE
-/turf/proc/visibilityChanged()
- GLOB.cameranet.updateVisibility(src)
-
/turf/proc/burn_tile()
return
diff --git a/code/modules/admin/verbs/debug.dm b/code/modules/admin/verbs/debug.dm
index a981a5c3e74..47098fd8a3f 100644
--- a/code/modules/admin/verbs/debug.dm
+++ b/code/modules/admin/verbs/debug.dm
@@ -320,7 +320,7 @@ ADMIN_VERB(cmd_admin_areatest, R_DEBUG, "Test Areas", "Tests the areas for vario
areas_with_intercom.Add(A.type)
CHECK_TICK
- for(var/obj/machinery/camera/C as anything in GLOB.cameranet.cameras)
+ for(var/obj/machinery/camera/C as anything in SScameras.cameras)
var/area/A = get_area(C)
if(!A)
dat += "Skipped over [C] in invalid location, [C.loc].
"
diff --git a/code/modules/admin/verbs/mapping.dm b/code/modules/admin/verbs/mapping.dm
index dcb9d902f31..8adfd07671e 100644
--- a/code/modules/admin/verbs/mapping.dm
+++ b/code/modules/admin/verbs/mapping.dm
@@ -8,11 +8,11 @@ ADMIN_VERB(camera_view, R_DEBUG, "Camera Range Display", "Shows the range of cam
if(!on)
var/list/seen = list()
- for(var/obj/machinery/camera/C as anything in GLOB.cameranet.cameras)
- for(var/turf/T in C.can_see())
- seen[T]++
- for(var/turf/T in seen)
- T.maptext = MAPTEXT(seen[T])
+ for(var/obj/machinery/camera/cam as anything in SScameras.cameras)
+ for(var/turf/cam_turf as anything in cam.can_see())
+ seen[cam]++
+ for(var/turf/seen_turf as anything in seen)
+ seen_turf.maptext = MAPTEXT(seen[seen_turf])
BLACKBOX_LOG_ADMIN_VERB("Show Camera Range")
#ifdef TESTING
@@ -34,7 +34,7 @@ ADMIN_VERB_VISIBILITY(sec_camera_report, ADMIN_VERB_VISIBLITY_FLAG_MAPPING_DEBUG
ADMIN_VERB(sec_camera_report, R_DEBUG, "Camera Report", "Get a printout of all camera issues.", ADMIN_CATEGORY_MAPPING)
var/list/obj/machinery/camera/CL = list()
- for(var/obj/machinery/camera/C as anything in GLOB.cameranet.cameras)
+ for(var/obj/machinery/camera/C as anything in SScameras.cameras)
CL += C
var/output = {"Camera Abnormalities Report
diff --git a/code/modules/antagonists/abductor/machinery/camera.dm b/code/modules/antagonists/abductor/machinery/camera.dm
index 0141ae73cbf..10abf2577ee 100644
--- a/code/modules/antagonists/abductor/machinery/camera.dm
+++ b/code/modules/antagonists/abductor/machinery/camera.dm
@@ -72,7 +72,7 @@
use_delay = (world.time + abductor_pad_cooldown)
- if(GLOB.cameranet.checkTurfVis(remote_eye.loc))
+ if(SScameras.is_visible_by_cameras(remote_eye.loc))
P.PadToLoc(remote_eye.loc)
/datum/action/innate/teleport_out
@@ -116,7 +116,7 @@
use_delay = (world.time + teleport_self_cooldown)
- if(GLOB.cameranet.checkTurfVis(remote_eye.loc))
+ if(SScameras.is_visible_by_cameras(remote_eye.loc))
P.MobToLoc(remote_eye.loc,C)
/datum/action/innate/vest_mode_swap
diff --git a/code/modules/antagonists/malf_ai/malf_ai_modules.dm b/code/modules/antagonists/malf_ai/malf_ai_modules.dm
index 433d87fcbc3..377caebf7ea 100644
--- a/code/modules/antagonists/malf_ai/malf_ai_modules.dm
+++ b/code/modules/antagonists/malf_ai/malf_ai_modules.dm
@@ -699,8 +699,7 @@ GLOBAL_LIST_INIT(malf_modules, subtypesof(/datum/ai_module/malf))
var/turf/T = turfs[n]
if(!isfloorturf(T))
success = FALSE
- var/datum/camerachunk/C = GLOB.cameranet.getCameraChunk(T.x, T.y, T.z)
- if(!C.visibleTurfs[T])
+ if(!SScameras.is_visible_by_cameras(T))
alert_msg = "You don't have camera vision of this location!"
success = FALSE
for(var/atom/movable/AM in T.contents)
@@ -825,7 +824,7 @@ GLOBAL_LIST_INIT(malf_modules, subtypesof(/datum/ai_module/malf))
/datum/action/innate/ai/reactivate_cameras/Activate()
var/fixed_cameras = 0
- for(var/obj/machinery/camera/C as anything in GLOB.cameranet.cameras)
+ for(var/obj/machinery/camera/C as anything in SScameras.cameras)
if(!uses)
break
if(!C.camera_enabled || C.view_range != initial(C.view_range))
@@ -857,13 +856,12 @@ GLOBAL_LIST_INIT(malf_modules, subtypesof(/datum/ai_module/malf))
AI.update_sight()
var/upgraded_cameras = 0
- for(var/obj/machinery/camera/camera as anything in GLOB.cameranet.cameras)
+ for(var/obj/machinery/camera/camera as anything in SScameras.cameras)
var/upgraded = FALSE
if(!camera.isXRay())
camera.upgradeXRay(TRUE) //if this is removed you can get rid of camera_assembly/var/malf_xray_firmware_active and clean up isxray()
//Update what it can see.
- GLOB.cameranet.updateVisibility(camera, 0)
upgraded = TRUE
if(!camera.isEmpProof())
diff --git a/code/modules/events/camerafailure.dm b/code/modules/events/camerafailure.dm
index d2fcdda8314..f6929d4768f 100644
--- a/code/modules/events/camerafailure.dm
+++ b/code/modules/events/camerafailure.dm
@@ -12,7 +12,7 @@
/datum/round_event/camera_failure/start()
var/iterations = 1
- var/list/cameras = GLOB.cameranet.cameras.Copy()
+ var/list/cameras = SScameras.cameras.Copy()
while(prob(round(100/iterations)))
var/obj/machinery/camera/C = pick_n_take(cameras)
if (!C)
diff --git a/code/modules/lighting/lighting_atom.dm b/code/modules/lighting/lighting_atom.dm
index 21676d1741e..b671de56cfc 100644
--- a/code/modules/lighting/lighting_atom.dm
+++ b/code/modules/lighting/lighting_atom.dm
@@ -84,6 +84,8 @@
AddElement(/datum/element/light_blocking)
else
RemoveElement(/datum/element/light_blocking)
+ // Change in opacity could change camera visibility
+ SScameras.update_visibility(src)
/turf/set_opacity(new_opacity)
. = ..()
diff --git a/code/modules/mining/lavaland/mining_loot/megafauna/the_thing.dm b/code/modules/mining/lavaland/mining_loot/megafauna/the_thing.dm
index 19c04179d25..44172c5ed11 100644
--- a/code/modules/mining/lavaland/mining_loot/megafauna/the_thing.dm
+++ b/code/modules/mining/lavaland/mining_loot/megafauna/the_thing.dm
@@ -118,7 +118,7 @@
if(AI.controlled_equipment)
to_chat(AI, span_warning("You are already loaded into an onboard computer!"))
return
- if(!GLOB.cameranet.checkCameraVis(owner))
+ if(!SScameras.is_visible_by_cameras(owner))
to_chat(AI, span_warning("Target is no longer near active cameras."))
return
if(!isturf(AI.loc))
diff --git a/code/modules/mob/eye/camera/camera.dm b/code/modules/mob/eye/camera/camera.dm
index 58019f73859..8df02a444b2 100644
--- a/code/modules/mob/eye/camera/camera.dm
+++ b/code/modules/mob/eye/camera/camera.dm
@@ -71,7 +71,7 @@
SHOULD_CALL_PARENT(TRUE)
if(use_visibility)
- GLOB.cameranet.visibility(src)
+ SScameras.update_eye_chunk(src)
/mob/eye/camera/zMove(dir, turf/target, z_move_flags = NONE, recursions_left = 1, list/falling_movs)
. = ..()
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index 9d86aa3eb06..c3a0f479558 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -1328,7 +1328,7 @@
if(onSyndieBase() && !(ROLE_SYNDICATE in user?.faction))
return FALSE
// Now, are they viewable by a camera? (This is last because it's the most intensive check)
- if(!GLOB.cameranet.checkCameraVis(src))
+ if(!SScameras.is_visible_by_cameras(src))
return FALSE
return TRUE
diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm
index 9ad600b0f88..329a5f29823 100644
--- a/code/modules/mob/living/silicon/ai/ai.dm
+++ b/code/modules/mob/living/silicon/ai/ai.dm
@@ -310,7 +310,7 @@
return ISINRANGE(target_turf.x, ai_turf.x - interaction_range, ai_turf.x + interaction_range) \
&& ISINRANGE(target_turf.y, ai_turf.y - interaction_range, ai_turf.y + interaction_range)
else
- return GLOB.cameranet.checkTurfVis(target_turf)
+ return SScameras.is_visible_by_cameras(target_turf)
/mob/living/silicon/ai/cancel_camera()
view_core()
@@ -432,7 +432,7 @@
return
if (href_list["switchcamera"])
- switchCamera(locate(href_list["switchcamera"]) in GLOB.cameranet.cameras)
+ switchCamera(locate(href_list["switchcamera"]) in SScameras.cameras)
if (href_list["showalerts"])
alert_control.ui_interact(src)
#ifdef AI_VOX
@@ -475,7 +475,7 @@
if(controlled_equipment)
to_chat(src, span_warning("You are already loaded into an onboard computer!"))
return
- if(!GLOB.cameranet.checkCameraVis(M))
+ if(!SScameras.is_visible_by_cameras(M))
to_chat(src, span_warning("Exosuit is no longer near active cameras."))
return
if(!isturf(loc))
@@ -520,7 +520,7 @@
//The target must be in view of a camera or near the core.
if(turf_check in range(get_turf(src)))
call_bot(turf_check)
- else if(GLOB.cameranet && GLOB.cameranet.checkTurfVis(turf_check))
+ else if(SScameras.is_visible_by_cameras(turf_check))
call_bot(turf_check)
else
to_chat(src, span_danger("Selected location is not visible."))
@@ -578,7 +578,7 @@
var/mob/living/silicon/ai/U = usr
- for (var/obj/machinery/camera/C in GLOB.cameranet.cameras)
+ for (var/obj/machinery/camera/C in SScameras.cameras)
var/turf/camera_turf = get_turf(C) //get camera's turf in case it's built into something so we don't get z=0
var/list/tempnetwork = C.network
@@ -600,7 +600,7 @@
if(isnull(network))
network = old_network // If nothing is selected
else
- for(var/obj/machinery/camera/C in GLOB.cameranet.cameras)
+ for(var/obj/machinery/camera/C in SScameras.cameras)
if(!C.can_use())
continue
if(network in C.network)
@@ -806,7 +806,7 @@
if(isturf(loc)) //AI in core, check if on cameras
//get_turf_pixel() is because APCs in maint aren't actually in view of the inner camera
//apc_override is needed here because AIs use their own APC when depowered
- return ((GLOB.cameranet && GLOB.cameranet.checkTurfVis(get_turf_pixel(A))) || (A == apc_override))
+ return (SScameras.is_visible_by_cameras(get_turf_pixel(A)) || (A == apc_override))
//AI is carded/shunted
//view(src) returns nothing for carded/shunted AIs and they have X-ray vision so just use get_dist
var/list/viewscale = getviewsize(client.view)
@@ -1045,7 +1045,7 @@
. = ..()
/mob/living/silicon/ai/proc/camera_visibility(mob/eye/camera/ai/moved_eye)
- GLOB.cameranet.visibility(moved_eye)
+ SScameras.update_eye_chunk(moved_eye)
/mob/living/silicon/ai/forceMove(atom/destination)
. = ..()
@@ -1079,7 +1079,7 @@
REMOVE_TRAIT(src, TRAIT_INCAPACITATED, POWER_LACK_TRAIT)
/mob/living/silicon/ai/proc/show_camera_list()
- var/list/cameras = GLOB.cameranet.get_available_camera_by_tag_list(network)
+ var/list/cameras = SScameras.get_available_camera_by_tag_list(network)
var/camera_tag = tgui_input_list(src, "Choose which camera you want to view", "Cameras", cameras)
if(isnull(camera_tag))
return
diff --git a/code/modules/mob/living/silicon/ai/freelook/cameranet.dm b/code/modules/mob/living/silicon/ai/freelook/cameranet.dm
deleted file mode 100644
index 5e534297d77..00000000000
--- a/code/modules/mob/living/silicon/ai/freelook/cameranet.dm
+++ /dev/null
@@ -1,253 +0,0 @@
-// CAMERA NET
-//
-// The datum containing all the chunks.
-
-GLOBAL_DATUM_INIT(cameranet, /datum/cameranet, new)
-
-/datum/cameranet
- /// Name to show for VV and stat()
- var/name = "Camera Net"
-
- /// The cameras on the map, no matter if they work or not. Updated in obj/machinery/camera.dm in Initialize() and Destroy().
- var/list/obj/machinery/camera/cameras = list()
- /// The chunks of the map, mapping the areas that the cameras can see.
- var/list/chunks = list()
-
- /// List of images cloned by all chunk static images put onto turfs cameras cant see
- /// Indexed by the plane offset to use
- var/list/image/obscured_images
-
-/datum/cameranet/New()
- obscured_images = list()
- update_offsets(SSmapping.max_plane_offset)
- RegisterSignal(SSmapping, COMSIG_PLANE_OFFSET_INCREASE, PROC_REF(on_offset_growth))
-
-/datum/cameranet/proc/update_offsets(new_offset)
- for(var/i in length(obscured_images) to new_offset)
- var/image/obscured = new('icons/effects/cameravis.dmi')
- SET_PLANE_W_SCALAR(obscured, CAMERA_STATIC_PLANE, i)
- obscured.appearance_flags = RESET_TRANSFORM | RESET_ALPHA | RESET_COLOR | KEEP_APART
- obscured.override = TRUE
- obscured_images += obscured
-
-/datum/cameranet/proc/on_offset_growth(datum/source, old_offset, new_offset)
- SIGNAL_HANDLER
- update_offsets(new_offset)
-
-/// Checks if a chunk has been Generated in x, y, z.
-/datum/cameranet/proc/chunkGenerated(x, y, z)
- x = GET_CHUNK_COORD(x)
- y = GET_CHUNK_COORD(y)
- if(GET_LOWEST_STACK_OFFSET(z) != 0)
- var/turf/lowest = get_lowest_turf(locate(x, y, z))
- return chunks["[x],[y],[lowest.z]"]
-
- return chunks["[x],[y],[z]"]
-
-// Returns the chunk in the x, y, z.
-// If there is no chunk, it creates a new chunk and returns that.
-/datum/cameranet/proc/getCameraChunk(x, y, z)
- x = GET_CHUNK_COORD(x)
- y = GET_CHUNK_COORD(y)
- var/turf/lowest = get_lowest_turf(locate(x, y, z))
- var/key = "[x],[y],[lowest.z]"
- . = chunks[key]
- if(!.)
- chunks[key] = . = new /datum/camerachunk(x, y, lowest.z)
-
-/// Updates what the camera eye can see. It is recommended you use this when a camera eye moves or its location is set.
-/datum/cameranet/proc/visibility(list/moved_eyes)
- if(!islist(moved_eyes))
- moved_eyes = moved_eyes ? list(moved_eyes) : list()
-
- for(var/mob/eye/camera/eye as anything in moved_eyes)
- var/list/visibleChunks = list()
- //Get the eye's turf in case its located in an object like a mecha
- var/turf/eye_turf = get_turf(eye)
- if(eye.loc)
- var/static_range = eye.static_visibility_range
- var/x1 = max(1, eye_turf.x - static_range)
- var/y1 = max(1, eye_turf.y - static_range)
- var/x2 = min(world.maxx, eye_turf.x + static_range)
- var/y2 = min(world.maxy, eye_turf.y + static_range)
-
- for(var/x = x1; x <= x2; x += CHUNK_SIZE)
- for(var/y = y1; y <= y2; y += CHUNK_SIZE)
- visibleChunks |= getCameraChunk(x, y, eye_turf.z)
-
- var/list/remove = eye.visibleCameraChunks - visibleChunks
- var/list/add = visibleChunks - eye.visibleCameraChunks
-
- for(var/datum/camerachunk/chunk as anything in remove)
- chunk.remove(eye)
-
- for(var/datum/camerachunk/chunk as anything in add)
- chunk.add(eye)
-
-/// Updates the chunks that the turf is located in. Use this when obstacles are destroyed or when doors open.
-/datum/cameranet/proc/updateVisibility(atom/A, opacity_check = 1)
- if(!SSticker || (opacity_check && !A.opacity))
- return
- majorChunkChange(A, 2)
-
-/datum/cameranet/proc/updateChunk(x, y, z)
- var/datum/camerachunk/chunk = chunkGenerated(x, y, z)
- if (!chunk)
- return
- chunk.hasChanged()
-
-/// Removes a camera from a chunk.
-/datum/cameranet/proc/removeCamera(obj/machinery/camera/c)
- majorChunkChange(c, 0)
-
-/// Add a camera to a chunk.
-/datum/cameranet/proc/addCamera(obj/machinery/camera/c)
- if(c.can_use())
- majorChunkChange(c, 1)
-
-/**
- * Used for Cyborg/mecha cameras. Since portable cameras can be in ANY chunk.
- * update_delay_buffer is passed all the way to hasChanged() from their camera updates on movement
- * to change the time between static updates.
-*/
-/datum/cameranet/proc/updatePortableCamera(obj/machinery/camera/updating_camera, update_delay_buffer)
- if(updating_camera.can_use())
- majorChunkChange(updating_camera, 1, update_delay_buffer)
-
-/**
- * Never access this proc directly!!!!
- * This will update the chunk and all the surrounding chunks.
- * It will also add the atom to the cameras list if you set the choice to 1.
- * Setting the choice to 0 will remove the camera from the chunks.
- * If you want to update the chunks around an object, without adding/removing a camera, use choice 2.
- * update_delay_buffer is passed all the way to hasChanged() from portable camera updates on movement
- * to change the time between static updates.
- */
-/datum/cameranet/proc/majorChunkChange(atom/c, choice, update_delay_buffer)
- PROTECTED_PROC(TRUE)
-
- if(QDELETED(c) && choice == 1)
- CRASH("Tried to add a qdeleting camera to the net")
-
- var/turf/T = get_turf(c)
- if(T)
- var/x1 = max(1, T.x - (CHUNK_SIZE / 2))
- var/y1 = max(1, T.y - (CHUNK_SIZE / 2))
- var/x2 = min(world.maxx, T.x + (CHUNK_SIZE / 2))
- var/y2 = min(world.maxy, T.y + (CHUNK_SIZE / 2))
- for(var/x = x1; x <= x2; x += CHUNK_SIZE)
- for(var/y = y1; y <= y2; y += CHUNK_SIZE)
- var/datum/camerachunk/chunk = chunkGenerated(x, y, T.z)
- if(chunk)
- if(choice == 0)
- // Remove the camera.
- chunk.cameras["[T.z]"] -= c
- else if(choice == 1)
- // You can't have the same camera in the list twice.
- chunk.cameras["[T.z]"] |= c
- chunk.hasChanged(update_delay_buffer = update_delay_buffer)
-
-/// A faster, turf only version of [/datum/cameranet/proc/majorChunkChange]
-/// For use in sensitive code, be careful with it
-/datum/cameranet/proc/bareMajorChunkChange(turf/changed)
- var/x1 = max(1, changed.x - (CHUNK_SIZE / 2))
- var/y1 = max(1, changed.y - (CHUNK_SIZE / 2))
- var/x2 = min(world.maxx, changed.x + (CHUNK_SIZE / 2))
- var/y2 = min(world.maxy, changed.y + (CHUNK_SIZE / 2))
- for(var/x = x1; x <= x2; x += CHUNK_SIZE)
- for(var/y = y1; y <= y2; y += CHUNK_SIZE)
- var/datum/camerachunk/chunk = chunkGenerated(x, y, changed.z)
- chunk?.hasChanged()
-
-/// Will check if a mob is on a viewable turf. Returns 1 if it is, otherwise returns 0.
-/datum/cameranet/proc/checkCameraVis(mob/living/target)
- var/turf/position = get_turf(target)
- if(!position)
- return
- return checkTurfVis(position)
-
-/datum/cameranet/proc/checkTurfVis(turf/position)
- var/datum/camerachunk/chunk = getCameraChunk(position.x, position.y, position.z)
- if(chunk)
- if(chunk.changed)
- chunk.hasChanged(1) // Update now, no matter if it's visible or not.
- if(chunk.visibleTurfs[position])
- return TRUE
- return FALSE
-
-/datum/cameranet/proc/getTurfVis(turf/position)
- RETURN_TYPE(/datum/camerachunk)
- var/datum/camerachunk/chunk = getCameraChunk(position.x, position.y, position.z)
- if(!chunk)
- return FALSE
- if(chunk.changed)
- chunk.hasChanged(1) // Update now, no matter if it's visible or not.
- if(chunk.visibleTurfs[position])
- return chunk
-
-/// Returns list of available cameras, ready to use for UIs displaying list of them
-/// The format is: list("name" = "camera.c_tag", ref = REF(camera))
-/datum/cameranet/proc/get_available_cameras_data(list/networks_available, list/z_levels_available)
- var/list/available_cameras_data = list()
- for(var/obj/machinery/camera/camera as anything in get_filtered_and_sorted_cameras(networks_available, z_levels_available))
- available_cameras_data += list(list(
- name = camera.c_tag,
- ref = REF(camera),
- ))
-
- return available_cameras_data
-
-/**
- * get_available_camera_by_tag_list
- *
- * Builds a list of all available cameras that can be seen to networks_available and in z_levels_available.
- * Entries are stored in `c_tag[camera.can_use() ? null : " (Deactivated)"]` => `camera` format
- * Args:
- * networks_available - List of networks that we use to see which cameras are visible to it.
- * z_levels_available - List of z levels to filter camera by. If empty, all z levels are considered valid.
- * sort_by_ctag - If the resulting list should be sorted by `c_tag`.
- */
-/datum/cameranet/proc/get_available_camera_by_tag_list(list/networks_available, list/z_levels_available)
- var/list/available_cameras_by_tag = list()
- for(var/obj/machinery/camera/camera as anything in get_filtered_and_sorted_cameras(networks_available, z_levels_available))
- available_cameras_by_tag["[camera.c_tag][camera.can_use() ? null : " (Deactivated)"]"] = camera
-
- return available_cameras_by_tag
-
-/// Returns list of all cameras that passed `is_camera_available` filter and sorted by `cmp_camera_ctag_asc`
-/datum/cameranet/proc/get_filtered_and_sorted_cameras(list/networks_available, list/z_levels_available)
- PRIVATE_PROC(TRUE)
-
- var/list/filtered_cameras = list()
- for(var/obj/machinery/camera/camera as anything in cameras)
- if(!is_camera_available(camera, networks_available, z_levels_available))
- continue
-
- filtered_cameras += camera
-
- return sortTim(filtered_cameras, GLOBAL_PROC_REF(cmp_camera_ctag_asc))
-
-/// Checks if the `camera_to_check` meets the requirements of availability.
-/datum/cameranet/proc/is_camera_available(obj/machinery/camera/camera_to_check, list/networks_available, list/z_levels_available)
- PRIVATE_PROC(TRUE)
-
- if(!camera_to_check.c_tag)
- return FALSE
-
- if(length(z_levels_available) && !(camera_to_check.z in z_levels_available))
- return FALSE
-
- return length(camera_to_check.network & networks_available) > 0
-
-/obj/effect/overlay/camera_static
- name = "static"
- icon = null
- icon_state = null
- anchored = TRUE // should only appear in vis_contents, but to be safe
- appearance_flags = RESET_TRANSFORM | TILE_BOUND | LONG_GLIDE
- // this combination makes the static block clicks to everything below it,
- // without appearing in the right-click menu for non-AI clients
- mouse_opacity = MOUSE_OPACITY_ICON
- invisibility = INVISIBILITY_ABSTRACT
-
- plane = CAMERA_STATIC_PLANE
diff --git a/code/modules/mob/living/silicon/ai/freelook/chunk.dm b/code/modules/mob/living/silicon/ai/freelook/chunk.dm
index a2dcee69cd1..23098ab15e9 100644
--- a/code/modules/mob/living/silicon/ai/freelook/chunk.dm
+++ b/code/modules/mob/living/silicon/ai/freelook/chunk.dm
@@ -1,5 +1,3 @@
-#define UPDATE_BUFFER_TIME (2.5 SECONDS)
-
/**
* 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 it can and cannot see.
@@ -12,7 +10,7 @@
var/list/visibleTurfs = list()
///cameras that can see into our grid
///indexed by the z level of the camera
- var/list/cameras = list()
+ var/alist/cameras = alist()
///list of all turfs, associative with that turf's static image
///turf -> /image
var/list/turfs = list()
@@ -21,18 +19,19 @@
///images currently in use on obscured turfs.
var/list/active_static_images = list()
- var/changed = FALSE
var/x = 0
var/y = 0
var/lower_z
var/upper_z
-/// Add a camera eye to the chunk, then update if changed.
+ /// List of atoms that caused the chunk to update - assoc atom ref() to opacity on queue
+ var/list/update_sources = list()
+
+/// Add a camera eye to the chunk, updating the chunk if necessary.
/datum/camerachunk/proc/add(mob/eye/camera/eye)
eye.visibleCameraChunks += src
seenby += eye
- if(changed)
- update()
+ force_update()
var/client/client = eye.GetViewerClient()
if(client && eye.use_visibility)
@@ -47,36 +46,74 @@
if(client && eye.use_visibility && seenby.len == 0)
client.images -= active_static_images
-/// Called when a chunk has changed. I.E: A wall was deleted.
-/datum/camerachunk/proc/visibilityChanged(turf/loc)
- if(!visibleTurfs[loc])
+/**
+ * Queues the chuck to be updated after a delay.
+ *
+ * * update_source - the atom that caused the update
+ * * update_delay_buffer - the delay before the update is performed. Defaults to 0 (instant).
+ */
+/datum/camerachunk/proc/queue_update(atom/update_source, update_delay_buffer = 0)
+ // This chunk is being actively observed, skip queuing
+ if(length(seenby))
+ addtimer(CALLBACK(src, PROC_REF(update)), update_delay_buffer || 1, TIMER_UNIQUE)
return
- hasChanged()
+
+ // Only start queue if this is the first thing to queue an update
+ var/start_queue = !length(update_sources)
+
+ var/update_key = REF(update_source)
+ // Camera updates will never be second guessed.
+ // Track the number of times the camera has queued an update instead of opacity (just for fun)
+ if(istype(update_source, /obj/machinery/camera))
+ update_sources[update_source] += 1
+
+ // Otherwise track this atom's opacity at time of queue
+ else if(isnull(update_sources[update_key]))
+ update_sources[update_key] = update_source.opacity
+
+ // If the tracked opacity does not match current opacity,
+ // that implies that the atom changed opacity twice in the time before the update happened
+ // So we can safely remove this atom as a "source of update"
+ else if(update_sources[update_key] != update_source.opacity)
+ update_sources -= update_key
+ return
+
+ if(!start_queue)
+ return
+
+ if(update_delay_buffer <= 0)
+ _queue_update()
+ else
+ addtimer(CALLBACK(src, PROC_REF(_queue_update)), update_delay_buffer, TIMER_UNIQUE)
+
+/datum/camerachunk/proc/_queue_update()
+ PRIVATE_PROC(TRUE)
+ // Something forced an update during the delay
+ if(!length(update_sources))
+ return
+ SScameras.chunks_to_update[src] += 1
/**
- * 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 an AI Eye moves near it.
+ * Forces the chunk to update immediately
*
- * update_delay_buffer is used for cameras that are moving around, which are cyborg inbuilt cameras and
- * mecha onboard cameras. This buffer should be usually lower than UPDATE_BUFFER_TIME because
- * otherwise a moving camera can run out of its own view before updating static.
+ * * only_if_necessary - if TRUE, will not update the chunk unless it's been marked to update.
*/
-/datum/camerachunk/proc/hasChanged(update_now = 0, update_delay_buffer = UPDATE_BUFFER_TIME)
- if(seenby.len || update_now)
- addtimer(CALLBACK(src, PROC_REF(update)), update_delay_buffer, TIMER_UNIQUE)
- else
- changed = TRUE
+/datum/camerachunk/proc/force_update(only_if_necessary = TRUE)
+ if(only_if_necessary && !length(update_sources))
+ return
+ update()
/// The actual updating. It gathers the visible turfs from cameras and puts them into the appropiate lists.
-/// Accepts an optional partial_update argument, that blocks any calls out to chunks that could affect us, like above or below
-/datum/camerachunk/proc/update(partial_update = FALSE)
- if(GLOB.block_camera_updates)
+/datum/camerachunk/proc/update()
+ if(SScameras.disable_camera_updates)
return
+ update_sources.Cut()
+
var/list/updated_visible_turfs = list()
for(var/z_level in lower_z to upper_z)
- for(var/obj/machinery/camera/current_camera as anything in cameras["[z_level]"])
+ for(var/obj/machinery/camera/current_camera as anything in cameras[z_level])
if(!current_camera || !current_camera.can_use())
continue
@@ -84,9 +121,8 @@
if(get_dist(point, current_camera) > CHUNK_SIZE + (CHUNK_SIZE / 2))
continue
- for(var/turf/vis_turf in current_camera.can_see())
- if(turfs[vis_turf])
- updated_visible_turfs[vis_turf] = vis_turf
+ for(var/turf/vis_turf as anything in turfs & current_camera.can_see())
+ updated_visible_turfs[vis_turf] = vis_turf
///new turfs that we couldnt see last update but can now
var/list/newly_visible_turfs = updated_visible_turfs - visibleTurfs
@@ -121,8 +157,6 @@
active_static_images += static_image
visibleTurfs = updated_visible_turfs
- changed = FALSE
-
for(var/mob/eye/camera/client_eye as anything in seenby)
var/client/client = client_eye.GetViewerClient()
if(!client)
@@ -156,9 +190,9 @@
if(mech.chassis_camera?.can_use())
local_cameras += mech.chassis_camera
- cameras["[z_level]"] = local_cameras
+ cameras[z_level] = local_cameras
- var/image/mirror_from = GLOB.cameranet.obscured_images[GET_Z_PLANE_OFFSET(z_level) + 1]
+ var/image/mirror_from = SScameras.obscured_images[GET_Z_PLANE_OFFSET(z_level) + 1]
var/turf/chunk_corner = locate(x, y, z_level)
for(var/turf/lad as anything in CORNER_BLOCK(chunk_corner, CHUNK_SIZE, CHUNK_SIZE)) //we use CHUNK_SIZE for width and height here as it handles subtracting 1 from those two parameters by itself
var/image/our_image = new /image(mirror_from)
@@ -172,18 +206,10 @@
if(!camera.can_use())
continue
- for(var/turf/vis_turf in camera.can_see())
- if(turfs[vis_turf])
- visibleTurfs[vis_turf] = vis_turf
+ for(var/turf/vis_turf as anything in turfs & camera.can_see())
+ visibleTurfs[vis_turf] = vis_turf
for(var/turf/obscured_turf as anything in turfs - visibleTurfs)
var/image/new_static = turfs[obscured_turf]
active_static_images += new_static
obscuredTurfs[obscured_turf] = new_static
-
-#undef UPDATE_BUFFER_TIME
-
-GLOBAL_VAR_INIT(block_camera_updates, FALSE)
-
-ADMIN_VERB(pause_camera_updates, R_ADMIN, "Toggle Camera Updates", "Stop security cameras from updating, meaning what they see now is what they will see forever.", ADMIN_CATEGORY_DEBUG)
- GLOB.block_camera_updates = !GLOB.block_camera_updates
diff --git a/code/modules/mob/living/silicon/ai/freelook/eye.dm b/code/modules/mob/living/silicon/ai/freelook/eye.dm
index 900ff158ee7..6d279194f8f 100644
--- a/code/modules/mob/living/silicon/ai/freelook/eye.dm
+++ b/code/modules/mob/living/silicon/ai/freelook/eye.dm
@@ -221,7 +221,7 @@
/mob/eye/camera/ai/Hear(atom/movable/speaker, datum/language/message_language, raw_message, radio_freq, radio_freq_name, radio_freq_color, list/spans, list/message_mods = list(), message_range)
. = ..()
- if(relay_speech && speaker && ai && !radio_freq && speaker != ai && GLOB.cameranet.checkCameraVis(speaker))
+ if(relay_speech && speaker && ai && !radio_freq && speaker != ai && SScameras.is_visible_by_cameras(speaker))
ai.relay_speech(speaker, message_language, raw_message, radio_freq, spans, message_mods)
/obj/effect/overlay/ai_detect_hud
diff --git a/code/modules/mob/living/silicon/silicon_movement.dm b/code/modules/mob/living/silicon/silicon_movement.dm
index 0ea6ece169c..7ead122301b 100644
--- a/code/modules/mob/living/silicon/silicon_movement.dm
+++ b/code/modules/mob/living/silicon/silicon_movement.dm
@@ -19,6 +19,6 @@
*/
/mob/living/silicon/proc/do_camera_update(oldLoc)
if(oldLoc != get_turf(src))
- GLOB.cameranet.updatePortableCamera(builtInCamera, SILICON_CAMERA_BUFFER)
+ SScameras.update_portable_camera(builtInCamera, SILICON_CAMERA_BUFFER)
updating = FALSE
#undef SILICON_CAMERA_BUFFER
diff --git a/code/modules/modular_computers/file_system/programs/secureye.dm b/code/modules/modular_computers/file_system/programs/secureye.dm
index 4a90f25f5b0..51e3cfed303 100644
--- a/code/modules/modular_computers/file_system/programs/secureye.dm
+++ b/code/modules/modular_computers/file_system/programs/secureye.dm
@@ -129,7 +129,7 @@
data["network"] = network
data["mapRef"] = cam_screen.assigned_map
data["can_spy"] = !!spying
- data["cameras"] = GLOB.cameranet.get_available_cameras_data(network)
+ data["cameras"] = SScameras.get_available_cameras_data(network)
return data
/datum/computer_file/program/secureye/ui_act(action, params, datum/tgui/ui, datum/ui_state/state)
@@ -145,7 +145,7 @@
if(!spying)
playsound(computer, SFX_TERMINAL_TYPE, 25, FALSE)
- var/obj/machinery/camera/selected_camera = locate(params["camera"]) in GLOB.cameranet.cameras
+ var/obj/machinery/camera/selected_camera = locate(params["camera"]) in SScameras.cameras
if(selected_camera)
camera_ref = WEAKREF(selected_camera)
else
@@ -168,12 +168,13 @@
/datum/computer_file/program/secureye/proc/on_track_target(datum/trackable/source, mob/living/target)
SIGNAL_HANDLER
- var/datum/camerachunk/target_camerachunk = GLOB.cameranet.getTurfVis(get_turf(target))
+ var/target_turf = get_turf(target)
+ var/datum/camerachunk/target_camerachunk = SScameras.get_turf_camera_chunk(target_turf)
if(!target_camerachunk)
CRASH("[src] was able to track [target] through /datum/trackable, but was not on a visible turf to cameras.")
- for(var/obj/machinery/camera/cameras as anything in target_camerachunk.cameras["[target.z]"])
- var/found_target = locate(target) in cameras.can_see()
- if(!found_target)
+ for(var/obj/machinery/camera/cameras as anything in target_camerachunk.cameras[target.z])
+ // We need to find a particular camera that can see this turf
+ if(!(target_turf in cameras.can_see()))
continue
var/new_camera = WEAKREF(cameras)
if(camera_ref == new_camera)
diff --git a/code/modules/photography/camera/camera.dm b/code/modules/photography/camera/camera.dm
index 9ef40666fc3..c0c956e3698 100644
--- a/code/modules/photography/camera/camera.dm
+++ b/code/modules/photography/camera/camera.dm
@@ -191,17 +191,11 @@
/// 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
+ return SScameras.is_visible_by_cameras(target_turf)
/// 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 : WIDESCREEN_VIEWPORT_SIZE
- var/user_eye = user.client ? user.client.eye : user
- if(!(target_turf in get_hear(user_view, user_eye)))
- return FALSE
- return TRUE
+ return (target_turf in get_hear_turfs(user.client?.view || world.view, user.client?.eye || user))
/obj/item/camera/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers)
// Always skip on storage and tables
@@ -260,18 +254,17 @@
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/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)
- var/viewc = user?.client ? user.client.eye : target
- seen = get_hear(viewr, viewc)
+ var/list/viewlist = getviewsize(user?.client?.view || world.view)
+ var/view_range = max(viewlist[1], viewlist[2]) + max(size_x, size_y)
+ var/viewer = user?.client?.eye || user || target // not sure why target is a fallback
+ var/list/seen = get_hear_turfs(view_range, viewer)
var/list/turfs = list()
var/list/mobs = list()
var/blueprints = FALSE
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/cameranet_user = isAI(user) || istype(viewer, /mob/eye/camera)
var/width = size_x * 2 + 1
var/height = size_y * 2 + 1
@@ -279,7 +272,7 @@
if(isnull(seen_placeholder))
continue
- if(cameranet_user && !GLOB.cameranet.checkTurfVis(seen_placeholder))
+ if(cameranet_user && !SScameras.is_visible_by_cameras(seen_placeholder))
continue
if(!cameranet_user && !(seen_placeholder in seen))
continue
diff --git a/code/modules/research/xenobiology/xenobio_camera.dm b/code/modules/research/xenobiology/xenobio_camera.dm
index 960fa43a61c..ffe23c8e5b4 100644
--- a/code/modules/research/xenobiology/xenobio_camera.dm
+++ b/code/modules/research/xenobiology/xenobio_camera.dm
@@ -199,7 +199,7 @@
/// Validates whether the target turf can be interacted with.
/obj/machinery/computer/camera_advanced/xenobio/proc/validate_turf(mob/living/user, turf/open/target_turf)
- if(!GLOB.cameranet.checkTurfVis(target_turf))
+ if(!SScameras.is_visible_by_cameras(target_turf))
target_turf.balloon_alert(user, "outside of view!")
return FALSE
diff --git a/code/modules/shuttle/mobile_port/shuttle_move_callbacks.dm b/code/modules/shuttle/mobile_port/shuttle_move_callbacks.dm
index 89b20e7adbc..9f9220c3f8d 100644
--- a/code/modules/shuttle/mobile_port/shuttle_move_callbacks.dm
+++ b/code/modules/shuttle/mobile_port/shuttle_move_callbacks.dm
@@ -238,11 +238,11 @@ All ShuttleMove procs go here
. = ..()
if(. & MOVE_AREA)
. |= MOVE_CONTENTS
- GLOB.cameranet.removeCamera(src)
+ SScameras.remove_camera_from_chunk(src)
/obj/machinery/camera/afterShuttleMove(turf/oldT, list/movement_force, shuttle_dir, shuttle_preferred_direction, move_dir, rotation)
. = ..()
- GLOB.cameranet.addCamera(src)
+ SScameras.add_camera_to_chunk(src)
/obj/machinery/mech_bay_recharge_port/afterShuttleMove(turf/oldT, list/movement_force, shuttle_dir, shuttle_preferred_direction, move_dir)
. = ..()
diff --git a/code/modules/transport/tram/tram_doors.dm b/code/modules/transport/tram/tram_doors.dm
index 17173e79cd9..95c1e1c386f 100644
--- a/code/modules/transport/tram/tram_doors.dm
+++ b/code/modules/transport/tram/tram_doors.dm
@@ -54,7 +54,6 @@
set_density(FALSE)
if(!isnull(filler))
filler.set_density(FALSE)
- update_freelook_sight()
flags_1 &= ~PREVENT_CLICK_UNDER_1
air_update_turf(TRUE, FALSE)
var/open_delay = animation_segment_delay(AIRLOCK_OPENING_FINISHED) - passable_delay
@@ -113,7 +112,6 @@
set_density(TRUE)
if(!isnull(filler))
filler.set_density(TRUE)
- update_freelook_sight()
flags_1 |= PREVENT_CLICK_UNDER_1
air_update_turf(TRUE, TRUE)
crush()
diff --git a/code/modules/vehicles/mecha/mecha_movement.dm b/code/modules/vehicles/mecha/mecha_movement.dm
index b5817d81378..46b49cc2ee5 100644
--- a/code/modules/vehicles/mecha/mecha_movement.dm
+++ b/code/modules/vehicles/mecha/mecha_movement.dm
@@ -205,6 +205,6 @@
*/
/obj/vehicle/sealed/mecha/proc/do_camera_update(oldLoc)
if(oldLoc != get_turf(src))
- GLOB.cameranet.updatePortableCamera(chassis_camera, MECH_CAMERA_BUFFER)
+ SScameras.update_portable_camera(chassis_camera, MECH_CAMERA_BUFFER)
updating = FALSE
#undef MECH_CAMERA_BUFFER
diff --git a/code/modules/wiremod/components/atom/remotecam.dm b/code/modules/wiremod/components/atom/remotecam.dm
index bbbab6043af..070d164c01c 100644
--- a/code/modules/wiremod/components/atom/remotecam.dm
+++ b/code/modules/wiremod/components/atom/remotecam.dm
@@ -189,7 +189,7 @@
/obj/item/circuit_component/remotecam/proc/update_camera_location(atom/old_loc, movement_dir, forced, list/old_locs, momentum_change)
SIGNAL_HANDLER
if(current_camera_state && current_cameranet_state)
- GLOB.cameranet.updatePortableCamera(shell_camera, 0.5 SECONDS)
+ SScameras.update_portable_camera(shell_camera, 0.5 SECONDS)
/**
* Add camera from global cameranet
@@ -197,8 +197,8 @@
/obj/item/circuit_component/remotecam/proc/cameranet_add()
if(current_cameranet_state)
return
- GLOB.cameranet.cameras += shell_camera
- GLOB.cameranet.addCamera(shell_camera)
+ SScameras.cameras += shell_camera
+ SScameras.add_camera_to_chunk(shell_camera)
current_cameranet_state = TRUE
/**
@@ -207,8 +207,8 @@
/obj/item/circuit_component/remotecam/proc/cameranet_remove()
if(!current_cameranet_state)
return
- GLOB.cameranet.removeCamera(shell_camera)
- GLOB.cameranet.cameras -= shell_camera
+ SScameras.remove_camera_from_chunk(shell_camera)
+ SScameras.cameras -= shell_camera
current_cameranet_state = FALSE
/**
diff --git a/tgstation.dme b/tgstation.dme
index 874f9bd1cad..1343a9f44b0 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -675,6 +675,7 @@
#include "code\controllers\subsystem\ban_cache.dm"
#include "code\controllers\subsystem\blackbox.dm"
#include "code\controllers\subsystem\blood_drying.dm"
+#include "code\controllers\subsystem\cameras.dm"
#include "code\controllers\subsystem\chat.dm"
#include "code\controllers\subsystem\dbcore.dm"
#include "code\controllers\subsystem\dcs.dm"
@@ -5520,7 +5521,6 @@
#include "code\modules\mob\living\silicon\ai\robot_control.dm"
#include "code\modules\mob\living\silicon\ai\vox_sounds.dm"
#include "code\modules\mob\living\silicon\ai\ai_actions\remote_power.dm"
-#include "code\modules\mob\living\silicon\ai\freelook\cameranet.dm"
#include "code\modules\mob\living\silicon\ai\freelook\chunk.dm"
#include "code\modules\mob\living\silicon\ai\freelook\eye.dm"
#include "code\modules\mob\living\silicon\robot\death.dm"