diff --git a/code/__HELPERS/game.dm b/code/__HELPERS/game.dm index e5cfa74f2a0..c2a1ea6c21d 100644 --- a/code/__HELPERS/game.dm +++ b/code/__HELPERS/game.dm @@ -629,3 +629,17 @@ datum/projectile_data if (!client_or_usr) return winset(client_or_usr, "mainwindow", "flash=5") + +// used for the multiz camera console stolen from vorestatiobn +/proc/get_bbox_of_atoms(list/atoms) + var/list/list_x = list() + var/list/list_y = list() + for(var/_a in atoms) + var/atom/a = _a + list_x += a.x + list_y += a.y + return list( + min(list_x), + min(list_y), + max(list_x), + max(list_y)) \ No newline at end of file diff --git a/code/_onclick/hud/fullscreen.dm b/code/_onclick/hud/fullscreen.dm index 7060451b275..67fc0086f2d 100644 --- a/code/_onclick/hud/fullscreen.dm +++ b/code/_onclick/hud/fullscreen.dm @@ -122,9 +122,5 @@ layer = FULLSCREEN_LAYER /obj/screen/fullscreen/fishbed - icon_state = "fishbed" + icon_state = "fishbed" -#undef FULLSCREEN_LAYER -#undef BLIND_LAYER -#undef DAMAGE_LAYER -#undef CRIT_LAYER \ No newline at end of file diff --git a/code/_onclick/hud/map_popups.dm b/code/_onclick/hud/map_popups.dm new file mode 100644 index 00000000000..aae5c808c97 --- /dev/null +++ b/code/_onclick/hud/map_popups.dm @@ -0,0 +1,171 @@ +/client + /** + * Assoc list with all the active maps - when a screen obj is added to + * a map, it's put in here as well. + * + * Format: list( = list(/obj/screen)) + */ + var/list/screen_maps = list() + +/obj/screen + /** + * Map name assigned to this object. + * Automatically set by /client/proc/register_map_obj. + */ + var/assigned_map + /** + * Mark this object as garbage-collectible after you clean the map + * it was registered on. + * + * This could probably be changed to be a proc, for conditional removal. + * But for now, this works. + */ + var/del_on_map_removal = TRUE + +/** + * A screen object, which acts as a container for turfs and other things + * you want to show on the map, which you usually attach to "vis_contents". + */ +/obj/screen/map_view + icon_state = "blank" + // Map view has to be on the lowest plane to enable proper lighting + layer = SPACE_PLANE + plane = SPACE_PLANE + +/** + * A generic background object. + * It is also implicitly used to allocate a rectangle on the map, which will + * be used for auto-scaling the map. + */ +/obj/screen/background + name = "background" + icon = 'icons/mob/map_backgrounds.dmi' + icon_state = "clear" + layer = SPACE_PLANE + plane = SPACE_PLANE + +/** + * Sets screen_loc of this screen object, in form of point coordinates, + * with optional pixel offset (px, py). + * + * If applicable, "assigned_map" has to be assigned before this proc call. + */ +/obj/screen/proc/set_position(x, y, px = 0, py = 0) + if(assigned_map) + screen_loc = "[assigned_map]:[x]:[px],[y]:[py]" + else + screen_loc = "[x]:[px],[y]:[py]" + +/** + * Sets screen_loc to fill a rectangular area of the map. + * + * If applicable, "assigned_map" has to be assigned before this proc call. + */ +/obj/screen/proc/fill_rect(x1, y1, x2, y2) + if(assigned_map) + screen_loc = "[assigned_map]:[x1],[y1] to [x2],[y2]" + else + screen_loc = "[x1],[y1] to [x2],[y2]" + +/** + * Registers screen obj with the client, which makes it visible on the + * assigned map, and becomes a part of the assigned map's lifecycle. + */ +/client/proc/register_map_obj(obj/screen/screen_obj) + if(!screen_obj.assigned_map) + CRASH("Can't register [screen_obj] without 'assigned_map' property.") + if(!screen_maps[screen_obj.assigned_map]) + screen_maps[screen_obj.assigned_map] = list() + // NOTE: Possibly an expensive operation + var/list/screen_map = screen_maps[screen_obj.assigned_map] + if(!screen_map.Find(screen_obj)) + screen_map += screen_obj + if(!screen.Find(screen_obj)) + screen += screen_obj + +/** + * Clears the map of registered screen objects. + * + * Not really needed most of the time, as the client's screen list gets reset + * on relog. any of the buttons are going to get caught by garbage collection + * anyway. they're effectively qdel'd. + */ +/client/proc/clear_map(map_name) + if(!map_name || !(map_name in screen_maps)) + return FALSE + for(var/obj/screen/screen_obj in screen_maps[map_name]) + screen_maps[map_name] -= screen_obj + if(screen_obj.del_on_map_removal) + qdel(screen_obj) + screen_maps -= map_name + +/** + * Clears all the maps of registered screen objects. + */ +/client/proc/clear_all_maps() + for(var/map_name in screen_maps) + clear_map(map_name) + +/** + * Creates a popup window with a basic map element in it, without any + * further initialization. + * + * Ratio is how many pixels by how many pixels (keep it simple). + * + * Returns a map name. + */ +/client/proc/create_popup(name, ratiox = 100, ratioy = 100) + winclone(src, "popupwindow", name) + var/list/winparams = list() + winparams["size"] = "[ratiox]x[ratioy]" + winparams["on-close"] = "handle-popup-close [name]" + winset(src, "[name]", list2params(winparams)) + winshow(src, "[name]", 1) + + var/list/params = list() + params["parent"] = "[name]" + params["type"] = "map" + params["size"] = "[ratiox]x[ratioy]" + params["anchor1"] = "0,0" + params["anchor2"] = "[ratiox],[ratioy]" + winset(src, "[name]_map", list2params(params)) + + return "[name]_map" + +/** + * Create the popup, and get it ready for generic use by giving + * it a background. + * + * Width and height are multiplied by 64 by default. + */ +/client/proc/setup_popup(popup_name, width = 9, height = 9, \ + tilesize = 2, bg_icon) + if(!popup_name) + return + clear_map("[popup_name]_map") + var/x_value = world.icon_size * tilesize * width + var/y_value = world.icon_size * tilesize * height + var/map_name = create_popup(popup_name, x_value, y_value) + + var/obj/screen/background/background = new + background.assigned_map = map_name + background.fill_rect(1, 1, width, height) + if(bg_icon) + background.icon_state = bg_icon + register_map_obj(background) + + return map_name + +/** + * Closes a popup. + */ +/client/proc/close_popup(popup) + winshow(src, popup, 0) + handle_popup_close(popup) + +/** + * When the popup closes in any way (player or proc call) it calls this. + */ +/client/verb/handle_popup_close(window_id as text) + set hidden = TRUE + clear_map("[window_id]_map") diff --git a/code/_onclick/hud/skybox.dm b/code/_onclick/hud/skybox.dm index 6c251676bc0..b85f2695d0c 100644 --- a/code/_onclick/hud/skybox.dm +++ b/code/_onclick/hud/skybox.dm @@ -3,8 +3,10 @@ #define SKYBOX_TURFS (SKYBOX_PIXELS/WORLD_ICON_SIZE) // Skybox screen object. -/obj/skybox +/obj/screen/skybox name = "skybox" + icon = null + appearance_flags = TILE_BOUND|PIXEL_SCALE mouse_opacity = 0 anchored = TRUE simulated = FALSE @@ -13,7 +15,7 @@ blend_mode = BLEND_MULTIPLY // You actually need to do it this way or you see it in occlusion. // Adjust transform property to scale for client's view var. We assume the skybox is 736x736 px -/obj/skybox/proc/scale_to_view(var/view) +/obj/screen/skybox/proc/scale_to_view(var/view) var/matrix/M = matrix() // Translate to center the icon over us! M.Translate(-(SKYBOX_PIXELS - WORLD_ICON_SIZE) / 2) @@ -23,7 +25,7 @@ src.transform = M /client - var/obj/skybox/skybox + var/obj/screen/skybox/skybox /client/proc/update_skybox(rebuild) if(!skybox) diff --git a/code/controllers/subsystems/skybox.dm b/code/controllers/subsystems/skybox.dm index 4ec9e76a8cc..b00f839deb5 100644 --- a/code/controllers/subsystems/skybox.dm +++ b/code/controllers/subsystems/skybox.dm @@ -136,7 +136,7 @@ SUBSYSTEM_DEF(skybox) for(var/z in zlevels) skybox_cache["[z]"] = generate_skybox(z) - for(var/client/C) + for(var/client/C in GLOB.clients) var/their_z = get_z(C.mob) if(!their_z) // Nullspace continue diff --git a/code/game/machinery/camera/camera.dm b/code/game/machinery/camera/camera.dm index 9265d21b48c..1f64b2317b6 100644 --- a/code/game/machinery/camera/camera.dm +++ b/code/game/machinery/camera/camera.dm @@ -225,12 +225,7 @@ if(U.name == "Unknown") O << "[U] holds \a [itemname] up to one of your cameras ..." else O << "[U] holds \a [itemname] up to one of your cameras ..." O << browse(text("[][]", itemname, info), text("window=[]", itemname)) - for(var/mob/O in player_list) - if (istype(O.machine, /obj/machinery/computer/security)) - var/obj/machinery/computer/security/S = O.machine - if (S.current_camera == src) - to_chat(O, "[U] holds \a [itemname] up to one of the cameras ...") - O << browse(text("[][]", itemname, info), text("window=[]", itemname)) + else if (istype(W, /obj/item/camera_bug)) if (!src.can_use()) @@ -480,7 +475,7 @@ else cameranet.updateVisibility(src, 0) - invalidateCameraCache() + // Resets the camera's wires to fully operational state. Used by one of Malfunction abilities. /obj/machinery/camera/proc/reset_wires() diff --git a/code/game/machinery/camera/presets.dm b/code/game/machinery/camera/presets.dm index 3bdcbade8b1..f52084b8fdb 100644 --- a/code/game/machinery/camera/presets.dm +++ b/code/game/machinery/camera/presets.dm @@ -172,7 +172,7 @@ var/global/list/engineering_networks = list( if(C.number) number = max(number, C.number+1) c_tag = "[A.name] #[number]" - invalidateCameraCache() + // CHECKS diff --git a/code/game/machinery/computer/aifixer.dm b/code/game/machinery/computer/aifixer.dm index 591c7dd6542..950bb38651f 100644 --- a/code/game/machinery/computer/aifixer.dm +++ b/code/game/machinery/computer/aifixer.dm @@ -1,141 +1,132 @@ /obj/machinery/computer/aifixer name = "\improper AI system integrity restorer" - icon_keyboard = "rd_key" - icon_screen = "ai-fixer" - light_color = "#a97faa" - circuit = /obj/item/circuitboard/aifixer + desc = "Used with intelliCards containing nonfunctional AIs to restore them to working order." req_one_access = list(access_robotics, access_heads) - var/mob/living/silicon/ai/occupant = null - var/active = 0 + circuit = /obj/item/circuitboard/aifixer + icon_keyboard = "tech_key" + icon_screen = "ai-fixer" + light_color = LIGHT_COLOR_PINK -/obj/machinery/computer/aifixer/New() - ..() - update_icon() + active_power_usage = 1000 -/obj/machinery/computer/aifixer/proc/load_ai(var/mob/living/silicon/ai/transfer, var/obj/item/aicard/card, var/mob/user) - - if(!transfer) - return - - // Transfer over the AI. - to_chat(transfer, "You have been transferred into a stationary terminal. Sadly, there is no remote access from here.") - to_chat(user, "Transfer successful: [transfer.name] placed within stationary terminal.") - - transfer.loc = src - transfer.cancel_camera() - transfer.control_disabled = 1 - occupant = transfer - - if(card) - card.clear() - - update_icon() - -/obj/machinery/computer/aifixer/attackby(I as obj, user as mob) + /// Variable containing transferred AI + var/mob/living/silicon/ai/occupier + /// Variable dictating if we are in the process of restoring the occupier AI + var/restoring = FALSE +/obj/machinery/computer/aifixer/attackby(obj/item/I, mob/living/user) + if(I.is_screwdriver()) + if(occupier) + if(stat & (NOPOWER|BROKEN)) + to_chat(user, "The screws on [name]'s screen won't budge.") + else + to_chat(user, "The screws on [name]'s screen won't budge and it emits a warning beep.") + return if(istype(I, /obj/item/aicard)) - if(stat & (NOPOWER|BROKEN)) - to_chat(user, "This terminal isn't functioning right now.") + to_chat(user, "This terminal isn't functioning right now.") + return + if(restoring) + to_chat(user, "Terminal is busy restoring [occupier] right now.") return var/obj/item/aicard/card = I - var/mob/living/silicon/ai/comp_ai = locate() in src - var/mob/living/silicon/ai/card_ai = locate() in card + if(occupier) + if(card.grab_ai(occupier, user)) + occupier = null + else if(card.carded_ai) + var/mob/living/silicon/ai/new_occupant = card.carded_ai + to_chat(new_occupant, "You have been transferred into a stationary terminal. Sadly there is no remote access from here.") + to_chat(user, "Transfer Successful: [new_occupant] placed within stationary terminal.") + new_occupant.forceMove(src) + new_occupant.cancel_camera() + new_occupant.control_disabled = TRUE + occupier = new_occupant + card.clear() + update_icon() + else + to_chat(user, "There is no AI loaded onto this computer, and no AI loaded onto [I]. What exactly are you trying to do here?") + return ..() - if(istype(comp_ai)) - if(active) - to_chat(user, "ERROR: Reconstruction in progress.") - return - card.grab_ai(comp_ai, user) - if(!(locate(/mob/living/silicon/ai) in src)) occupant = null - else if(istype(card_ai)) - load_ai(card_ai,card,user) - occupant = locate(/mob/living/silicon/ai) in src - - update_icon() +/obj/machinery/computer/aifixer/attack_hand(mob/user) + if(stat & (NOPOWER|BROKEN)) return - ..() - return + ui_interact(user) -/obj/machinery/computer/aifixer/attack_ai(var/mob/user as mob) - return attack_hand(user) +/obj/machinery/computer/aifixer/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "AiRestorer", name) + ui.open() -/obj/machinery/computer/aifixer/attack_hand(var/mob/user as mob) +/obj/machinery/computer/aifixer/ui_data(mob/user) + var/list/data = list() + + data["ejectable"] = FALSE + data["AI_present"] = FALSE + data["error"] = null + if(!occupier) + data["error"] = "Please transfer an AI unit." + else + data["AI_present"] = TRUE + data["name"] = occupier.name + data["restoring"] = restoring + data["health"] = (occupier.health + 100) / 2 + data["isDead"] = occupier.stat == DEAD + var/list/laws = list() + for(var/datum/ai_law/law in occupier.laws.all_laws()) + laws += "[law.get_index()]: [law.law]" + data["laws"] = laws + + return data + +/obj/machinery/computer/aifixer/ui_act(action, params) if(..()) return + if(!occupier) + restoring = FALSE - user.set_machine(src) - var/dat = "

