Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Camera Console Recalibration and Bodycameras #3952

Open
wants to merge 40 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
8db0c49
test change
zimon9 Nov 28, 2024
372ebe4
Merge branch 'master' into custom-camera-networks-new
zimon9 Nov 28, 2024
696f399
Merge branch 'master' into custom-camera-networks-new
zimon9 Dec 24, 2024
599b94d
IT WORKS. Network switching is now doable. TODO: Fix UI layout, add a…
zimon9 Dec 27, 2024
4c93d3c
custom network input is working as hoped
zimon9 Dec 27, 2024
569a651
completes multi-choice list for multitool interactions on cameras
zimon9 Dec 27, 2024
d906bfc
bodycameras have been implemented. That was genuinely a very formativ…
zimon9 Dec 31, 2024
3ab35c9
allows bodycameras to be stored in helmets and belts/webbings, cleans…
zimon9 Dec 31, 2024
6e2c06e
oopsy daisy
zimon9 Dec 31, 2024
b931b91
allows bodycameras to be clipped to the belt slot
zimon9 Jan 1, 2025
7ded8a6
activation/deactivation sounds were changed from the placeholder
zimon9 Jan 1, 2025
5f9c878
The answer was in plain sight.. how could I have been so BLIND... HAH…
zimon9 Jan 1, 2025
8945b3c
adds a supply kit for a bodycamera
zimon9 Jan 1, 2025
b08313c
whooops
zimon9 Jan 1, 2025
efc5416
examine_more change
zimon9 Jan 1, 2025
a2e5b9b
potentially satisfies linters, fixes examine_more
zimon9 Jan 1, 2025
737f375
removes two characters that the linters loathed
zimon9 Jan 1, 2025
6898438
attempts to satisfy the linter lords again
zimon9 Jan 1, 2025
9716615
away with you
zimon9 Jan 1, 2025
3b4fb4e
cleans up the camera map after switching to a different network
zimon9 Jan 1, 2025
0cbd857
modifies the examine text again
zimon9 Jan 3, 2025
257a321
adds random name initialization
zimon9 Jan 5, 2025
ac8cd28
updates name, description and crate for bodycameras, and adds a varia…
zimon9 Jan 5, 2025
33ff4ca
makes the location check a little nicer. you should be able to use th…
zimon9 Jan 5, 2025
19c8b35
implements logic to check if a camera is allowed to transmit across a…
zimon9 Jan 5, 2025
ffeb593
better camera console parity with securEye. makes certain layers visi…
zimon9 Jan 6, 2025
58d0992
Merge branch 'master' into custom-camera-networks-new
zimon9 Jan 6, 2025
f52397a
makes the nametag and network sentences for bodycamera examine text n…
zimon9 Jan 8, 2025
e326674
Merge branch 'master' into custom-camera-networks-new
zimon9 Jan 8, 2025
4e119df
sanitizes tempNetwork input, updates remote view of cameras
zimon9 Jan 10, 2025
aba617e
cleans up some code
zimon9 Jan 10, 2025
5540d3b
Merge branch 'master' into custom-camera-networks-new
zimon9 Jan 10, 2025
44a6d9c
modifies the cargo crate description
zimon9 Jan 13, 2025
b80413e
updates the examine block for bodycameras
zimon9 Jan 13, 2025
a21668b
Merge branch 'master' into custom-camera-networks-new
zimon9 Jan 14, 2025
1b75fab
Merge branch 'master' into custom-camera-networks-new
zimon9 Jan 15, 2025
c36a2a7
emergency fix for the grinch bug, in case the other fix PR isn't in t…
zimon9 Jan 17, 2025
6c758dc
Merge branch 'custom-camera-networks-new' of https://github.com/zimon…
zimon9 Jan 17, 2025
9e93313
OOPS
zimon9 Jan 17, 2025
9ee1bd5
my spelling hinders me
zimon9 Jan 17, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions code/datums/components/storage/concrete/pockets.dm
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@
/obj/item/clothing/glasses/sunglasses/ballistic,
/obj/item/ammo_casing,
/obj/item/ammo_box/magazine/illestren_a850r,
/obj/item/bodycamera,
))

/datum/component/storage/concrete/pockets/holster
Expand Down
2 changes: 2 additions & 0 deletions code/datums/components/storage/storage.dm
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,8 @@
if(!len)
to_chat(M, "<span class='warning'>You failed to pick up anything with [parent]!</span>")
return
if(!M.CanReach(I, src)) //Emergency fix for the grinch bug. Will be removed for the actual fix.
return
if(I.anchored)
to_chat(M, "<span class='warning'>\The [I] is stuck to the ground and cannot be picked up by [parent]!</span>")
return
Expand Down
26 changes: 23 additions & 3 deletions code/game/machinery/camera/camera.dm
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
var/busy = FALSE
var/emped = FALSE //Number of consecutive EMP's on this camera
var/in_use_lights = 0
var/can_transmit_across_z_levels = FALSE

