mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-21 20:20:33 +01:00
## About The Pull Request Cyborgs now have an alert that shows when someone is watching their internal camera through a security camera console. SyndEye is excluded from this as it's spying instead. Made as an alternative to https://github.com/tgstation/tgstation/pull/90410 but they also aren't mutually exclusive, though I have the same fix for borg camera cutting in this one too. https://github.com/user-attachments/assets/5dfe92f8-8948-4a80-bb64-cbeaca4f80e0 ##### This is part of the same code bounty as https://github.com/tgstation/tgstation/pull/90410 ## Why It's Good For The Game Pretty much the same reason this PR is an alternative to, it's hard for borgs to get away with doing antagonist stuff when people are able to silently watch them at any point from any console or laptop laying around the station, of which there are many. This hopefully lets borgs know if someone is watching, to avoid doing anything in front of the cameras, and to encourage them to get said cameras cut if they plan on doing anything. ## Changelog 🆑 add: Drones now have internal cameras that shows up on camera consoles (like Cyborgs) balance: Cyborgs (and drones) now get a notification when someone is watching them through a security camera console. fix: Cutting a Cyborg's camera wire now properly disables it, and mending it now properly re-enables it. /🆑 --------- Co-authored-by: Ghom <42542238+Ghommie@users.noreply.github.com>
231 lines
7.6 KiB
Plaintext
231 lines
7.6 KiB
Plaintext
#define DEFAULT_MAP_SIZE 15
|
|
|
|
/obj/machinery/computer/security
|
|
name = "security camera console"
|
|
desc = "Used to access the various cameras on the station."
|
|
icon_screen = "cameras"
|
|
icon_keyboard = "security_key"
|
|
circuit = /obj/item/circuitboard/computer/security
|
|
light_color = COLOR_SOFT_RED
|
|
interaction_flags_machine = INTERACT_MACHINE_ALLOW_SILICON|INTERACT_MACHINE_REQUIRES_SIGHT
|
|
|
|
var/list/network = list(CAMERANET_NETWORK_SS13)
|
|
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/atom/movable/screen/map_view/camera/cam_screen
|
|
|
|
/obj/machinery/computer/security/Initialize(mapload)
|
|
. = ..()
|
|
// Map name has to start and end with an A-Z character,
|
|
// and definitely NOT with a square bracket or even a number.
|
|
// I wasted 6 hours on this. :agony:
|
|
var/map_name = "camera_console_[REF(src)]_map"
|
|
// Convert networks to lowercase
|
|
for(var/i in network)
|
|
network -= i
|
|
network += LOWER_TEXT(i)
|
|
// Initialize map objects
|
|
cam_screen = new
|
|
cam_screen.generate_view(map_name)
|
|
|
|
/obj/machinery/computer/security/Destroy()
|
|
QDEL_NULL(cam_screen)
|
|
return ..()
|
|
|
|
/obj/machinery/computer/security/connect_to_shuttle(mapload, obj/docking_port/mobile/port, obj/docking_port/stationary/dock)
|
|
for(var/i in network)
|
|
network -= i
|
|
network += "[port.shuttle_id]_[i]"
|
|
|
|
/obj/machinery/computer/security/ui_interact(mob/user, datum/tgui/ui)
|
|
. = ..()
|
|
if(!user.client) //prevents errors by trying to pass clients that don't exist.
|
|
return
|
|
// Update UI
|
|
ui = SStgui.try_update_ui(user, src, ui)
|
|
|
|
// Update the camera, showing static if necessary and updating data if the location has moved.
|
|
update_active_camera_screen()
|
|
|
|
if(!ui)
|
|
var/user_ref = REF(user)
|
|
var/is_living = isliving(user)
|
|
// Ghosts shouldn't count towards concurrent users, which produces
|
|
// an audible terminal_on click.
|
|
if(is_living)
|
|
concurrent_users += user_ref
|
|
// Turn on the console
|
|
if(length(concurrent_users) == 1 && is_living)
|
|
playsound(src, 'sound/machines/terminal/terminal_on.ogg', 25, FALSE)
|
|
use_energy(active_power_usage)
|
|
// Open UI
|
|
ui = new(user, src, "CameraConsole", name)
|
|
ui.open()
|
|
// Register map objects
|
|
cam_screen.display_to(user, ui.window)
|
|
|
|
/obj/machinery/computer/security/ui_status(mob/user, datum/ui_state/state)
|
|
. = ..()
|
|
if(. == UI_DISABLED)
|
|
return UI_CLOSE
|
|
return .
|
|
|
|
/obj/machinery/computer/security/ui_data()
|
|
var/list/data = list()
|
|
data["activeCamera"] = null
|
|
if(active_camera)
|
|
data["activeCamera"] = list(
|
|
name = active_camera.c_tag,
|
|
ref = REF(active_camera),
|
|
status = active_camera.camera_enabled,
|
|
)
|
|
return data
|
|
|
|
/obj/machinery/computer/security/ui_static_data()
|
|
var/list/data = list()
|
|
data["network"] = network
|
|
data["mapRef"] = cam_screen.assigned_map
|
|
data["cameras"] = GLOB.cameranet.get_available_cameras_data(network)
|
|
return data
|
|
|
|
/obj/machinery/computer/security/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
|
|
. = ..()
|
|
if(.)
|
|
return
|
|
|
|
if(action == "switch_camera")
|
|
active_camera?.on_stop_watching(src)
|
|
var/obj/machinery/camera/selected_camera = locate(params["camera"]) in GLOB.cameranet.cameras
|
|
active_camera = selected_camera
|
|
|
|
if(isnull(active_camera))
|
|
return TRUE
|
|
|
|
active_camera.on_start_watching(src)
|
|
update_active_camera_screen()
|
|
|
|
return TRUE
|
|
|
|
/obj/machinery/computer/security/proc/update_active_camera_screen()
|
|
// Show static if can't use the camera
|
|
if(!active_camera?.can_use())
|
|
cam_screen.show_camera_static()
|
|
return
|
|
|
|
var/list/visible_turfs = list()
|
|
|
|
// Get the camera's turf to correctly gather what's visible from its turf, in case it's located in a moving object (borgs / mechs)
|
|
var/new_cam_turf = get_turf(active_camera)
|
|
|
|
// 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(last_camera_turf == new_cam_turf)
|
|
return
|
|
|
|
// Cameras that get here are moving, and are likely attached to some moving atom such as cyborgs.
|
|
last_camera_turf = new_cam_turf
|
|
|
|
//Here we gather what's visible from the camera's POV based on its view_range and xray modifier if present
|
|
var/list/visible_things = active_camera.isXRay(ignore_malf_upgrades = TRUE) ? range(active_camera.view_range, new_cam_turf) : view(active_camera.view_range, new_cam_turf)
|
|
|
|
for(var/turf/visible_turf in visible_things)
|
|
visible_turfs += visible_turf
|
|
|
|
//Get coordinates for a rectangle area that contains the turfs we see so we can then clear away the static in the resulting rectangle area
|
|
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.show_camera(visible_turfs, size_x, size_y)
|
|
|
|
/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
|
|
cam_screen?.hide_from(user)
|
|
// Turn off the console
|
|
if(length(concurrent_users) == 0 && is_living)
|
|
active_camera?.on_stop_watching(src)
|
|
active_camera = null
|
|
last_camera_turf = null
|
|
playsound(src, 'sound/machines/terminal/terminal_off.ogg', 25, FALSE)
|
|
|
|
/atom/movable/screen/map_view/camera
|
|
/// All the plane masters that need to be applied.
|
|
var/atom/movable/screen/background/cam_background
|
|
|
|
/atom/movable/screen/map_view/camera/Destroy()
|
|
QDEL_NULL(cam_background)
|
|
return ..()
|
|
|
|
/atom/movable/screen/map_view/camera/generate_view(map_key)
|
|
. = ..()
|
|
cam_background = new
|
|
cam_background.del_on_map_removal = FALSE
|
|
cam_background.assigned_map = assigned_map
|
|
|
|
/atom/movable/screen/map_view/camera/display_to_client(client/show_to)
|
|
show_to.register_map_obj(cam_background)
|
|
. = ..()
|
|
|
|
/atom/movable/screen/map_view/camera/proc/show_camera(list/visible_turfs, size_x, size_y)
|
|
vis_contents = visible_turfs
|
|
cam_background.icon_state = "clear"
|
|
cam_background.fill_rect(1, 1, size_x, size_y)
|
|
|
|
/atom/movable/screen/map_view/camera/proc/show_camera_static()
|
|
vis_contents.Cut()
|
|
cam_background.icon_state = "scanline2"
|
|
cam_background.fill_rect(1, 1, DEFAULT_MAP_SIZE, DEFAULT_MAP_SIZE)
|
|
|
|
// SECURITY MONITORS
|
|
/obj/machinery/computer/security/wooden_tv
|
|
name = "security camera monitor"
|
|
desc = "An old TV hooked into the station's camera network."
|
|
icon_state = "television"
|
|
icon_keyboard = null
|
|
icon_screen = "detective_tv"
|
|
pass_flags = PASSTABLE
|
|
|
|
/obj/machinery/computer/security/mining
|
|
name = "outpost camera console"
|
|
desc = "Used to access the various cameras on the outpost."
|
|
icon_screen = "mining"
|
|
icon_keyboard = "mining_key"
|
|
network = list(CAMERANET_NETWORK_MINE, CAMERANET_NETWORK_AUXBASE)
|
|
circuit = /obj/item/circuitboard/computer/mining
|
|
|
|
/obj/machinery/computer/security/research
|
|
name = "research camera console"
|
|
desc = "Used to access the various cameras in science."
|
|
network = list(CAMERANET_NETWORK_RD)
|
|
circuit = /obj/item/circuitboard/computer/research
|
|
|
|
/obj/machinery/computer/security/hos
|
|
name = "\improper Head of Security's camera console"
|
|
desc = "A custom security console with added access to the labor camp network."
|
|
network = list(CAMERANET_NETWORK_SS13, CAMERANET_NETWORK_LABOR)
|
|
circuit = null
|
|
|
|
/obj/machinery/computer/security/labor
|
|
name = "labor camp monitoring"
|
|
desc = "Used to access the various cameras on the labor camp."
|
|
network = list(CAMERANET_NETWORK_LABOR)
|
|
circuit = null
|
|
|
|
/obj/machinery/computer/security/qm
|
|
name = "\improper Quartermaster's camera console"
|
|
desc = "A console with access to the mining, auxiliary base and vault camera networks."
|
|
network = list(CAMERANET_NETWORK_MINE, CAMERANET_NETWORK_AUXBASE, CAMERANET_NETWORK_VAULT)
|
|
circuit = null
|
|
|
|
#undef DEFAULT_MAP_SIZE
|