AI System Integrity Restorer



" + switch(action) + if("PRG_beginReconstruction") + if(occupier?.health < 100) + to_chat(usr, "Reconstruction in progress. This will take several minutes.") + playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 25, FALSE) + restoring = TRUE + var/mob/observer/dead/ghost = occupier.get_ghost() + if(ghost) + ghost.notify_revive("Your core files are being restored!", source = src) + . = TRUE - if (src.occupant) - var/laws - dat += "Stored AI: [src.occupant.name]
System integrity: [src.occupant.hardware_integrity()]%
Backup Capacitor: [src.occupant.backup_capacitor()]%
" +/obj/machinery/computer/aifixer/proc/Fix() + use_power(active_power_usage) + occupier.adjustOxyLoss(-5, 0, FALSE) + occupier.adjustFireLoss(-5, 0, FALSE) + occupier.adjustBruteLoss(-5, 0) + if(occupier.health >= 0 && occupier.stat == DEAD) + occupier.revive() - for (var/datum/ai_law/law in occupant.laws.all_laws()) - laws += "[law.get_index()]: [law.law]
" - - dat += "Laws:
[laws]
" - - if (src.occupant.stat == 2) - dat += "AI nonfunctional" - else - dat += "AI functional" - if (!src.active) - dat += {"

Begin Reconstruction"} - else - dat += "