// Upgrades bitflag
var/upgrades = 0
Expand Down Expand Up @@ -258,8 +259,27 @@
if(!panel_open)
return

setViewRange((view_range == initial(view_range)) ? short_range : initial(view_range))
to_chat(user, "<span class='notice'>You [(view_range == initial(view_range)) ? "restore" : "mess up"] the camera's focus.</span>")
var/obj/item/multitool/M = I
var/list/choice_list = list("Occlude the camera lens", "Save the camera network to the multitool buffer", "Transfer the network in the buffer to the camera", "Change the camera network")
var/choice = input(user, "Select a function", "Camera Settings") as null|anything in choice_list

switch(choice)
if("Occlude the camera lens")
setViewRange((view_range == initial(view_range)) ? short_range : initial(view_range))
to_chat(user, "<span class='notice'>You [(view_range == initial(view_range)) ? "restore" : "mess up"] the camera's focus.</span>")

if("Save the camera network to the multitool buffer")
M.buffer = network[1]
to_chat(user, "<span class='notice'>You add network '[network[1]]' to the multitool's buffer.</span>")

if("Transfer the network in the buffer to the camera")
network[1] = M.buffer
to_chat(user, "<span class='notice'>You tune [src] to transmit across the '[network[1]]' network using the saved data from the multiool's buffer.</span>")

if("Change the camera network")
network[1] = stripped_input(user, "Tune [src] to a specific network. Enter the network name and ensure that it is no bigger than 32 characters long. Network names are not case sensitive.", "Network Tuning", max_length = 32)
to_chat(user, "<span class='notice'>You set [src] to transmit across the '[network[1]]' network.</span>")

return TRUE

/obj/machinery/camera/welder_act(mob/living/user, obj/item/I)
Expand Down Expand Up @@ -508,6 +528,6 @@
user.sight |= (SEE_TURFS|SEE_MOBS|SEE_OBJS)
user.see_in_dark = max(user.see_in_dark, 8)
else
user.sight = 0
user.sight = SEE_BLACKNESS
user.see_in_dark = 2
return 1
238 changes: 180 additions & 58 deletions code/game/machinery/computer/camera.dm
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,18 @@
light_color = COLOR_SOFT_RED

var/list/network = list("ss13")
var/tempNetwork = list("")
var/obj/machinery/camera/active_camera
/// The turf where the camera was last updated.
var/turf/last_camera_turf
var/list/concurrent_users = list()

// Stuff needed to render the map
var/map_name
var/const/default_map_size = 15
var/atom/movable/screen/map_view/cam_screen
var/atom/movable/screen/plane_master/lighting/cam_plane_master
/// All the plane masters that need to be applied.
var/list/cam_plane_masters
var/atom/movable/screen/background/cam_background

/obj/machinery/computer/security/retro
Expand Down Expand Up @@ -43,18 +47,20 @@
cam_screen.assigned_map = map_name
cam_screen.del_on_map_removal = FALSE
cam_screen.screen_loc = "[map_name]:1,1"
cam_plane_master = new
cam_plane_master.name = "plane_master"
cam_plane_master.assigned_map = map_name
cam_plane_master.del_on_map_removal = FALSE
cam_plane_master.screen_loc = "[map_name]:CENTER"
cam_plane_masters = list()
for(var/plane in subtypesof(/atom/movable/screen/plane_master))
var/atom/movable/screen/instance = new plane()
instance.assigned_map = map_name
instance.del_on_map_removal = FALSE
instance.screen_loc = "[map_name]:CENTER"
cam_plane_masters += instance
cam_background = new
cam_background.assigned_map = map_name
cam_background.del_on_map_removal = FALSE

/obj/machinery/computer/security/Destroy()
qdel(cam_screen)
qdel(cam_plane_master)
QDEL_LIST(cam_plane_masters)
qdel(cam_background)
return ..()

Expand All @@ -63,12 +69,21 @@
network -= i
network += "[REF(port)][i]"

/obj/machinery/computer/security/multitool_act(mob/living/user, obj/item/I)
. = ..()
var/obj/item/multitool/M = I
if(M.buffer != null)
network = M.buffer
to_chat(user, "<span class='notice'>You input network '[M.buffer]' from the multitool's buffer into [src].</span>")
return

