Adds actual video calls to communicators

'chu heard me. S'what it does. When you're on a call, there's a "Start Video" button next to each person. When you click it, you actually look through their thing, like a ghost might if they ghostcalled. You have to stay within 1 tile of a video showing communicator to see it.

Multiple people can share one communicator video stream by setting it on a table or whatever, and examining it. There's a message like "It appears to be showing a video: [view]" and you can click view and you can all look at the video. COLLABORATION. SYNERGY. OTHER BUZZWORDS.

I also added the feature of DECLINING requests from people.

Also communicators show up on a special camera list on security consoles, but the same EPv2 network visibility turns this off as well if you wanna be all hidey. This does mean that people with the visiblity on serve as sort of roaming AI cameras for the AI as well. So the AI can watch you repair the outside of the station or whatever if you want.
This commit is contained in:
Arokha Sieyes
2016-06-13 02:55:05 -04:00
parent 36fd6fb4d6
commit b624e7703a
7 changed files with 192 additions and 23 deletions

View File

@@ -50,6 +50,7 @@ var/global/defer_powernet_rebuild = 0 // True if net rebuild will be called
#define NETWORK_SECURITY "Security"
#define NETWORK_TELECOM "Tcomsat"
#define NETWORK_THUNDER "Thunderdome"
#define NETWORK_COMMUNICATORS "Communicators"
// Those networks can only be accessed by pre-existing terminals. AIs and new terminals can't use them.
var/list/restricted_camera_networks = list(NETWORK_ERT,NETWORK_MERCENARY,"Secret")

View File

