Communicator UI Improvements + Camera Console Moving Camera Fixes (rebundled)

This commit is contained in:
Chompstation Bot
2021-02-11 13:16:12 +00:00
committed by Darlantan
parent d8f37898d3
commit 653d2b9179
11 changed files with 459 additions and 187 deletions

View File

@@ -173,6 +173,5 @@ What is the naming convention for planes or layers?
plane = initial(plane) plane = initial(plane)
layer = initial(layer) layer = initial(layer)
// Check if a mob can "logically" see an atom plane // 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)) #define MOB_CAN_SEE_PLANE(M, P) (P <= PLANE_WORLD || (P in M.planes_visible))

View File

@@ -1609,6 +1609,46 @@ GLOBAL_REAL_VAR(list/stack_trace_storage)
/proc/href(href_src, list/href_params, href_text) /proc/href(href_src, list/href_params, href_text)
return "<a href='?src=\ref[href_src];[list2params(href_params)]'>[href_text]</a>" return "<a href='?src=\ref[href_src];[list2params(href_params)]'>[href_text]</a>"
// 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) /proc/CallAsync(datum/source, proctype, list/arguments)
set waitfor = FALSE set waitfor = FALSE
return call(source, proctype)(arglist(arguments)) return call(source, proctype)(arglist(arguments))

View File

@@ -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() // Proc: tgui_state()
// Parameters: User // Parameters: User
// Description: This tells TGUI to only allow us to be interacted with while in a mob inventory. // 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. // 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) /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) ui = SStgui.try_update_ui(user, src, ui)
// Update the camera every SStgui tick in case it moves
update_active_camera_screen()
if(!ui) 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) ui = new(user, src, "Communicator", name)
if(custom_state) if(custom_state)
ui.set_state(custom_state) ui.set_state(custom_state)
@@ -188,6 +296,7 @@
data["target_feed"] = data["feeds"][newsfeed_channel] data["target_feed"] = data["feeds"][newsfeed_channel]
else else
data["target_feed"] = null data["target_feed"] = null
data["selfie_mode"] = selfie_mode
return data return data
@@ -200,6 +309,7 @@
if(data_core) if(data_core)
data_core.get_manifest_list() data_core.get_manifest_list()
data["manifest"] = PDA_Manifest data["manifest"] = PDA_Manifest
data["mapRef"] = map_name
return data return data
// Proc: tgui-act() // Proc: tgui-act()
@@ -231,6 +341,9 @@
if("toggle_ringer") if("toggle_ringer")
ringer = !ringer ringer = !ringer
if("selfie_mode")
selfie_mode = !selfie_mode
if("add_hex") if("add_hex")
var/hex = params["add_hex"] var/hex = params["add_hex"]
add_to_EPv2(hex) add_to_EPv2(hex)
@@ -324,3 +437,4 @@
if("newsfeed") if("newsfeed")
newsfeed_channel = text2num(params["newsfeed"]) newsfeed_channel = text2num(params["newsfeed"])
#undef DEFAULT_MAP_SIZE

View File