/obj/machinery/computer/security/ui_interact(mob/user, datum/tgui/ui)
// Update UI
ui = SStgui.try_update_ui(user, src, ui)

// Show static if can't use the camera
if(!active_camera?.can_use())
show_camera_static()
update_active_camera_screen()

if(!ui)
var/user_ref = REF(user)
var/is_living = isliving(user)
Expand All @@ -82,7 +97,8 @@
use_power(active_power_usage)
// Register map objects
user.client.register_map_obj(cam_screen)
user.client.register_map_obj(cam_plane_master)
for(var/plane in cam_plane_masters)
user.client.register_map_obj(plane)
user.client.register_map_obj(cam_background)
// Open UI
ui = new(user, src, "CameraConsole", name)
Expand All @@ -93,16 +109,33 @@
data["network"] = network
data["activeCamera"] = null
if(active_camera)
if(!active_camera?.can_use())
data["activeCamera"] = list(
name = active_camera.c_tag + " (DEACTIVATED)",
status = active_camera.status,
)
else
data["activeCamera"] = list(
name = active_camera.c_tag,
status = active_camera.status,
)
if(istype(active_camera, /obj/machinery/camera))
var/obj/machinery/camera/active_camera_S = active_camera
if(!active_camera_S?.can_use())
data["activeCamera"] = list(
name = active_camera_S.c_tag,
status = active_camera_S.status,
)
else
data["activeCamera"] = list(
name = active_camera_S.c_tag,
status = active_camera_S.status,
)
active_camera = active_camera_S

else if(istype(active_camera, /obj/item/bodycamera))
var/obj/machinery/camera/active_camera_B = active_camera
if(!active_camera_B?.can_use())
data["activeCamera"] = list(
name = active_camera_B.c_tag,
status = active_camera_B.status,
)
else
data["activeCamera"] = list(
name = active_camera_B.c_tag,
status = active_camera_B.status,
)
active_camera = active_camera_B
return data

/obj/machinery/computer/security/ui_static_data()
Expand All @@ -111,38 +144,77 @@
var/list/cameras = get_available_cameras()
data["cameras"] = list()
for(var/i in cameras)
var/obj/machinery/camera/C = cameras[i]
if(!C?.can_use())
var/obj/C = cameras[i]
if(istype(C, /obj/machinery/camera))
var/obj/machinery/camera/C_cam = C
data["cameras"] += list(list(
name = C.c_tag + " (DEACTIVATED)",
name = C_cam.c_tag,
))
else
else if(istype(C, /obj/item/bodycamera))
var/obj/item/bodycamera/C_cam = C
data["cameras"] += list(list(
name = C.c_tag,
name = C_cam.c_tag,
))
return data

/obj/machinery/computer/security/ui_act(action, params)
//This is the only way to refresh the UI, from what I've found
/obj/machinery/computer/security/proc/ui_refresh(mob/user, datum/tgui/ui)
ui.close()
ui_interact(user, ui)
show_camera_static()

/obj/machinery/computer/security/ui_act(action, params, ui)
. = ..()
if(.)
return

if(action == "set_network")
network = tempNetwork
ui_refresh(usr, ui)

if(action == "set_temp_network")
tempNetwork = sanitize_filename(params["name"])

if(action == "refresh")
ui_refresh(usr, ui)

if(action == "switch_camera")
var/c_tag = params["name"]
var/list/cameras = get_available_cameras()
var/obj/machinery/camera/C = cameras[c_tag]
var/obj/C = cameras[c_tag]
active_camera = C
playsound(src, get_sfx("terminal_type"), 25, FALSE)

update_active_camera_screen()

return TRUE

/obj/machinery/computer/security/ui_close(mob/user)
var/user_ref = REF(user)
var/is_living = isliving(user)
// Living creature or not, we remove you anyway.
concurrent_users -= user_ref
// Unregister map objects
user.client.clear_map(map_name)
// Turn off the console
if(length(concurrent_users) == 0 && is_living)
active_camera = null
playsound(src, 'sound/machines/terminal_off.ogg', 25, FALSE)
use_power(0)

/obj/machinery/computer/security/proc/update_active_camera_screen()
if(istype(active_camera, /obj/machinery/camera))
var/obj/machinery/camera/active_camera_S = active_camera

// Show static if can't use the camera
if(!active_camera?.can_use())
if(!active_camera_S?.can_use())
show_camera_static()
return TRUE