@@ -35,10 +35,13 @@
var/affected_by_emp_until = 0
var/client_huds = list()
/obj/machinery/camera/New()
wires = new(src)
assembly = new(src)
assembly.state = 4
client_huds |= global_hud.whitense
/* // Use this to look for cameras that have the same c_tag.
for(var/obj/machinery/camera/C in cameranet.cameras)

View File

@@ -17,7 +17,8 @@ var/global/list/station_networks = list(
NETWORK_RESEARCH_OUTPOST,
NETWORK_ROBOTS,
NETWORK_PRISON,
NETWORK_SECURITY
NETWORK_SECURITY,
NETWORK_COMMUNICATORS
)
var/global/list/engineering_networks = list(
NETWORK_ENGINE,

View File

@@ -18,6 +18,8 @@ var/global/list/obj/item/device/communicator/all_communicators = list()
matter = list(DEFAULT_WALL_MATERIAL = 30,"glass" = 10)
var/video_range = 4
var/obj/machinery/camera/communicator/video_source // Their camera
var/obj/machinery/camera/communicator/camera // Our camera
var/list/voice_mobs = list()
var/list/voice_requests = list()
@@ -63,6 +65,9 @@ var/global/list/obj/item/device/communicator/all_communicators = list()
all_communicators = sortAtom(all_communicators)
node = get_exonet_node()
processing_objects |= src
camera = new(src)
camera.name = "[src] #[rand(100,999)]"
camera.c_tag = camera.name
//This is a pretty terrible way of doing this.
spawn(5 SECONDS) //Wait for our mob to finish spawning.
if(ismob(loc))
@@ -74,6 +79,14 @@ var/global/list/obj/item/device/communicator/all_communicators = list()
register_device(S.loc)
initialize_exonet(S.loc)
// Proc: examine()
// Parameters: user - the user doing the examining
// Description: Allows the user to click a link when examining to look at video if one is going.
/obj/item/device/communicator/examine(mob/user)
. = ..(user, 1)
if(. && video_source)
user << "<span class='notice'>It looks like it's on a video call: <a href='?src=\ref[src];watchvideo=1'>\[view\]</a></span>"
// Proc: initialize_exonet()
// Parameters: 1 (user - the person the communicator belongs to)
// Description: Sets up the exonet datum, gives the device an address, and then gets a node reference. Afterwards, populates the device
@@ -257,27 +270,27 @@ var/global/list/obj/item/device/communicator/all_communicators = list()
//Now for ghosts who we pretend have communicators.
for(var/mob/observer/dead/O in known_devices)
if(O.client && O.client.prefs.communicator_visibility == 1 && O.exonet)
communicators[++communicators.len] = list("name" = sanitize("[O.client.prefs.real_name]'s communicator"), "address" = O.exonet.address)
communicators[++communicators.len] = list("name" = sanitize("[O.client.prefs.real_name]'s communicator"), "address" = O.exonet.address, "ref" = "\ref[O]")
//Lists all the other communicators that we invited.
for(var/obj/item/device/communicator/comm in voice_invites)
if(comm.exonet)
invites[++invites.len] = list("name" = sanitize(comm.name), "address" = comm.exonet.address)
invites[++invites.len] = list("name" = sanitize(comm.name), "address" = comm.exonet.address, "ref" = "\ref[comm]")
//Ghosts we invited.
for(var/mob/observer/dead/O in voice_invites)
if(O.exonet && O.client)
invites[++invites.len] = list("name" = sanitize("[O.client.prefs.real_name]'s communicator"), "address" = O.exonet.address)
invites[++invites.len] = list("name" = sanitize("[O.client.prefs.real_name]'s communicator"), "address" = O.exonet.address, "ref" = "\ref[O]")
//Communicators that want to talk to us.
for(var/obj/item/device/communicator/comm in voice_requests)
if(comm.exonet)
requests[++requests.len] = list("name" = sanitize(comm.name), "address" = comm.exonet.address)
requests[++requests.len] = list("name" = sanitize(comm.name), "address" = comm.exonet.address, "ref" = "\ref[comm]")
//Ghosts that want to talk to us.
for(var/mob/observer/dead/O in voice_requests)
if(O.exonet && O.client)
requests[++requests.len] = list("name" = sanitize("[O.client.prefs.real_name]'s communicator"), "address" = O.exonet.address)
requests[++requests.len] = list("name" = sanitize("[O.client.prefs.real_name]'s communicator"), "address" = O.exonet.address, "ref" = "\ref[O]")
//Now for all the voice mobs inside the communicator.
for(var/mob/living/voice/voice in contents)
@@ -285,12 +298,12 @@ var/global/list/obj/item/device/communicator/all_communicators = list()
//Finally, all the communicators linked to this one.
for(var/obj/item/device/communicator/comm in communicating)
connected_communicators[++connected_communicators.len] = list("name" = sanitize(comm.name), "true_name" = sanitize(comm.name))
connected_communicators[++connected_communicators.len] = list("name" = sanitize(comm.name), "true_name" = sanitize(comm.name), "ref" = "\ref[comm]")
//Devices that have been messaged or recieved messages from.
for(var/obj/item/device/communicator/comm in im_contacts)
if(comm.exonet)
im_contacts_ui[++im_contacts_ui.len] = list("name" = sanitize(comm.name), "address" = comm.exonet.address)
im_contacts_ui[++im_contacts_ui.len] = list("name" = sanitize(comm.name), "address" = comm.exonet.address, "ref" = "\ref[comm]")
//Actual messages.
for(var/I in im_list)
@@ -313,6 +326,7 @@ var/global/list/obj/item/device/communicator/all_communicators = list()
data["requestsReceived"] = requests
data["voice_mobs"] = voices
data["communicating"] = connected_communicators
data["video_comm"] = video_source ? "\ref[video_source.loc]" : null
data["imContacts"] = im_contacts_ui
data["imList"] = im_list_ui
data["time"] = worldtime2text()
@@ -325,7 +339,7 @@ var/global/list/obj/item/device/communicator/all_communicators = list()
if(!ui)
// the ui does not exist, so we'll create a new() one
// for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm
ui = new(user, src, ui_key, "communicator.tmpl", "Communicator", 450, 700)
ui = new(user, src, ui_key, "communicator.tmpl", "Communicator", 475, 700)
// when the ui is first opened this is the data it will use
ui.set_initial_data(data)
// open the new ui window
@@ -344,9 +358,20 @@ var/global/list/obj/item/device/communicator/all_communicators = list()
if(new_name)
owner = new_name
name = "[owner]'s [initial(name)]"
if(camera)
camera.name = name
camera.c_tag = name
if(href_list["toggle_visibility"])
network_visibility = !network_visibility
switch(network_visibility)
if(1) //Visible, becoming invisbile
network_visibility = 0
if(camera)
camera.remove_network(NETWORK_COMMUNICATORS)
if(0) //Invisible, becoming visible
network_visibility = 1
if(camera)
camera.add_network(NETWORK_COMMUNICATORS)
if(href_list["toggle_ringer"])
ringer = !ringer
@@ -371,6 +396,12 @@ var/global/list/obj/item/device/communicator/all_communicators = list()
var/their_address = href_list["dial"]
exonet.send_message(their_address, "voice")
if(href_list["decline"])
var/ref_to_remove = href_list["decline"]
var/atom/decline = locate(ref_to_remove)
if(decline)
del_request(decline)
if(href_list["message"])
if(!get_connection_to_tcomms())
usr << "<span class='danger'>Error: Cannot connect to Exonet node.</span>"
@@ -385,10 +416,24 @@ var/global/list/obj/item/device/communicator/all_communicators = list()
var/name_to_disconnect = href_list["disconnect"]
for(var/mob/living/voice/V in contents)
if(name_to_disconnect == V.name)
close_connection(usr, V, "[usr] hung up.")
close_connection(usr, V, "[usr] hung up")
for(var/obj/item/device/communicator/comm in communicating)
if(name_to_disconnect == comm.name)
close_connection(usr, comm, "[usr] hung up.")
close_connection(usr, comm, "[usr] hung up")
if(href_list["startvideo"])
var/ref_to_video = href_list["startvideo"]
var/obj/item/device/communicator/comm = locate(ref_to_video)
if(comm)
connect_video(usr, comm)
if(href_list["endvideo"])
if(video_source)
end_video()
if(href_list["watchvideo"])
if(video_source)
watch_video(usr,video_source.loc)
if(href_list["copy"])
target_address = href_list["copy"]
@@ -398,9 +443,9 @@ var/global/list/obj/item/device/communicator/all_communicators = list()
if(href_list["hang_up"])
for(var/mob/living/voice/V in contents)
close_connection(usr, V, "[usr] hung up.")
close_connection(usr, V, "[usr] hung up")
for(var/obj/item/device/communicator/comm in communicating)
close_connection(usr, comm, "[usr] hung up.")
close_connection(usr, comm, "[usr] hung up")
if(href_list["switch_tab"])
selected_tab = href_list["switch_tab"]
@@ -475,7 +520,9 @@ var/global/list/obj/item/device/communicator/all_communicators = list()
owner = user.name
name = "[owner]'s [initial(name)]"
if(camera)
camera.name = name
camera.c_tag = name
// Proc: add_communicating()
// Parameters: 1 (comm - the communicator to add to communicating)
@@ -485,6 +532,7 @@ var/global/list/obj/item/device/communicator/all_communicators = list()
communicating |= comm
listening_objects |= src
update_icon()
// Proc: del_communicating()
// Parameters: 1 (comm - the communicator to remove from communicating)
@@ -493,6 +541,7 @@ var/global/list/obj/item/device/communicator/all_communicators = list()
if(!comm || !istype(comm)) return
communicating.Remove(comm)
update_icon()
// Proc: open_connection()
// Parameters: 2 (user - the person who initiated the connecting being opened, candidate - the communicator or observer that will connect to the device)
@@ -601,15 +650,19 @@ var/global/list/obj/item/device/communicator/all_communicators = list()
visible_message("<span class='danger'>\icon[src] [reason].</span>")
voice_mobs.Remove(voice)
qdel(voice)
update_icon()
for(var/obj/item/device/communicator/comm in communicating) //Now we handle real communicators.
if(target && comm != target)
continue
comm.visible_message("<span class='danger'>\icon[src] [reason].</span>")
visible_message("<span class='danger'>\icon[src] [reason].</span>")
src.del_communicating(comm)
comm.del_communicating(src)
update_icon()
comm.visible_message("<span class='danger'>\icon[src] [reason].</span>")
visible_message("<span class='danger'>\icon[src] [reason].</span>")
if(comm.camera && video_source == comm.camera) //We hung up on the person on video
end_video()
if(camera && comm.video_source == camera) //We hung up on them while they were watching us
comm.end_video()
if(voice_mobs.len == 0 && communicating.len == 0)
listening_objects.Remove(src)
@@ -649,6 +702,29 @@ var/global/list/obj/item/device/communicator/all_communicators = list()
if(L)
L << "<span class='notice'>\icon[src] Communications request from [who].</span>"
// Proc: del_request()
// Parameters: 1 (candidate - the ghost or communicator to be declined)
// Description: Declines a request and cleans up both ends
/obj/item/device/communicator/proc/del_request(var/atom/candidate)
if(!(candidate in voice_requests))
return
if(isobserver(candidate))
candidate << "<span class='warning'>Your communicator call request was declined.</span>"
else if(istype(candidate, /obj/item/device/communicator))
var/obj/item/device/communicator/comm = candidate
comm.voice_invites -= src
voice_requests -= candidate
//Search for holder of our device.
var/mob/living/us = null
if(loc && isliving(loc))
us = loc
if(us)
us << "<span class='notice'>\icon[src] Declined request.</span>"
// Proc: request_im()
// Parameters: 3 (candidate - the communicator wanting to message the device, origin_address - the address of the sender, text - the message)
// Description: Response to a communicator trying to message the device.
@@ -700,6 +776,8 @@ var/global/list/obj/item/device/communicator/all_communicators = list()
all_communicators -= src
processing_objects -= src
listening_objects.Remove(src)
qdel(camera)
camera = null
if(exonet)
exonet.remove_address()
exonet = null
@@ -709,7 +787,11 @@ var/global/list/obj/item/device/communicator/all_communicators = list()
// Parameters: None
// Description: Self explanatory
/obj/item/device/communicator/update_icon()
if(voice_mobs.len > 0)
if(video_source)
icon_state = "communicator-video"
return
if(voice_mobs.len || communicating.len)
icon_state = "communicator-active"
return
@@ -833,7 +915,71 @@ var/global/list/obj/item/device/communicator/all_communicators = list()
src << "A communications request has been sent to [chosen_communicator]. Now you need to wait until someone answers."
/obj/item/device/communicator/integrated //For synths who have no hands.
// Proc: connect_video()
// Parameters: user - the mob doing the viewing of video, comm - the communicator at the far end
// Description: Sets up a videocall and puts the first view into it using watch_video, and updates the icon
/obj/item/device/communicator/proc/connect_video(mob/user,obj/item/device/communicator/comm)
if((!user) || (!comm) || user.stat) return //KO or dead, or already in a video
if(video_source) //Already in a video
user << "<span class='danger'>You are already connected to a video call!</span>"
if(user.blinded) //User is blinded
user << "<span class='danger'>You cannot see well enough to do that!</span>"
if(!(src in comm.communicating) || !comm.camera) //You called someone with a broken communicator or one that's fake or yourself or something
user << "<span class='danger'>\icon[src]ERROR: Video failed. Either bandwidth is too low, or the other communicator is malfunctioning.</span>"
user << "<span class='notice'>\icon[src] Attempting to start video over existing call.</span>"
sleep(30)
user << "<span class='notice'>\icon[src] Please wait...</span>"
video_source = comm.camera
comm.visible_message("<span class='danger'>\icon[src] New video connection from [comm].</span>")
watch_video(user)
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)
user << "<span class='notice'>Now viewing video session. To leave camera view: OOC -> Cancel Camera View</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())
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()
// 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)
video_source = null
. = "<span class='danger'>\icon[src] [reason ? reason : "Video session ended"].</span>"
visible_message(.)
update_icon()
//For synths who have no hands.
/obj/item/device/communicator/integrated
name = "integrated communicator"
desc = "A circuit used for long-range communications, able to be integrated into a system."
@@ -864,3 +1010,12 @@ var/global/list/obj/item/device/communicator/all_communicators = list()
return
src.attack_self(usr)
// A camera preset for spawning in the communicator
/obj/machinery/camera/communicator
network = list(NETWORK_COMMUNICATORS)
/obj/machinery/camera/communicator/New()
..()
client_huds |= global_hud.whitense
client_huds |= global_hud.darkMask

View File

@@ -1051,7 +1051,11 @@
if(copytext(hud.icon_state,1,4) == "hud") //ugly, but icon comparison is worse, I believe
client.images.Remove(hud)
client.screen.Remove(global_hud.blurry, global_hud.druggy, global_hud.vimpaired, global_hud.darkMask, global_hud.nvg, global_hud.thermal, global_hud.meson, global_hud.science)
client.screen.Remove(global_hud.blurry, global_hud.druggy, global_hud.vimpaired, global_hud.darkMask, global_hud.nvg, global_hud.thermal, global_hud.meson, global_hud.science, global_hud.whitense)
if(istype(client.eye,/obj/machinery/camera))
var/obj/machinery/camera/cam = client.eye
client.screen |= cam.client_huds
if(damageoverlay.overlays)
damageoverlay.overlays = list()

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 44 KiB

View File

@@ -103,7 +103,12 @@ Used In File(s): code\game\objects\items\devices\communicator\communicator.dm
{{:value.name}}
</div>
<div class="itemContent">
<div style="float: left; width: 200px;">{{:helper.link('Disconnect', 'close', {'disconnect' : value.true_name}, null, 'redButton')}}</div>
{{:helper.link('Disconnect', 'close', {'disconnect' : value.true_name}, null, 'redButton')}}
{{if data.video_comm == null}}
{{:helper.link('Start Video', 'signal-diag', {'startvideo' : value.ref})}}
{{else data.video_comm == value.ref}}
{{:helper.link('End Video', 'signal-diag', {'endvideo' : value.true_name}, null, 'redButton')}}
{{/if}}
</div>
</div>
{{/for}}
@@ -114,7 +119,7 @@ Used In File(s): code\game\objects\items\devices\communicator\communicator.dm
{{:value.name}}
</div>
<div class="itemContent">
<div style="float: left; width: 300px;">{{:value.address}}</div> {{:helper.link('Dial', 'signal-diag', {'dial' : value.address})}}
<div style="float: left; width: 300px;">{{:value.address}}</div> {{:helper.link('Accept', 'signal-diag', {'dial' : value.address})}}{{:helper.link('Decline', 'close', {'decline' : value.ref})}}
</div>
</div>
{{/for}}