@@ -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) origin_tech = list(TECH_ENGINEERING = 2, TECH_MAGNET = 2, TECH_BLUESPACE = 2, TECH_DATA = 2)
matter = list(DEFAULT_WALL_MATERIAL = 30,"glass" = 10) 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/video_source // Their camera
var/obj/machinery/camera/communicator/camera // Our 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/update_ticks = 0
var/newsfeed_channel = 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() // Proc: New()
// Parameters: None // 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 // 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.name = "[src] #[rand(100,999)]"
camera.c_tag = camera.name camera.c_tag = camera.name
setup_tgui_camera()
//This is a pretty terrible way of doing this. //This is a pretty terrible way of doing this.
addtimer(CALLBACK(src, .proc/register_to_holder), 5 SECONDS) 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) /obj/item/device/communicator/examine(mob/user)
. = ..() . = ..()
if(Adjacent(user) && video_source)
. += "<span class='notice'>It looks like it's on a video call: <a href='?src=\ref[src];watchvideo=1'>\[view\]</a></span>"
for(var/mob/living/voice/voice in contents) for(var/mob/living/voice/voice in contents)
. += "<span class='notice'>On the screen, you can see a image feed of [voice].</span>" . += "<span class='notice'>On the screen, you can see a image feed of [voice].</span>"
@@ -142,17 +144,6 @@ var/global/list/obj/item/device/communicator/all_communicators = list()
else else
. += "<span class='notice'>The device doesn't appear to be transmitting any data.</span>" . += "<span class='notice'>The device doesn't appear to be transmitting any data.</span>"
// 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() // Proc: emp_act()
// Parameters: None // Parameters: None
// Description: Drops all calls when EMPed, so the holder can then get murdered by the antagonist. // 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 alert_called = 0
update_icon() update_icon()
tgui_interact(user) tgui_interact(user)
if(video_source)
watch_video(user)
// Proc: MouseDrop() // Proc: MouseDrop()
//Same thing PDAs do //Same thing PDAs do
@@ -316,6 +305,11 @@ var/global/list/obj/item/device/communicator/all_communicators = list()
QDEL_NULL(camera) QDEL_NULL(camera)
QDEL_NULL(exonet) QDEL_NULL(exonet)
last_camera_turf = null
qdel(cam_screen)
QDEL_LIST(cam_plane_masters)
qdel(cam_background)
return ..() return ..()
// Proc: update_icon() // Proc: update_icon()

View File

@@ -332,12 +332,15 @@
if(video_source) //Already in a video if(video_source) //Already in a video
to_chat(user, "<span class='danger'>You are already connected to a video call!</span>") to_chat(user, "<span class='danger'>You are already connected to a video call!</span>")
return
if(user.blinded) //User is blinded if(user.blinded) //User is blinded
to_chat(user, "<span class='danger'>You cannot see well enough to do that!</span>") to_chat(user, "<span class='danger'>You cannot see well enough to do that!</span>")
return
if(!(src in comm.communicating) || !comm.camera) //You called someone with a broken communicator or one that's fake or yourself or something 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, "<span class='danger'>[bicon(src)]ERROR: Video failed. Either bandwidth is too low, or the other communicator is malfunctioning.</span>") to_chat(user, "<span class='danger'>[bicon(src)]ERROR: Video failed. Either bandwidth is too low, or the other communicator is malfunctioning.</span>")
return
to_chat(user, "<span class='notice'>[bicon(src)] Attempting to start video over existing call.</span>") to_chat(user, "<span class='notice'>[bicon(src)] Attempting to start video over existing call.</span>")
sleep(30) sleep(30)
@@ -345,42 +348,16 @@
video_source = comm.camera video_source = comm.camera
comm.visible_message("<span class='danger'>[bicon(src)] New video connection from [comm].</span>") comm.visible_message("<span class='danger'>[bicon(src)] New video connection from [comm].</span>")
watch_video(user) update_active_camera_screen()
GLOB.moved_event.register(video_source, src, .proc/update_active_camera_screen)
update_icon() 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, "<span class='notice'>Now viewing video session. To leave camera view, close the communicator window OR: OOC -> Cancel Camera View</span>")
to_chat(user, "<span class='notice'>To return to an active video session, use the communicator in your hand.</span>")
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, "<span class='warning'>The screen bursts into static, then goes black.</span>")
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() // Proc: end_video()
// Parameters: reason - the text reason to print for why it ended // Parameters: reason - the text reason to print for why it ended
// Description: Ends the video call by clearing video_source // Description: Ends the video call by clearing video_source
/obj/item/device/communicator/proc/end_video(var/reason) /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 video_source = null
. = "<span class='danger'>[bicon(src)] [reason ? reason : "Video session ended"].</span>" . = "<span class='danger'>[bicon(src)] [reason ? reason : "Video session ended"].</span>"

View File

@@ -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 = new(user, src, tgui_id, name, parent_ui)
ui.open() 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) /datum/tgui_module/proc/relaymove(mob/user, direction)
return FALSE return FALSE

View File

