From fdf6761c11c3593e996851d1099a9565a16543d8 Mon Sep 17 00:00:00 2001 From: Verkister Date: Thu, 11 Feb 2021 14:57:29 +0200 Subject: [PATCH 1/2] Squashed commit of the following: commit 2c0465065974b694402a65573a0e7d4c6cf898d3 Merge: c2b6efe46c f7328a7935 Author: Verkister Date: Thu Feb 11 14:54:21 2021 +0200 Merge branch 'master' into communicator_video_enhancement commit c2b6efe46c8159ee215378364e977c34c16b1ef6 Merge: 981d50f43c 9a6def6c8a Author: Verkister Date: Wed Oct 28 15:07:20 2020 +0200 Merge branch 'master' into communicator_video_enhancement commit 981d50f43c35ee7697051b88fdacd611ce166eb1 Author: ShadowLarkens Date: Mon Sep 21 19:27:32 2020 -0700 Communicator UI Improvements + Camera Console Moving Camera Fixes --- code/__defines/_planes+layers.dm | 1 - code/_helpers/unsorted.dm | 42 +++- .../items/devices/communicator/UI_tgui.dm | 114 ++++++++++ .../devices/communicator/communicator.dm | 28 +-- .../items/devices/communicator/phone.dm | 37 +-- code/modules/tgui/modules/_base.dm | 41 ---- .../tgui/modules/appearance_changer.dm | 43 ++-- code/modules/tgui/modules/camera.dm | 56 ++--- tgui/packages/tgui/interfaces/Communicator.js | 212 +++++++++++++++++- tgui/packages/tgui/public/tgui.bundle.js | 2 +- 10 files changed, 424 insertions(+), 152 deletions(-) diff --git a/code/__defines/_planes+layers.dm b/code/__defines/_planes+layers.dm index 4e9d7fd6ad7..a2e15e6890e 100644 --- a/code/__defines/_planes+layers.dm +++ b/code/__defines/_planes+layers.dm @@ -173,6 +173,5 @@ What is the naming convention for planes or layers? plane = initial(plane) layer = initial(layer) - // Check if a mob can "logically" see an atom plane #define MOB_CAN_SEE_PLANE(M, P) (P <= PLANE_WORLD || (P in M.planes_visible)) diff --git a/code/_helpers/unsorted.dm b/code/_helpers/unsorted.dm index c759e84c02d..10fcc662f14 100644 --- a/code/_helpers/unsorted.dm +++ b/code/_helpers/unsorted.dm @@ -1616,6 +1616,46 @@ GLOBAL_REAL_VAR(list/stack_trace_storage) /proc/href(href_src, list/href_params, href_text) return "[href_text]" +// This is a helper for anything that wants to render the map in TGUI +/proc/get_tgui_plane_masters() + . = list() + // 'Utility' planes + . += new /obj/screen/plane_master/fullbright //Lighting system (lighting_overlay objects) + . += new /obj/screen/plane_master/lighting //Lighting system (but different!) + . += new /obj/screen/plane_master/ghosts //Ghosts! + . += new /obj/screen/plane_master{plane = PLANE_AI_EYE} //AI Eye! + + . += new /obj/screen/plane_master{plane = PLANE_CH_STATUS} //Status is the synth/human icon left side of medhuds + . += new /obj/screen/plane_master{plane = PLANE_CH_HEALTH} //Health bar + . += new /obj/screen/plane_master{plane = PLANE_CH_LIFE} //Alive-or-not icon + . += new /obj/screen/plane_master{plane = PLANE_CH_ID} //Job ID icon + . += new /obj/screen/plane_master{plane = PLANE_CH_WANTED} //Wanted status + . += new /obj/screen/plane_master{plane = PLANE_CH_IMPLOYAL} //Loyalty implants + . += new /obj/screen/plane_master{plane = PLANE_CH_IMPTRACK} //Tracking implants + . += new /obj/screen/plane_master{plane = PLANE_CH_IMPCHEM} //Chemical implants + . += new /obj/screen/plane_master{plane = PLANE_CH_SPECIAL} //"Special" role stuff + . += new /obj/screen/plane_master{plane = PLANE_CH_STATUS_OOC} //OOC status HUD + + . += new /obj/screen/plane_master{plane = PLANE_ADMIN1} //For admin use + . += new /obj/screen/plane_master{plane = PLANE_ADMIN2} //For admin use + . += new /obj/screen/plane_master{plane = PLANE_ADMIN3} //For admin use + + . += new /obj/screen/plane_master{plane = PLANE_MESONS} //Meson-specific things like open ceilings. + . += new /obj/screen/plane_master{plane = PLANE_BUILDMODE} //Things that only show up while in build mode + + // Real tangible stuff planes + . += new /obj/screen/plane_master/main{plane = TURF_PLANE} + . += new /obj/screen/plane_master/main{plane = OBJ_PLANE} + . += new /obj/screen/plane_master/main{plane = MOB_PLANE} + . += new /obj/screen/plane_master/cloaked //Cloaked atoms! + + //VOREStation Add - Random other plane masters + . += new /obj/screen/plane_master{plane = PLANE_CH_STATUS_R} //Right-side status icon + . += new /obj/screen/plane_master{plane = PLANE_CH_HEALTH_VR} //Health bar but transparent at 100 + . += new /obj/screen/plane_master{plane = PLANE_CH_BACKUP} //Backup implant status + . += new /obj/screen/plane_master{plane = PLANE_CH_VANTAG} //Vore Antags + . += new /obj/screen/plane_master{plane = PLANE_AUGMENTED} //Augmented reality + //VOREStation Add End /proc/CallAsync(datum/source, proctype, list/arguments) set waitfor = FALSE - return call(source, proctype)(arglist(arguments)) \ No newline at end of file + return call(source, proctype)(arglist(arguments)) diff --git a/code/game/objects/items/devices/communicator/UI_tgui.dm b/code/game/objects/items/devices/communicator/UI_tgui.dm index 8dabf77f42f..4745a024885 100644 --- a/code/game/objects/items/devices/communicator/UI_tgui.dm +++ b/code/game/objects/items/devices/communicator/UI_tgui.dm @@ -1,3 +1,103 @@ +// Etc UI-only vars +/obj/item/device/communicator + // Stuff for moving cameras + var/turf/last_camera_turf + // Stuff needed to render the map + var/map_name + var/obj/screen/map_view/cam_screen + var/list/cam_plane_masters + var/obj/screen/background/cam_background + var/obj/screen/skybox/local_skybox + +// Proc: setup_tgui_camera() +// Parameters: None +// Description: This sets up all of the variables above to handle in-UI map windows. +/obj/item/device/communicator/proc/setup_tgui_camera() + map_name = "communicator_[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 = get_tgui_plane_masters() + + for(var/plane in cam_plane_masters) + var/obj/screen/instance = plane + instance.assigned_map = map_name + instance.del_on_map_removal = FALSE + instance.screen_loc = "[map_name]:CENTER" + + 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 + +// Proc: update_active_camera_screen() +// Parameters: None +// Description: This refreshes the camera location +/obj/item/device/communicator/proc/update_active_camera_screen() + if(!video_source?.can_use()) + show_static() + return + + var/newturf = get_turf(video_source) + if(!is_on_same_plane_or_station(get_z(newturf), get_z(src))) + show_static() + return + + var/obj/item/device/communicator/communicator = video_source.loc + if(istype(communicator)) + if(communicator.selfie_mode) + var/mob/target = get(communicator, /mob) + if(istype(target)) + cam_screen.vis_contents = list(target) + else + cam_screen.vis_contents = list(communicator) + cam_background.fill_rect(1, 1, 1, 1) + cam_background.icon_state = "clear" + local_skybox.cut_overlays() + return + + // 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. + if(last_camera_turf == newturf) + return + + // We get a new turf in case they've moved in the last half decisecond (it's BYOND, it might happen) + last_camera_turf = get_turf(video_source) + + if(!is_on_same_plane_or_station(get_z(last_camera_turf), get_z(src))) + show_static() + return + + var/list/visible_turfs = list() + var/list/visible_things = view(video_range, last_camera_turf) + for(var/turf/visible_turf in visible_things) + visible_turfs += visible_turf + + cam_screen.vis_contents = visible_turfs + cam_background.icon_state = "clear" + cam_background.fill_rect(1, 1, (video_range * 2), (video_range * 2)) + + local_skybox.cut_overlays() + local_skybox.add_overlay(SSskybox.get_skybox(get_z(last_camera_turf))) + local_skybox.scale_to_view(video_range * 2) + local_skybox.set_position("CENTER", "CENTER", (world.maxx>>1) - last_camera_turf.x, (world.maxy>>1) - last_camera_turf.y) + +/obj/item/device/communicator/proc/show_static() + cam_screen.vis_contents.Cut() + cam_background.icon_state = "scanline2" + cam_background.fill_rect(1, 1, (video_range * 2), (video_range * 2)) + local_skybox.cut_overlays() + // Proc: tgui_state() // Parameters: User // Description: This tells TGUI to only allow us to be interacted with while in a mob inventory. @@ -9,7 +109,15 @@ // Description: This proc handles opening the UI. It's basically just a standard stub. /obj/item/device/communicator/tgui_interact(mob/user, datum/tgui/ui, datum/tgui/parent_ui, datum/tgui_state/custom_state) ui = SStgui.try_update_ui(user, src, ui) + // Update the camera every SStgui tick in case it moves + update_active_camera_screen() if(!ui) + // 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) + // Setup UI ui = new(user, src, "Communicator", name) if(custom_state) ui.set_state(custom_state) @@ -188,6 +296,7 @@ data["target_feed"] = data["feeds"][newsfeed_channel] else data["target_feed"] = null + data["selfie_mode"] = selfie_mode return data @@ -200,6 +309,7 @@ if(data_core) data_core.get_manifest_list() data["manifest"] = PDA_Manifest + data["mapRef"] = map_name return data // Proc: tgui-act() @@ -231,6 +341,9 @@ if("toggle_ringer") ringer = !ringer + if("selfie_mode") + selfie_mode = !selfie_mode + if("add_hex") var/hex = params["add_hex"] add_to_EPv2(hex) @@ -324,3 +437,4 @@ if("newsfeed") newsfeed_channel = text2num(params["newsfeed"]) +#undef DEFAULT_MAP_SIZE \ No newline at end of file diff --git a/code/game/objects/items/devices/communicator/communicator.dm b/code/game/objects/items/devices/communicator/communicator.dm index 358ef3b61b6..46c89b0683b 100644 --- a/code/game/objects/items/devices/communicator/communicator.dm +++ b/code/game/objects/items/devices/communicator/communicator.dm @@ -28,7 +28,7 @@ var/global/list/obj/item/device/communicator/all_communicators = list() origin_tech = list(TECH_ENGINEERING = 2, TECH_MAGNET = 2, TECH_BLUESPACE = 2, TECH_DATA = 2) matter = list(DEFAULT_WALL_MATERIAL = 30,"glass" = 10) - var/video_range = 4 + var/video_range = 3 var/obj/machinery/camera/communicator/video_source // Their camera var/obj/machinery/camera/communicator/camera // Our camera @@ -72,6 +72,9 @@ var/global/list/obj/item/device/communicator/all_communicators = list() var/update_ticks = 0 var/newsfeed_channel = 0 + // If you turn this on, it changes the way communicator video works. User configurable option. + var/selfie_mode = FALSE + // Proc: New() // Parameters: None // Description: Adds the new communicator to the global list of all communicators, sorts the list, obtains a reference to the Exonet node, then tries to @@ -86,6 +89,8 @@ var/global/list/obj/item/device/communicator/all_communicators = list() camera.name = "[src] #[rand(100,999)]" camera.c_tag = camera.name + setup_tgui_camera() + //This is a pretty terrible way of doing this. addtimer(CALLBACK(src, .proc/register_to_holder), 5 SECONDS) @@ -124,9 +129,6 @@ var/global/list/obj/item/device/communicator/all_communicators = list() /obj/item/device/communicator/examine(mob/user) . = ..() - if(Adjacent(user) && video_source) - . += "It looks like it's on a video call: \[view\]" - for(var/mob/living/voice/voice in contents) . += "On the screen, you can see a image feed of [voice]." @@ -142,17 +144,6 @@ var/global/list/obj/item/device/communicator/all_communicators = list() else . += "The device doesn't appear to be transmitting any data." -// Proc: Topic() -// Parameters: href, href_list - Data from a link -// Description: Used by the above examine. -/obj/item/device/communicator/Topic(href, href_list) - if(..()) - return 1 - - if(href_list["watchvideo"]) - if(video_source) - watch_video(usr) - // Proc: emp_act() // Parameters: None // Description: Drops all calls when EMPed, so the holder can then get murdered by the antagonist. @@ -234,8 +225,6 @@ var/global/list/obj/item/device/communicator/all_communicators = list() alert_called = 0 update_icon() tgui_interact(user) - if(video_source) - watch_video(user) // Proc: MouseDrop() //Same thing PDAs do @@ -316,6 +305,11 @@ var/global/list/obj/item/device/communicator/all_communicators = list() QDEL_NULL(camera) QDEL_NULL(exonet) + last_camera_turf = null + qdel(cam_screen) + QDEL_LIST(cam_plane_masters) + qdel(cam_background) + return ..() // Proc: update_icon() diff --git a/code/game/objects/items/devices/communicator/phone.dm b/code/game/objects/items/devices/communicator/phone.dm index 7075dbe9582..bb25dce953f 100644 --- a/code/game/objects/items/devices/communicator/phone.dm +++ b/code/game/objects/items/devices/communicator/phone.dm @@ -332,12 +332,15 @@ if(video_source) //Already in a video to_chat(user, "You are already connected to a video call!") + return if(user.blinded) //User is blinded to_chat(user, "You cannot see well enough to do that!") + return if(!(src in comm.communicating) || !comm.camera) //You called someone with a broken communicator or one that's fake or yourself or something to_chat(user, "[bicon(src)]ERROR: Video failed. Either bandwidth is too low, or the other communicator is malfunctioning.") + return to_chat(user, "[bicon(src)] Attempting to start video over existing call.") sleep(30) @@ -345,42 +348,16 @@ video_source = comm.camera comm.visible_message("[bicon(src)] New video connection from [comm].") - watch_video(user) + update_active_camera_screen() + GLOB.moved_event.register(video_source, src, .proc/update_active_camera_screen) update_icon() -// Proc: watch_video() -// Parameters: user - the mob doing the viewing of video -// Description: Moves a mob's eye to the far end for the duration of viewing the far end -/obj/item/device/communicator/proc/watch_video(mob/user) - if(!Adjacent(user) || !video_source) return - user.set_machine(video_source) - user.reset_view(video_source) - to_chat(user, "Now viewing video session. To leave camera view, close the communicator window OR: OOC -> Cancel Camera View") - to_chat(user, "To return to an active video session, use the communicator in your hand.") - spawn(0) - while(user.machine == video_source && Adjacent(user)) - var/turf/T = get_turf(video_source) - if(!T || !is_on_same_plane_or_station(T.z, user.z) || !video_source.can_use()) - to_chat(user, "The screen bursts into static, then goes black.") - video_cleanup(user) - return - sleep(10) - - video_cleanup(user) - -// Proc: video_cleanup() -// Parameters: user - the mob who doesn't want to see video anymore -// Description: Cleans up mob's client when they stop watching a video -/obj/item/device/communicator/proc/video_cleanup(mob/user) - if(!user) return - - user.reset_view(null) - user.unset_machine() - // Proc: end_video() // Parameters: reason - the text reason to print for why it ended // Description: Ends the video call by clearing video_source /obj/item/device/communicator/proc/end_video(var/reason) + GLOB.moved_event.unregister(video_source, src, .proc/update_active_camera_screen) + show_static() video_source = null . = "[bicon(src)] [reason ? reason : "Video session ended"]." diff --git a/code/modules/tgui/modules/_base.dm b/code/modules/tgui/modules/_base.dm index a573ab241af..52891ab18de 100644 --- a/code/modules/tgui/modules/_base.dm +++ b/code/modules/tgui/modules/_base.dm @@ -84,46 +84,5 @@ Code is pretty much ripped verbatim from nano modules, but with un-needed stuff ui = new(user, src, tgui_id, name, parent_ui) ui.open() -// This is a helper for anything that wants to render the map. -/datum/tgui_module/proc/get_plane_masters() - . = list() - // 'Utility' planes - . += new /obj/screen/plane_master/fullbright //Lighting system (lighting_overlay objects) - . += new /obj/screen/plane_master/lighting //Lighting system (but different!) - . += new /obj/screen/plane_master/ghosts //Ghosts! - . += new /obj/screen/plane_master{plane = PLANE_AI_EYE} //AI Eye! - - . += new /obj/screen/plane_master{plane = PLANE_CH_STATUS} //Status is the synth/human icon left side of medhuds - . += new /obj/screen/plane_master{plane = PLANE_CH_HEALTH} //Health bar - . += new /obj/screen/plane_master{plane = PLANE_CH_LIFE} //Alive-or-not icon - . += new /obj/screen/plane_master{plane = PLANE_CH_ID} //Job ID icon - . += new /obj/screen/plane_master{plane = PLANE_CH_WANTED} //Wanted status - . += new /obj/screen/plane_master{plane = PLANE_CH_IMPLOYAL} //Loyalty implants - . += new /obj/screen/plane_master{plane = PLANE_CH_IMPTRACK} //Tracking implants - . += new /obj/screen/plane_master{plane = PLANE_CH_IMPCHEM} //Chemical implants - . += new /obj/screen/plane_master{plane = PLANE_CH_SPECIAL} //"Special" role stuff - . += new /obj/screen/plane_master{plane = PLANE_CH_STATUS_OOC} //OOC status HUD - - . += new /obj/screen/plane_master{plane = PLANE_ADMIN1} //For admin use - . += new /obj/screen/plane_master{plane = PLANE_ADMIN2} //For admin use - . += new /obj/screen/plane_master{plane = PLANE_ADMIN3} //For admin use - - . += new /obj/screen/plane_master{plane = PLANE_MESONS} //Meson-specific things like open ceilings. - . += new /obj/screen/plane_master{plane = PLANE_BUILDMODE} //Things that only show up while in build mode - - // Real tangible stuff planes - . += new /obj/screen/plane_master/main{plane = TURF_PLANE} - . += new /obj/screen/plane_master/main{plane = OBJ_PLANE} - . += new /obj/screen/plane_master/main{plane = MOB_PLANE} - . += new /obj/screen/plane_master/cloaked //Cloaked atoms! - - //VOREStation Add - Random other plane masters - . += new /obj/screen/plane_master{plane = PLANE_CH_STATUS_R} //Right-side status icon - . += new /obj/screen/plane_master{plane = PLANE_CH_HEALTH_VR} //Health bar but transparent at 100 - . += new /obj/screen/plane_master{plane = PLANE_CH_BACKUP} //Backup implant status - . += new /obj/screen/plane_master{plane = PLANE_CH_VANTAG} //Vore Antags - . += new /obj/screen/plane_master{plane = PLANE_AUGMENTED} //Augmented reality - //VOREStation Add End - /datum/tgui_module/proc/relaymove(mob/user, direction) return FALSE diff --git a/code/modules/tgui/modules/appearance_changer.dm b/code/modules/tgui/modules/appearance_changer.dm index ab75e1d67c0..180e63ec7df 100644 --- a/code/modules/tgui/modules/appearance_changer.dm +++ b/code/modules/tgui/modules/appearance_changer.dm @@ -20,10 +20,8 @@ var/list/cam_plane_masters var/obj/screen/background/cam_background 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 + // Stuff for moving cameras + var/turf/last_camera_turf var/list/valid_earstyles = list() var/list/valid_tailstyles = list() @@ -45,7 +43,7 @@ cam_screen.del_on_map_removal = FALSE cam_screen.screen_loc = "[map_name]:1,1" - cam_plane_masters = get_plane_masters() + cam_plane_masters = get_tgui_plane_masters() for(var/plane in cam_plane_masters) var/obj/screen/instance = plane @@ -62,14 +60,18 @@ cam_background = new cam_background.assigned_map = map_name cam_background.del_on_map_removal = FALSE - reload_cameraview() + update_active_camera_screen() owner = H + if(owner) + GLOB.moved_event.register(owner, src, .proc/update_active_camera_screen) check_whitelist = check_species_whitelist whitelist = species_whitelist blacklist = species_blacklist /datum/tgui_module/appearance_changer/Destroy() + GLOB.moved_event.unregister(owner, src, .proc/update_active_camera_screen) + last_camera_turf = null qdel(cam_screen) QDEL_LIST(cam_plane_masters) qdel(cam_background) @@ -286,8 +288,8 @@ return ui = SStgui.try_update_ui(user, src, ui) + update_active_camera_screen() if(!ui) - reload_cameraview() // Register map objects user.client.register_map_obj(cam_screen) for(var/plane in cam_plane_masters) @@ -332,7 +334,7 @@ /datum/tgui_module/appearance_changer/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state) var/list/data = ..() - differential_check() + generate_data(check_whitelist, whitelist, blacklist) var/mob/living/carbon/human/target = owner if(customize_usr) @@ -402,26 +404,15 @@ data["mapRef"] = map_name return data -/datum/tgui_module/appearance_changer/proc/differential_check() - var/turf/T = get_turf(customize_usr ? tgui_host() : owner) - 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/appearance_changer/proc/reload_cameraview() - var/turf/camTurf = get_turf(customize_usr ? tgui_host() : owner) - if(!camTurf) +/datum/tgui_module/appearance_changer/proc/update_active_camera_screen() + var/turf/newturf = get_turf(customize_usr ? tgui_host() : owner) + if(newturf == last_camera_turf) return - camera_diff_x = camTurf.x - camera_diff_y = camTurf.y - camera_diff_z = camTurf.z + last_camera_turf = newturf var/list/visible_turfs = list() - for(var/turf/T in range(1, camTurf)) + for(var/turf/T in range(1, newturf)) visible_turfs += T cam_screen.vis_contents = visible_turfs @@ -429,9 +420,9 @@ cam_background.fill_rect(1, 1, 3, 3) local_skybox.cut_overlays() - local_skybox.add_overlay(SSskybox.get_skybox(get_z(camTurf))) + local_skybox.add_overlay(SSskybox.get_skybox(get_z(newturf))) local_skybox.scale_to_view(3) - local_skybox.set_position("CENTER", "CENTER", (world.maxx>>1) - camTurf.x, (world.maxy>>1) - camTurf.y) + local_skybox.set_position("CENTER", "CENTER", (world.maxx>>1) - newturf.x, (world.maxy>>1) - newturf.y) /datum/tgui_module/appearance_changer/proc/update_dna() var/mob/living/carbon/human/target = owner diff --git a/code/modules/tgui/modules/camera.dm b/code/modules/tgui/modules/camera.dm index dbb3c7ae976..86eb923c1c5 100644 --- a/code/modules/tgui/modules/camera.dm +++ b/code/modules/tgui/modules/camera.dm @@ -18,10 +18,8 @@ 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 + // Stuff for moving cameras + var/turf/last_camera_turf /datum/tgui_module/camera/New(host, list/network_computer) . = ..() @@ -37,7 +35,7 @@ cam_screen.del_on_map_removal = FALSE cam_screen.screen_loc = "[map_name]:1,1" - cam_plane_masters = get_plane_masters() + cam_plane_masters = get_tgui_plane_masters() for(var/plane in cam_plane_masters) var/obj/screen/instance = plane @@ -70,6 +68,10 @@ cam_foreground.add_overlay(noise) /datum/tgui_module/camera/Destroy() + if(active_camera) + GLOB.moved_event.unregister(active_camera, src, .proc/update_active_camera_screen) + active_camera = null + last_camera_turf = null qdel(cam_screen) QDEL_LIST(cam_plane_masters) qdel(cam_background) @@ -106,7 +108,6 @@ var/list/data = list() data["activeCamera"] = null if(active_camera) - differential_check() data["activeCamera"] = list( name = active_camera.c_tag, status = active_camera.status, @@ -139,9 +140,12 @@ var/c_tag = params["name"] var/list/cameras = get_available_cameras(usr) var/obj/machinery/camera/C = cameras["[ckey(c_tag)]"] + if(active_camera) + GLOB.moved_event.unregister(active_camera, src, .proc/update_active_camera_screen) active_camera = C + GLOB.moved_event.register(active_camera, src, .proc/update_active_camera_screen) playsound(tgui_host(), get_sfx("terminal_type"), 25, FALSE) - reload_cameraview() + update_active_camera_screen() return TRUE if(action == "pan") @@ -163,36 +167,34 @@ target = C if(target) + if(active_camera) + GLOB.moved_event.unregister(active_camera, src, .proc/update_active_camera_screen) active_camera = target + GLOB.moved_event.register(active_camera, src, .proc/update_active_camera_screen) playsound(tgui_host(), get_sfx("terminal_type"), 25, FALSE) - reload_cameraview() + update_active_camera_screen() . = 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() +/datum/tgui_module/camera/proc/update_active_camera_screen() // 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) + // 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/turf/newturf = get_turf(active_camera) + if(newturf == last_camera_turf) + return - camera_diff_x = camTurf.x - camera_diff_y = camTurf.y - camera_diff_z = camTurf.z + // Cameras that get here are moving, and are likely attached to some moving atom such as cyborgs. + last_camera_turf = get_turf(active_camera) 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))) + ? range(active_camera.view_range, newturf) \ + : view(active_camera.view_range, newturf))) visible_turfs += T var/list/bbox = get_bbox_of_atoms(visible_turfs) @@ -206,9 +208,9 @@ 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.add_overlay(SSskybox.get_skybox(get_z(newturf))) local_skybox.scale_to_view(size_x) - local_skybox.set_position("CENTER", "CENTER", (world.maxx>>1) - camTurf.x, (world.maxy>>1) - camTurf.y) + local_skybox.set_position("CENTER", "CENTER", (world.maxx>>1) - newturf.x, (world.maxy>>1) - newturf.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. @@ -271,6 +273,8 @@ user.client.clear_map(map_name) // Turn off the console if(length(concurrent_users) == 0 && is_living) + if(active_camera) + GLOB.moved_event.unregister(active_camera, src, .proc/update_active_camera_screen) active_camera = null playsound(tgui_host(), 'sound/machines/terminal_off.ogg', 25, FALSE) diff --git a/tgui/packages/tgui/interfaces/Communicator.js b/tgui/packages/tgui/interfaces/Communicator.js index 90a52fe5784..cc566e70d59 100644 --- a/tgui/packages/tgui/interfaces/Communicator.js +++ b/tgui/packages/tgui/interfaces/Communicator.js @@ -2,7 +2,7 @@ import { filter } from 'common/collections'; import { decodeHtmlEntities, toTitleCase } from 'common/string'; import { Fragment } from 'inferno'; import { useBackend, useLocalState } from "../backend"; -import { Box, Button, Flex, Icon, LabeledList, Input, ProgressBar, Section, Table } from "../components"; +import { Box, ByondUi, Button, Flex, Icon, LabeledList, Input, ProgressBar, Section, Table } from "../components"; import { Window } from "../layouts"; import { CrewManifestContent } from './CrewManifest'; @@ -24,23 +24,151 @@ export const Communicator = (props, context) => { const { currentTab, + video_comm, + mapRef, } = data; + /* 0: Fullscreen Video + * 1: Popup Video + * 2: Minimized Video + */ + const [videoSetting, setVideoSetting] = useLocalState(context, 'videoSetting', 0); + return ( - - - {TabToTemplate[currentTab] || } - - + {video_comm && } + {(!video_comm || videoSetting !== 0) && ( + + + + {TabToTemplate[currentTab] || } + + + + )} ); }; +const VideoComm = (props, context) => { + const { act, data } = useBackend(context); + + const { + video_comm, + mapRef, + } = data; + + const { + videoSetting, + setVideoSetting, + } = props; + + if (videoSetting === 0) { + return ( + + + + +