mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-03-30 08:54:16 +01:00
* [MDB IGNORE] dir sanity, primarily on WALLITEMs (#62601) About The Pull Request Wall items mostly use the direction from the floor to the wall in the named mapping helper. Wall items mostly use the direction from the wall to the floor for the internal dir variable. This leads to a headache when it comes to working out what conflicts with what, and what needs placing where. Wall frames provided a member, inverse, which specified whether or not to invert the direction of the item when looking for conflicts. It was also used to specify whether to look for conflicts outside of the wall (cameras and lights appear external to the wall) or inside the wall (most wall items). This flag was set for Intercoms, APCs, and Lights. Since APCs and Lights expect a floor-to-wall direction, and Intercoms expect a wall-to-floor direction, this means that APCs and Lights were getting the correct direction, and Intercoms were getting the wrong direction. Some implications of this setup were: You could build an APC on top of another wall item, provided there was nothing external attached to the wall and the area didn't have an APC. You could stack Intercoms indefinitely on top of the same wall, provided you weren't in a one-tile wide corridor with something on the opposite wall. Or both! Here's twenty Intercoms placed on the wall, and a freshly placed APC frame after placing all Intercoms and deconstructing the old APC: endless-stack-of-intercoms Not everything used this inverse variable to adjust to the correct direction. For example, /obj/machinery/defibrillator_mount just used a negative pixel_offset to be visually placed in the correct direction, even though the internal direction was wrong, and never set! This also let you stack an indefinite number of defib mounts on the same wall, provided it wasn't a northern wall... except you could do this to northern walls too, since defibs weren't considered a wall item for the purposes of checking collisions at all! Ultimately, every constructable interior wall item either used this inverse variable to adjust to the correct placement, set a negative pixel_offset variable to have its offset adjusted to the correct placement, or overrode New or Initialize to run its own checks and assignment to pixel_x and pixel_y! Inventory: Table of various paths, related paths, and the adjustments they used Unfortunately, untangling /obj/structure/sign is going to be another major headache, and this has already exploded in scope enough already, so we can't get rid of the get_turf_pixel call just yet. This also doesn't fix problems with the special 2x1 /obj/structure/sign/barsign. Some non-wall items have been made to use the new MAPPING_DIRECTIONAL_HELPERS as part of the directional cleanup. tl;dr: All wall mounted items and some directional objects now use the same direction that they were labelled as. More consistent directional types everywhere. Why It's Good For The Game fml Changelog cl refactor: Wall mounted and directional objects have undergone major internal simplification. Please report anything unusual! fix: You can no longer stack an indefinite amount of Intercoms on the same wall. fix: Defibrillator Mounts, Bluespace Gas Vendors, Turret Controlers, and Ticket Machines are now considered wall items. fix: Wall mounted items on top of the wall now consistently check against other items on top of the wall, and items coming out of the wall now consistently check against other items coming out of the wall. fix: The various directional pixel offsets within an APC, Fire Extinguisher Cabinet, Intercom, or Newscaster have been made consistent with each other. fix: The pixel offsets of Intercoms, Fire Alarms, Fire Extinguisher Cabinets, Flashers, and Newscasters have been made consistent between roundstart and constructed instances. fix: Constructed Turret Controls will no longer oddly overhang the wall they were placed on. qol: Defibrillator mounts now better indicate which side of the wall they are on. fix: Some instances where there were multiple identical lights on the same tile have been fixed to only have one. /cl * [MDB IGNORE] dir sanity, primarily on WALLITEMs * apc directionals * bluespace vendor fix * defib fix Co-authored-by: esainane <esainane+github@gmail.com> Co-authored-by: jjpark-kb <55967837+jjpark-kb@users.noreply.github.com>
376 lines
12 KiB
Plaintext
376 lines
12 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
|
|
|
|
var/list/network = list("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/map_name
|
|
var/atom/movable/screen/map_view/cam_screen
|
|
/// All the plane masters that need to be applied.
|
|
var/list/cam_plane_masters
|
|
var/atom/movable/screen/background/cam_background
|
|
|
|
interaction_flags_machine = INTERACT_MACHINE_ALLOW_SILICON | INTERACT_MACHINE_SET_MACHINE | INTERACT_MACHINE_REQUIRES_SIGHT
|
|
|
|
/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:
|
|
map_name = "camera_console_[REF(src)]_map"
|
|
// Convert networks to lowercase
|
|
for(var/i in network)
|
|
network -= i
|
|
network += lowertext(i)
|
|
// Initialize map objects
|
|
cam_screen = new
|
|
cam_screen.name = "screen"
|
|
cam_screen.assigned_map = map_name
|
|
cam_screen.del_on_map_removal = FALSE
|
|
cam_screen.screen_loc = "[map_name]:1,1"
|
|
cam_plane_masters = list()
|
|
for(var/plane in subtypesof(/atom/movable/screen/plane_master) - /atom/movable/screen/plane_master/blackness)
|
|
var/atom/movable/screen/plane_master/instance = new plane()
|
|
if(instance.blend_mode_override)
|
|
instance.blend_mode = instance.blend_mode_override
|
|
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_NULL(cam_screen)
|
|
QDEL_LIST(cam_plane_masters)
|
|
QDEL_NULL(cam_background)
|
|
return ..()
|
|
|
|
/obj/machinery/computer/security/connect_to_shuttle(obj/docking_port/mobile/port, obj/docking_port/stationary/dock)
|
|
for(var/i in network)
|
|
network -= i
|
|
network += "[port.id]_[i]"
|
|
|
|
/obj/machinery/computer/security/ui_interact(mob/user, datum/tgui/ui)
|
|
// 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_on.ogg', 25, FALSE)
|
|
use_power(active_power_usage)
|
|
// Register map objects
|
|
user.client.register_map_obj(cam_screen)
|
|
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)
|
|
ui.open()
|
|
|
|
/obj/machinery/computer/security/ui_data()
|
|
var/list/data = list()
|
|
data["network"] = network
|
|
data["activeCamera"] = null
|
|
if(active_camera)
|
|
data["activeCamera"] = list(
|
|
name = active_camera.c_tag,
|
|
status = active_camera.status,
|
|
)
|
|
return data
|
|
|
|
/obj/machinery/computer/security/ui_static_data()
|
|
var/list/data = list()
|
|
data["mapRef"] = map_name
|
|
var/list/cameras = get_available_cameras()
|
|
data["cameras"] = list()
|
|
for(var/i in cameras)
|
|
var/obj/machinery/camera/C = cameras[i]
|
|
data["cameras"] += list(list(
|
|
name = C.c_tag,
|
|
))
|
|
|
|
return data
|
|
|
|
/obj/machinery/computer/security/ui_act(action, params)
|
|
. = ..()
|
|
if(.)
|
|
return
|
|
|
|
if(action == "switch_camera")
|
|
var/c_tag = params["name"]
|
|
var/list/cameras = get_available_cameras()
|
|
var/obj/machinery/camera/selected_camera = cameras[c_tag]
|
|
active_camera = selected_camera
|
|
playsound(src, get_sfx("terminal_type"), 25, FALSE)
|
|
|
|
if(!selected_camera)
|
|
return TRUE
|
|
|
|
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())
|
|
show_camera_static()
|
|
return
|
|
|
|
var/list/visible_turfs = list()
|
|
|
|
// 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 = isliving(active_camera.loc) ? active_camera.loc : 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.
|
|
var/newturf = get_turf(cam_location)
|
|
if(last_camera_turf == newturf)
|
|
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 = active_camera.isXRay() ? range(active_camera.view_range, cam_location) : view(active_camera.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/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/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(z) || is_away_level(C.z)) && (C.z != z))//if on away mission, can only receive feed from same z_level cameras
|
|
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
|
|
return D
|
|
|
|
// 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("mine", "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("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("ss13", "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("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("mine", "auxbase", "vault")
|
|
circuit = null
|
|
|
|
// TELESCREENS
|
|
|
|
/obj/machinery/computer/security/telescreen
|
|
name = "\improper Telescreen"
|
|
desc = "Used for watching an empty arena."
|
|
icon = 'icons/obj/stationobjs.dmi'
|
|
icon_state = "telescreen"
|
|
icon_keyboard = null
|
|
layer = SIGN_LAYER
|
|
network = list("thunder")
|
|
density = FALSE
|
|
circuit = null
|
|
light_power = 0
|
|
|
|
/obj/machinery/computer/security/telescreen/update_icon_state()
|
|
icon_state = initial(icon_state)
|
|
if(machine_stat & BROKEN)
|
|
icon_state += "b"
|
|
return ..()
|
|
|
|
/obj/machinery/computer/security/telescreen/entertainment
|
|
name = "entertainment monitor"
|
|
desc = "Damn, they better have the /tg/ channel on these things."
|
|
icon = 'icons/obj/status_display.dmi'
|
|
icon_state = "entertainment_blank"
|
|
network = list("thunder")
|
|
density = FALSE
|
|
circuit = null
|
|
interaction_flags_atom = NONE // interact() is called by BigClick()
|
|
var/icon_state_off = "entertainment_blank"
|
|
var/icon_state_on = "entertainment"
|
|
|
|
MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/computer/security/telescreen/entertainment, 32)
|
|
|
|
/obj/machinery/computer/security/telescreen/entertainment/Initialize(mapload)
|
|
. = ..()
|
|
RegisterSignal(src, COMSIG_CLICK, .proc/BigClick)
|
|
|
|
// Bypass clickchain to allow humans to use the telescreen from a distance
|
|
/obj/machinery/computer/security/telescreen/entertainment/proc/BigClick()
|
|
SIGNAL_HANDLER
|
|
|
|
INVOKE_ASYNC(src, /atom.proc/interact, usr)
|
|
|
|
/obj/machinery/computer/security/telescreen/entertainment/proc/notify(on)
|
|
if(on && icon_state == icon_state_off)
|
|
say(pick(
|
|
"Feats of bravery live now at the thunderdome!",
|
|
"Two enter, one leaves! Tune in now!",
|
|
"Violence like you've never seen it before!",
|
|
"Spears! Camera! Action! LIVE NOW!"))
|
|
icon_state = icon_state_on
|
|
else
|
|
icon_state = icon_state_off
|
|
|
|
/obj/machinery/computer/security/telescreen/rd
|
|
name = "\improper Research Director's telescreen"
|
|
desc = "Used for watching the AI and the RD's goons from the safety of his office."
|
|
network = list("rd", "aicore", "aiupload", "minisat", "xeno", "test", "toxins")
|
|
|
|
/obj/machinery/computer/security/telescreen/research
|
|
name = "research telescreen"
|
|
desc = "A telescreen with access to the research division's camera network."
|
|
network = list("rd")
|
|
|
|
/obj/machinery/computer/security/telescreen/ce
|
|
name = "\improper Chief Engineer's telescreen"
|
|
desc = "Used for watching the engine, telecommunications and the minisat."
|
|
network = list("engine", "singularity", "tcomms", "minisat")
|
|
|
|
/obj/machinery/computer/security/telescreen/cmo
|
|
name = "\improper Chief Medical Officer's telescreen"
|
|
desc = "A telescreen with access to the medbay's camera network."
|
|
network = list("medbay")
|
|
|
|
/obj/machinery/computer/security/telescreen/vault
|
|
name = "vault monitor"
|
|
desc = "A telescreen that connects to the vault's camera network."
|
|
network = list("vault")
|
|
|
|
/obj/machinery/computer/security/telescreen/ordnance
|
|
name = "bomb test site monitor"
|
|
desc = "A telescreen that connects to the bomb test site's camera."
|
|
network = list("ordnance")
|
|
|
|
/obj/machinery/computer/security/telescreen/engine
|
|
name = "engine monitor"
|
|
desc = "A telescreen that connects to the engine's camera network."
|
|
network = list("engine")
|
|
|
|
/obj/machinery/computer/security/telescreen/turbine
|
|
name = "turbine monitor"
|
|
desc = "A telescreen that connects to the turbine's camera."
|
|
network = list("turbine")
|
|
|
|
/obj/machinery/computer/security/telescreen/interrogation
|
|
name = "interrogation room monitor"
|
|
desc = "A telescreen that connects to the interrogation room's camera."
|
|
network = list("interrogation")
|
|
|
|
/obj/machinery/computer/security/telescreen/prison
|
|
name = "prison monitor"
|
|
desc = "A telescreen that connects to the permabrig's camera network."
|
|
network = list("prison")
|
|
|
|
/obj/machinery/computer/security/telescreen/auxbase
|
|
name = "auxiliary base monitor"
|
|
desc = "A telescreen that connects to the auxiliary base's camera."
|
|
network = list("auxbase")
|
|
|
|
/obj/machinery/computer/security/telescreen/minisat
|
|
name = "minisat monitor"
|
|
desc = "A telescreen that connects to the minisat's camera network."
|
|
network = list("minisat")
|
|
|
|
/obj/machinery/computer/security/telescreen/aiupload
|
|
name = "\improper AI upload monitor"
|
|
desc = "A telescreen that connects to the AI upload's camera network."
|
|
network = list("aiupload")
|
|
|
|
/obj/machinery/computer/security/telescreen/bar
|
|
name = "bar monitor"
|
|
desc = "A telescreen that connects to the bar's camera network. Perfect for checking on customers."
|
|
network = list("bar")
|
|
|
|
#undef DEFAULT_MAP_SIZE
|