@@ -20,10 +20,8 @@
var/list/cam_plane_masters var/list/cam_plane_masters
var/obj/screen/background/cam_background var/obj/screen/background/cam_background
var/obj/screen/skybox/local_skybox var/obj/screen/skybox/local_skybox
// Needed for moving camera support // Stuff for moving cameras
var/camera_diff_x = -1 var/turf/last_camera_turf
var/camera_diff_y = -1
var/camera_diff_z = -1
var/list/valid_earstyles = list() var/list/valid_earstyles = list()
var/list/valid_tailstyles = list() var/list/valid_tailstyles = list()
@@ -45,7 +43,7 @@
cam_screen.del_on_map_removal = FALSE cam_screen.del_on_map_removal = FALSE
cam_screen.screen_loc = "[map_name]:1,1" 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) for(var/plane in cam_plane_masters)
var/obj/screen/instance = plane var/obj/screen/instance = plane
@@ -62,14 +60,18 @@
cam_background = new cam_background = new
cam_background.assigned_map = map_name cam_background.assigned_map = map_name
cam_background.del_on_map_removal = FALSE cam_background.del_on_map_removal = FALSE
reload_cameraview() update_active_camera_screen()
owner = H owner = H
if(owner)
GLOB.moved_event.register(owner, src, .proc/update_active_camera_screen)
check_whitelist = check_species_whitelist check_whitelist = check_species_whitelist
whitelist = species_whitelist whitelist = species_whitelist
blacklist = species_blacklist blacklist = species_blacklist
/datum/tgui_module/appearance_changer/Destroy() /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(cam_screen)
QDEL_LIST(cam_plane_masters) QDEL_LIST(cam_plane_masters)
qdel(cam_background) qdel(cam_background)
@@ -286,8 +288,8 @@
return return
ui = SStgui.try_update_ui(user, src, ui) ui = SStgui.try_update_ui(user, src, ui)
update_active_camera_screen()
if(!ui) if(!ui)
reload_cameraview()
// Register map objects // Register map objects
user.client.register_map_obj(cam_screen) user.client.register_map_obj(cam_screen)
for(var/plane in cam_plane_masters) 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) /datum/tgui_module/appearance_changer/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state)
var/list/data = ..() var/list/data = ..()
differential_check() generate_data(check_whitelist, whitelist, blacklist)
var/mob/living/carbon/human/target = owner var/mob/living/carbon/human/target = owner
if(customize_usr) if(customize_usr)
@@ -402,26 +404,15 @@
data["mapRef"] = map_name data["mapRef"] = map_name
return data return data
/datum/tgui_module/appearance_changer/proc/differential_check() /datum/tgui_module/appearance_changer/proc/update_active_camera_screen()
var/turf/T = get_turf(customize_usr ? tgui_host() : owner) var/turf/newturf = get_turf(customize_usr ? tgui_host() : owner)
if(T) if(newturf == last_camera_turf)
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)
return return
camera_diff_x = camTurf.x last_camera_turf = newturf
camera_diff_y = camTurf.y
camera_diff_z = camTurf.z
var/list/visible_turfs = list() var/list/visible_turfs = list()
for(var/turf/T in range(1, camTurf)) for(var/turf/T in range(1, newturf))
visible_turfs += T visible_turfs += T
cam_screen.vis_contents = visible_turfs cam_screen.vis_contents = visible_turfs
@@ -429,9 +420,9 @@
cam_background.fill_rect(1, 1, 3, 3) cam_background.fill_rect(1, 1, 3, 3)
local_skybox.cut_overlays() 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.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() /datum/tgui_module/appearance_changer/proc/update_dna()
var/mob/living/carbon/human/target = owner var/mob/living/carbon/human/target = owner

View File

@@ -10,7 +10,7 @@
return STATUS_CLOSE return STATUS_CLOSE
return ..() return ..()
/datum/tgui_module/appearance_changer/vore/reload_cameraview() /datum/tgui_module/appearance_changer/vore/update_active_camera_screen()
cam_screen.vis_contents = list(owner) cam_screen.vis_contents = list(owner)
cam_background.icon_state = "clear" cam_background.icon_state = "clear"
cam_background.fill_rect(1, 1, 1, 1) cam_background.fill_rect(1, 1, 1, 1)

View File

