Files
BatrachophrenoandGitHub 7172d3dbe3 NTNet Extensibility (#22908)
Fixes https://github.com/Aurorastation/Aurora.3/issues/21944

Spiritual successor of
https://github.com/Aurorastation/Aurora.3/pull/22352
Cherrypicked changes from the C3 Crew Management branch, this PR
contains all NTNet extensibility features, including Field Relay
construction, off-site camera usage (contingent on standing up remote
hardware and being out of jamming), etc. This also fixes a handful of
outstanding telecomms and view/mobeye-related bugs adjacent to where I
was working.

changes:
- rscadd: "Adds NTNet extensibility, requires new machinery constructed
on away-site z-levels."
- rscadd: "Breaks NTNet Relay machinery into Quantum Relay ('core' obj
on Horizon) and Field Relay (deployable off-site constructions)."
- qol: "Embedded cameras (basically every video camera EXCEPT those
which are installed on walls) can now be viewed from NTNet-connected
z-level, not just connected ones."
- rscadd: "Adds several public camera monitor consoles to the Horizon
(near where public sensor grid feed consoles already exist)."
- rscadd: "Adds some spare camera console boards to Horizon technical
storage."
- qol: "Adds action button for 'cancel-camera-view' verb while viewing
cameras or looking up/down z-level."
- bugfix: "Fixes Jockey comms (frequency was missing from ANTAG_FREQS)."
- bugfix: "Fixes look-up/look-down actions intermittently failing
without first using cancel-camera-view."
2026-08-02 01:24:45 +00:00

343 lines
10 KiB
Plaintext

