From 109854659bd889f425856d34d8b6daeaa10b98bd Mon Sep 17 00:00:00 2001
From: ascio <81930475+asciodev@users.noreply.github.com>
Date: Sun, 5 Jan 2025 14:58:26 -0500
Subject: [PATCH 1/6] Fix unmoving video cam feeds.
This change refactor the screen-setting logic in camera consoles to its
own method, and additionally calls that method whenever a video camera
is moved, if that video camera's feed is being watched by a camera
console.
---
.../game/machinery/computer/camera_console.dm | 37 +++++++++++--------
.../mob/living/carbon/human/human_movement.dm | 8 ++++
code/modules/paperwork/photography.dm | 9 +++++
3 files changed, 38 insertions(+), 16 deletions(-)
diff --git a/code/game/machinery/computer/camera_console.dm b/code/game/machinery/computer/camera_console.dm
index dfb4e1f08ea4..553fabee7fda 100644
--- a/code/game/machinery/computer/camera_console.dm
+++ b/code/game/machinery/computer/camera_console.dm
@@ -11,6 +11,7 @@
var/list/network = list("SS13","Mining Outpost")
var/obj/machinery/camera/active_camera
var/list/watchers = list()
+ var/list/living_watchers = list()
// Stuff needed to render the map
var/map_name
@@ -64,10 +65,11 @@
var/is_living = isliving(user)
// Ghosts shouldn't count towards concurrent users, which produces
// an audible terminal_on click.
+ watchers += user_uid
if(is_living)
- watchers += user_uid
+ living_watchers += user_uid
// Turn on the console
- if(length(watchers) == 1 && is_living)
+ if(length(living_watchers) == 1 && is_living)
if(!silent_console)
playsound(src, 'sound/machines/terminal_on.ogg', 25, FALSE)
use_power(active_power_consumption)
@@ -89,6 +91,7 @@
/obj/machinery/computer/security/ui_close(mob/user)
..()
watchers -= user.UID()
+ living_watchers -= user.UID()
user.client.clear_map(map_name)
/obj/machinery/computer/security/ui_data()
@@ -126,25 +129,27 @@
active_camera = C
if(!silent_console)
playsound(src, get_sfx("terminal_type"), 25, FALSE)
+ return update_viewer()
- // Show static if can't use the camera
- if(!active_camera?.can_use())
- show_camera_static()
- return TRUE
+/obj/machinery/computer/security/proc/update_viewer()
+ // Show static if can't use the camera
+ if(!active_camera?.can_use())
+ show_camera_static()
+ return TRUE
- var/list/visible_turfs = list()
- for(var/turf/T in view(C.view_range, get_turf(C)))
- visible_turfs += T
+ var/list/visible_turfs = list()
+ for(var/turf/T in view(active_camera.view_range, get_turf(active_camera)))
+ visible_turfs += T
- var/list/bbox = get_bbox_of_atoms(visible_turfs)
- var/size_x = bbox[3] - bbox[1] + 1
- var/size_y = bbox[4] - bbox[2] + 1
+ var/list/bbox = get_bbox_of_atoms(visible_turfs)
+ var/size_x = bbox[3] - bbox[1] + 1
+ var/size_y = bbox[4] - bbox[2] + 1
- cam_screen.vis_contents = visible_turfs
- cam_background.icon_state = "clear"
- cam_background.fill_rect(1, 1, size_x, size_y)
+ cam_screen.vis_contents = visible_turfs
+ cam_background.icon_state = "clear"
+ cam_background.fill_rect(1, 1, size_x, size_y)
- return TRUE
+ return TRUE
// Returns the list of cameras accessible from this computer
/obj/machinery/computer/security/proc/get_available_cameras()
diff --git a/code/modules/mob/living/carbon/human/human_movement.dm b/code/modules/mob/living/carbon/human/human_movement.dm
index f9d728abfab8..9d14f0fd8b02 100644
--- a/code/modules/mob/living/carbon/human/human_movement.dm
+++ b/code/modules/mob/living/carbon/human/human_movement.dm
@@ -55,3 +55,11 @@
if(bloody_feet[blood_state] > BLOOD_LOSS_IN_SPREAD)
createFootprintsFrom(src, dir, T)
update_inv_shoes()
+
+ var/obj/item/videocam/cam = belt
+ if(!istype(cam))
+ cam = r_hand
+ if(!istype(cam))
+ cam = l_hand
+ if(istype(cam))
+ cam.update_viewers()
diff --git a/code/modules/paperwork/photography.dm b/code/modules/paperwork/photography.dm
index 38bd6bf1b3cc..637ede43131f 100644
--- a/code/modules/paperwork/photography.dm
+++ b/code/modules/paperwork/photography.dm
@@ -572,6 +572,7 @@ GLOBAL_LIST_INIT(SpookyGhosts, list("ghost","shade","shade2","ghost-narsie","hor
materials = list(MAT_METAL = 1000, MAT_GLASS = 500)
var/on = FALSE
var/video_cooldown = 0
+ var/update_viewer_cooldown = 0
var/obj/machinery/camera/camera
var/icon_on = "videocam_on"
var/icon_off = "videocam"
@@ -630,6 +631,14 @@ GLOBAL_LIST_INIT(SpookyGhosts, list("ghost","shade","shade2","ghost-narsie","hor
if(T.watchers[M] == camera)
T.atom_say(msg)
+/obj/item/videocam/proc/update_viewers()
+ if(!camera || !on || world.timeofday < update_viewer_cooldown)
+ return TRUE
+ update_viewer_cooldown = world.timeofday + 3 SECONDS
+ for(var/obj/machinery/computer/security/telescreen/T in GLOB.machines)
+ if(T.active_camera == camera && length(T.watchers))
+ T.update_viewer()
+
/obj/item/videocam/advanced
name = "advanced video camera"
desc = "This video camera allows you to send live feeds even when attached to a belt."
From 5a93b4d0064aee99ae3ed3accea34d35f5d8a380 Mon Sep 17 00:00:00 2001
From: ascio <81930475+asciodev@users.noreply.github.com>
Date: Sun, 5 Jan 2025 16:52:22 -0500
Subject: [PATCH 2/6] Reduce follow lag and add stabilization to vidcams
Extracts camera follow lag into a variable on video cameras called
`update_viewer_cooldown_rate` and reduces it from 3 to 2 seconds.
Additionally, adds another variable on video cameras called
`update_viewer_stabilization_rate`, which updates all tuned-in camera
consoles with the new position of the video camera a final time, so the
feed remains centered on the camera.
---
code/modules/paperwork/photography.dm | 14 +++++++++++---
1 file changed, 11 insertions(+), 3 deletions(-)
diff --git a/code/modules/paperwork/photography.dm b/code/modules/paperwork/photography.dm
index 637ede43131f..411fc4a768f0 100644
--- a/code/modules/paperwork/photography.dm
+++ b/code/modules/paperwork/photography.dm
@@ -572,7 +572,14 @@ GLOBAL_LIST_INIT(SpookyGhosts, list("ghost","shade","shade2","ghost-narsie","hor
materials = list(MAT_METAL = 1000, MAT_GLASS = 500)
var/on = FALSE
var/video_cooldown = 0
+ /// The default amount of time the camera will wait before updating consoles with its new position.
+ var/update_viewer_cooldown_rate = 2 SECONDS
+ /// The next time that the camera should update consoles with its new position.
var/update_viewer_cooldown = 0
+ /// The amount of time after each move the camera will wait before updating consoles
+ /// with its position a final time. It should be higher than `update_viewer_cooldown_rate` to ensure
+ /// that it isn't debounced. (This ensures the feed tries to remain centered on the camera.)
+ var/update_viewer_stabilize_rate = 3 SECONDS
var/obj/machinery/camera/camera
var/icon_on = "videocam_on"
var/icon_off = "videocam"
@@ -632,12 +639,13 @@ GLOBAL_LIST_INIT(SpookyGhosts, list("ghost","shade","shade2","ghost-narsie","hor
T.atom_say(msg)
/obj/item/videocam/proc/update_viewers()
- if(!camera || !on || world.timeofday < update_viewer_cooldown)
- return TRUE
- update_viewer_cooldown = world.timeofday + 3 SECONDS
+ if(!camera || world.timeofday < update_viewer_cooldown)
+ return
+ update_viewer_cooldown = world.timeofday + update_viewer_cooldown_rate
for(var/obj/machinery/computer/security/telescreen/T in GLOB.machines)
if(T.active_camera == camera && length(T.watchers))
T.update_viewer()
+ addtimer(CALLBACK(src, PROC_REF(update_viewers)), update_viewer_stabilize_rate)
/obj/item/videocam/advanced
name = "advanced video camera"
From 096efd1a3f5b7566989aef5d24f1457e9a9cc7e0 Mon Sep 17 00:00:00 2001
From: ascio <81930475+asciodev@users.noreply.github.com>
Date: Sun, 5 Jan 2025 21:08:35 -0500
Subject: [PATCH 3/6] Change video camera feed follow to use signals
---
code/modules/mob/living/carbon/human/human_movement.dm | 8 --------
code/modules/paperwork/photography.dm | 5 +++++
2 files changed, 5 insertions(+), 8 deletions(-)
diff --git a/code/modules/mob/living/carbon/human/human_movement.dm b/code/modules/mob/living/carbon/human/human_movement.dm
index 9d14f0fd8b02..f9d728abfab8 100644
--- a/code/modules/mob/living/carbon/human/human_movement.dm
+++ b/code/modules/mob/living/carbon/human/human_movement.dm
@@ -55,11 +55,3 @@
if(bloody_feet[blood_state] > BLOOD_LOSS_IN_SPREAD)
createFootprintsFrom(src, dir, T)
update_inv_shoes()
-
- var/obj/item/videocam/cam = belt
- if(!istype(cam))
- cam = r_hand
- if(!istype(cam))
- cam = l_hand
- if(istype(cam))
- cam.update_viewers()
diff --git a/code/modules/paperwork/photography.dm b/code/modules/paperwork/photography.dm
index 411fc4a768f0..92d9b8df65fd 100644
--- a/code/modules/paperwork/photography.dm
+++ b/code/modules/paperwork/photography.dm
@@ -584,15 +584,19 @@ GLOBAL_LIST_INIT(SpookyGhosts, list("ghost","shade","shade2","ghost-narsie","hor
var/icon_on = "videocam_on"
var/icon_off = "videocam"
var/canhear_range = 7
+ var/mob/holder
/obj/item/videocam/proc/camera_state(mob/living/carbon/user)
if(!on)
+ holder = loc
+ RegisterSignal(holder, COMSIG_MOVABLE_MOVED, PROC_REF(update_viewers))
on = TRUE
camera = new /obj/machinery/camera(src)
icon_state = icon_on
camera.network = list("news")
camera.c_tag = user.name
else
+ UnregisterSignal(holder, COMSIG_MOVABLE_MOVED)
on = FALSE
icon_state = icon_off
camera.c_tag = null
@@ -639,6 +643,7 @@ GLOBAL_LIST_INIT(SpookyGhosts, list("ghost","shade","shade2","ghost-narsie","hor
T.atom_say(msg)
/obj/item/videocam/proc/update_viewers()
+ var/mob/holder = loc
if(!camera || world.timeofday < update_viewer_cooldown)
return
update_viewer_cooldown = world.timeofday + update_viewer_cooldown_rate
From 76245d3c3f7ce37ded7aabe4dc6439a78cba7073 Mon Sep 17 00:00:00 2001
From: ascio <81930475+asciodev@users.noreply.github.com>
Date: Thu, 9 Jan 2025 20:18:40 -0500
Subject: [PATCH 4/6] Fix unmoving camera console feeds
Makes camera consoles refresh their feeds every 2 seconds as long as
they have active watchers. This fixes camera bugs, ERT cameras, video
cameras (including the journalist's advanced video camera), and any
other mobile cameras on a console's camera network.
---
code/_globalvars/lists/objects.dm | 1 +
.../game/machinery/computer/camera_console.dm | 30 ++++++++++++++++-
code/modules/paperwork/photography.dm | 32 ++++---------------
3 files changed, 36 insertions(+), 27 deletions(-)
diff --git a/code/_globalvars/lists/objects.dm b/code/_globalvars/lists/objects.dm
index f918fe47277f..efa225f48b7c 100644
--- a/code/_globalvars/lists/objects.dm
+++ b/code/_globalvars/lists/objects.dm
@@ -20,6 +20,7 @@ GLOBAL_LIST_EMPTY(pandemics)
GLOBAL_LIST_EMPTY(all_areas)
GLOBAL_LIST_EMPTY(all_unique_areas) // List of all unique areas. AKA areas with there_can_be_many = FALSE
GLOBAL_LIST_EMPTY(machines)
+GLOBAL_LIST_EMPTY(telescreens) /// List of entertainment telescreens connected to the "news" cameranet
GLOBAL_LIST_EMPTY(rcd_list) //list of Rapid Construction Devices.
GLOBAL_LIST_EMPTY(apcs)
diff --git a/code/game/machinery/computer/camera_console.dm b/code/game/machinery/computer/camera_console.dm
index 553fabee7fda..b4550d3a77bc 100644
--- a/code/game/machinery/computer/camera_console.dm
+++ b/code/game/machinery/computer/camera_console.dm
@@ -1,3 +1,10 @@
+/// The amount of time a camera console actively being watched will wait between feed refreshes.
+#define FOLLOW_COOLDOWN_RATE 2 SECONDS
+
+/// The root type of all camera consoles. When used, these open a UI that has a left-hand sidebar
+/// displaying a list of active cameras in the console's assigned camera `network`. The bulk of the window
+/// on the right hand side displays a camera feed of the selected camera. This feed will attempt to refresh
+/// at periods of `FOLLOW_COOLDOWN_RATE` as long as there are active watchers, living or dead.
/obj/machinery/computer/security
name = "security camera console"
desc = "Used to access the various cameras networks on the station."
@@ -10,7 +17,9 @@
var/list/network = list("SS13","Mining Outpost")
var/obj/machinery/camera/active_camera
+ /// The list of total watchers, living and dead, of this console.
var/list/watchers = list()
+ /// The list of living watchers of this console. Used for playing a "terminal on" sound on first live viewer.
var/list/living_watchers = list()
// Stuff needed to render the map
@@ -21,12 +30,15 @@
var/list/cam_plane_masters
var/atom/movable/screen/background/cam_background
- // Parent object this camera is assigned to. Used for camera bugs
+ /// Parent object this camera is assigned to. Used for camera bugs
var/atom/movable/parent
/// is the console silent when switching cameras?
var/silent_console = FALSE
+ /// Timer for the follow refresh cooldown; fires periodically every `FOLLOW_COOLDOWN_RATE` while there are watchers.
+ var/follow_cooldown
+
/obj/machinery/computer/security/ui_host()
return parent ? parent : src
@@ -47,6 +59,7 @@
cam_background.del_on_map_removal = FALSE
/obj/machinery/computer/security/Destroy()
+ deltimer(follow_cooldown)
qdel(cam_screen)
qdel(cam_background)
return ..()
@@ -87,6 +100,14 @@
// Open UI
ui = new(user, src, "CameraConsole", name)
ui.open()
+ follow_cooldown = addtimer(CALLBACK(src, PROC_REF(refresh_feed)), FOLLOW_COOLDOWN_RATE, TIMER_UNIQUE)
+
+/obj/machinery/computer/security/proc/refresh_feed()
+ update_viewer()
+ if(length(watchers))
+ follow_cooldown = addtimer(CALLBACK(src, PROC_REF(refresh_feed)), FOLLOW_COOLDOWN_RATE, TIMER_UNIQUE)
+ else if(follow_cooldown)
+ deltimer(follow_cooldown)
/obj/machinery/computer/security/ui_close(mob/user)
..()
@@ -238,6 +259,11 @@
/obj/machinery/computer/security/telescreen/entertainment/Initialize(mapload)
. = ..()
set_light(1, LIGHTING_MINIMUM_POWER) //so byond doesnt cull, and we get an emissive appearance
+ GLOB.telescreens += src
+
+/obj/machinery/computer/security/telescreen/entertainment/Destroy()
+ GLOB.telescreens -= src
+ return ..()
/obj/machinery/computer/security/telescreen/entertainment/update_overlays()
if(feeds_on)
@@ -374,3 +400,5 @@
desc = "Used for watching the turbine vent.";
network = list("Turbine")
circuit = /obj/item/circuitboard/camera/turbine
+
+#undef FOLLOW_COOLDOWN_RATE
diff --git a/code/modules/paperwork/photography.dm b/code/modules/paperwork/photography.dm
index 92d9b8df65fd..f63ecdfb8c41 100644
--- a/code/modules/paperwork/photography.dm
+++ b/code/modules/paperwork/photography.dm
@@ -561,6 +561,7 @@ GLOBAL_LIST_INIT(SpookyGhosts, list("ghost","shade","shade2","ghost-narsie","hor
/**************
*video camera *
***************/
+/// The amount of time after being turned off that the camera is too hot to turn back on.
#define CAMERA_STATE_COOLDOWN 2 SECONDS
/obj/item/videocam
@@ -571,47 +572,36 @@ GLOBAL_LIST_INIT(SpookyGhosts, list("ghost","shade","shade2","ghost-narsie","hor
w_class = WEIGHT_CLASS_NORMAL
materials = list(MAT_METAL = 1000, MAT_GLASS = 500)
var/on = FALSE
- var/video_cooldown = 0
- /// The default amount of time the camera will wait before updating consoles with its new position.
- var/update_viewer_cooldown_rate = 2 SECONDS
- /// The next time that the camera should update consoles with its new position.
- var/update_viewer_cooldown = 0
- /// The amount of time after each move the camera will wait before updating consoles
- /// with its position a final time. It should be higher than `update_viewer_cooldown_rate` to ensure
- /// that it isn't debounced. (This ensures the feed tries to remain centered on the camera.)
- var/update_viewer_stabilize_rate = 3 SECONDS
var/obj/machinery/camera/camera
var/icon_on = "videocam_on"
var/icon_off = "videocam"
var/canhear_range = 7
- var/mob/holder
+
+ COOLDOWN_DECLARE(video_cooldown)
/obj/item/videocam/proc/camera_state(mob/living/carbon/user)
if(!on)
- holder = loc
- RegisterSignal(holder, COMSIG_MOVABLE_MOVED, PROC_REF(update_viewers))
on = TRUE
camera = new /obj/machinery/camera(src)
icon_state = icon_on
camera.network = list("news")
camera.c_tag = user.name
else
- UnregisterSignal(holder, COMSIG_MOVABLE_MOVED)
on = FALSE
icon_state = icon_off
camera.c_tag = null
QDEL_NULL(camera)
visible_message("The video camera has been turned [on ? "on" : "off"].")
- for(var/obj/machinery/computer/security/telescreen/entertainment/TV in GLOB.machines)
+ for(var/obj/machinery/computer/security/telescreen/entertainment/TV in GLOB.telescreens)
if(on)
TV.feeds_on++
else
TV.feeds_on--
TV.update_icon(UPDATE_OVERLAYS)
- video_cooldown = world.time + CAMERA_STATE_COOLDOWN
+ COOLDOWN_START(src, video_cooldown, CAMERA_STATE_COOLDOWN)
/obj/item/videocam/attack_self__legacy__attackchain(mob/user)
- if(world.time < video_cooldown)
+ if(!COOLDOWN_FINISHED(src, video_cooldown))
to_chat(user, "[src] is overheating, give it some time.")
return
camera_state(user)
@@ -642,16 +632,6 @@ GLOBAL_LIST_INIT(SpookyGhosts, list("ghost","shade","shade2","ghost-narsie","hor
if(T.watchers[M] == camera)
T.atom_say(msg)
-/obj/item/videocam/proc/update_viewers()
- var/mob/holder = loc
- if(!camera || world.timeofday < update_viewer_cooldown)
- return
- update_viewer_cooldown = world.timeofday + update_viewer_cooldown_rate
- for(var/obj/machinery/computer/security/telescreen/T in GLOB.machines)
- if(T.active_camera == camera && length(T.watchers))
- T.update_viewer()
- addtimer(CALLBACK(src, PROC_REF(update_viewers)), update_viewer_stabilize_rate)
-
/obj/item/videocam/advanced
name = "advanced video camera"
desc = "This video camera allows you to send live feeds even when attached to a belt."
From e230b589138610a1e6df7f216e5a47a04454236d Mon Sep 17 00:00:00 2001
From: ascio <81930475+asciodev@users.noreply.github.com>
Date: Fri, 17 Jan 2025 17:13:36 -0500
Subject: [PATCH 5/6] Replace manual camera feed cooldown with process()
Co-authored-by: Charlie Nolan
---
.../game/machinery/computer/camera_console.dm | 19 +++++--------------
1 file changed, 5 insertions(+), 14 deletions(-)
diff --git a/code/game/machinery/computer/camera_console.dm b/code/game/machinery/computer/camera_console.dm
index a3f17153403c..03deb8a30825 100644
--- a/code/game/machinery/computer/camera_console.dm
+++ b/code/game/machinery/computer/camera_console.dm
@@ -1,6 +1,3 @@
-/// The amount of time a camera console actively being watched will wait between feed refreshes.
-#define FOLLOW_COOLDOWN_RATE 2 SECONDS
-
/// The root type of all camera consoles. When used, these open a UI that has a left-hand sidebar
/// displaying a list of active cameras in the console's assigned camera `network`. The bulk of the window
/// on the right hand side displays a camera feed of the selected camera. This feed will attempt to refresh
@@ -36,9 +33,6 @@
/// is the console silent when switching cameras?
var/silent_console = FALSE
- /// Timer for the follow refresh cooldown; fires periodically every `FOLLOW_COOLDOWN_RATE` while there are watchers.
- var/follow_cooldown
-
/obj/machinery/computer/security/ui_host()
return parent ? parent : src
@@ -59,7 +53,7 @@
cam_background.del_on_map_removal = FALSE
/obj/machinery/computer/security/Destroy()
- deltimer(follow_cooldown)
+ STOP_PROCESSING(SSobj, src)
qdel(cam_screen)
qdel(cam_background)
return ..()
@@ -100,14 +94,13 @@
// Open UI
ui = new(user, src, "CameraConsole", name)
ui.open()
- follow_cooldown = addtimer(CALLBACK(src, PROC_REF(refresh_feed)), FOLLOW_COOLDOWN_RATE, TIMER_UNIQUE)
+ START_PROCESSING(SSobj, src)
-/obj/machinery/computer/security/proc/refresh_feed()
+/obj/machinery/computer/security/process()
update_viewer()
if(length(watchers))
- follow_cooldown = addtimer(CALLBACK(src, PROC_REF(refresh_feed)), FOLLOW_COOLDOWN_RATE, TIMER_UNIQUE)
- else if(follow_cooldown)
- deltimer(follow_cooldown)
+ return
+ STOP_PROCESSING(SSobj, src)
/obj/machinery/computer/security/ui_close(mob/user)
..()
@@ -400,5 +393,3 @@
desc = "Used for watching the turbine vent.";
network = list("Turbine")
circuit = /obj/item/circuitboard/camera/turbine
-
-#undef FOLLOW_COOLDOWN_RATE
From 5a17bcf48a058af9cab3f1935e13770badf0ce5d Mon Sep 17 00:00:00 2001
From: ascio <81930475+asciodev@users.noreply.github.com>
Date: Fri, 17 Jan 2025 17:43:11 -0500
Subject: [PATCH 6/6] Update Camera Console autodoc to reflect process()
implementation
---
code/game/machinery/computer/camera_console.dm | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/code/game/machinery/computer/camera_console.dm b/code/game/machinery/computer/camera_console.dm
index 03deb8a30825..4825064a7d69 100644
--- a/code/game/machinery/computer/camera_console.dm
+++ b/code/game/machinery/computer/camera_console.dm
@@ -1,7 +1,7 @@
/// The root type of all camera consoles. When used, these open a UI that has a left-hand sidebar
/// displaying a list of active cameras in the console's assigned camera `network`. The bulk of the window
/// on the right hand side displays a camera feed of the selected camera. This feed will attempt to refresh
-/// at periods of `FOLLOW_COOLDOWN_RATE` as long as there are active watchers, living or dead.
+/// every time process() is called by SSobj as long as there are active watchers, living or dead.
/obj/machinery/computer/security
name = "security camera console"
desc = "Used to access the various cameras networks on the station."