@@ -18,10 +18,8 @@
var/obj/screen/background/cam_background var/obj/screen/background/cam_background
var/obj/screen/background/cam_foreground var/obj/screen/background/cam_foreground
var/obj/screen/skybox/local_skybox var/obj/screen/skybox/local_skybox
// Needed for moving camera support // Stuff for moving cameras
var/camera_diff_x = -1 var/turf/last_camera_turf
var/camera_diff_y = -1
var/camera_diff_z = -1
/datum/tgui_module/camera/New(host, list/network_computer) /datum/tgui_module/camera/New(host, list/network_computer)
. = ..() . = ..()
@@ -37,7 +35,7 @@
cam_screen.del_on_map_removal = FALSE cam_screen.del_on_map_removal = FALSE
cam_screen.screen_loc = "[map_name]:1,1" 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) for(var/plane in cam_plane_masters)
var/obj/screen/instance = plane var/obj/screen/instance = plane
@@ -70,6 +68,10 @@
cam_foreground.add_overlay(noise) cam_foreground.add_overlay(noise)
/datum/tgui_module/camera/Destroy() /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(cam_screen)
QDEL_LIST(cam_plane_masters) QDEL_LIST(cam_plane_masters)
qdel(cam_background) qdel(cam_background)
@@ -106,7 +108,6 @@
var/list/data = list() var/list/data = list()
data["activeCamera"] = null data["activeCamera"] = null
if(active_camera) if(active_camera)
differential_check()
data["activeCamera"] = list( data["activeCamera"] = list(
name = active_camera.c_tag, name = active_camera.c_tag,
status = active_camera.status, status = active_camera.status,
@@ -139,9 +140,12 @@
var/c_tag = params["name"] var/c_tag = params["name"]
var/list/cameras = get_available_cameras(usr) var/list/cameras = get_available_cameras(usr)
var/obj/machinery/camera/C = cameras["[ckey(c_tag)]"] 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 active_camera = C
GLOB.moved_event.register(active_camera, src, .proc/update_active_camera_screen)
playsound(tgui_host(), get_sfx("terminal_type"), 25, FALSE) playsound(tgui_host(), get_sfx("terminal_type"), 25, FALSE)
reload_cameraview() update_active_camera_screen()
return TRUE return TRUE
if(action == "pan") if(action == "pan")
@@ -163,36 +167,34 @@
target = C target = C
if(target) if(target)
if(active_camera)
GLOB.moved_event.unregister(active_camera, src, .proc/update_active_camera_screen)
active_camera = target active_camera = target
GLOB.moved_event.register(active_camera, src, .proc/update_active_camera_screen)
playsound(tgui_host(), get_sfx("terminal_type"), 25, FALSE) playsound(tgui_host(), get_sfx("terminal_type"), 25, FALSE)
reload_cameraview() update_active_camera_screen()
. = TRUE . = TRUE
/datum/tgui_module/camera/proc/differential_check() /datum/tgui_module/camera/proc/update_active_camera_screen()
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 // Show static if can't use the camera
if(!active_camera?.can_use()) if(!active_camera?.can_use())
show_camera_static() show_camera_static()
return TRUE 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 // Cameras that get here are moving, and are likely attached to some moving atom such as cyborgs.
camera_diff_y = camTurf.y last_camera_turf = get_turf(active_camera)
camera_diff_z = camTurf.z
var/list/visible_turfs = list() var/list/visible_turfs = list()
for(var/turf/T in (active_camera.isXRay() \ for(var/turf/T in (active_camera.isXRay() \
? range(active_camera.view_range, camTurf) \ ? range(active_camera.view_range, newturf) \
: view(active_camera.view_range, camTurf))) : view(active_camera.view_range, newturf)))
visible_turfs += T visible_turfs += T
var/list/bbox = get_bbox_of_atoms(visible_turfs) var/list/bbox = get_bbox_of_atoms(visible_turfs)
@@ -206,9 +208,9 @@
cam_foreground.fill_rect(1, 1, size_x, size_y) cam_foreground.fill_rect(1, 1, size_x, size_y)
local_skybox.cut_overlays() 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.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 // 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. // 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) user.client.clear_map(map_name)
// Turn off the console // Turn off the console
if(length(concurrent_users) == 0 && is_living) 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 active_camera = null
playsound(tgui_host(), 'sound/machines/terminal_off.ogg', 25, FALSE) playsound(tgui_host(), 'sound/machines/terminal_off.ogg', 25, FALSE)

View File

@@ -2,7 +2,7 @@ import { filter } from 'common/collections';
import { decodeHtmlEntities, toTitleCase } from 'common/string'; import { decodeHtmlEntities, toTitleCase } from 'common/string';
import { Fragment } from 'inferno'; import { Fragment } from 'inferno';
import { useBackend, useLocalState } from "../backend"; 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 { Window } from "../layouts";
import { CrewManifestContent } from './CrewManifest'; import { CrewManifestContent } from './CrewManifest';
@@ -24,23 +24,151 @@ export const Communicator = (props, context) => {
const { const {
currentTab, currentTab,
video_comm,
mapRef,
} = data; } = data;
/* 0: Fullscreen Video
* 1: Popup Video
* 2: Minimized Video
*/
const [videoSetting, setVideoSetting] = useLocalState(context, 'videoSetting', 0);
return ( return (
<Window width={475} height={700} resizable> <Window width={475} height={700} resizable>
<Window.Content> <Window.Content>
<CommunicatorHeader /> {video_comm && <VideoComm videoSetting={videoSetting} setVideoSetting={setVideoSetting} />}
<Box height="88%" mb={1} style={{ {(!video_comm || videoSetting !== 0) && (
"overflow-y": "auto", <Fragment>
}}> <CommunicatorHeader />
{TabToTemplate[currentTab] || <TemplateError />} <Box height="88%" mb={1} style={{
</Box> "overflow-y": "auto",
<CommunicatorFooter /> }}>
{TabToTemplate[currentTab] || <TemplateError />}
</Box>
<CommunicatorFooter
videoSetting={videoSetting}
setVideoSetting={setVideoSetting} />
</Fragment>
)}
</Window.Content> </Window.Content>
</Window> </Window>
); );
}; };
const VideoComm = (props, context) => {
const { act, data } = useBackend(context);
const {
video_comm,
mapRef,
} = data;
const {
videoSetting,
setVideoSetting,
} = props;
if (videoSetting === 0) {
return (
<Box width="100%" height="100%">
<ByondUi
width="100%"
height="95%"
params={{
id: mapRef,
type: 'map',
}} />
<Flex justify="space-between" spacing={1} mt={0.5}>
<Flex.Item grow={1}>
<Button
textAlign="center"
fluid
fontSize={1.5}
icon="window-minimize"
onClick={() => setVideoSetting(1)} />
</Flex.Item>
<Flex.Item grow={1}>
<Button
textAlign="center"
fluid
fontSize={1.5}
color="bad"
icon="video-slash"
onClick={() => act("endvideo")} />
</Flex.Item>
<Flex.Item grow={1}>
<Button
textAlign="center"
fluid
fontSize={1.5}
color="bad"
icon="phone-slash"
onClick={() => act("hang_up")} />
</Flex.Item>
</Flex>
</Box>
);
} else if (videoSetting === 1) {
return (
<Box
style={{
"position": "absolute",
"right": "5px",
"bottom": "50px",
"z-index": 1,
}}>
<Section p={0} m={0}>
<Flex justify="space-between" spacing={1}>
<Flex.Item grow={1}>
<Button
textAlign="center"
fluid
fontSize={1.5}
icon="window-minimize"
onClick={() => setVideoSetting(2)} />
</Flex.Item>
<Flex.Item grow={1}>
<Button
textAlign="center"
fluid
fontSize={1.5}
icon="window-maximize"
onClick={() => setVideoSetting(0)} />
</Flex.Item>
<Flex.Item grow={1}>
<Button
textAlign="center"
fluid
fontSize={1.5}
color="bad"
icon="video-slash"
onClick={() => act("endvideo")} />
</Flex.Item>
<Flex.Item grow={1}>
<Button
textAlign="center"
fluid
fontSize={1.5}
color="bad"
icon="phone-slash"
onClick={() => act("hang_up")} />
</Flex.Item>
</Flex>
</Section>
<ByondUi
width="200px"
height="200px"
params={{
id: mapRef,
type: 'map',
}} />
</Box>
);
}
return null;
};
const TemplateError = (props, context) => { const TemplateError = (props, context) => {
const { act, data } = useBackend(context); const { act, data } = useBackend(context);
@@ -95,9 +223,14 @@ const CommunicatorFooter = (props, context) => {
flashlight, flashlight,
} = data; } = data;
const {
videoSetting,
setVideoSetting,
} = props;
return ( return (
<Flex> <Flex>
<Flex.Item basis="80%"> <Flex.Item basis={videoSetting === 2 ? "60%" : "80%"}>
<Button <Button
p={1} p={1}
fluid fluid
@@ -118,10 +251,45 @@ const CommunicatorFooter = (props, context) => {
tooltipPosition="top" tooltipPosition="top"
onClick={() => act("Light")} /> onClick={() => act("Light")} />
</Flex.Item> </Flex.Item>
{videoSetting === 2 && (
<Flex.Item basis="20%">
<Button
icon="video"
iconSize={2}
p={1}
fluid
textAlign="center"
tooltip="Open Video"
tooltipPosition="top"
onClick={() => setVideoSetting(1)} />
</Flex.Item>
)}
</Flex> </Flex>
); );
}; };
/* Helper for notifications (yes this is a mess, but whatever, it works) */
const hasNotifications = (app, context) => {
const { data } = useBackend(context);
const {
/* Phone Notifications */
voice_mobs,
communicating,
requestsReceived,
invitesSent,
video_comm,
} = data;
if (app === "Phone") {
if (voice_mobs.length || communicating.length || requestsReceived.length || invitesSent.length || video_comm) {
return true;
}
}
return false;
};
/* Tabs Below this point */ /* Tabs Below this point */
/* Home tab, provides access to all other tabs. */ /* Home tab, provides access to all other tabs. */
@@ -146,6 +314,8 @@ const HomeTab = (props, context) => {
position="relative" position="relative"
onClick={() => act("switch_tab", { switch_tab: app.number })}> onClick={() => act("switch_tab", { switch_tab: app.number })}>
<Icon <Icon
spin={hasNotifications(app.module, context)}
color={hasNotifications(app.module, context) ? "bad" : null}
name={app.icon} name={app.icon}
position="absolute" position="absolute"
size={3} size={3}
@@ -174,6 +344,7 @@ const PhoneTab = (props, context) => {
requestsReceived, requestsReceived,
invitesSent, invitesSent,
video_comm, video_comm,
selfie_mode,
} = data; } = data;
return ( return (
@@ -197,6 +368,18 @@ const PhoneTab = (props, context) => {
</LabeledList> </LabeledList>
<NumberPad /> <NumberPad />
<Section title="Connection Management" level={2} mt={2}> <Section title="Connection Management" level={2} mt={2}>
<LabeledList>
<LabeledList.Item label="Camera Mode">
<Button
fluid
content={
selfie_mode
? "Front-facing Camera"
: "Rear-facing Camera"
}
onClick={() => act("selfie_mode")} />
</LabeledList.Item>
</LabeledList>
<Section title="External Connections" level={3}> <Section title="External Connections" level={3}>
{!!voice_mobs.length && ( {!!voice_mobs.length && (
<LabeledList> <LabeledList>
@@ -891,6 +1074,7 @@ const SettingsTab = (props, context) => {
address, address,
visible, visible,
ring, ring,
selfie_mode,
} = data; } = data;
return ( return (
@@ -903,6 +1087,16 @@ const SettingsTab = (props, context) => {
content={decodeHtmlEntities(owner)} content={decodeHtmlEntities(owner)}
onClick={() => act("rename")} /> onClick={() => act("rename")} />
</LabeledList.Item> </LabeledList.Item>
<LabeledList.Item label="Camera Mode">
<Button
fluid
content={
selfie_mode
? "Front-facing Camera"
: "Rear-facing Camera"
}
onClick={() => act("selfie_mode")} />
</LabeledList.Item>
<LabeledList.Item label="Occupation"> <LabeledList.Item label="Occupation">
{decodeHtmlEntities(occupation)} {decodeHtmlEntities(occupation)}
</LabeledList.Item> </LabeledList.Item>