var/list/visible_turfs = list()
for(var/turf/T in (C.isXRay() \
? range(C.view_range, C) \
: view(C.view_range, C)))
for(var/turf/T in (active_camera_S.isXRay() \
? range(active_camera_S.view_range, active_camera_S) \
: view(active_camera_S.view_range, active_camera_S)))
visible_turfs += T

var/list/bbox = get_bbox_of_atoms(visible_turfs)
Expand All @@ -153,44 +225,94 @@
cam_background.icon_state = "clear"
cam_background.fill_rect(1, 1, size_x, size_y)

return TRUE
if(istype(active_camera, /obj/item/bodycamera))
var/obj/item/bodycamera/active_camera_B = active_camera

/obj/machinery/computer/security/ui_close(mob/user)
var/user_ref = REF(user)
var/is_living = isliving(user)
// Living creature or not, we remove you anyway.
concurrent_users -= user_ref
// Unregister map objects
user.client.clear_map(map_name)
// Turn off the console
if(length(concurrent_users) == 0 && is_living)
active_camera = null
playsound(src, 'sound/machines/terminal_off.ogg', 25, FALSE)
use_power(0)
// Show static if can't use the camera
if(!active_camera_B?.can_use())
show_camera_static()
return TRUE

var/list/visible_turfs = list()

// Derived from https://github.com/tgstation/tgstation/pull/52767
// Is this camera located in or attached to a living thing? If so, assume the camera's loc is the living thing.
var/cam_location = active_camera_B.loc

// Is the camera in the following items? If so, let it transmit an image as normal
if((istype(cam_location, /obj/item/clothing/suit)) || (istype(cam_location, /obj/item/clothing/head/helmet)) || istype(cam_location, /obj/item/storage/belt))
cam_location = active_camera_B.loc.loc

// If we're not forcing an update for some reason and the cameras are in the same location,
// we don't need to update anything.
// Most security cameras will end here as they're not moving.
if(istype(active_camera, /obj/machinery/camera))
return

// Cameras that get here are moving, and are likely attached to some moving atom such as cyborgs.
last_camera_turf = get_turf(cam_location)

var/list/visible_things = view(active_camera_B.view_range, cam_location)

for(var/turf/visible_turf in visible_things)
visible_turfs += visible_turf

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)

/obj/machinery/computer/security/proc/show_camera_static()
cam_screen.vis_contents.Cut()
cam_background.icon_state = "scanline2"
cam_background.fill_rect(1, 1, default_map_size, default_map_size)

// Returns the list of cameras accessible from this computer
/obj/machinery/computer/security/proc/get_available_cameras()
var/list/L = list()
for (var/obj/machinery/camera/C in GLOB.cameranet.cameras)
if((is_away_level(src) || is_away_level(C)) && (C.virtual_z() != virtual_z()))//if on away mission, can only receive feed from same z_level cameras
continue
for (var/obj/C in GLOB.cameranet.cameras)
if(istype(C, /obj/machinery/camera))
var/obj/machinery/camera/cam = C
if(cam.virtual_z() != virtual_z())
if(cam.can_transmit_across_z_levels)
//let them transmit
else
continue
else if(istype(C, /obj/item/bodycamera))
var/obj/item/bodycamera/cam = C
if((cam.virtual_z() != virtual_z()) || (cam.can_transmit_across_z_levels))//if on away mission, can only receive feed from same z_level cameras
if(cam.can_transmit_across_z_levels)
//let them transmit
else
continue
L.Add(C)
var/list/D = list()
for(var/obj/machinery/camera/C in L)
if(!C.network)
stack_trace("Camera in a cameranet has no camera network")
continue
if(!(islist(C.network)))
stack_trace("Camera in a cameranet has a non-list camera network")
continue
var/list/tempnetwork = C.network & network
if(tempnetwork.len)
D["[C.c_tag]"] = C
for(var/obj/C in L)
if(istype(C, /obj/machinery/camera))
var/obj/machinery/camera/cam = C
if(!cam.network)
stack_trace("Camera in a cameranet has no camera network")
continue
if(!(islist(cam.network)))
stack_trace("Camera in a cameranet has a non-list camera network")
continue
var/list/tempnetwork = cam.network & network
if(tempnetwork.len)
D["[cam.c_tag]"] = C

else if(istype(C, /obj/item/bodycamera))
var/obj/item/bodycamera/cam = C
if(!cam.network)
stack_trace("Camera in a cameranet has no camera network")
continue
if(!(islist(cam.network)))
stack_trace("Camera in a cameranet has a non-list camera network")
continue
var/list/tempnetwork = cam.network & network
if(tempnetwork.len)
D["[cam.c_tag]"] = cam
return D

// SECURITY MONITORS
Expand Down
Loading
Loading