/obj/structure/machinery/computer/security
name = "security camera monitor"
desc = "Used to access the various cameras on the station."
icon_screen = "cameras"
icon_keyboard = "yellow_key"
icon_keyboard_emis = "yellow_key_mask"
light_color = LIGHT_COLOR_YELLOW
var/current_network = null
var/obj/structure/machinery/camera/current_camera = null
var/last_pic = 1.0
var/list/console_networks
var/mapping = 0
var/cache_id = 0
/// A currently active program running on the computer.
var/datum/computer_file/program/camera_monitor/camera_monitor_program
circuit = /obj/item/circuitboard/security
/obj/structure/machinery/computer/security/Initialize()
if(!console_networks)
console_networks = SSatlas.current_map.station_networks.Copy()
. = ..()
if(console_networks.len)
current_network = console_networks[1]
// Create a new camera_monitor program that can run independently of a modular computer.
camera_monitor_program = new("Compless")
// Make sure that camera_monitor knows its been generated from a dedicated console; this will define its network access.
camera_monitor_program.monitored_networks = console_networks
camera_monitor_program.ntnet_endpoint = src
camera_monitor_program.ntnet_endpoint_ethernet = TRUE
/obj/structure/machinery/computer/security/Destroy()
reset_remote_viewers()
if(camera_monitor_program)
camera_monitor_program.reset_current()
camera_monitor_program = null
. = ..()
/obj/structure/machinery/computer/security/proc/reset_remote_viewers()
for(var/mob/M in GLOB.player_list)
if(M.machine == src && M.is_viewing_remote_view())
M.unset_machine()
M.reset_view(null)
/obj/structure/machinery/computer/security/attack_ai(var/mob/user as mob)
if(!ai_can_interact(user))
return
return attack_hand(user)
/obj/structure/machinery/computer/security/check_eye(var/mob/user as mob)
if (user.stat || user.blinded || !operable())
return -1
if(camera_monitor_program?.current_camera)
return camera_monitor_program.check_eye(user)
if(!current_camera)
return 0
var/viewflag = current_camera.check_eye(user)
if ( viewflag < 0 ) //camera doesn't work
reset_current()
return viewflag
/obj/structure/machinery/computer/security/grants_equipment_vision(var/mob/user as mob)
if(user.stat || user.blinded || !operable())
return FALSE
if(camera_monitor_program?.current_camera)
return camera_monitor_program.grants_equipment_vision(user)
if(!current_camera)
return FALSE
var/viewflag = current_camera.check_eye(user)
if (viewflag < 0) //camera doesn't work
return FALSE
return TRUE
/obj/structure/machinery/computer/security/proc/can_access_network(var/mob/user, var/network_access)
// No access passed, or 0 which is considered no access requirement. Allow it.
if(!network_access)
return TRUE
return (check_camera_access(user, ACCESS_SECURITY) && GLOB.security_level >= SEC_LEVEL_BLUE) || check_camera_access(user, network_access)
/obj/structure/machinery/computer/security/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
if(camera_monitor_program)
ui = new(user, src, camera_monitor_program.tgui_id, camera_monitor_program.filedesc)
ui.autoupdate = camera_monitor_program.ui_auto_update
else
to_chat(usr, "Something has gone wrong; please report the circumstances in which you received this message to the GitHub issues tracker.")
return
ui.open()
/obj/structure/machinery/computer/security/ui_data(mob/user)
var/list/data = list()
if(camera_monitor_program)
data += camera_monitor_program.ui_data(user)
return data
/obj/structure/machinery/computer/security/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
if(camera_monitor_program)
. = camera_monitor_program.ui_act(action, params, ui, state)
/obj/structure/machinery/computer/security/attack_hand(var/mob/user as mob)
if (!(src.z in GetConnectedZlevels(starting_z_level)) && !SSatlas.current_map.is_always_available_network_level(src.z))
to_chat(user, "Unable to establish a connection.")
return
if(stat & (NOPOWER|BROKEN)) return
if(!isAI(user))
user.set_machine(src)
user.reset_view(current_camera)
ui_interact(user)
/obj/structure/machinery/computer/security/proc/switch_to_camera(var/mob/user, var/obj/structure/machinery/camera/C)
//don't need to check if the camera works for AI because the AI jumps to the camera location and doesn't actually look through cameras.
if(isAI(user))
var/mob/living/silicon/ai/A = user
// Only allow non-carded AIs to view because the interaction with the eye gets all wonky otherwise.
if(!A.is_in_chassis())
return 0
A.eyeobj.setLoc(get_turf(C))
A.client.eye = A.eyeobj
return 1
if (user.stat || user.blinded || !operable())
return 0
if(!C.can_use())
return 0
set_current(C)
if (!(C.z in GetConnectedZlevels(starting_z_level)) && !(SSatlas.current_map.is_always_available_network_level(src.z) && C.z == src.z))
to_chat(user, SPAN_NOTICE("This camera is too far away to connect to!"))
return FALSE
if(ishuman(user))
var/mob/living/carbon/human/H = user
H.reset_view(current_camera)
else
user.reset_view(current_camera)
check_eye(user)
return 1
/obj/structure/machinery/computer/security/proc/check_camera_access(var/mob/user, var/access)
if(!access)
return 1
if(!istype(user))
return 0
var/I = user.GetIdCard()
if(!I)
return 0
var/list/access_list
if(islist(I))
access_list = I
else if(istype(I, /obj/item))
var/obj/item/item = I
access_list = item.GetAccess()
if(access in access_list)
return 1
return 0
//Camera control: moving.
/obj/structure/machinery/computer/security/proc/jump_on_click(var/mob/user,var/A)
if(user.machine != src)
return
var/obj/structure/machinery/camera/jump_to
if(istype(A,/obj/structure/machinery/camera))
jump_to = A
else if(ismob(A))
if(ishuman(A))
var/mob/living/carbon/human/H = A
jump_to = locate() in H.head
else if(isrobot(A))
var/mob/living/silicon/robot/R = A
jump_to = R.camera
else if(isobj(A))
jump_to = locate() in A
else if(isturf(A))
var/best_dist = INFINITY
var/check_area = get_area(A)
if (!check_area)
return
var/list/invalid_cameras
for(var/cc in SSmachinery.all_cameras)
if(!istype(cc, /obj/structure/machinery/camera))
LAZYADD(invalid_cameras, cc)
continue
var/obj/structure/machinery/camera/camera = cc
if(QDELETED(camera))
LAZYADD(invalid_cameras, cc)
continue
if(!camera.loc)
continue
if (camera.loc.loc != check_area)
continue
if(!camera.can_use())
continue
if(!can_access_camera(camera))
continue
var/dist = get_dist(camera,A)
if(dist < best_dist)
best_dist = dist
jump_to = camera
if(invalid_cameras)
SSmachinery.all_cameras -= invalid_cameras
if(isnull(jump_to))
return
if(can_access_camera(jump_to))
switch_to_camera(user,jump_to)
/obj/structure/machinery/computer/security/proc/can_access_camera(var/obj/structure/machinery/camera/C)
var/list/shared_networks = src.console_networks & C.network
if(shared_networks.len)
return 1
return 0
/obj/structure/machinery/computer/security/proc/set_current(var/obj/structure/machinery/camera/C)
if(current_camera == C)
return
if(current_camera)
reset_current()
src.current_camera = C
if(current_camera)
update_use_power(POWER_USE_ACTIVE)
var/mob/living/L = current_camera.loc
if(istype(L))
L.tracking_initiated()
/obj/structure/machinery/computer/security/proc/reset_current()
if(current_camera)
var/mob/living/L = current_camera.loc
if(istype(L))
L.tracking_cancelled()
current_camera = null
update_use_power(POWER_USE_IDLE)
/obj/structure/machinery/computer/security/telescreen
name = "Telescreen"
desc = "Used for watching an empty arena."
icon = 'icons/obj/computer.dmi'
icon_state = "wallframe"
icon_screen = null
light_range_on = 0
console_networks = list(NETWORK_THUNDER)
density = 0
circuit = null
is_holographic = FALSE
/obj/structure/machinery/computer/security/telescreen/entertainment
name = "entertainment monitor"
desc = "Damn, why do they never have anything interesting on these things?"
icon_screen = "entertainment"
light_color = "#FFEEDB"
light_range_on = 2
circuit = null
/obj/structure/machinery/computer/security/wooden_tv
name = "security camera monitor"
desc = "An old TV hooked into the stations camera network."
icon = 'icons/obj/computer.dmi'
icon_state = "television"
icon_screen = "detective_tv"
circuit = null
light_color = "#3848B3"
light_power_on = 0.5
/obj/structure/machinery/computer/security/mining
name = "outpost camera monitor"
desc = "Used to access the various cameras on the outpost."
icon_screen = "miningcameras"
icon_keyboard = "purple_key"
icon_keyboard_emis = "purple_key_mask"
light_color = LIGHT_COLOR_PURPLE
console_networks = list("MINE")
circuit = /obj/item/circuitboard/security/mining
/obj/structure/machinery/computer/security/engineering
name = "engineering camera monitor"
desc = "Used to monitor fires and breaches."
icon_screen = "engineeringcameras"
icon_keyboard = "yellow_key"
icon_keyboard_emis = "yellow_key_mask"
light_color = LIGHT_COLOR_YELLOW
circuit = /obj/item/circuitboard/security/engineering
/obj/structure/machinery/computer/security/engineering/terminal
name = "engineering camera monitor"
icon = 'icons/obj/modular_computers/modular_terminal.dmi'
icon_screen = "engines"
icon_keyboard = "power_key"
icon_keyboard_emis = "power_key_mask"
is_connected = TRUE
has_off_keyboards = TRUE
can_pass_under = FALSE
light_power_on = 1
/obj/structure/machinery/computer/security/engineering/Initialize()
if(!console_networks)
console_networks = GLOB.engineering_networks.Copy()
. = ..()
/obj/structure/machinery/computer/security/nuclear
name = "head mounted camera monitor"
desc = "Used to access the built-in cameras in helmets."
icon_screen = "syndicam"
icon_keyboard = "red_key"
icon_keyboard_emis = "red_key_mask"
light_color = LIGHT_COLOR_RED
console_networks = list(NETWORK_MERCENARY)
circuit = null
/obj/structure/machinery/computer/security/nuclear/Initialize()
. = ..()
req_access = list(150)
/obj/structure/machinery/computer/security/terminal
name = "public camera monitor"
icon = 'icons/obj/modular_computers/modular_terminal.dmi'
icon_screen = "cameras"
icon_keyboard = "security_key"
icon_keyboard_emis = "security_key_mask"
is_connected = TRUE
has_off_keyboards = TRUE
can_pass_under = FALSE
light_power_on = 1
/obj/structure/machinery/computer/security/terminal/public
console_networks = list(NETWORK_EXPEDITION, NETWORK_NEWS)
circuit = /obj/item/circuitboard/security/public