Reconstruction in process, please wait.
" - dat += {" Close"} - - user << browse(dat, "window=computer;size=400x500") - onclose(user, "computer") - return + return occupier.health < 100 /obj/machinery/computer/aifixer/process() if(..()) - src.updateDialog() - return - -/obj/machinery/computer/aifixer/Topic(href, href_list) - if(..()) - return 1 - if (href_list["fix"]) - src.active = 1 - src.overlays += image(icon, "ai-fixer-on") - while (src.occupant.getOxyLoss() > 0 || src.occupant.getFireLoss() > 0 || src.occupant.getToxLoss() > 0 || src.occupant.getBruteLoss() > 0) - src.occupant.adjustOxyLoss(-1) - src.occupant.adjustFireLoss(-1) - src.occupant.adjustToxLoss(-1) - src.occupant.adjustBruteLoss(-1) - src.occupant.updatehealth() - if (src.occupant.health >= 0 && src.occupant.stat == DEAD) - src.occupant.stat = CONSCIOUS - src.occupant.lying = 0 - dead_mob_list -= src.occupant - living_mob_list += src.occupant - src.overlays -= image(icon, "ai-fixer-404") - src.overlays += image(icon, "ai-fixer-full") - src.occupant.add_ai_verbs() - src.updateUsrDialog() - sleep(10) - src.active = 0 - src.overlays -= image(icon, "ai-fixer-on") - - - src.add_fingerprint(usr) - src.updateUsrDialog() - return - + if(restoring) + var/oldstat = occupier.stat + restoring = Fix() + if(oldstat != occupier.stat) + update_icon() /obj/machinery/computer/aifixer/update_icon() - ..() - if((stat & BROKEN) || (stat & NOPOWER)) + . = ..() + if(stat & (NOPOWER|BROKEN)) return - if(occupant) - if(occupant.stat) - overlays += image(icon, "ai-fixer-404", overlay_layer) - else - overlays += image(icon, "ai-fixer-full", overlay_layer) + if(restoring) + . += "ai-fixer-on" + if (occupier) + switch (occupier.stat) + if (CONSCIOUS) + . += "ai-fixer-full" + if (UNCONSCIOUS) + . += "ai-fixer-404" else - overlays += image(icon, "ai-fixer-empty", overlay_layer) + . += "ai-fixer-empty" diff --git a/code/game/machinery/computer/camera.dm b/code/game/machinery/computer/camera.dm index 829a27bf97f..7904a2a228a 100644 --- a/code/game/machinery/computer/camera.dm +++ b/code/game/machinery/computer/camera.dm @@ -3,204 +3,46 @@ /obj/machinery/computer/security name = "security camera monitor" desc = "Used to access the various cameras on the station." + icon_keyboard = "security_key" icon_screen = "cameras" light_color = "#a91515" - var/current_network = null - var/obj/machinery/camera/current_camera = null - var/last_pic = 1.0 - var/list/network - var/mapping = 0//For the overview file, interesting bit of code. - var/cache_id = 0 circuit = /obj/item/circuitboard/security -/obj/machinery/computer/security/New() - if(!network) + var/mapping = 0//For the overview file, interesting bit of code. + var/list/network = list() + + var/datum/tgui_module/camera/camera + +/obj/machinery/computer/security/Initialize() + . = ..() + if(!LAZYLEN(network)) network = GLOB.using_map.station_networks.Copy() - ..() - if(network.len) - current_network = network[1] + camera = new(src, network) -/obj/machinery/computer/security/attack_ai(var/mob/user as mob) - return attack_hand(user) +/obj/machinery/computer/security/Destroy() + QDEL_NULL(camera) + return ..() -/obj/machinery/computer/security/check_eye(var/mob/user as mob) - if (user.stat || ((get_dist(user, src) > 1 || !( user.canmove ) || user.blinded) && !istype(user, /mob/living/silicon))) //user can't see - not sure why canmove is here. - return -1 - if(!current_camera) - return 0 - var/viewflag = current_camera.check_eye(user) - if ( viewflag < 0 ) //camera doesn't work - reset_current() - return viewflag +/obj/machinery/computer/security/ui_interact(mob/user, datum/tgui/ui = null) + camera.ui_interact(user, ui) -/obj/machinery/computer/security/nano_ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1) - if(stat & (NOPOWER|BROKEN)) return - if(user.stat) return - - var/data[0] - - data["current_camera"] = current_camera ? current_camera.nano_structure() : null - data["current_network"] = current_network - data["networks"] = network ? network : list() - if(current_network) - data["cameras"] = camera_repository.cameras_in_network(current_network) - if(current_camera) - switch_to_camera(user, current_camera) - data["map_levels"] = GLOB.using_map.get_map_levels(src.z) - - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) - if (!ui) - ui = new(user, src, ui_key, "sec_camera.tmpl", "Camera Console", 900, 800) - - // adding a template with the key "mapContent" enables the map ui functionality - ui.add_template("mapContent", "sec_camera_map_content.tmpl") - // adding a template with the key "mapHeader" replaces the map header content - ui.add_template("mapHeader", "sec_camera_map_header.tmpl") - - ui.set_initial_data(data) - ui.open() - -/obj/machinery/computer/security/Topic(href, href_list) - if(..()) - return 1 - if(href_list["switch_camera"]) - if(stat&(NOPOWER|BROKEN)) return //VOREStation Edit - Removed zlevel check - if(usr.stat || ((get_dist(usr, src) > 1 || !( usr.canmove ) || usr.blinded) && !istype(usr, /mob/living/silicon))) return - var/obj/machinery/camera/C = locate(href_list["switch_camera"]) in cameranet.cameras - if(!C) - return - if(!(current_network in C.network)) - return - - switch_to_camera(usr, C) - return 1 - else if(href_list["switch_network"]) - if(stat&(NOPOWER|BROKEN)) return //VOREStation Edit - Removed zlevel check - if(usr.stat || ((get_dist(usr, src) > 1 || !( usr.canmove ) || usr.blinded) && !istype(usr, /mob/living/silicon))) return - if(href_list["switch_network"] in network) - current_network = href_list["switch_network"] - return 1 - else if(href_list["reset"]) - if(stat&(NOPOWER|BROKEN)) return //VOREStation Edit - Removed zlevel check - if(usr.stat || ((get_dist(usr, src) > 1 || !( usr.canmove ) || usr.blinded) && !istype(usr, /mob/living/silicon))) return - reset_current() - usr.reset_view(current_camera) - return 1 - else - . = ..() - -/obj/machinery/computer/security/attack_hand(var/mob/user as mob) - if (GLOB.using_map && !(src.z in GLOB.using_map.contact_levels)) - to_chat(user, "Unable to establish a connection: You're too far away from the station!") +/obj/machinery/computer/security/attack_hand(mob/user) + add_fingerprint(user) + if(stat & (BROKEN|NOPOWER)) return - if(stat & (NOPOWER|BROKEN)) return + ui_interact(user) - if(!isAI(user)) - user.set_machine(src) - nano_ui_interact(user) +/obj/machinery/computer/security/attack_ai(mob/user) + to_chat(user, "You realise its kind of stupid to access a camera console when you have the entire camera network at your metaphorical fingertips") + return -/obj/machinery/computer/security/proc/switch_to_camera(var/mob/user, var/obj/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 +/obj/machinery/computer/security/proc/set_network(list/new_network) + network = new_network + camera.network = network + camera.access_based = FALSE - A.eyeobj.setLoc(get_turf(C)) - A.client.eye = A.eyeobj - return 1 - - if (!C.can_use() || user.stat || (get_dist(user, src) > 1 || user.machine != src || user.blinded || !( user.canmove ) && !istype(user, /mob/living/silicon))) - return 0 - set_current(C) - user.reset_view(current_camera) - check_eye(user) - return 1 - -//Camera control: moving. -/obj/machinery/computer/security/proc/jump_on_click(var/mob/user,var/A) - if(user.machine != src) - return - var/obj/machinery/camera/jump_to - if(istype(A,/obj/machinery/camera)) - jump_to = A - else if(ismob(A)) - if(ishuman(A)) - jump_to = locate() in A:head - else if(isrobot(A)) - jump_to = A:camera - else if(isobj(A)) - jump_to = locate() in A - else if(isturf(A)) - var/best_dist = INFINITY - for(var/obj/machinery/camera/camera in get_area(A)) - 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(isnull(jump_to)) - return - if(can_access_camera(jump_to)) - switch_to_camera(user,jump_to) - -/obj/machinery/computer/security/process() - if(cache_id != camera_repository.camera_cache_id) - cache_id = camera_repository.camera_cache_id - SSnanoui.update_uis(src) - -/obj/machinery/computer/security/proc/can_access_camera(var/obj/machinery/camera/C) - var/list/shared_networks = src.network & C.network - if(shared_networks.len) - return 1 - return 0 - -/obj/machinery/computer/security/proc/set_current(var/obj/machinery/camera/C) - if(current_camera == C) - return - - if(current_camera) - reset_current() - - src.current_camera = C - if(current_camera) - current_camera.camera_computers_using_this.Add(src) - update_use_power(USE_POWER_ACTIVE) - var/mob/living/L = current_camera.loc - if(istype(L)) - L.tracking_initiated() - -/obj/machinery/computer/security/proc/reset_current() - if(current_camera) - current_camera.camera_computers_using_this.Remove(src) - var/mob/living/L = current_camera.loc - if(istype(L)) - L.tracking_cancelled() - current_camera = null - use_power = USE_POWER_IDLE - -//Camera control: mouse. -/atom/DblClick() - ..() - if(istype(usr.machine,/obj/machinery/computer/security)) - var/obj/machinery/computer/security/console = usr.machine - console.jump_on_click(usr,src) //Camera control: arrow keys. -/mob/Move(n,direct) - if(istype(machine,/obj/machinery/computer/security)) - var/obj/machinery/computer/security/console = machine - var/turf/T = get_turf(console.current_camera) - for(var/i;i<10;i++) - T = get_step(T,direct) - console.jump_on_click(src,T) - return - return ..(n,direct) - /obj/machinery/computer/security/telescreen name = "Telescreen" desc = "Used for watching an empty arena." @@ -244,7 +86,7 @@ /obj/machinery/computer/security/wooden_tv name = "security camera monitor" - desc = "An old TV hooked into the stations camera network." + desc = "An old TV hooked into the station's camera network." icon_state = "television" icon_keyboard = null icon_screen = "detective_tv" @@ -252,20 +94,9 @@ light_color = "#3848B3" light_power_on = 0.5 -/obj/machinery/computer/security/wooden_tv/service - name = "security camera monitor" - desc = "An old TV hooked into the stations entertainment network." - icon_state = "television" - icon_keyboard = null - icon_screen = "detective_tv" - network = list(NETWORK_THUNDER) - circuit = /obj/item/circuitboard/security/telescreen/entertainment - light_color = "#3848B3" - light_power_on = 0.9 - /obj/machinery/computer/security/mining name = "outpost camera monitor" - desc = "Used to access the various cameras on the outpost." + desc = "Used to watch over mining operations." icon_keyboard = "mining_key" icon_screen = "mining" network = list("Mining Outpost") diff --git a/code/game/machinery/computer/camera_circuit.dm b/code/game/machinery/computer/camera_circuit.dm deleted file mode 100644 index c27aa77a6d3..00000000000 --- a/code/game/machinery/computer/camera_circuit.dm +++ /dev/null @@ -1,116 +0,0 @@ - -//the researchable camera circuit that can connect to any camera network - -/obj/item/circuitboard/camera - //name = "Circuit board (Camera)" - var/secured = 1 - var/authorised = 0 - var/possibleNets[0] - var/network = "" - build_path = null - -//when adding a new camera network, you should only need to update these two procs - New() - possibleNets["Engineering"] = access_ce - possibleNets["SS13"] = access_hos - possibleNets["Mining"] = access_mining - possibleNets["Cargo"] = access_qm - possibleNets["Research"] = access_rd - possibleNets["Medbay"] = access_cmo - ..() - - proc/updateBuildPath() - build_path = null - if(authorised && secured) - switch(network) - if("SS13") - build_path = /obj/machinery/computer/security - if("Engineering") - build_path = /obj/machinery/computer/security/engineering - if("Mining") - build_path = /obj/machinery/computer/security/mining - if("Research") - build_path = /obj/machinery/computer/security/research - if("Medbay") - build_path = /obj/machinery/computer/security/medbay - if("Cargo") - build_path = /obj/machinery/computer/security/cargo - - attackby(var/obj/item/I, var/mob/user)//if(health > 50) - ..() - else if(I.is_screwdriver()) - secured = !secured - user.visible_message("The [src] can [secured ? "no longer" : "now"] be modified.") - playsound(src, I.usesound, 50, 1) - updateBuildPath() - return - - attack_self(var/mob/user) - if(!secured && ishuman(user)) - user.machine = src - interact(user, 0) - - proc/interact(var/mob/user, var/ai=0) - if(secured) - return - if (!ishuman(user)) - return ..(user) - var/t = "Circuitboard Console - Camera Monitoring Computer
" - t += "Close
" - t += "
Please select a camera network:
" - - for(var/curNet in possibleNets) - if(network == curNet) - t += "- [curNet]
" - else - t += "- [curNet]
" - t += "
" - if(network) - if(authorised) - t += "Authenticated (Clear Auth)
" - else - t += "*Authenticate* (Requires an appropriate access ID)
" - else - t += "*Authenticate* (Requires an appropriate access ID)
" - t += "Close
" - user << browse(t, "window=camcircuit;size=500x400") - onclose(user, "camcircuit") - - Topic(href, href_list) - ..() - if( href_list["close"] ) - usr << browse(null, "window=camcircuit") - usr.machine = null - return - else if(href_list["net"]) - network = href_list["net"] - authorised = 0 - else if( href_list["auth"] ) - var/mob/M = usr - var/obj/item/card/id/I = M.equipped() - if (istype(I, /obj/item/pda)) - var/obj/item/pda/pda = I - I = pda.id - if (I && istype(I)) - if(access_captain in I.access) - authorised = 1 - else if (possibleNets[network] in I.access) - authorised = 1 - if(istype(I,/obj/item/card/emag)) - I.resolve_attackby(src, usr) - else if( href_list["removeauth"] ) - authorised = 0 - updateDialog() - - updateDialog() - if(istype(src.loc,/mob)) - attack_self(src.loc) - -/obj/item/circuitboard/camera/emag_act(var/remaining_charges, var/mob/user) - if(network) - authorised = 1 - to_chat(user, "You authorised the circuit network!") - updateDialog() - return 1 - else - to_chat(user, "You must select a camera network circuit!") diff --git a/code/game/machinery/computer/crew.dm b/code/game/machinery/computer/crew.dm index aac32a03a81..54241800ee9 100644 --- a/code/game/machinery/computer/crew.dm +++ b/code/game/machinery/computer/crew.dm @@ -8,7 +8,7 @@ idle_power_usage = 250 active_power_usage = 500 circuit = /obj/item/circuitboard/crew - var/datum/nano_module/crew_monitor/crew_monitor + var/datum/tgui_module/crew_monitor/crew_monitor /obj/machinery/computer/crew/New() crew_monitor = new(src) @@ -20,16 +20,16 @@ ..() /obj/machinery/computer/crew/attack_ai(mob/user) - nano_ui_interact(user) + attack_hand(user) /obj/machinery/computer/crew/attack_hand(mob/user) add_fingerprint(user) if(stat & (BROKEN|NOPOWER)) return - nano_ui_interact(user) + ui_interact(user) -/obj/machinery/computer/crew/nano_ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - crew_monitor.nano_ui_interact(user, ui_key, ui, force_open) +/obj/machinery/computer/crew/ui_interact(mob/user, datum/tgui/ui = null) + crew_monitor.ui_interact(user, ui) /obj/machinery/computer/crew/interact(mob/user) - crew_monitor.nano_ui_interact(user) + crew_monitor.ui_interact(user) diff --git a/code/game/objects/items/weapons/circuitboards/computer/camera_monitor.dm b/code/game/objects/items/weapons/circuitboards/computer/camera_monitor.dm index b715d7ef070..498a5a978ab 100644 --- a/code/game/objects/items/weapons/circuitboards/computer/camera_monitor.dm +++ b/code/game/objects/items/weapons/circuitboards/computer/camera_monitor.dm @@ -45,7 +45,7 @@ /obj/item/circuitboard/security/construct(var/obj/machinery/computer/security/C) if (..(C)) - C.network = network.Copy() + C.set_network(network.Copy()) /obj/item/circuitboard/security/deconstruct(var/obj/machinery/computer/security/C) if (..(C)) diff --git a/code/game/objects/structures/mirror.dm b/code/game/objects/structures/mirror.dm index 433a1781fcb..91b407efcf2 100644 --- a/code/game/objects/structures/mirror.dm +++ b/code/game/objects/structures/mirror.dm @@ -28,7 +28,7 @@ AC = new(src, user) AC.name = "SalonPro Nano-Mirror™" ui_users[user] = AC - AC.ui_interact(user) + AC.nano_ui_interact(user) /obj/structure/mirror/proc/shatter() if(!glass) return @@ -142,6 +142,7 @@ qdel(user) ..() + //Long mirrors. /obj/structure/mirror/long name = "mirror" @@ -193,4 +194,3 @@ icon_state = "long_mir_r_broke" density = 0 anchored = 1 - shattered = 1 diff --git a/code/game/sound.dm b/code/game/sound.dm index 03153dacd26..298f2a8f1bb 100644 --- a/code/game/sound.dm +++ b/code/game/sound.dm @@ -259,6 +259,11 @@ GLOBAL_VAR_INIT(sound_distance_offscreen, 7) if("smdelam") soundin = pick('sound/machines/sm/accent/delam/1.ogg', 'sound/machines/sm/accent/normal/2.ogg', 'sound/machines/sm/accent/normal/3.ogg', 'sound/machines/sm/accent/normal/4.ogg', 'sound/machines/sm/accent/normal/5.ogg', 'sound/machines/sm/accent/normal/6.ogg', 'sound/machines/sm/accent/normal/7.ogg', 'sound/machines/sm/accent/normal/8.ogg', 'sound/machines/sm/accent/normal/9.ogg', 'sound/machines/sm/accent/normal/10.ogg', 'sound/machines/sm/accent/normal/11.ogg', 'sound/machines/sm/accent/normal/12.ogg', 'sound/machines/sm/accent/normal/13.ogg', 'sound/machines/sm/accent/normal/14.ogg', 'sound/machines/sm/accent/normal/15.ogg', 'sound/machines/sm/accent/normal/16.ogg', 'sound/machines/sm/accent/normal/17.ogg', 'sound/machines/sm/accent/normal/18.ogg', 'sound/machines/sm/accent/normal/19.ogg', 'sound/machines/sm/accent/normal/20.ogg', 'sound/machines/sm/accent/normal/21.ogg', 'sound/machines/sm/accent/normal/22.ogg', 'sound/machines/sm/accent/normal/23.ogg', 'sound/machines/sm/accent/normal/24.ogg', 'sound/machines/sm/accent/normal/25.ogg', 'sound/machines/sm/accent/normal/26.ogg', 'sound/machines/sm/accent/normal/27.ogg', 'sound/machines/sm/accent/normal/28.ogg', 'sound/machines/sm/accent/normal/29.ogg', 'sound/machines/sm/accent/normal/30.ogg', 'sound/machines/sm/accent/normal/31.ogg', 'sound/machines/sm/accent/normal/32.ogg', 'sound/machines/sm/accent/normal/33.ogg', 'sound/machines/sm/supermatter1.ogg', 'sound/machines/sm/supermatter2.ogg', 'sound/machines/sm/supermatter3.ogg') + if ("terminal_type") + soundin = pick('sound/machines/terminal_button01.ogg', 'sound/machines/terminal_button02.ogg', 'sound/machines/terminal_button03.ogg', \ + 'sound/machines/terminal_button04.ogg', 'sound/machines/terminal_button05.ogg', 'sound/machines/terminal_button06.ogg', \ + 'sound/machines/terminal_button07.ogg', 'sound/machines/terminal_button08.ogg') + //END VORESTATION EDIT return soundin diff --git a/code/modules/alarm/alarm.dm b/code/modules/alarm/alarm.dm index ac9b103b560..3ad1df433f9 100644 --- a/code/modules/alarm/alarm.dm +++ b/code/modules/alarm/alarm.dm @@ -18,7 +18,6 @@ var/list/sources = new() //List of sources triggering the alarm. Used to determine when the alarm should be cleared. var/list/sources_assoc = new() //Associative list of source triggers. Used to efficiently acquire the alarm source. var/list/cameras //List of cameras that can be switched to, if the player has that capability. - var/cache_id //ID for camera cache, changed by invalidateCameraCache(). var/area/last_area //The last acquired area, used should origin be lost (for example a destroyed borg containing an alarming camera). var/area/last_name //The last acquired name, used should origin be lost var/area/last_camera_area //The last area in which cameras where fetched, used to see if the camera list should be updated. @@ -78,15 +77,10 @@ return last_name /datum/alarm/proc/cameras() - // reset camera cache - if(camera_repository.camera_cache_id != cache_id) - cameras = null - cache_id = camera_repository.camera_cache_id // If the alarm origin has changed area, for example a borg containing an alarming camera, reset the list of cameras - else if(cameras && (last_camera_area != alarm_area())) + if(cameras && (last_camera_area != alarm_area())) cameras = null - // The list of cameras is also reset by /proc/invalidateCameraCache() if(!cameras) cameras = origin ? origin.get_alarm_cameras() : last_area.get_alarm_cameras() diff --git a/code/modules/clothing/glasses/hud.dm b/code/modules/clothing/glasses/hud.dm index 782a76a6d5f..7fad7dd8697 100644 --- a/code/modules/clothing/glasses/hud.dm +++ b/code/modules/clothing/glasses/hud.dm @@ -53,6 +53,8 @@ icon_state = "glasses" var/datum/nano_module/arscreen var/arscreen_path + var/datum/tgui_module/tgarscreen + var/tgarscreen_path var/flash_prot = 0 //0 for none, 1 for flash weapon protection, 2 for welder protection enables_planes = list(VIS_CH_ID,VIS_CH_HEALTH_VR,VIS_AUGMENTED,VIS_CH_BACKUP) plane_slots = list(slot_glasses) @@ -61,21 +63,29 @@ ..() if(arscreen_path) arscreen = new arscreen_path(src) + if(tgarscreen_path) + tgarscreen = new tgarscreen_path(src) /obj/item/clothing/glasses/omnihud/Destroy() QDEL_NULL(arscreen) + QDEL_NULL(tgarscreen) . = ..() /obj/item/clothing/glasses/omnihud/dropped() if(arscreen) SSnanoui.close_uis(src) + if(tgarscreen) + SStgui.close_uis(src) ..() /obj/item/clothing/glasses/omnihud/emp_act(var/severity) var/disconnect_ar = arscreen arscreen = null + var/disconnect_tgar = tgarscreen + tgarscreen = null spawn(20 SECONDS) arscreen = disconnect_ar + tgarscreen = disconnect_tgar ..() /obj/item/clothing/glasses/omnihud/proc/flashed() @@ -116,13 +126,13 @@ These have been upgraded with medical records access and virus database integration." mode = "med" action_button_name = "AR Console (Crew Monitor)" - arscreen_path = /datum/nano_module/crew_monitor + tgarscreen_path = /datum/tgui_module/crew_monitor/glasses enables_planes = list(VIS_CH_ID,VIS_CH_HEALTH_VR,VIS_CH_STATUS_R,VIS_CH_BACKUP,VIS_AUGMENTED) - ar_interact(var/mob/living/carbon/human/user) - if(arscreen) - arscreen.nano_ui_interact(user,"main",null,1,glasses_state) - return 1 +/obj/item/clothing/glasses/omnihud/med/ar_interact(var/mob/living/carbon/human/user) + if(tgarscreen) + tgarscreen.ui_interact(user) + return 1 /obj/item/clothing/glasses/omnihud/sec name = "\improper AR-S glasses" @@ -134,10 +144,10 @@ arscreen_path = /datum/nano_module/alarm_monitor/security enables_planes = list(VIS_CH_ID,VIS_CH_HEALTH_VR,VIS_CH_WANTED,VIS_AUGMENTED) - ar_interact(var/mob/living/carbon/human/user) - if(arscreen) - arscreen.nano_ui_interact(user,"main",null,1,glasses_state) - return 1 +/obj/item/clothing/glasses/omnihud/sec/ar_interact(var/mob/living/carbon/human/user) + if(arscreen) + arscreen.nano_ui_interact(user,"main",null,1,glasses_state) + return 1 /obj/item/clothing/glasses/omnihud/eng name = "\improper AR-E glasses" @@ -148,10 +158,10 @@ action_button_name = "AR Console (Station Alerts)" arscreen_path = /datum/nano_module/alarm_monitor/engineering - ar_interact(var/mob/living/carbon/human/user) - if(arscreen) - arscreen.nano_ui_interact(user,"main",null,1,glasses_state) - return 1 +/obj/item/clothing/glasses/omnihud/eng/ar_interact(var/mob/living/carbon/human/user) + if(arscreen) + arscreen.nano_ui_interact(user,"main",null,1,glasses_state) + return 1 /obj/item/clothing/glasses/omnihud/rnd name = "\improper AR-R glasses" diff --git a/code/modules/mob/freelook/ai/update_triggers.dm b/code/modules/mob/freelook/ai/update_triggers.dm index 045d0522efe..797e5cc23b5 100644 --- a/code/modules/mob/freelook/ai/update_triggers.dm +++ b/code/modules/mob/freelook/ai/update_triggers.dm @@ -39,7 +39,6 @@ /obj/machinery/camera/deactivate(user as mob, var/choice = 1) ..(user, choice) - invalidateCameraCache() if(src.can_use()) cameranet.addCamera(src) else diff --git a/code/modules/mob/living/silicon/subystems.dm b/code/modules/mob/living/silicon/subystems.dm index a143dacb6fc..bdbd90a1f89 100644 --- a/code/modules/mob/living/silicon/subystems.dm +++ b/code/modules/mob/living/silicon/subystems.dm @@ -2,7 +2,7 @@ var/register_alarms = 1 var/datum/nano_module/alarm_monitor/all/alarm_monitor var/datum/nano_module/atmos_control/atmos_control - var/datum/nano_module/crew_monitor/crew_monitor + var/datum/tgui_module/crew_monitor/robot/crew_monitor var/datum/nano_module/law_manager/law_manager var/datum/nano_module/power_monitor/power_monitor var/datum/nano_module/rcon/rcon @@ -67,7 +67,7 @@ set category = "Subystems" set name = "Crew Monitor" - crew_monitor.nano_ui_interact(usr, state = self_state) + crew_monitor.ui_interact(usr, state = self_state) /**************** * Law Manager * diff --git a/code/modules/mob/logout.dm b/code/modules/mob/logout.dm index 573c55f1740..ec5f059d311 100644 --- a/code/modules/mob/logout.dm +++ b/code/modules/mob/logout.dm @@ -1,6 +1,7 @@ /mob/Logout() SEND_SIGNAL(src, COMSIG_MOB_CLIENT_LOGOUT, client) SSnanoui.user_logout(src) // this is used to clean up (remove) this user's Nano UIs + SStgui.on_logout(src) // Cleanup any TGUIs the user has open player_list -= src disconnect_time = world.realtime //VOREStation Addition: logging when we disappear. update_client_z(null) @@ -14,4 +15,4 @@ send2irc("LOGOUT", "[key_name(src)] logged out - no more admins online.") ..() - return 1 \ No newline at end of file + return 1 diff --git a/code/modules/modular_computers/computers/modular_computer/core.dm b/code/modules/modular_computers/computers/modular_computer/core.dm index e2660d3a777..7ceb2c59481 100644 --- a/code/modules/modular_computers/computers/modular_computer/core.dm +++ b/code/modules/modular_computers/computers/modular_computer/core.dm @@ -163,6 +163,7 @@ idle_threads.Add(active_program) active_program.program_state = PROGRAM_STATE_BACKGROUND // Should close any existing UIs SSnanoui.close_uis(active_program.NM ? active_program.NM : active_program) + SStgui.close_uis(active_program.TM ? active_program.TM : active_program) active_program = null update_icon() if(istype(user)) @@ -202,7 +203,6 @@ minimize_program(user) if(P.run_program(user)) - active_program = P update_icon() return 1 diff --git a/code/modules/modular_computers/file_system/program.dm b/code/modules/modular_computers/file_system/program.dm index 8d1419537cc..d9e3e3d78b6 100644 --- a/code/modules/modular_computers/file_system/program.dm +++ b/code/modules/modular_computers/file_system/program.dm @@ -5,8 +5,13 @@ var/required_access = null // List of required accesses to run/download the program. var/requires_access_to_run = 1 // Whether the program checks for required_access when run. var/requires_access_to_download = 1 // Whether the program checks for required_access when downloading. + //nanoui var/datum/nano_module/NM = null // If the program uses NanoModule, put it here and it will be automagically opened. Otherwise implement nano_ui_interact. var/nanomodule_path = null // Path to nanomodule, make sure to set this if implementing new program. + // t gui + var/datum/tgui_module/TM = null // If the program uses TGUIModule, put it here and it will be automagically opened. Otherwise implement ui_interact. + var/tguimodule_path = null // Path to tguimodule, make sure to set this if implementing new program. + // misc program stuff var/program_state = PROGRAM_STATE_KILLED// PROGRAM_STATE_KILLED or PROGRAM_STATE_BACKGROUND or PROGRAM_STATE_ACTIVE - specifies whether this program is running. var/obj/item/modular_computer/computer // Device that runs this program. var/filedesc = "Unknown Program" // User-friendly name of this program. @@ -125,9 +130,14 @@ // When implementing new program based device, use this to run the program. /datum/computer_file/program/proc/run_program(var/mob/living/user) if(can_run(user, 1) || !requires_access_to_run) + computer.active_program = src if(nanomodule_path) NM = new nanomodule_path(src, new /datum/topic_manager/program(src), src) NM.using_access = user.GetAccess() + if(tguimodule_path) + TM = new tguimodule_path(src) + TM.using_access = user.GetAccess() + TM.ui_interact(user) if(requires_ntnet && network_destination) generate_network_log("Connection opened to [network_destination].") program_state = PROGRAM_STATE_ACTIVE @@ -139,9 +149,11 @@ program_state = PROGRAM_STATE_KILLED if(network_destination) generate_network_log("Connection to [network_destination] closed.") - if(NM) - qdel(NM) - NM = null + QDEL_NULL(NM) + if(TM) + SStgui.close_uis(TM) + qdel(TM) + TM = null return 1 // This is called every tick when the program is enabled. Ensure you do parent call if you override it. If parent returns 1 continue with UI initialisation. @@ -154,6 +166,9 @@ if(istype(NM)) NM.nano_ui_interact(user, ui_key, null, force_open) return 0 + if(istype(TM)) + TM.ui_interact(user) + return 0 return 1 diff --git a/code/modules/modular_computers/file_system/programs/antagonist/hacked_camera.dm b/code/modules/modular_computers/file_system/programs/antagonist/hacked_camera.dm index 4a5d158d90e..ba38e00ef25 100644 --- a/code/modules/modular_computers/file_system/programs/antagonist/hacked_camera.dm +++ b/code/modules/modular_computers/file_system/programs/antagonist/hacked_camera.dm @@ -1,7 +1,7 @@ /datum/computer_file/program/camera_monitor/hacked filename = "camcrypt" filedesc = "Camera Decryption Tool" - nanomodule_path = /datum/nano_module/camera_monitor/hacked + tguimodule_path = /datum/tgui_module/camera/ntos/hacked program_icon_state = "hostile" program_key_state = "security_key" program_menu_icon = "zoomin" @@ -15,25 +15,9 @@ if(program_state != PROGRAM_STATE_ACTIVE) // Background programs won't trigger alarms. return - var/datum/nano_module/camera_monitor/hacked/HNM = NM // The program is active and connected to one of the station's networks. Has a very small chance to trigger IDS alarm every tick. - if(HNM && HNM.current_network && (HNM.current_network in GLOB.using_map.station_networks) && prob(0.1)) + if(prob(0.1)) if(ntnet_global.intrusion_detection_enabled) - ntnet_global.add_log("IDS WARNING - Unauthorised access detected to camera network [HNM.current_network] by device with NID [computer.network_card.get_network_tag()]") + ntnet_global.add_log("IDS WARNING - Unauthorised access detected to camera network by device with NID [computer.network_card.get_network_tag()]") ntnet_global.intrusion_detection_alarm = 1 - - -/datum/nano_module/camera_monitor/hacked - name = "Hacked Camera Monitoring Program" - //available_to_ai = FALSE - -/datum/nano_module/camera_monitor/hacked/can_access_network(var/mob/user, var/network_access) - return 1 - -// The hacked variant has access to all commonly used networks. -/datum/nano_module/camera_monitor/hacked/modify_networks_list(var/list/networks) - networks.Add(list(list("tag" = NETWORK_MERCENARY, "has_access" = 1))) - networks.Add(list(list("tag" = NETWORK_ERT, "has_access" = 1))) - networks.Add(list(list("tag" = NETWORK_CRESCENT, "has_access" = 1))) - return networks \ No newline at end of file diff --git a/code/modules/modular_computers/file_system/programs/generic/camera.dm b/code/modules/modular_computers/file_system/programs/generic/camera.dm index f589301da3a..4ec7667d80e 100644 --- a/code/modules/modular_computers/file_system/programs/generic/camera.dm +++ b/code/modules/modular_computers/file_system/programs/generic/camera.dm @@ -24,7 +24,7 @@ /datum/computer_file/program/camera_monitor filename = "cammon" filedesc = "Camera Monitoring" - nanomodule_path = /datum/nano_module/camera_monitor + tguimodule_path = /datum/tgui_module/camera/ntos program_icon_state = "cameras" program_key_state = "generic_key" program_menu_icon = "search" @@ -33,161 +33,11 @@ available_on_ntnet = 1 requires_ntnet = 1 -/datum/nano_module/camera_monitor - name = "Camera Monitoring program" - var/obj/machinery/camera/current_camera = null - var/current_network = null - -/datum/nano_module/camera_monitor/nano_ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1, state = default_state) - var/list/data = host.initial_data() - - data["current_camera"] = current_camera ? current_camera.nano_structure() : null - data["current_network"] = current_network - - var/list/all_networks[0] - for(var/network in GLOB.using_map.station_networks) - if(can_access_network(user, get_camera_access(network), 1)) - all_networks.Add(list(list( - "tag" = network, - "has_access" = 1 - ))) - for(var/network in GLOB.using_map.secondary_networks) - if(can_access_network(user, get_camera_access(network), 0)) - all_networks.Add(list(list( - "tag" = network, - "has_access" = 1 - ))) - - all_networks = modify_networks_list(all_networks) - - data["networks"] = all_networks - - if(current_network) - data["cameras"] = camera_repository.cameras_in_network(current_network) - - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) - if (!ui) - ui = new(user, src, ui_key, "mod_sec_camera.tmpl", "Camera Monitoring", 900, 800) - // ui.auto_update_layout = 1 // Disabled as with suit sensors monitor - breaks the UI map. Re-enable once it's fixed somehow. - - ui.add_template("mapContent", "sec_camera_map_content.tmpl") - ui.add_template("mapHeader", "mod_sec_camera_map_header.tmpl") - ui.set_initial_data(data) - ui.open() - -// Intended to be overriden by subtypes to manually add non-station networks to the list. -/datum/nano_module/camera_monitor/proc/modify_networks_list(var/list/networks) - return networks - -/datum/nano_module/camera_monitor/proc/can_access_network(var/mob/user, var/network_access, var/station_network = 0) - // No access passed, or 0 which is considered no access requirement. Allow it. - if(!network_access) - return 1 - - if(station_network) - return check_access(user, network_access) || check_access(user, access_security) || check_access(user, access_heads) - else - return check_access(user, network_access) - -/datum/nano_module/camera_monitor/Topic(href, href_list) - if(..()) - return 1 - - if(href_list["switch_camera"]) - var/obj/machinery/camera/C = locate(href_list["switch_camera"]) in cameranet.cameras - if(!C) - return - if(!(current_network in C.network)) - return - - switch_to_camera(usr, C) - return 1 - - else if(href_list["switch_network"]) - // Either security access, or access to the specific camera network's department is required in order to access the network. - if(can_access_network(usr, get_camera_access(href_list["switch_network"]), (href_list["switch_network"] in GLOB.using_map.station_networks))) - current_network = href_list["switch_network"] - else - to_chat(usr, "\The [nano_host()] shows an \"Network Access Denied\" error message.") - return 1 - - else if(href_list["reset"]) - reset_current() - usr.reset_view(current_camera) - return 1 - -/datum/nano_module/camera_monitor/proc/switch_to_camera(var/mob/user, var/obj/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 - - set_current(C) - user.machine = nano_host() - user.reset_view(C) - return 1 - -/datum/nano_module/camera_monitor/proc/set_current(var/obj/machinery/camera/C) - if(current_camera == C) - return - - if(current_camera) - reset_current() - - current_camera = C - if(current_camera) - var/mob/living/L = current_camera.loc - if(istype(L)) - L.tracking_initiated() - -/datum/nano_module/camera_monitor/proc/reset_current() - if(current_camera) - var/mob/living/L = current_camera.loc - if(istype(L)) - L.tracking_cancelled() - current_camera = null - -/datum/nano_module/camera_monitor/check_eye(var/mob/user as mob) - if(!current_camera) - return 0 - var/viewflag = current_camera.check_eye(user) - if ( viewflag < 0 ) //camera doesn't work - reset_current() - return viewflag - - // ERT Variant of the program /datum/computer_file/program/camera_monitor/ert filename = "ntcammon" filedesc = "Advanced Camera Monitoring" extended_desc = "This program allows remote access to the camera system. Some camera networks may have additional access requirements. This version has an integrated database with additional encrypted keys." size = 14 - nanomodule_path = /datum/nano_module/camera_monitor/ert + tguimodule_path = /datum/tgui_module/camera/ntos/ert available_on_ntnet = 0 - -/datum/nano_module/camera_monitor/ert - name = "Advanced Camera Monitoring Program" - //available_to_ai = FALSE - -// The ERT variant has access to ERT and crescent cams, but still checks for accesses. ERT members should be able to use it. -/datum/nano_module/camera_monitor/ert/modify_networks_list(var/list/networks) - ..() - networks.Add(list(list("tag" = NETWORK_ERT, "has_access" = 1))) - networks.Add(list(list("tag" = NETWORK_CRESCENT, "has_access" = 1))) - return networks - -/datum/nano_module/camera_monitor/apply_visual(mob/M) - if(current_camera) - current_camera.apply_visual(M) - else - remove_visual(M) - -/datum/nano_module/camera_monitor/remove_visual(mob/M) - if(current_camera) - current_camera.remove_visual(M) diff --git a/code/modules/modular_computers/file_system/programs/medical/suit_sensors.dm b/code/modules/modular_computers/file_system/programs/medical/suit_sensors.dm index b96cba454ae..9b94707eb22 100644 --- a/code/modules/modular_computers/file_system/programs/medical/suit_sensors.dm +++ b/code/modules/modular_computers/file_system/programs/medical/suit_sensors.dm @@ -1,7 +1,7 @@ /datum/computer_file/program/suit_sensors filename = "sensormonitor" filedesc = "Suit Sensors Monitoring" - nanomodule_path = /datum/nano_module/crew_monitor + tguimodule_path = /datum/tgui_module/crew_monitor/ntos program_icon_state = "crew" program_key_state = "med_key" program_menu_icon = "heart" @@ -10,66 +10,3 @@ requires_ntnet = 1 network_destination = "crew lifesigns monitoring system" size = 11 - - - - - -/datum/nano_module/crew_monitor - name = "Crew monitor" - -/datum/nano_module/crew_monitor/Topic(href, href_list) - if(..()) return 1 - var/turf/T = get_turf(nano_host()) // TODO: Allow setting any GLOB.using_map.contact_levels from the interface. - if (!T || !(T.z in GLOB.using_map.player_levels)) - to_chat(usr, "Unable to establish a connection: You're too far away from the station!") - return 0 - if(href_list["track"]) - if(isAI(usr)) - var/mob/living/silicon/ai/AI = usr - var/mob/living/carbon/human/H = locate(href_list["track"]) in mob_list - if(hassensorlevel(H, SUIT_SENSOR_TRACKING)) - AI.ai_actual_track(H) - return 1 - -/datum/nano_module/crew_monitor/nano_ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state) - var/list/data = host.initial_data() - var/turf/T = get_turf(nano_host()) - - data["isAI"] = isAI(user) - data["map_levels"] = GLOB.using_map.get_map_levels(T.z, FALSE) - data["crewmembers"] = list() - for(var/z in data["map_levels"]) // VOREStation Edit - data["crewmembers"] += crew_repository.health_data(z) - - if(!data["map_levels"].len) - to_chat(user, "The crew monitor doesn't seem like it'll work here.") - if(ui) // VOREStation Addition - ui.close() // VOREStation Addition - return - - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) - if(!ui) - ui = new(user, src, ui_key, "crew_monitor.tmpl", "Crew Monitoring Computer", 900, 800, state = state) - - // adding a template with the key "mapContent" enables the map ui functionality - ui.add_template("mapContent", "crew_monitor_map_content.tmpl") - // adding a template with the key "mapHeader" replaces the map header content - ui.add_template("mapHeader", "crew_monitor_map_header.tmpl") - if(!(ui.map_z_level in data["map_levels"])) - ui.set_map_z_level(data["map_levels"][1]) - - ui.set_initial_data(data) - ui.open() - - // should make the UI auto-update; doesn't seem to? - ui.set_auto_update(1) - -/*/datum/nano_module/crew_monitor/proc/scan() - for(var/mob/living/carbon/human/H in mob_list) - if(istype(H.w_uniform, /obj/item/clothing/under)) - var/obj/item/clothing/under/C = H.w_uniform - if (C.has_sensor) - tracked |= C - return 1 -*/ diff --git a/code/modules/nifsoft/software/06_screens.dm b/code/modules/nifsoft/software/06_screens.dm index 787430008f1..487745f9efd 100644 --- a/code/modules/nifsoft/software/06_screens.dm +++ b/code/modules/nifsoft/software/06_screens.dm @@ -5,23 +5,23 @@ access = access_medical cost = 625 p_drain = 0.025 - var/datum/nano_module/crew_monitor/arscreen + var/datum/tgui_module/crew_monitor/nif/arscreen - New() - ..() - arscreen = new(nif) +/datum/nifsoft/crewmonitor/New() + ..() + arscreen = new(nif) - Destroy() +/datum/nifsoft/crewmonitor/Destroy() QDEL_NULL(arscreen) return ..() - activate() - if((. = ..())) - arscreen.nano_ui_interact(nif.human,"main",null,1,nif_state) - return TRUE +/datum/nifsoft/crewmonitor/activate() + if((. = ..())) + arscreen.ui_interact(nif.human) + return TRUE - stat_text() - return "Show Monitor" +/datum/nifsoft/crewmonitor/stat_text() + return "Show Monitor" /datum/nifsoft/alarmmonitor name = "Alarm Monitor" @@ -32,18 +32,18 @@ p_drain = 0.025 var/datum/nano_module/alarm_monitor/engineering/arscreen - New() - ..() - arscreen = new(nif) +/datum/nifsoft/alarmmonitor/New() + ..() + arscreen = new(nif) - Destroy() +/datum/nifsoft/alarmmonitor/Destroy() QDEL_NULL(arscreen) return ..() - activate() - if((. = ..())) - arscreen.nano_ui_interact(nif.human,"main",null,1,nif_state) - return TRUE +/datum/nifsoft/alarmmonitor/activate() + if((. = ..())) + arscreen.nano_ui_interact(nif.human,"main",null,1,nif_state) + return TRUE - stat_text() - return "Show Monitor" +/datum/nifsoft/alarmmonitor/stat_text() + return "Show Monitor" diff --git a/code/modules/tgui/modules/_base.dm b/code/modules/tgui/modules/_base.dm new file mode 100644 index 00000000000..c85ba2751ef --- /dev/null +++ b/code/modules/tgui/modules/_base.dm @@ -0,0 +1,46 @@ +/* +TGUI MODULES + +This allows for datum-based TGUIs that can be hooked into objects. +This is useful for things such as the power monitor, which needs to exist on a physical console in the world, but also as a virtual device the AI can use + +Code is pretty much ripped verbatim from nano modules, but with un-needed stuff removed +*/ +/datum/tgui_module + var/name + var/datum/host + var/list/using_access + + var/tgui_id + +/datum/tgui_module/New(var/host) + src.host = host + +/datum/tgui_module/ui_host() + return host ? host : src + +/datum/tgui_module/ui_close(mob/user) + if(host) + host.ui_close(user) + +/datum/tgui_module/proc/check_access(mob/user, access) + if(!access) + return 1 + + if(using_access) + if(access in using_access) + return 1 + else + return 0 + + if(!istype(user)) + return 0 + + var/obj/item/card/id/I = user.GetIdCard() + if(!I) + return 0 + + if(access in I.access) + return 1 + + return 0 \ No newline at end of file diff --git a/code/modules/tgui/modules/camera.dm b/code/modules/tgui/modules/camera.dm new file mode 100644 index 00000000000..72521648513 --- /dev/null +++ b/code/modules/tgui/modules/camera.dm @@ -0,0 +1,295 @@ +/datum/tgui_module/camera + name = "Security Cameras" + tgui_id = "CameraConsole" + + var/access_based = FALSE + var/list/network = list() + var/list/additional_networks = list() + + var/obj/machinery/camera/active_camera + var/list/concurrent_users = list() + + // Stuff needed to render the map + var/map_name + var/const/default_map_size = 15 + var/obj/screen/map_view/cam_screen + /// All the plane masters that need to be applied. + var/list/cam_plane_masters + var/obj/screen/background/cam_background + var/obj/screen/background/cam_foreground + var/obj/screen/skybox/local_skybox + // Needed for moving camera support + var/camera_diff_x = -1 + var/camera_diff_y = -1 + var/camera_diff_z = -1 + +/datum/tgui_module/camera/New(host, list/network_computer) + . = ..() + if(!LAZYLEN(network_computer)) + access_based = TRUE + else + network = network_computer + map_name = "camera_console_[REF(src)]_map" + // 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(/obj/screen/plane_master)) + var/obj/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 + + local_skybox = new() + local_skybox.assigned_map = map_name + local_skybox.del_on_map_removal = FALSE + local_skybox.screen_loc = "[map_name]:CENTER,CENTER" + cam_plane_masters += local_skybox + + cam_background = new + cam_background.assigned_map = map_name + cam_background.del_on_map_removal = FALSE + + var/mutable_appearance/scanlines = mutable_appearance('icons/effects/static.dmi', "scanlines") + scanlines.alpha = 50 + scanlines.layer = FULLSCREEN_LAYER + + var/mutable_appearance/noise = mutable_appearance('icons/effects/static.dmi', "1 light") + noise.layer = FULLSCREEN_LAYER + + cam_foreground = new + cam_foreground.assigned_map = map_name + cam_foreground.del_on_map_removal = FALSE + cam_foreground.plane = PLANE_FULLSCREEN + cam_foreground.add_overlay(scanlines) + cam_foreground.add_overlay(noise) + +/datum/tgui_module/camera/Destroy() + qdel(cam_screen) + QDEL_LIST(cam_plane_masters) + qdel(cam_background) + qdel(cam_foreground) + return ..() + +/datum/tgui_module/camera/ui_interact(mob/user, datum/tgui/ui = null) + // 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() + 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(ui_host(), 'sound/machines/terminal_on.ogg', 25, FALSE) + // 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) + user.client.register_map_obj(cam_foreground) + // Open UI + ui = new(user, src, tgui_id, name) + ui.open() + +/datum/tgui_module/camera/ui_data() + var/list/data = list() + data["activeCamera"] = null + if(active_camera) + differential_check() + data["activeCamera"] = list( + name = active_camera.c_tag, + status = active_camera.status, + ) + return data + +/datum/tgui_module/camera/ui_static_data(mob/user) + var/list/data = list() + data["mapRef"] = map_name + var/list/cameras = get_available_cameras(user) + data["cameras"] = list() + data["allNetworks"] = list() + for(var/i in cameras) + var/obj/machinery/camera/C = cameras[i] + data["cameras"] += list(list( + name = C.c_tag, + networks = C.network + )) + data["allNetworks"] |= C.network + return data + +/datum/tgui_module/camera/ui_act(action, params) + if(..()) + return + + if(action == "switch_camera") + var/c_tag = params["name"] + var/list/cameras = get_available_cameras(usr) + var/obj/machinery/camera/C = cameras["[ckey(c_tag)]"] + active_camera = C + playsound(ui_host(), get_sfx("terminal_type"), 25, FALSE) + + reload_cameraview() + + return TRUE + +/datum/tgui_module/camera/proc/differential_check() + var/turf/T = get_turf(active_camera) + if(T) + var/new_x = T.x + var/new_y = T.y + var/new_z = T.z + if((new_x != camera_diff_x) || (new_y != camera_diff_y) || (new_z != camera_diff_z)) + reload_cameraview() + +/datum/tgui_module/camera/proc/reload_cameraview() + // Show static if can't use the camera + if(!active_camera?.can_use()) + show_camera_static() + return TRUE + + var/turf/camTurf = get_turf(active_camera) + + camera_diff_x = camTurf.x + camera_diff_y = camTurf.y + camera_diff_z = camTurf.z + + var/list/visible_turfs = list() + for(var/turf/T in (active_camera.isXRay() \ + ? range(active_camera.view_range, camTurf) \ + : view(active_camera.view_range, camTurf))) + 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 + + cam_screen.vis_contents = visible_turfs + cam_background.icon_state = "clear" + cam_background.fill_rect(1, 1, size_x, size_y) + + cam_foreground.fill_rect(1, 1, size_x, size_y) + + local_skybox.cut_overlays() + local_skybox.add_overlay(SSskybox.get_skybox(get_z(camTurf))) + local_skybox.scale_to_view(size_x) + local_skybox.set_position("CENTER", "CENTER", (world.maxx>>1) - camTurf.x, (world.maxy>>1) - camTurf.y) + +// Returns the list of cameras accessible from this computer +// This proc operates in two distinct ways depending on the context in which the module is created. +// It can either return a list of cameras sharing the same the internal `network` variable, or +// It can scan all station networks and determine what cameras to show based on the access of the user. +/datum/tgui_module/camera/proc/get_available_cameras(mob/user) + var/list/all_networks = list() + // Access Based + if(access_based) + for(var/network in GLOB.using_map.station_networks) + if(can_access_network(user, get_camera_access(network), 1)) + all_networks.Add(network) + for(var/network in GLOB.using_map.secondary_networks) + if(can_access_network(user, get_camera_access(network), 0)) + all_networks.Add(network) + // Network Based + else + all_networks = network.Copy() + + if(additional_networks) + all_networks += additional_networks + + var/list/D = list() + for(var/obj/machinery/camera/C in cameranet.cameras) + 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 & all_networks + if(tempnetwork.len) + D["[ckey(C.c_tag)]"] = C + return D + +/datum/tgui_module/camera/proc/can_access_network(mob/user, network_access, station_network = 0) + // No access passed, or 0 which is considered no access requirement. Allow it. + if(!network_access) + return 1 + + if(station_network) + return check_access(user, network_access) || check_access(user, access_security) || check_access(user, access_heads) + else + return check_access(user, network_access) + +/datum/tgui_module/camera/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) + local_skybox.cut_overlays() + +/datum/tgui_module/camera/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 + if(user.client) + user.client.clear_map(map_name) + // Turn off the console + if(length(concurrent_users) == 0 && is_living) + active_camera = null + playsound(ui_host(), 'sound/machines/terminal_off.ogg', 25, FALSE) + +// NTOS Version +// Please note, this isn't a very good replacement for converting modular computers 100% to TGUI +// If/when that is done, just move all the PC_ specific data and stuff to the modular computers themselves +// instead of copying this approach here. +/datum/tgui_module/camera/ntos + tgui_id = "NtosCameraConsole" + +/datum/tgui_module/camera/ntos/ui_state() + return GLOB.ntos_state + +/datum/tgui_module/camera/ntos/ui_static_data() + . = ..() + + var/datum/computer_file/program/host = ui_host() + if(istype(host) && host.computer) + . += host.computer.get_header_data() + +/datum/tgui_module/camera/ntos/ui_act(action, params) + if(..()) + return + + var/datum/computer_file/program/host = ui_host() + if(istype(host) && host.computer) + if(action == "PC_exit") + host.computer.kill_program() + return TRUE + if(action == "PC_shutdown") + host.computer.shutdown_computer() + return TRUE + if(action == "PC_minimize") + host.computer.minimize_program(usr) + return TRUE + +// ERT Version provides some additional networks. +/datum/tgui_module/camera/ntos/ert + additional_networks = list(NETWORK_ERT, NETWORK_CRESCENT) + +// Hacked version also provides some additional networks, +// but we want it to show *all* the networks 24/7, so we convert it into a non-access-based UI. +/datum/tgui_module/camera/ntos/hacked + additional_networks = list(NETWORK_MERCENARY, NETWORK_ERT, NETWORK_CRESCENT) + +/datum/tgui_module/camera/ntos/hacked/New(host) + . = ..(host, GLOB.using_map.station_networks.Copy()) diff --git a/code/modules/tgui/modules/crew_monitor.dm b/code/modules/tgui/modules/crew_monitor.dm new file mode 100644 index 00000000000..f9684b7f70a --- /dev/null +++ b/code/modules/tgui/modules/crew_monitor.dm @@ -0,0 +1,103 @@ +/datum/tgui_module/crew_monitor + name = "Crew monitor" + tgui_id = "CrewMonitor" + +/datum/tgui_module/crew_monitor/ui_act(action, params, datum/tgui/ui) + if(..()) + return TRUE + + var/turf/T = get_turf(usr) + if(!T || !(T.z in GLOB.using_map.player_levels)) + to_chat(usr, "Unable to establish a connection: You're too far away from the station!") + return FALSE + + switch(action) + if("track") + if(isAI(usr)) + var/mob/living/silicon/ai/AI = usr + var/mob/living/carbon/human/H = locate(params["track"]) in mob_list + if(hassensorlevel(H, SUIT_SENSOR_TRACKING)) + AI.ai_actual_track(H) + return TRUE + if("setZLevel") + ui.set_map_z_level(params["mapZLevel"]) + SStgui.update_uis(src) + +/datum/tgui_module/crew_monitor/ui_interact(mob/user, datum/tgui/ui = null) + var/z = get_z(user) + var/list/map_levels = GLOB.using_map.get_map_levels(z, TRUE, om_range = DEFAULT_OVERMAP_RANGE) + + if(!map_levels.len) + to_chat(user, "The crew monitor doesn't seem like it'll work here.") + if(ui) + ui.close() + return null + + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, tgui_id, name) + ui.autoupdate = TRUE + ui.open() + + +/datum/tgui_module/crew_monitor/ui_data(mob/user, ui_key = "main", datum/tgui_state/state = GLOB.default_state) + var/data[0] + + data["isAI"] = isAI(user) + + var/z = get_z(user) + var/list/map_levels = uniqueList(GLOB.using_map.get_map_levels(z, TRUE, om_range = DEFAULT_OVERMAP_RANGE)) + data["map_levels"] = map_levels + + data["crewmembers"] = list() + for(var/zlevel in map_levels) + data["crewmembers"] += crew_repository.health_data(zlevel) + + return data + +/datum/tgui_module/crew_monitor/ntos + tgui_id = "NtosCrewMonitor" + +/datum/tgui_module/crew_monitor/ntos/ui_state(mob/user) + return GLOB.ntos_state + +/datum/tgui_module/crew_monitor/ntos/ui_static_data() + . = ..() + + var/datum/computer_file/program/host = ui_host() + if(istype(host) && host.computer) + . += host.computer.get_header_data() + +/datum/tgui_module/crew_monitor/ntos/ui_act(action, params) + if(..()) + return + + var/datum/computer_file/program/host = ui_host() + if(istype(host) && host.computer) + if(action == "PC_exit") + host.computer.kill_program() + return TRUE + if(action == "PC_shutdown") + host.computer.shutdown_computer() + return TRUE + if(action == "PC_minimize") + host.computer.minimize_program(usr) + return TRUE + +// Subtype for glasses_state +/datum/tgui_module/crew_monitor/glasses + +/datum/tgui_module/crew_monitor/glasses/ui_state(mob/user) + return GLOB.ui_glasses_state + +// Subtype for self_state +/datum/tgui_module/crew_monitor/robot + +/datum/tgui_module/crew_monitor/robot/ui_state(mob/user) + return GLOB.self_state + +// Subtype for nif_state +/datum/tgui_module/crew_monitor/nif + +/datum/tgui_module/crew_monitor/nif/ui_state(mob/user) + return GLOB.ui_nif_state diff --git a/code/modules/tgui/states/inventory.dm b/code/modules/tgui/states/inventory.dm index dc5dd0d57e6..56afbd2addd 100644 --- a/code/modules/tgui/states/inventory.dm +++ b/code/modules/tgui/states/inventory.dm @@ -14,3 +14,33 @@ GLOBAL_DATUM_INIT(inventory_state, /datum/ui_state/inventory_state, new) if(!(src_object in user)) return UI_CLOSE return user.shared_ui_interaction(src_object) + +GLOBAL_DATUM_INIT(ui_glasses_state, /datum/ui_state/glasses_state, new) + +/datum/ui_state/glasses_state/can_use_topic(var/src_object, var/mob/user) + if(ishuman(user)) + var/mob/living/carbon/human/H = user + if(H.glasses == src_object) + return user.shared_ui_interaction() + + return STATUS_CLOSE + +GLOBAL_DATUM_INIT(ui_nif_state, /datum/ui_state/nif_state, new) + +/datum/ui_state/nif_state/can_use_topic(var/src_object, var/mob/user) + if(ishuman(user)) + var/mob/living/carbon/human/H = user + if(H.nif && H.nif.stat == NIF_WORKING && src_object == H.nif) + return user.shared_ui_interaction() + + return STATUS_CLOSE + +GLOBAL_DATUM_INIT(ui_commlink_state, /datum/ui_state/commlink_state, new) + +/datum/ui_state/commlink_state/can_use_topic(var/src_object, var/mob/user) + if(ishuman(user)) + var/mob/living/carbon/human/H = user + if(H.nif && H.nif.stat == NIF_WORKING && H.nif.comm == src_object) + return user.shared_ui_interaction() + + return STATUS_CLOSE diff --git a/code/modules/tgui/states/ntos.dm b/code/modules/tgui/states/ntos.dm new file mode 100644 index 00000000000..dd29544225e --- /dev/null +++ b/code/modules/tgui/states/ntos.dm @@ -0,0 +1,15 @@ +/** + * tgui state: ntos_state + * + * Checks a number of things -- mostly physical distance for humans and view for robots. + * This is basically the same as default, except instead of src_object, it uses the computer + * it's attached to. + **/ + +GLOBAL_DATUM_INIT(ntos_state, /datum/ui_state/ntos, new) + +/datum/ui_state/ntos/can_use_topic(src_object, mob/user) + var/datum/computer_file/program/P = src_object + if(!istype(P) || !P.computer) + return FALSE + return user.default_can_use_ui_topic(P.computer) // Call the individual mob-overridden procs. \ No newline at end of file diff --git a/code/modules/tgui/tgui.dm b/code/modules/tgui/tgui.dm index 7322c4179bb..9f0eae08080 100644 --- a/code/modules/tgui/tgui.dm +++ b/code/modules/tgui/tgui.dm @@ -33,6 +33,8 @@ var/status = UI_INTERACTIVE /// Topic state used to determine status/interactability. var/datum/ui_state/state = null + // The map z-level to display. + var/map_z_level = 1 /** * public @@ -268,6 +270,15 @@ if(needs_update) window.send_message("update", get_payload()) + +/** + Sets the current map z level of the tgui window (so we know which Z we need to be interacting with.) + */ + +/datum/tgui/proc/set_map_z_level(nz) + map_z_level = nz + + /** * private * diff --git a/icons/mob/map_backgrounds.dmi b/icons/mob/map_backgrounds.dmi new file mode 100644 index 00000000000..dc6e3e46b16 Binary files /dev/null and b/icons/mob/map_backgrounds.dmi differ diff --git a/modular_citadel/code/modules/clothing/glasses/hud_vr.dm b/modular_citadel/code/modules/clothing/glasses/hud_vr.dm deleted file mode 100644 index d525035330d..00000000000 --- a/modular_citadel/code/modules/clothing/glasses/hud_vr.dm +++ /dev/null @@ -1,23 +0,0 @@ -/obj/item/clothing/glasses/omnihud - name = "\improper AR glasses" - desc = "The Nerd Glasses are a design from Nerd Co. Made in Montreal, China. For when reality just isn't cutting it, Nerd Glasses are a cut above the rest." - -/obj/item/clothing/glasses/omnihud/med - desc = "The Nurse Glasses are a design from Nerd Co. \ - These have been upgraded with medical records access and virus database integration." - -/obj/item/clothing/glasses/omnihud/sec - desc = "The Bully Glasses are a design from Nerd Co. \ - These have been upgraded with security records integration and flash protection." - -/obj/item/clothing/glasses/omnihud/eng - desc = "The Genius Glasses are a design from Nerd Co. \ - These have been upgraded with advanced electrochromic lenses to protect your eyes during welding." - -/obj/item/clothing/glasses/omnihud/rnd - desc = "The Geek Glasses are a design from Nerd Co. \ - These have been modified to fit a different frame." - -/obj/item/clothing/glasses/omnihud/all - desc = "The Super Dork Glasses are a design from Nerd Co. \ - These have been stocked with every feature from the entire Nerd Co. lineup of AR Specs. Jocks beware." diff --git a/sound/machines/terminal_alert.ogg b/sound/machines/terminal_alert.ogg new file mode 100644 index 00000000000..a790c03ebdf Binary files /dev/null and b/sound/machines/terminal_alert.ogg differ diff --git a/sound/machines/terminal_button01.ogg b/sound/machines/terminal_button01.ogg new file mode 100644 index 00000000000..88b10c88912 Binary files /dev/null and b/sound/machines/terminal_button01.ogg differ diff --git a/sound/machines/terminal_button02.ogg b/sound/machines/terminal_button02.ogg new file mode 100644 index 00000000000..8b30fd892ee Binary files /dev/null and b/sound/machines/terminal_button02.ogg differ diff --git a/sound/machines/terminal_button03.ogg b/sound/machines/terminal_button03.ogg new file mode 100644 index 00000000000..7a00168cfc3 Binary files /dev/null and b/sound/machines/terminal_button03.ogg differ diff --git a/sound/machines/terminal_button04.ogg b/sound/machines/terminal_button04.ogg new file mode 100644 index 00000000000..c56b23919cc Binary files /dev/null and b/sound/machines/terminal_button04.ogg differ diff --git a/sound/machines/terminal_button05.ogg b/sound/machines/terminal_button05.ogg new file mode 100644 index 00000000000..e660ecf154c Binary files /dev/null and b/sound/machines/terminal_button05.ogg differ diff --git a/sound/machines/terminal_button06.ogg b/sound/machines/terminal_button06.ogg new file mode 100644 index 00000000000..bef143ac521 Binary files /dev/null and b/sound/machines/terminal_button06.ogg differ diff --git a/sound/machines/terminal_button07.ogg b/sound/machines/terminal_button07.ogg new file mode 100644 index 00000000000..91a31a1156a Binary files /dev/null and b/sound/machines/terminal_button07.ogg differ diff --git a/sound/machines/terminal_button08.ogg b/sound/machines/terminal_button08.ogg new file mode 100644 index 00000000000..fc0131f5f4e Binary files /dev/null and b/sound/machines/terminal_button08.ogg differ diff --git a/sound/machines/terminal_insert_disc.ogg b/sound/machines/terminal_insert_disc.ogg new file mode 100644 index 00000000000..dd226c1ebde Binary files /dev/null and b/sound/machines/terminal_insert_disc.ogg differ diff --git a/sound/machines/terminal_off.ogg b/sound/machines/terminal_off.ogg new file mode 100644 index 00000000000..90da8d75daf Binary files /dev/null and b/sound/machines/terminal_off.ogg differ diff --git a/sound/machines/terminal_on.ogg b/sound/machines/terminal_on.ogg new file mode 100644 index 00000000000..3c69d85de54 Binary files /dev/null and b/sound/machines/terminal_on.ogg differ diff --git a/sound/machines/terminal_prompt.ogg b/sound/machines/terminal_prompt.ogg new file mode 100644 index 00000000000..74de1c9a298 Binary files /dev/null and b/sound/machines/terminal_prompt.ogg differ diff --git a/sound/machines/terminal_prompt_confirm.ogg b/sound/machines/terminal_prompt_confirm.ogg new file mode 100644 index 00000000000..897fec28e9a Binary files /dev/null and b/sound/machines/terminal_prompt_confirm.ogg differ diff --git a/sound/machines/terminal_prompt_deny.ogg b/sound/machines/terminal_prompt_deny.ogg new file mode 100644 index 00000000000..fda065f0d46 Binary files /dev/null and b/sound/machines/terminal_prompt_deny.ogg differ diff --git a/tgui/docs/migration-to-v4-from-v3.md b/tgui/docs/migration-to-v4-from-v3.md index 6f047a6cc7e..6e019c148c3 100644 --- a/tgui/docs/migration-to-v4-from-v3.md +++ b/tgui/docs/migration-to-v4-from-v3.md @@ -14,7 +14,7 @@ - `code/__HELPERS/_logging.dm` If you have a dual nano/tgui setup, then make sure to rename all ui procs -on `/datum`, such as `ui_interact` to `tgui_interact`, to avoid namespace +on `/datum`, such as `ui_interact` to `ui_interact`, to avoid namespace clashing. Usual stuff. ## Update `ui_interact` proc signatures diff --git a/tgui/packages/tgui/interfaces/CrewMonitor.js b/tgui/packages/tgui/interfaces/CrewMonitor.js new file mode 100644 index 00000000000..e5d8dff2ba8 --- /dev/null +++ b/tgui/packages/tgui/interfaces/CrewMonitor.js @@ -0,0 +1,173 @@ +import { sortBy } from 'common/collections'; +import { useBackend, useLocalState } from "../backend"; +import { Window } from "../layouts"; +import { NanoMap, Box, Table, Button, Tabs, Icon, NumberInput } from "../components"; +import { TableCell } from '../components/Table'; +import { COLORS } from '../constants.js'; +import { Fragment } from 'inferno'; + +export const CrewMonitor = () => { + return ( + + + + + + ); +}; + +export const CrewMonitorContent = (props, context) => { + const { act, data, config } = useBackend(context); + const [tabIndex, setTabIndex] = useLocalState(context, 'tabIndex', 0); + const crew = sortBy( + cm => cm.name, + )(data.crewmembers || []); + + const [ + mapZoom, + setZoom, + ] = useLocalState(context, 'number', 1); + let body; + // Data view + if (tabIndex === 0) { + body = ( + + + + Name + + + Status + + + Location + + + {crew.map(cm => ( + + + {cm.name} ({cm.assignment}) + + + + {cm.dead ? 'Deceased' : 'Living'} + + {cm.sensor_type >= 2 ? ( + + {'('} + + {cm.brute} + + {'|'} + + {cm.fire} + + {'|'} + + {cm.tox} + + {'|'} + + {cm.oxy} + + {')'} + + ) : null} + + + {cm.sensor_type === 3 ? ( + data.isAI ? ( +
+ ); + } else if (tabIndex === 1) { + body = ( + + Zoom Level: + setZoom(value)} /> + Z-Level: + {data.map_levels + .sort((a, b) => Number(a) - Number(b)) + .map(level => ( +