mirror of
https://github.com/CHOMPStation2/CHOMPStation2.git
synced 2025-12-10 10:12:45 +00:00
Upgrades communicators to NanoUI, adds a new EPv2 fake networking system, adds new semi-telecomms machine, lets communicators call ghosts, call other communicators, and have as many ghosts or communicators linked as the user desires. Adds preference option to remain invisible on communicator device searches.
This commit is contained in:
127
code/datums/EPv2.dm
Normal file
127
code/datums/EPv2.dm
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
/*
|
||||||
|
Exonet Protocol Version 2
|
||||||
|
|
||||||
|
This is designed to be a fairly simple fake-networking system, allowing you to send and receive messages
|
||||||
|
between the exonet_protocol datums, and for atoms to react to those messages, based on the contents of the message.
|
||||||
|
Hopefully, this can evolve to be a more robust fake-networking system and allow for some devious network hacking in the future.
|
||||||
|
|
||||||
|
Version 1 never existed.
|
||||||
|
|
||||||
|
*Setting up*
|
||||||
|
|
||||||
|
To set up the exonet link, define a variable on your desired atom it is like this;
|
||||||
|
var/datum/exonet_protocol/exonet = null
|
||||||
|
Afterwards, before you want to do networking, call exonet = New(src), then exonet.make_address(string), and give it a string to hash into the new IP.
|
||||||
|
The reason it needs a string is so you can have the addresses be persistant, assuming no-one already took it first.
|
||||||
|
|
||||||
|
When you're no longer wanting to use the address and want to free it up, like when you want to Destroy() it, you need to call remove_address()
|
||||||
|
|
||||||
|
*Sending messages*
|
||||||
|
|
||||||
|
To send a message to another datum, you need to know it's EPv2 (fake IP) address. Once you know that, call send_message(), place your
|
||||||
|
intended address in the first argument, then the message in the second. For example, send_message(exonet.address, "ping") will make you
|
||||||
|
ping yourself.
|
||||||
|
|
||||||
|
*Receiving messages*
|
||||||
|
You don't need to do anything special to receive the messages, other than give your target exonet datum an address as well. Once something hits
|
||||||
|
your datum with send_message(), receive_message() is called, and the default action is to call receive_exonet_message() on the datum's holder.
|
||||||
|
You'll want to override receive_exonet_message() on your atom, and define what will occur when the message is received.
|
||||||
|
The receiving atom will receive the origin atom (the atom that sent the message), the origin address, and finally the message itself.
|
||||||
|
It's suggested to start with an if or switch statement for the message, to determine what to do.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var/global/list/all_exonet_connections = list()
|
||||||
|
|
||||||
|
/datum/exonet_protocol
|
||||||
|
var/address = "" //Resembles IPv6, but with only five 'groups', e.g. XXXX:XXXX:XXXX:XXXX:XXXX
|
||||||
|
var/atom/movable/holder = null
|
||||||
|
|
||||||
|
/datum/exonet_protocol/New(var/atom/holder)
|
||||||
|
src.holder = holder
|
||||||
|
..()
|
||||||
|
|
||||||
|
|
||||||
|
// Proc: make_address()
|
||||||
|
// Parameters: 1 (string - used to make into a hash that will be part of the new address)
|
||||||
|
// Description: Allocates a new address based on the string supplied. It results in consistant addresses for each round assuming it is not already taken..
|
||||||
|
/datum/exonet_protocol/proc/make_address(var/string)
|
||||||
|
if(string)
|
||||||
|
var/new_address = null
|
||||||
|
while(new_address == find_address(new_address)) //Collision test.
|
||||||
|
var/hash = md5(string)
|
||||||
|
var/raw_address = copytext(hash,1,25)
|
||||||
|
var/addr_0 = "fc00" //Used for unique local address in real-life IPv6.
|
||||||
|
var/addr_1 = hexadecimal_to_EPv2(raw_address)
|
||||||
|
|
||||||
|
new_address = "[addr_0]:[addr_1]"
|
||||||
|
string = "[string]0" //If we did get a collision, this should make the next attempt not have one.
|
||||||
|
sleep(1)
|
||||||
|
address = new_address
|
||||||
|
all_exonet_connections |= src
|
||||||
|
|
||||||
|
|
||||||
|
// Proc: make_arbitrary_address()
|
||||||
|
// Parameters: 1 (new_address - the desired address)
|
||||||
|
// Description: Allocates that specific address, if it is available.
|
||||||
|
/datum/exonet_protocol/proc/make_arbitrary_address(var/new_address)
|
||||||
|
if(new_address)
|
||||||
|
if(new_address == find_address(new_address) ) //Collision test.
|
||||||
|
return 0
|
||||||
|
address = new_address
|
||||||
|
all_exonet_connections |= src
|
||||||
|
return 1
|
||||||
|
|
||||||
|
// Proc: hexadecimal_to_EPv2()
|
||||||
|
// Parameters: 1 (hex - a string of hexadecimals to convert)
|
||||||
|
// Description: Helper proc to add colons to a string in the right places.
|
||||||
|
/proc/hexadecimal_to_EPv2(var/hex)
|
||||||
|
if(!hex)
|
||||||
|
return null
|
||||||
|
var/addr_1 = copytext(hex,1,5)
|
||||||
|
var/addr_2 = copytext(hex,5,9)
|
||||||
|
var/addr_3 = copytext(hex,9,13)
|
||||||
|
var/addr_4 = copytext(hex,13,17)
|
||||||
|
var/new_address = "[addr_1]:[addr_2]:[addr_3]:[addr_4]"
|
||||||
|
return new_address
|
||||||
|
|
||||||
|
|
||||||
|
// Proc: remove_address()
|
||||||
|
// Parameters: None
|
||||||
|
// Description: Deallocates the address, freeing it for use.
|
||||||
|
/datum/exonet_protocol/proc/remove_address()
|
||||||
|
address = ""
|
||||||
|
all_exonet_connections.Remove(src)
|
||||||
|
|
||||||
|
|
||||||
|
// Proc: find_address()
|
||||||
|
// Parameters: 1 (target_address - the desired address to find)
|
||||||
|
// Description: Searches the global list all_exonet_connections for a specific address, and returns it if found, otherwise returns null.
|
||||||
|
/datum/exonet_protocol/proc/find_address(var/target_address)
|
||||||
|
for(var/datum/exonet_protocol/exonet in all_exonet_connections)
|
||||||
|
if(exonet.address == target_address)
|
||||||
|
return exonet.address
|
||||||
|
return null
|
||||||
|
|
||||||
|
// Proc: send_message()
|
||||||
|
// Parameters: 2 (target_address - the desired address to send the message to, message - the message to send)
|
||||||
|
// Description: Sends the message to target_address, by calling receive_message() on the desired datum.
|
||||||
|
/datum/exonet_protocol/proc/send_message(var/target_address, var/message)
|
||||||
|
if(!address)
|
||||||
|
return 0
|
||||||
|
for(var/datum/exonet_protocol/exonet in all_exonet_connections)
|
||||||
|
if(exonet.address == target_address)
|
||||||
|
exonet.receive_message(holder, address, message)
|
||||||
|
break
|
||||||
|
|
||||||
|
// Proc: receive_message()
|
||||||
|
// Parameters: 3 (origin_atom - the origin datum's holder, origin_address - the address the message originated from, message - the message that was sent)
|
||||||
|
// Description: Called when send_message() successfully reaches the intended datum. By default, calls receive_exonet_message() on the holder atom.
|
||||||
|
/datum/exonet_protocol/proc/receive_message(var/atom/origin_atom, var/origin_address, var/message)
|
||||||
|
holder.receive_exonet_message(origin_atom, origin_address, message)
|
||||||
|
return
|
||||||
|
|
||||||
|
// Proc: receive_exonet_message()
|
||||||
|
// Parameters: 3 (origin_atom - the origin datum's holder, origin_address - the address the message originated from, message - the message that was sent)
|
||||||
|
// Description: Override this to make your atom do something when a message is received.
|
||||||
|
/atom/proc/receive_exonet_message(var/atom/origin_atom, var/origin_address, var/message)
|
||||||
|
return
|
||||||
@@ -8,7 +8,7 @@
|
|||||||
if(creator)
|
if(creator)
|
||||||
parent = creator
|
parent = creator
|
||||||
|
|
||||||
var/global/datum/locations/milky_way/locations = new()
|
var/global/datum/locations/milky_way/all_locations = new()
|
||||||
|
|
||||||
//Galaxy
|
//Galaxy
|
||||||
|
|
||||||
@@ -26,6 +26,24 @@ var/global/datum/locations/milky_way/locations = new()
|
|||||||
new /datum/locations/uueoa_esa(src),
|
new /datum/locations/uueoa_esa(src),
|
||||||
new /datum/locations/vir(src)
|
new /datum/locations/vir(src)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
/proc/choose_location_datum(client/user)
|
||||||
|
var/datum/locations/choice = all_locations
|
||||||
|
while(length(choice.contents) > 0) //For some reason it wouldn't let me do contents.len even when I defined it as a list.
|
||||||
|
var/specific = alert(user, "The location currently selected is [choice.name]. More specific options exist, would you like to pick a more specific location?",
|
||||||
|
"Choose location", "Yes", "No")
|
||||||
|
if(specific == "Yes" && length(choice.contents) > 0)
|
||||||
|
choice = input(user, "Please choose a location.","Locations") as null|anything in choice.contents
|
||||||
|
else
|
||||||
|
break
|
||||||
|
user << choice.name
|
||||||
|
user << choice.desc
|
||||||
|
return choice
|
||||||
|
|
||||||
|
// var/datum/locations/choice = input(user, "Please choose a location.","Locations") as null|anything in all_locations
|
||||||
|
// if(choice && choice.contents.len > 0)
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
/datum/locations/proc/show_contents()
|
/datum/locations/proc/show_contents()
|
||||||
// world << "[src]\n[desc]"
|
// world << "[src]\n[desc]"
|
||||||
|
|||||||
@@ -52,6 +52,17 @@
|
|||||||
desc = "The Northern Star is an asteroid colony owned and operated by Nanotrasen, among many other asteroid installations. \
|
desc = "The Northern Star is an asteroid colony owned and operated by Nanotrasen, among many other asteroid installations. \
|
||||||
Originally conceived as 'just another pitstop' for weary asteroid miners, it has grown to become a significant installation in the Kara subsystem."
|
Originally conceived as 'just another pitstop' for weary asteroid miners, it has grown to become a significant installation in the Kara subsystem."
|
||||||
|
|
||||||
|
/datum/locations/northern_star/New(var/creator)
|
||||||
|
contents.Add(
|
||||||
|
new /datum/locations/northern_star_interior(src)
|
||||||
|
)
|
||||||
|
..(creator)
|
||||||
|
|
||||||
|
/datum/locations/northern_star_interior
|
||||||
|
name = "Northern Star Inner Level"
|
||||||
|
desc = "The Northern Star contains multiple layers, this one being the inner level, also known as the residentual area. It contains most of the \
|
||||||
|
homes for the Northern Star, as well as acting as the heart of commerece, with many shops and markets near the center."
|
||||||
|
|
||||||
/datum/locations/rota
|
/datum/locations/rota
|
||||||
name = "Rota"
|
name = "Rota"
|
||||||
desc = "A Neptune-like ice giant, with a beautiful ring system circling it. It is 165 kelvin (-157<35>C)."
|
desc = "A Neptune-like ice giant, with a beautiful ring system circling it. It is 165 kelvin (-157<35>C)."
|
||||||
173
code/game/machinery/exonet_node.dm
Normal file
173
code/game/machinery/exonet_node.dm
Normal file
@@ -0,0 +1,173 @@
|
|||||||
|
/obj/machinery/exonet_node
|
||||||
|
name = "exonet node"
|
||||||
|
desc = "This machine is one of many, many nodes inside Vir's section of the Exonet, connecting the Northern Star to the rest of the system, at least \
|
||||||
|
electronically."
|
||||||
|
icon = 'icons/obj/stationobjs.dmi'
|
||||||
|
icon_state = "exonet_node"
|
||||||
|
idle_power_usage = 2500
|
||||||
|
density = 1
|
||||||
|
var/on = 1
|
||||||
|
var/toggle = 1
|
||||||
|
|
||||||
|
var/allow_external_PDAs = 1
|
||||||
|
var/allow_external_communicators = 1
|
||||||
|
var/allow_external_newscasters = 1
|
||||||
|
|
||||||
|
var/opened = 0
|
||||||
|
|
||||||
|
// Proc: New()
|
||||||
|
// Parameters: None
|
||||||
|
// Description: Adds components to the machine for deconstruction.
|
||||||
|
/obj/machinery/exonet_node/New()
|
||||||
|
..()
|
||||||
|
|
||||||
|
component_parts = list()
|
||||||
|
component_parts += new /obj/item/weapon/circuitboard/telecomms/exonet_node(src)
|
||||||
|
component_parts += new /obj/item/weapon/stock_parts/subspace/ansible(src)
|
||||||
|
component_parts += new /obj/item/weapon/stock_parts/subspace/filter(src)
|
||||||
|
component_parts += new /obj/item/weapon/stock_parts/manipulator(src)
|
||||||
|
component_parts += new /obj/item/weapon/stock_parts/manipulator(src)
|
||||||
|
component_parts += new /obj/item/weapon/stock_parts/micro_laser(src)
|
||||||
|
component_parts += new /obj/item/weapon/stock_parts/subspace/crystal(src)
|
||||||
|
component_parts += new /obj/item/weapon/stock_parts/subspace/treatment(src)
|
||||||
|
component_parts += new /obj/item/weapon/stock_parts/subspace/treatment(src)
|
||||||
|
component_parts += new /obj/item/stack/cable_coil(src, 2)
|
||||||
|
RefreshParts()
|
||||||
|
|
||||||
|
// Proc: update_icon()
|
||||||
|
// Parameters: None
|
||||||
|
// Description: Self explanatory.
|
||||||
|
/obj/machinery/exonet_node/update_icon()
|
||||||
|
if(on)
|
||||||
|
if(!allow_external_PDAs && !allow_external_communicators && !allow_external_newscasters)
|
||||||
|
icon_state = "[initial(icon_state)]_idle"
|
||||||
|
else
|
||||||
|
icon_state = initial(icon_state)
|
||||||
|
else
|
||||||
|
icon_state = "[initial(icon_state)]_off"
|
||||||
|
|
||||||
|
// Proc: update_power()
|
||||||
|
// Parameters: None
|
||||||
|
// Description: Sets the device on/off and adjusts power draw based on stat and toggle variables.
|
||||||
|
/obj/machinery/exonet_node/proc/update_power()
|
||||||
|
if(toggle)
|
||||||
|
if(stat & (BROKEN|NOPOWER|EMPED))
|
||||||
|
on = 0
|
||||||
|
idle_power_usage = 0
|
||||||
|
else
|
||||||
|
on = 1
|
||||||
|
idle_power_usage = 2500
|
||||||
|
else
|
||||||
|
on = 0
|
||||||
|
idle_power_usage = 0
|
||||||
|
|
||||||
|
// Proc: emp_act()
|
||||||
|
// Parameters: 1 (severity - how strong the EMP is, with lower numbers being stronger)
|
||||||
|
// Description: Shuts off the machine for awhile if an EMP hits it. Ion anomalies also call this to turn it off.
|
||||||
|
/obj/machinery/exonet_node/emp_act(severity)
|
||||||
|
if(!(stat & EMPED))
|
||||||
|
stat |= EMPED
|
||||||
|
var/duration = (300 * 10)/severity
|
||||||
|
spawn(rand(duration - 20, duration + 20))
|
||||||
|
stat &= ~EMPED
|
||||||
|
update_icon()
|
||||||
|
..()
|
||||||
|
|
||||||
|
// Proc: process()
|
||||||
|
// Parameters: None
|
||||||
|
// Description: Calls the procs below every tick.
|
||||||
|
/obj/machinery/exonet_node/process()
|
||||||
|
update_power()
|
||||||
|
|
||||||
|
// Proc: attackby()
|
||||||
|
// Parameters: 2 (I - the item being whacked against the machine, user - the person doing the whacking)
|
||||||
|
// Description: Handles deconstruction.
|
||||||
|
/obj/machinery/exonet_node/attackby(obj/item/I, mob/user)
|
||||||
|
if(istype(I, /obj/item/weapon/screwdriver))
|
||||||
|
default_deconstruction_screwdriver(user, I)
|
||||||
|
else if(istype(I, /obj/item/weapon/crowbar))
|
||||||
|
default_deconstruction_crowbar(user, I)
|
||||||
|
else
|
||||||
|
..()
|
||||||
|
|
||||||
|
// Proc: attack_ai()
|
||||||
|
// Parameters: 1 (user - the AI clicking on the machine)
|
||||||
|
// Description: Redirects to attack_hand()
|
||||||
|
/obj/machinery/exonet_node/attack_ai(mob/user)
|
||||||
|
attack_hand(user)
|
||||||
|
|
||||||
|
// Proc: attack_hand()
|
||||||
|
// Parameters: 1 (user - the person clicking on the machine)
|
||||||
|
// Description: Opens the NanoUI interface with ui_interact()
|
||||||
|
/obj/machinery/exonet_node/attack_hand(mob/user)
|
||||||
|
ui_interact(user)
|
||||||
|
|
||||||
|
// Proc: ui_interact()
|
||||||
|
// Parameters: 4 (standard NanoUI arguments)
|
||||||
|
// Description: Allows the user to turn the machine on or off, or open or close certain 'ports' for things like external PDA messages, newscasters, etc.
|
||||||
|
/obj/machinery/exonet_node/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
|
||||||
|
// this is the data which will be sent to the ui
|
||||||
|
var/data[0]
|
||||||
|
|
||||||
|
|
||||||
|
data["on"] = toggle ? 1 : 0
|
||||||
|
data["allowPDAs"] = allow_external_PDAs
|
||||||
|
data["allowCommunicators"] = allow_external_communicators
|
||||||
|
data["allowNewscasters"] = allow_external_newscasters
|
||||||
|
|
||||||
|
|
||||||
|
// update the ui if it exists, returns null if no ui is passed/found
|
||||||
|
ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open)
|
||||||
|
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, "exonet_node.tmpl", "Exonet Node #157", 400, 400)
|
||||||
|
// when the ui is first opened this is the data it will use
|
||||||
|
ui.set_initial_data(data)
|
||||||
|
// open the new ui window
|
||||||
|
ui.open()
|
||||||
|
// auto update every Master Controller tick
|
||||||
|
ui.set_auto_update(1)
|
||||||
|
|
||||||
|
// Proc: Topic()
|
||||||
|
// Parameters: 2 (standard Topic arguments)
|
||||||
|
// Description: Responds to button presses on the NanoUI interface.
|
||||||
|
/obj/machinery/exonet_node/Topic(href, href_list)
|
||||||
|
if(..())
|
||||||
|
return 1
|
||||||
|
if(href_list["toggle_power"])
|
||||||
|
toggle = !toggle
|
||||||
|
update_power()
|
||||||
|
if(!toggle)
|
||||||
|
var/msg = "[usr.client.key] ([usr]) has turned [src] off, at [x],[y],[z]."
|
||||||
|
message_admins(msg)
|
||||||
|
log_game(msg)
|
||||||
|
|
||||||
|
if(href_list["toggle_PDA_port"])
|
||||||
|
allow_external_PDAs = !allow_external_PDAs
|
||||||
|
|
||||||
|
if(href_list["toggle_communicator_port"])
|
||||||
|
allow_external_communicators = !allow_external_communicators
|
||||||
|
if(!allow_external_communicators)
|
||||||
|
var/msg = "[usr.client.key] ([usr]) has turned [src]'s communicator port off, at [x],[y],[z]."
|
||||||
|
message_admins(msg)
|
||||||
|
log_game(msg)
|
||||||
|
|
||||||
|
if(href_list["toggle_newscaster_port"])
|
||||||
|
allow_external_newscasters = !allow_external_newscasters
|
||||||
|
if(!allow_external_newscasters)
|
||||||
|
var/msg = "[usr.client.key] ([usr]) has turned [src]'s newscaster port off, at [x],[y],[z]."
|
||||||
|
message_admins(msg)
|
||||||
|
log_game(msg)
|
||||||
|
|
||||||
|
update_icon()
|
||||||
|
nanomanager.update_uis(src)
|
||||||
|
add_fingerprint(usr)
|
||||||
|
|
||||||
|
// Proc: get_exonet_node()
|
||||||
|
// Parameters: None
|
||||||
|
// Description: Helper proc to get a reference to an Exonet node.
|
||||||
|
/proc/get_exonet_node()
|
||||||
|
for(var/obj/machinery/exonet_node/E in machines)
|
||||||
|
if(E.on)
|
||||||
|
return E
|
||||||
@@ -166,6 +166,7 @@ var/list/obj/machinery/newscaster/allCasters = list() //Global list that will co
|
|||||||
var/datum/feed_channel/viewing_channel = null
|
var/datum/feed_channel/viewing_channel = null
|
||||||
light_range = 0
|
light_range = 0
|
||||||
anchored = 1
|
anchored = 1
|
||||||
|
var/obj/machinery/exonet_node/node = null
|
||||||
|
|
||||||
|
|
||||||
/obj/machinery/newscaster/security_unit //Security unit
|
/obj/machinery/newscaster/security_unit //Security unit
|
||||||
@@ -178,6 +179,8 @@ var/list/obj/machinery/newscaster/allCasters = list() //Global list that will co
|
|||||||
for(var/obj/machinery/newscaster/NEWSCASTER in allCasters) // Let's give it an appropriate unit number
|
for(var/obj/machinery/newscaster/NEWSCASTER in allCasters) // Let's give it an appropriate unit number
|
||||||
src.unit_no++
|
src.unit_no++
|
||||||
src.update_icon() //for any custom ones on the map...
|
src.update_icon() //for any custom ones on the map...
|
||||||
|
spawn(10) //Should be enough time for the node to spawn at tcomms.
|
||||||
|
node = get_exonet_node()
|
||||||
..() //I just realised the newscasters weren't in the global machines list. The superconstructor call will tend to that
|
..() //I just realised the newscasters weren't in the global machines list. The superconstructor call will tend to that
|
||||||
|
|
||||||
/obj/machinery/newscaster/Destroy()
|
/obj/machinery/newscaster/Destroy()
|
||||||
@@ -247,6 +250,14 @@ var/list/obj/machinery/newscaster/allCasters = list() //Global list that will co
|
|||||||
if(!src.ispowered || src.isbroken)
|
if(!src.ispowered || src.isbroken)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if(!node)
|
||||||
|
node = get_exonet_node()
|
||||||
|
|
||||||
|
if(!node || !node.on || !node.allow_external_newscasters)
|
||||||
|
user << "<span class='danger'>Error: Cannot connect to external content. Please try again in a few minutes. If this error persists, please \
|
||||||
|
contact the system administrator.</span>"
|
||||||
|
return 0
|
||||||
|
|
||||||
if(!user.IsAdvancedToolUser())
|
if(!user.IsAdvancedToolUser())
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
@@ -984,12 +995,10 @@ obj/item/weapon/newspaper/attackby(obj/item/weapon/W as obj, mob/user as mob)
|
|||||||
src.paper_remaining--
|
src.paper_remaining--
|
||||||
return
|
return
|
||||||
|
|
||||||
//Removed for now so these aren't even checked every tick. Left this here in-case Agouri needs it later.
|
/obj/machinery/newscaster/proc/newsAlert(var/news_call)
|
||||||
///obj/machinery/newscaster/process() //Was thinking of doing the icon update through process, but multiple iterations per second does not
|
if(!node || !node.on || !node.allow_external_newscasters) //The messages will still be there once the connection returns.
|
||||||
// return //bode well with a newscaster network of 10+ machines. Let's just return it, as it's added in the machines list.
|
return
|
||||||
|
var/turf/T = get_turf(src)
|
||||||
/obj/machinery/newscaster/proc/newsAlert(var/news_call) //This isn't Agouri's work, for it is ugly and vile.
|
|
||||||
var/turf/T = get_turf(src) //Who the fuck uses spawn(600) anyway, jesus christ
|
|
||||||
if(news_call)
|
if(news_call)
|
||||||
for(var/mob/O in hearers(world.view-1, T))
|
for(var/mob/O in hearers(world.view-1, T))
|
||||||
O.show_message("<span class='newscaster'><EM>[src.name]</EM> beeps, \"[news_call]\"</span>",2)
|
O.show_message("<span class='newscaster'><EM>[src.name]</EM> beeps, \"[news_call]\"</span>",2)
|
||||||
|
|||||||
@@ -1,3 +1,7 @@
|
|||||||
|
// Communicators
|
||||||
|
//
|
||||||
|
// Allows ghosts to roleplay with crewmembers without having to commit to joining the round, and also allows communications between two communicators.
|
||||||
|
|
||||||
var/global/list/obj/item/device/communicator/all_communicators = list()
|
var/global/list/obj/item/device/communicator/all_communicators = list()
|
||||||
|
|
||||||
/obj/item/device/communicator
|
/obj/item/device/communicator
|
||||||
@@ -12,21 +16,33 @@ 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/mob/living/voice/voice_mob = null
|
var/list/voice_mobs = list()
|
||||||
var/list/voice_requests = list()
|
var/list/voice_requests = list()
|
||||||
var/owner = null
|
var/list/voice_invites = list()
|
||||||
var/owner_job = null
|
var/selected_tab = 1 //1 equals dialing, 2 equals reviewing requests/invites.
|
||||||
var/owner_rank = null //Is this different from job?
|
var/owner = ""
|
||||||
var/alert_called = 0
|
var/alert_called = 0
|
||||||
var/obj/machinery/message_server/server = null //Reference to the PDA server, to avoid having to look it up so often.
|
var/obj/machinery/exonet_node/node = null //Reference to the Exonet node, to avoid having to look it up so often.
|
||||||
|
|
||||||
|
var/target_address = ""
|
||||||
|
var/network_visibility = 1
|
||||||
|
var/list/known_devices = list()
|
||||||
|
var/datum/exonet_protocol/exonet = null
|
||||||
|
var/list/communicating = list()
|
||||||
|
var/update_ticks = 0
|
||||||
|
|
||||||
|
// 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
|
||||||
|
// assign the device to the holder's name automatically in a spectacularly shitty way.
|
||||||
/obj/item/device/communicator/New()
|
/obj/item/device/communicator/New()
|
||||||
..()
|
..()
|
||||||
all_communicators += src
|
all_communicators += src
|
||||||
all_communicators = sortAtom(all_communicators)
|
all_communicators = sortAtom(all_communicators)
|
||||||
server = get_message_server()
|
node = get_exonet_node()
|
||||||
|
processing_objects |= src
|
||||||
//This is a pretty terrible way of doing this.
|
//This is a pretty terrible way of doing this.
|
||||||
spawn(20) //Wait for our mob to finish spawning.
|
spawn(50) //Wait for our mob to finish spawning.
|
||||||
if(ismob(loc))
|
if(ismob(loc))
|
||||||
register_device(loc)
|
register_device(loc)
|
||||||
else if(istype(loc, /obj/item/weapon/storage))
|
else if(istype(loc, /obj/item/weapon/storage))
|
||||||
@@ -34,201 +50,452 @@ var/global/list/obj/item/device/communicator/all_communicators = list()
|
|||||||
if(ismob(S.loc))
|
if(ismob(S.loc))
|
||||||
register_device(S.loc)
|
register_device(S.loc)
|
||||||
|
|
||||||
|
// Proc: examine()
|
||||||
|
// Parameters: 1 (user - the person examining the device)
|
||||||
|
// Description: Shows all the voice mobs inside the device, and their status.
|
||||||
/obj/item/device/communicator/examine(mob/user)
|
/obj/item/device/communicator/examine(mob/user)
|
||||||
if(!..(user))
|
if(!..(user))
|
||||||
return
|
return
|
||||||
|
|
||||||
var/msg = ""
|
var/msg = ""
|
||||||
if(voice_mob)
|
for(var/mob/living/voice/voice in contents)
|
||||||
msg += "<span class='notice'>On the screen, you can see a image feed of [voice_mob].</span>\n"
|
msg += "<span class='notice'>On the screen, you can see a image feed of [voice].</span>\n"
|
||||||
msg += "<span class='warning'>"
|
msg += "<span class='warning'>"
|
||||||
|
|
||||||
if(src.voice_mob && src.voice_mob.key)
|
if(voice && voice.key)
|
||||||
switch(src.voice_mob.stat)
|
switch(voice.stat)
|
||||||
if(CONSCIOUS)
|
if(CONSCIOUS)
|
||||||
if(!src.voice_mob.client)
|
if(!voice.client)
|
||||||
msg += "[voice_mob] appears to be asleep.\n" //afk
|
msg += "[voice] appears to be asleep.\n" //afk
|
||||||
if(UNCONSCIOUS)
|
if(UNCONSCIOUS)
|
||||||
msg += "[voice_mob] doesn't appear to be conscious.\n"
|
msg += "[voice] doesn't appear to be conscious.\n"
|
||||||
if(DEAD)
|
if(DEAD)
|
||||||
msg += "<span class='deadsay'>[voice_mob] appears to have died...</span>\n" //Hopefully this never has to be used.
|
msg += "<span class='deadsay'>[voice] appears to have died...</span>\n" //Hopefully this never has to be used.
|
||||||
else
|
else
|
||||||
msg += "<span class='notice'>The device doesn't appear to be transmitting any data.</span>\n"
|
msg += "<span class='notice'>The device doesn't appear to be transmitting any data.</span>\n"
|
||||||
msg += "</span>"
|
msg += "</span>"
|
||||||
user << msg
|
user << msg
|
||||||
return
|
return
|
||||||
|
|
||||||
//This is pretty lengthy
|
// Proc: emp_act()
|
||||||
|
// Parameters: None
|
||||||
|
// Description: Drops all calls when EMPed, so the holder can then get murdered by the antagonist.
|
||||||
|
/obj/item/device/communicator/emp_act()
|
||||||
|
close_connection(reason = "Hardware error de%#_^@%-BZZZZZZZT")
|
||||||
|
|
||||||
|
// Proc: add_to_EPv2()
|
||||||
|
// Parameters: 1 (hex - a single hexadecimal character)
|
||||||
|
// Description: Called when someone is manually dialing with nanoUI. Adds colons when appropiate.
|
||||||
|
/obj/item/device/communicator/proc/add_to_EPv2(var/hex)
|
||||||
|
var/length = length(target_address)
|
||||||
|
if(length >= 24)
|
||||||
|
return
|
||||||
|
if(length == 4 || length == 9 || length == 14 || length == 19 || length == 24 || length == 29)
|
||||||
|
target_address += ":[hex]"
|
||||||
|
return
|
||||||
|
target_address += hex
|
||||||
|
|
||||||
|
// Proc: populate_known_devices()
|
||||||
|
// Parameters: 1 (user - the person using the device)
|
||||||
|
// Description: Searches all communicators and ghosts in the world, and adds them to the known_devices list if they are 'visible'.
|
||||||
|
/obj/item/device/communicator/proc/populate_known_devices(mob/user)
|
||||||
|
if(!exonet)
|
||||||
|
exonet = new(src)
|
||||||
|
src.known_devices.Cut()
|
||||||
|
if(!get_connection_to_tcomms()) //If the network's down, we can't see anything.
|
||||||
|
return
|
||||||
|
for(var/obj/item/device/communicator/comm in all_communicators)
|
||||||
|
if(!comm || !comm.exonet || !comm.exonet.address || comm.exonet.address == src.exonet.address) //Don't add addressless devices, and don't add ourselves.
|
||||||
|
continue
|
||||||
|
src.known_devices |= comm
|
||||||
|
for(var/mob/dead/observer/O in dead_mob_list)
|
||||||
|
if(!O.client || O.client.prefs.communicator_visibility == 0)
|
||||||
|
continue
|
||||||
|
src.known_devices |= O
|
||||||
|
|
||||||
|
// Proc: get_connection_to_tcomms()
|
||||||
|
// Parameters: None
|
||||||
|
// Description: Simple check to see if the exonet node is active.
|
||||||
|
/obj/item/device/communicator/proc/get_connection_to_tcomms()
|
||||||
|
if(node)
|
||||||
|
if(node.on && node.allow_external_communicators)
|
||||||
|
return 1
|
||||||
|
return 0
|
||||||
|
|
||||||
|
// Proc: process()
|
||||||
|
// Parameters: None
|
||||||
|
// Description: Ticks the update_ticks variable, and checks to see if it needs to disconnect communicators every five ticks..
|
||||||
|
/obj/item/device/communicator/process()
|
||||||
|
update_ticks++
|
||||||
|
if(update_ticks % 5)
|
||||||
|
if(!node)
|
||||||
|
node = get_exonet_node()
|
||||||
|
if(!node || !node.on || !node.allow_external_communicators)
|
||||||
|
close_connection(reason = "Connection timed out")
|
||||||
|
|
||||||
|
// Proc: attack_self()
|
||||||
|
// Parameters: 1 (user - the mob that clicked the device in their hand)
|
||||||
|
// Description: Makes an exonet datum if one does not exist, allocates an address for it, maintains the lists of all devies, clears the alert icon, and
|
||||||
|
// finally makes NanoUI appear.
|
||||||
/obj/item/device/communicator/attack_self(mob/user)
|
/obj/item/device/communicator/attack_self(mob/user)
|
||||||
if(!owner)
|
if(!exonet)
|
||||||
var/choice = alert(user,"Would you like to register [src]?","Communicator","Yes","No")
|
exonet = new(src)
|
||||||
if(choice == "Yes")
|
if(!exonet.address)
|
||||||
register_device(user)
|
exonet.make_address("communicator-[user.client]-[user.name]")
|
||||||
return
|
if(!node)
|
||||||
|
node = get_exonet_node()
|
||||||
if(voice_mob)
|
populate_known_devices()
|
||||||
var/choice = alert(user,"Would you like to hang up?","Communicator","No","Yes")
|
|
||||||
if(choice == "Yes")
|
|
||||||
close_connection(user, "[user] hung up")
|
|
||||||
return
|
|
||||||
|
|
||||||
if(!server || !server.active)
|
|
||||||
user << "<span class='danger'>\icon[src] Error: Cannot establish connection to telecommunications.</span>"
|
|
||||||
sleep(30)
|
|
||||||
user << "<span class='danger'>\icon[src] Attempting to connect to telecommunications.</span>"
|
|
||||||
sleep(30)
|
|
||||||
if(!server)
|
|
||||||
server = get_message_server()
|
|
||||||
if(!server)
|
|
||||||
user << "<span class='danger'>\icon[src] Query to telecommunications timed out.</span>"
|
|
||||||
else if(server.active)
|
|
||||||
user << "<span class='notice'>\icon[src] Connection re-established.</span>"
|
|
||||||
else
|
|
||||||
if(server.active)
|
|
||||||
user << "<span class='notice'>\icon[src] Connection re-established.</span>"
|
|
||||||
else
|
|
||||||
user << "<span class='danger'>\icon[src] Query to telecommunications timed out.</span>"
|
|
||||||
|
|
||||||
return
|
|
||||||
|
|
||||||
var/mode = alert(user,"Would you like to check for communications requests or attempt to send a communication request to an external device?",
|
|
||||||
"Communicator","Check Requests","Send Request", "Cancel")
|
|
||||||
switch(mode)
|
|
||||||
if("Check Requests")
|
|
||||||
if(!voice_requests.len)
|
|
||||||
user << "\icon[src] No incoming communications requests."
|
|
||||||
return
|
|
||||||
|
|
||||||
else
|
|
||||||
alert_called = 0
|
alert_called = 0
|
||||||
update_icon()
|
update_icon()
|
||||||
var/choice = input(user,"Would would you like to talk to?","Communicator") as null|anything in voice_requests
|
ui_interact(user)
|
||||||
if(choice)
|
|
||||||
open_connection(user, choice)
|
// Proc: attack_ghost()
|
||||||
|
// Parameters: 1 (user - the ghost clicking on the device)
|
||||||
|
// Description: Recreates the known_devices list, so that the ghost looking at the device can see themselves, then calls ..() so that NanoUI appears.
|
||||||
|
/obj/item/device/communicator/attack_ghost(mob/user)
|
||||||
|
populate_known_devices() //Update the devices so ghosts can see the list on NanoUI.
|
||||||
|
..()
|
||||||
|
|
||||||
|
/mob/dead/observer
|
||||||
|
var/datum/exonet_protocol/exonet = null
|
||||||
|
|
||||||
|
// Proc: New()
|
||||||
|
// Parameters: None
|
||||||
|
// Description: Gives ghosts an exonet address based on their key and ghost name.
|
||||||
|
/mob/dead/observer/New()
|
||||||
|
. = ..()
|
||||||
|
spawn(20)
|
||||||
|
exonet = new(src)
|
||||||
|
if(client)
|
||||||
|
exonet.make_address("communicator-[src.client]-[src.client.prefs.real_name]")
|
||||||
|
else
|
||||||
|
exonet.make_address("communicator-[key]-[src.real_name]")
|
||||||
|
..()
|
||||||
|
|
||||||
|
// Proc: Destroy()
|
||||||
|
// Parameters: None
|
||||||
|
// Description: Removes the ghost's address and nulls the exonet datum, to allow qdel()ing.
|
||||||
|
/mob/dead/observer/Destroy()
|
||||||
|
. = ..()
|
||||||
|
processing_objects -= src
|
||||||
|
if(exonet)
|
||||||
|
exonet.remove_address()
|
||||||
|
exonet = null
|
||||||
|
..()
|
||||||
|
|
||||||
|
// Proc: ui_interact()
|
||||||
|
// Parameters: 4 (standard NanoUI arguments)
|
||||||
|
// Description: Uses a bunch of for loops to turn lists into lists of lists, so they can be displayed in nanoUI, then displays various buttons to the user.
|
||||||
|
/obj/item/device/communicator/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
|
||||||
|
// this is the data which will be sent to the ui
|
||||||
|
var/data[0] //General nanoUI information
|
||||||
|
var/communicators[0] //List of communicators
|
||||||
|
var/invites[0] //Communicators and ghosts we've invited to our communicator.
|
||||||
|
var/requests[0] //Communicators and ghosts wanting to go in our communicator.
|
||||||
|
var/voices[0] //Current /mob/living/voice s inside the device.
|
||||||
|
var/connected_communicators[0] //Current communicators connected to the device.
|
||||||
|
|
||||||
|
//First we add other 'local' communicators.
|
||||||
|
for(var/obj/item/device/communicator/comm in known_devices)
|
||||||
|
if(comm.network_visibility && comm.exonet)
|
||||||
|
communicators[++communicators.len] = list("name" = sanitize(comm.name), "address" = comm.exonet.address)
|
||||||
|
|
||||||
|
//Now for ghosts who we pretend have communicators.
|
||||||
|
for(var/mob/dead/observer/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)
|
||||||
|
|
||||||
|
//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)
|
||||||
|
|
||||||
|
//Ghosts we invited.
|
||||||
|
for(var/mob/dead/observer/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)
|
||||||
|
|
||||||
|
//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)
|
||||||
|
|
||||||
|
//Ghosts that want to talk to us.
|
||||||
|
for(var/mob/dead/observer/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)
|
||||||
|
|
||||||
|
//Now for all the voice mobs inside the communicator.
|
||||||
|
for(var/mob/living/voice/voice in contents)
|
||||||
|
voices[++voices.len] = list("name" = sanitize("[voice.name]'s communicator"), "true_name" = sanitize(voice.name))
|
||||||
|
|
||||||
|
//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))
|
||||||
|
|
||||||
|
|
||||||
|
data["owner"] = owner ? owner : "Unset"
|
||||||
|
data["connectionStatus"] = get_connection_to_tcomms()
|
||||||
|
data["visible"] = network_visibility
|
||||||
|
data["address"] = exonet.address ? exonet.address : "Unallocated"
|
||||||
|
data["targetAddress"] = target_address
|
||||||
|
data["currentTab"] = selected_tab
|
||||||
|
data["knownDevices"] = communicators
|
||||||
|
data["invitesSent"] = invites
|
||||||
|
data["requestsReceived"] = requests
|
||||||
|
data["voice_mobs"] = voices
|
||||||
|
data["communicating"] = connected_communicators
|
||||||
|
|
||||||
|
// update the ui if it exists, returns null if no ui is passed/found
|
||||||
|
ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open)
|
||||||
|
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)
|
||||||
|
// when the ui is first opened this is the data it will use
|
||||||
|
ui.set_initial_data(data)
|
||||||
|
// open the new ui window
|
||||||
|
ui.open()
|
||||||
|
// auto update every five Master Controller tick
|
||||||
|
ui.set_auto_update(5)
|
||||||
|
|
||||||
|
// Proc: Topic()
|
||||||
|
// Parameters: 2 (standard Topic arguments)
|
||||||
|
// Description: Responds to NanoUI button presses.
|
||||||
|
/obj/item/device/communicator/Topic(href, href_list)
|
||||||
|
if(..())
|
||||||
|
return 1
|
||||||
|
if(href_list["rename"])
|
||||||
|
var/new_name = sanitizeSafe(input(usr,"Please enter your name.","Communicator",usr.name) )
|
||||||
|
if(new_name)
|
||||||
|
owner = new_name
|
||||||
|
name = "[owner]'s [initial(name)]"
|
||||||
|
|
||||||
|
if(href_list["toggle_visibility"])
|
||||||
|
network_visibility = !network_visibility
|
||||||
|
|
||||||
|
if(href_list["add_hex"])
|
||||||
|
var/hex = href_list["add_hex"]
|
||||||
|
add_to_EPv2(hex)
|
||||||
|
|
||||||
|
if(href_list["write_target_address"])
|
||||||
|
var/new_address = sanitizeSafe(input(usr,"Please enter the desired target EPv2 address. Note that you must write the colons \
|
||||||
|
yourself.","Communicator",src.target_address) )
|
||||||
|
if(new_address)
|
||||||
|
target_address = new_address
|
||||||
|
|
||||||
|
if(href_list["clear_target_address"])
|
||||||
|
target_address = ""
|
||||||
|
|
||||||
|
if(href_list["dial"])
|
||||||
|
if(!get_connection_to_tcomms())
|
||||||
|
usr << "<span class='danger'>Error: Cannot connect to Exonet node.</span>"
|
||||||
return
|
return
|
||||||
|
var/their_address = href_list["dial"]
|
||||||
|
exonet.send_message(their_address, "voice")
|
||||||
|
if(href_list["disconnect"])
|
||||||
|
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")
|
||||||
|
for(var/obj/item/device/communicator/comm in communicating)
|
||||||
|
if(name_to_disconnect == comm.name)
|
||||||
|
close_connection(usr, comm, "[usr] hung up")
|
||||||
|
|
||||||
if("Send Request")
|
if(href_list["copy"])
|
||||||
var/list/potential_candidates = list()
|
target_address = href_list["copy"]
|
||||||
for(var/mob/dead/observer/O in dead_mob_list) //To my knowledge, there isn't a list of all ghosts.
|
|
||||||
if(O.client) //We only want logged in ghosts.
|
|
||||||
potential_candidates.Add(O.client.prefs.real_name) //This is needed so you don't see ghosted maint drones on the list.
|
|
||||||
|
|
||||||
if(!potential_candidates.len)
|
if(href_list["switch_tab"])
|
||||||
user << "\icon[src] Unfortunately, no other communicators outside your local area could be found. Please try again later."
|
selected_tab = href_list["switch_tab"]
|
||||||
|
|
||||||
|
nanomanager.update_uis(src)
|
||||||
|
add_fingerprint(usr)
|
||||||
|
|
||||||
|
// Proc: receive_exonet_message()
|
||||||
|
// Parameters: 3 (origin atom - the source of the message's holder, origin_address - where the message came from, message - the message received)
|
||||||
|
// Description: Handles voice requests and invite messages originating from both real communicators and ghosts. Also includes a ping response.
|
||||||
|
/obj/item/device/communicator/receive_exonet_message(var/atom/origin_atom, origin_address, message)
|
||||||
|
if(message == "voice")
|
||||||
|
if(isobserver(origin_atom) || istype(origin_atom, /obj/item/device/communicator))
|
||||||
|
if(origin_atom in voice_invites)
|
||||||
|
var/user = null
|
||||||
|
if(ismob(origin_atom.loc))
|
||||||
|
user = origin_atom.loc
|
||||||
|
open_connection(user, origin_atom)
|
||||||
return
|
return
|
||||||
|
else if(origin_atom in voice_requests)
|
||||||
|
return //Spam prevention
|
||||||
|
else
|
||||||
|
request(origin_atom)
|
||||||
|
if(message == "ping")
|
||||||
|
if(network_visibility)
|
||||||
|
var/random = rand(200,350)
|
||||||
|
random = random / 10
|
||||||
|
exonet.send_message(origin_address, "64 bytes received from [exonet.address] ecmp_seq=1 ttl=51 time=[random] ms")
|
||||||
|
|
||||||
var/choice = input(src,"Please select a person to call. Due to the vast distances involved \
|
// Proc: receive_exonet_message()
|
||||||
as well as time-zones, please note that your request may take a long time to be noticed, and \
|
// Parameters: 3 (origin atom - the source of the message's holder, origin_address - where the message came from, message - the message received)
|
||||||
that the available people to call may change as time passes.") as null|anything in potential_candidates
|
// Description: Handles voice requests and invite messages originating from both real communicators and ghosts. Also includes a ping response.
|
||||||
if(choice)
|
/mob/dead/observer/receive_exonet_message(origin_atom, origin_address, message)
|
||||||
var/mob/dead/observer/boo = null
|
if(message == "voice")
|
||||||
for(var/mob/dead/observer/O in dead_mob_list)
|
if(istype(origin_atom, /obj/item/device/communicator))
|
||||||
if(O.client && O.client.prefs.real_name == choice)
|
var/obj/item/device/communicator/comm = origin_atom
|
||||||
boo = O
|
if(src in comm.voice_invites)
|
||||||
break
|
comm.open_connection(src)
|
||||||
boo << "\icon[src] A communications request has been received from [src]. If you would like to answer, please use the <b>Call Communicator</b> verb."
|
|
||||||
user << "\icon[src] A communications request has been sent to [choice]. Now you need to wait until they answer, if they do."
|
|
||||||
return
|
return
|
||||||
|
src << "<span class='notice'>\icon[origin_atom] Receiving communicator request from [origin_atom]. To answer, use the <b>Call Communicator</b> \
|
||||||
|
verb, and select that name to answer the call.</span>"
|
||||||
|
comm.voice_invites |= src
|
||||||
|
if(message == "ping")
|
||||||
|
if(client && client.prefs.communicator_visibility)
|
||||||
|
var/random = rand(450,700)
|
||||||
|
random = random / 10
|
||||||
|
exonet.send_message(origin_address, "64 bytes received from [exonet.address] ecmp_seq=1 ttl=51 time=[random] ms")
|
||||||
|
|
||||||
/obj/item/device/communicator/proc/register_device(mob/user, var/job = null)
|
// Proc: register_device()
|
||||||
|
// Parameters: 1 (user - the person to use their name for)
|
||||||
|
// Description: Updates the owner's name and the device's name.
|
||||||
|
/obj/item/device/communicator/proc/register_device(mob/user)
|
||||||
if(!user)
|
if(!user)
|
||||||
return
|
return
|
||||||
owner = user.name
|
owner = user.name
|
||||||
if(job)
|
|
||||||
owner_job = job
|
name = "[owner]'s [initial(name)]"
|
||||||
owner_rank = job
|
|
||||||
|
// 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)
|
||||||
|
// Description: Typechecks the candidate, then calls the correct proc for further connecting.
|
||||||
|
/obj/item/device/communicator/proc/open_connection(mob/user, var/atom/candidate)
|
||||||
|
if(isobserver(candidate))
|
||||||
|
voice_invites.Remove(candidate)
|
||||||
|
open_connection_to_ghost(user, candidate)
|
||||||
else
|
else
|
||||||
if(ishuman(user))
|
if(istype(candidate, /obj/item/device/communicator))
|
||||||
var/mob/living/carbon/human/H = user
|
open_connection_to_communicator(user, candidate)
|
||||||
var/obj/item/weapon/card/id/idcard = H.wear_id
|
|
||||||
if(idcard)
|
// Proc: open_connection_to_communicator()
|
||||||
owner_job = idcard.assignment
|
// Parameters: 2 (user - the person who initiated this and will be receiving feedback information, candidate - someone else's communicator)
|
||||||
owner_rank = idcard.rank
|
// Description: Adds the candidate and src to each other's communicating lists, allowing messages seen by the devices to be relayed.
|
||||||
else
|
/obj/item/device/communicator/proc/open_connection_to_communicator(mob/user, var/atom/candidate)
|
||||||
user << "<span class='warning'>\icon[src] ID not found. Please swipe your ID on the device.</span>"
|
if(!istype(candidate, /obj/item/device/communicator))
|
||||||
return
|
return
|
||||||
else //They're probably a synth.
|
var/obj/item/device/communicator/comm = candidate
|
||||||
owner_job = user.job
|
voice_invites.Remove(candidate)
|
||||||
owner_rank = user.job
|
comm.voice_requests.Remove(src)
|
||||||
|
|
||||||
name = "\improper [initial(name)]-[owner] ([owner_job])"
|
if(user)
|
||||||
|
comm.visible_message("<span class='notice'>\icon[src] Connecting to [src].</span>")
|
||||||
|
user << "<span class='notice'>\icon[src] Attempting to call [comm].</span>"
|
||||||
|
sleep(10)
|
||||||
|
user << "<span class='notice'>\icon[src] Dialing internally from [station_name()], Kara Subsystem, [system_name()].</span>"
|
||||||
|
sleep(20) //If they don't have an exonet something is very wrong and we want a runtime.
|
||||||
|
user << "<span class='notice'>\icon[src] Connection re-routed to [comm] at [comm.exonet.address].</span>"
|
||||||
|
sleep(40)
|
||||||
|
user << "<span class='notice'>\icon[src] Connection to [comm] at [comm.exonet.address] established.</span>"
|
||||||
|
comm.visible_message("<span class='notice'>\icon[src] Connection to [src] at [exonet.address] established.</span>")
|
||||||
|
sleep(20)
|
||||||
|
|
||||||
/*
|
communicating |= comm
|
||||||
owner = idcard.registered_name
|
comm.communicating |= src
|
||||||
owner_job = idcard.assignment
|
|
||||||
owner_rank = idcard.rank
|
|
||||||
name = "\improper communicator-[owner] ([ownjob])"
|
|
||||||
*/
|
|
||||||
|
|
||||||
/obj/item/device/communicator/proc/open_connection(mob/user, var/mob/candidate)
|
// Proc: open_connection_to_ghost()
|
||||||
|
// Parameters: 2 (user - the person who initiated this, candidate - the ghost that will be turned into a voice mob)
|
||||||
|
// Description: Pulls the candidate ghost from deadchat, makes a new voice mob, transfers their identity, then their client.
|
||||||
|
/obj/item/device/communicator/proc/open_connection_to_ghost(mob/user, var/mob/candidate)
|
||||||
if(!isobserver(candidate))
|
if(!isobserver(candidate))
|
||||||
return
|
return
|
||||||
|
|
||||||
//Handle moving the ghost into the new shell.
|
//Handle moving the ghost into the new shell.
|
||||||
announce_ghost_joinleave(candidate, 0, "They are occupying a personal communications device now.")
|
announce_ghost_joinleave(candidate, 0, "They are occupying a personal communications device now.")
|
||||||
voice_mob = new /mob/living/voice(src) //Make the voice mob the ghost is going to be.
|
voice_requests.Remove(candidate)
|
||||||
voice_mob.transfer_identity(candidate) //Now make the voice mob load from the ghost's active character in preferences.
|
voice_invites.Remove(candidate)
|
||||||
|
var/mob/living/voice/new_voice = new /mob/living/voice(src) //Make the voice mob the ghost is going to be.
|
||||||
|
new_voice.transfer_identity(candidate) //Now make the voice mob load from the ghost's active character in preferences.
|
||||||
//Do some simple logging since this is a tad risky as a concept.
|
//Do some simple logging since this is a tad risky as a concept.
|
||||||
var/msg = "[candidate.client ? "[candidate.client.key]" : "*no key*"] ([candidate]) has entered [src], triggered by \
|
var/msg = "[candidate && candidate.client ? "[candidate.client.key]" : "*no key*"] ([candidate]) has entered [src], triggered by \
|
||||||
[user.client ? "[user.client.key]" : "*no key*"] ([user]) at [x],[y],[z]. They have joined as [voice_mob.name]."
|
[user && user.client ? "[user.client.key]" : "*no key*"] ([user ? "[user]" : "*null*"]) at [x],[y],[z]. They have joined as [new_voice.name]."
|
||||||
message_admins(msg)
|
message_admins(msg)
|
||||||
log_game(msg)
|
log_game(msg)
|
||||||
voice_mob.mind = candidate.mind //Transfer the mind, if any.
|
new_voice.mind = candidate.mind //Transfer the mind, if any.
|
||||||
voice_mob.ckey = candidate.ckey //Finally, bring the client over.
|
new_voice.ckey = candidate.ckey //Finally, bring the client over.
|
||||||
|
voice_mobs.Add(new_voice)
|
||||||
|
|
||||||
var/obj/screen/blackness = new() //Makes a black screen, so the candidate can't see what's going on before actually 'connecting' to the communicator.
|
var/obj/screen/blackness = new() //Makes a black screen, so the candidate can't see what's going on before actually 'connecting' to the communicator.
|
||||||
blackness.screen_loc = ui_entire_screen
|
blackness.screen_loc = ui_entire_screen
|
||||||
blackness.icon = 'icons/effects/effects.dmi'
|
blackness.icon = 'icons/effects/effects.dmi'
|
||||||
blackness.icon_state = "1"
|
blackness.icon_state = "1"
|
||||||
blackness.mouse_opacity = 2 //Can't see anything!
|
blackness.mouse_opacity = 2 //Can't see anything!
|
||||||
voice_mob.client.screen.Add(blackness)
|
new_voice.client.screen.Add(blackness)
|
||||||
|
|
||||||
update_icon()
|
update_icon()
|
||||||
|
|
||||||
//Now for some connection fluff.
|
//Now for some connection fluff.
|
||||||
|
if(user)
|
||||||
user << "<span class='notice'>\icon[src] Connecting to [candidate].</span>"
|
user << "<span class='notice'>\icon[src] Connecting to [candidate].</span>"
|
||||||
src.voice_mob << "<span class='notice'>\icon[src] Attempting to call [src].</span>"
|
new_voice << "<span class='notice'>\icon[src] Attempting to call [src].</span>"
|
||||||
sleep(10)
|
sleep(10)
|
||||||
src.voice_mob << "<span class='notice'>\icon[src] Dialing to [station_name()], Kara Subsystem, [system_name()].</span>"
|
new_voice << "<span class='notice'>\icon[src] Dialing to [station_name()], Kara Subsystem, [system_name()].</span>"
|
||||||
sleep(20)
|
sleep(20)
|
||||||
src.voice_mob << "<span class='notice'>\icon[src] Connecting to [station_name()] telecommunications array.</span>"
|
new_voice << "<span class='notice'>\icon[src] Connecting to [station_name()] telecommunications array.</span>"
|
||||||
sleep(40)
|
sleep(40)
|
||||||
src.voice_mob << "<span class='notice'>\icon[src] Connection to [station_name()] telecommunications array established. Redirecting signal to [src].</span>"
|
new_voice << "<span class='notice'>\icon[src] Connection to [station_name()] telecommunications array established. Redirecting signal to [src].</span>"
|
||||||
sleep(20)
|
sleep(20)
|
||||||
|
|
||||||
//We're connected, no need to hide everything.
|
//We're connected, no need to hide everything.
|
||||||
voice_mob.client.screen.Remove(blackness)
|
new_voice.client.screen.Remove(blackness)
|
||||||
qdel(blackness)
|
qdel(blackness)
|
||||||
|
|
||||||
voice_mob.stable_connection = 1
|
new_voice << "<span class='notice'>\icon[src] Connection to [src] established.</span>"
|
||||||
voice_mob << "<span class='notice'>\icon[src] Connection to [src] established.</span>"
|
new_voice << "<b>To talk to the person on the other end of the call, just talk normally.</b>"
|
||||||
voice_mob << "<b>To talk to the person on the other end of the call, just talk normally.</b>"
|
new_voice << "<b>If you want to end the call, use the 'Hang Up' verb. The other person can also hang up at any time.</b>"
|
||||||
voice_mob << "<b>If you want to end the call, use the 'Hang Up' verb. The other person can also hang up at any time.</b>"
|
new_voice << "<b>Remember, your character does not know anything you've learned from observing!</b>"
|
||||||
voice_mob << "<b>Remember, your character does not know anything you've learned from observing!</b>"
|
if(new_voice.mind)
|
||||||
if(voice_mob.mind)
|
new_voice.mind.assigned_role = "Disembodied Voice"
|
||||||
voice_mob.mind.assigned_role = "Disembodied Voice"
|
if(user)
|
||||||
user << "<span class='notice'>\icon[src] Your communicator is now connected to [candidate]'s communicator.</span>"
|
user << "<span class='notice'>\icon[src] Your communicator is now connected to [candidate]'s communicator.</span>"
|
||||||
|
|
||||||
/obj/item/device/communicator/proc/close_connection(mob/user, var/reason)
|
// Proc: close_connection()
|
||||||
if(!voice_mob)
|
// Parameters: 3 (user - the user who initiated the disconnect, target - the mob or device being disconnected, reason - string shown when disconnected)
|
||||||
|
// Description: Deletes specific voice_mobs or disconnects communicators, and shows a message to everyone when doing so. If target is null, all communicators
|
||||||
|
// and voice mobs are removed.
|
||||||
|
/obj/item/device/communicator/proc/close_connection(mob/user, var/atom/target, var/reason)
|
||||||
|
if(voice_mobs.len == 0 && communicating.len == 0)
|
||||||
return
|
return
|
||||||
if(user != voice_mob)
|
|
||||||
user << "<span class='notice'>\icon[src] [reason].</span>"
|
|
||||||
voice_mob << "<span class='danger'>\icon[src] [reason].</span>"
|
|
||||||
else
|
|
||||||
if(ismob(loc))
|
|
||||||
user << "<span class='notice'>\icon[src] [reason].</span>"
|
|
||||||
loc << "<span class='danger'>\icon[src] [reason].</span>"
|
|
||||||
qdel(voice_mob)
|
|
||||||
|
|
||||||
|
for(var/mob/living/voice/voice in voice_mobs) //Handle ghost-callers
|
||||||
|
if(target && voice != target) //If no target is inputted, it deletes all of them.
|
||||||
|
continue
|
||||||
|
voice << "<span class='danger'>\icon[src] [reason].</span>"
|
||||||
|
visible_message("<span class='danger'>\icon[src] [reason].</span>")
|
||||||
|
voice_mobs.Remove(voice)
|
||||||
|
qdel(voice)
|
||||||
|
|
||||||
|
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>")
|
||||||
|
comm.communicating.Remove(src)
|
||||||
|
communicating.Remove(comm)
|
||||||
update_icon()
|
update_icon()
|
||||||
|
|
||||||
/obj/item/device/communicator/proc/request(var/mob/candidate)
|
// Proc: request()
|
||||||
if(!isobserver(candidate))
|
// Parameters: 1 (candidate - the ghost or communicator wanting to call the device)
|
||||||
return
|
// Description: Response to a communicator or observer trying to call the device. Adds them to the list of requesters
|
||||||
|
/obj/item/device/communicator/proc/request(var/atom/candidate)
|
||||||
if(candidate in voice_requests)
|
if(candidate in voice_requests)
|
||||||
return
|
return
|
||||||
voice_requests.Add(candidate)
|
var/who = null
|
||||||
|
if(isobserver(candidate))
|
||||||
|
who = candidate.name
|
||||||
|
else if(istype(candidate, /obj/item/device/communicator))
|
||||||
|
var/obj/item/device/communicator/comm = candidate
|
||||||
|
who = comm.owner
|
||||||
|
comm.voice_invites |= src
|
||||||
|
|
||||||
|
if(!who)
|
||||||
|
return
|
||||||
|
|
||||||
|
voice_requests |= candidate
|
||||||
|
|
||||||
playsound(loc, 'sound/machines/twobeep.ogg', 50, 1)
|
playsound(loc, 'sound/machines/twobeep.ogg', 50, 1)
|
||||||
for (var/mob/O in hearers(2, loc))
|
for (var/mob/O in hearers(2, loc))
|
||||||
@@ -243,17 +510,31 @@ var/global/list/obj/item/device/communicator/all_communicators = list()
|
|||||||
L = loc
|
L = loc
|
||||||
|
|
||||||
if(L)
|
if(L)
|
||||||
L << "<span class='notice'>\icon[src] Communications request from [candidate].</span>"
|
L << "<span class='notice'>\icon[src] Communications request from [who].</span>"
|
||||||
|
|
||||||
|
// Proc: Destroy()
|
||||||
|
// Parameters: None
|
||||||
|
// Description: Deletes all the voice mobs, disconnects all linked communicators, and cuts lists to allow successful qdel()
|
||||||
/obj/item/device/communicator/Destroy()
|
/obj/item/device/communicator/Destroy()
|
||||||
if(voice_mob)
|
for(var/mob/living/voice/voice in contents)
|
||||||
voice_mob << "<span class='danger'>\icon[src] Connection timed out with remote host.</span>"
|
voice_mobs.Remove(voice)
|
||||||
qdel(voice_mob)
|
voice << "<span class='danger'>\icon[src] Connection timed out with remote host.</span>"
|
||||||
|
qdel(voice)
|
||||||
|
close_connection(reason = "Connection timed out")
|
||||||
|
communicating.Cut()
|
||||||
|
voice_requests.Cut()
|
||||||
|
voice_invites.Cut()
|
||||||
all_communicators -= src
|
all_communicators -= src
|
||||||
|
if(exonet)
|
||||||
|
exonet.remove_address()
|
||||||
|
exonet = null
|
||||||
..()
|
..()
|
||||||
|
|
||||||
|
// Proc: update_icon()
|
||||||
|
// Parameters: None
|
||||||
|
// Description: Self explanatory
|
||||||
/obj/item/device/communicator/update_icon()
|
/obj/item/device/communicator/update_icon()
|
||||||
if(voice_mob)
|
if(voice_mobs.len > 0)
|
||||||
icon_state = "communicator-active"
|
icon_state = "communicator-active"
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -263,37 +544,54 @@ var/global/list/obj/item/device/communicator/all_communicators = list()
|
|||||||
|
|
||||||
icon_state = initial(icon_state)
|
icon_state = initial(icon_state)
|
||||||
|
|
||||||
|
// Proc: see_emote()
|
||||||
|
// Parameters: 2 (M - the mob the emote originated from, text - the emote's contents)
|
||||||
|
// Description: Relays the emote to all linked communicators.
|
||||||
/obj/item/device/communicator/see_emote(mob/living/M, text)
|
/obj/item/device/communicator/see_emote(mob/living/M, text)
|
||||||
if(voice_mob && voice_mob.client)
|
var/rendered = "\icon[src] <span class='message'>[text]</span>"
|
||||||
if(!voice_mob.stable_connection)
|
for(var/obj/item/device/communicator/comm in communicating)
|
||||||
return 0
|
for(var/mob/mob in viewers(comm)) //We can't use visible_message(), or else we will get an infinite loop if two communicators hear each other.
|
||||||
var/rendered = "<span class='message'>[text]</span>"
|
mob.show_message(rendered)
|
||||||
voice_mob.show_message(rendered, 2)
|
|
||||||
..()
|
..()
|
||||||
|
|
||||||
/obj/item/device/communicator/show_message(msg, type, alt, alt_type)
|
// Proc: hear_talk()
|
||||||
if(voice_mob && voice_mob.client)
|
// Parameters: 4 (M - the mob the speech originated from, text - what is being said, verb - the word used to describe how text is being said, speaking - language
|
||||||
if(!voice_mob.stable_connection)
|
// being used)
|
||||||
return 0
|
// Description: Relays the speech to all linked communicators.
|
||||||
var/rendered = "<span class='message'>[msg]</span>"
|
/obj/item/device/communicator/hear_talk(mob/living/M, text, verb, datum/language/speaking)
|
||||||
voice_mob.show_message(rendered, type)
|
for(var/obj/item/device/communicator/comm in communicating)
|
||||||
..()
|
for(var/mob/mob in viewers(comm))
|
||||||
|
//Can whoever is hearing us understand?
|
||||||
/obj/item/device/communicator/attackby(obj/item/C as obj, mob/user as mob)
|
if(!mob.say_understands(M, speaking))
|
||||||
if(istype(C, /obj/item/weapon/card/id))
|
if(speaking)
|
||||||
var/obj/item/weapon/card/id/idcard = C
|
text = speaking.scramble(text)
|
||||||
if(!idcard.registered_name)
|
|
||||||
user << "<span class='notice'>\icon[src] \The [src] rejects the ID.</span>"
|
|
||||||
return
|
|
||||||
if(!owner)
|
|
||||||
owner = idcard.registered_name
|
|
||||||
owner_job = idcard.assignment
|
|
||||||
owner_rank = idcard.rank
|
|
||||||
name = "\improper communicator-[owner] ([owner_job])"
|
|
||||||
user << "<span class='notice'>\icon[src] Card scanned.</span>"
|
|
||||||
else
|
else
|
||||||
|
text = stars(text)
|
||||||
|
var/name_used = M.GetVoice()
|
||||||
|
var/rendered = null
|
||||||
|
if(speaking) //Language being used
|
||||||
|
rendered = "<span class='game say'>\icon[src] <span class='name'>[name_used]</span> [speaking.format_message(text, verb)]</span>"
|
||||||
|
else
|
||||||
|
rendered = "<span class='game say'>\icon[src] <span class='name'>[name_used]</span> [verb], <span class='message'>\"[text]\"</span></span>"
|
||||||
|
mob.show_message(rendered, 2)
|
||||||
|
|
||||||
|
// Proc: show_message()
|
||||||
|
// Parameters: 4 (msg - the message, type - number to determine if message is visible or audible, alt - unknown, alt_type - unknown)
|
||||||
|
// Description: Relays the message to all linked communicators.
|
||||||
|
/obj/item/device/communicator/show_message(msg, type, alt, alt_type)
|
||||||
|
var/rendered = "\icon[src] <span class='message'>[msg]</span>"
|
||||||
|
for(var/mob/living/voice/voice in contents)
|
||||||
|
if(voice.client)
|
||||||
|
voice.show_message(rendered, type)
|
||||||
|
for(var/obj/item/device/communicator/comm in communicating)
|
||||||
|
// comm.audible_message(rendered)
|
||||||
|
for(var/mob/mob in hearers(comm)) //Ditto for audible messages.
|
||||||
|
mob.show_message(rendered)
|
||||||
..()
|
..()
|
||||||
|
|
||||||
|
// Verb: join_as_voice()
|
||||||
|
// Parameters: None
|
||||||
|
// Description: Allows ghosts to call communicators, if they meet all the requirements.
|
||||||
/mob/dead/verb/join_as_voice()
|
/mob/dead/verb/join_as_voice()
|
||||||
set category = "Ghost"
|
set category = "Ghost"
|
||||||
set name = "Call Communicator"
|
set name = "Call Communicator"
|
||||||
@@ -319,18 +617,27 @@ var/global/list/obj/item/device/communicator/all_communicators = list()
|
|||||||
src << "<span class='danger'>Your identity is already present in the game world. Please load in a different character first.</span>"
|
src << "<span class='danger'>Your identity is already present in the game world. Please load in a different character first.</span>"
|
||||||
return
|
return
|
||||||
|
|
||||||
if(!all_communicators.len)
|
var/obj/machinery/exonet_node/E = get_exonet_node()
|
||||||
|
if(!E || !E.on || !E.allow_external_communicators)
|
||||||
|
src << "<span class='danger'>The Exonet node at telecommunications is down at the moment, or is actively blocking you, so your call can't go through.</span>"
|
||||||
|
return
|
||||||
|
|
||||||
|
var/list/choices = list()
|
||||||
|
for(var/obj/item/device/communicator/comm in all_communicators)
|
||||||
|
if(!comm.network_visibility)
|
||||||
|
continue
|
||||||
|
choices.Add(comm)
|
||||||
|
|
||||||
|
if(!choices.len)
|
||||||
src << "<span class='danger'>There are no available communicators, sorry.</span>"
|
src << "<span class='danger'>There are no available communicators, sorry.</span>"
|
||||||
return
|
return
|
||||||
|
|
||||||
if(!get_message_server())
|
var/choice = input(src,"Send a voice request to whom?") as null|anything in choices
|
||||||
src << "<span class='danger'>The messaging server or telecommunications is down at the moment, so your call can't go through.</span>"
|
|
||||||
return
|
|
||||||
|
|
||||||
var/choice = input(src,"Send a voice request to whom?") as null|anything in all_communicators
|
|
||||||
if(choice)
|
if(choice)
|
||||||
var/obj/item/device/communicator/chosen_communicator = choice
|
var/obj/item/device/communicator/chosen_communicator = choice
|
||||||
chosen_communicator.request(src)
|
var/mob/dead/observer/O = src
|
||||||
|
if(O.exonet)
|
||||||
|
O.exonet.send_message(chosen_communicator.exonet.address, "voice")
|
||||||
|
|
||||||
src << "A communications request has been sent to [chosen_communicator]. Now you need to wait until someone answers."
|
src << "A communications request has been sent to [chosen_communicator]. Now you need to wait until someone answers."
|
||||||
|
|
||||||
@@ -338,6 +645,22 @@ var/global/list/obj/item/device/communicator/all_communicators = list()
|
|||||||
name = "integrated communicator"
|
name = "integrated communicator"
|
||||||
desc = "A circuit used for long-range communications, able to be integrated into a system."
|
desc = "A circuit used for long-range communications, able to be integrated into a system."
|
||||||
|
|
||||||
|
//A stupid hack because synths don't use languages properly or something.
|
||||||
|
//I don't want to go digging in saycode for a week, so BS it as translation software or something.
|
||||||
|
|
||||||
|
// Proc: open_connection_to_ghost()
|
||||||
|
// Parameters: 2 (refer to base definition for arguments)
|
||||||
|
// Description: Synths don't use languages properly, so this is a bandaid fix until that can be resolved..
|
||||||
|
/obj/item/device/communicator/integrated/open_connection_to_ghost(user, candidate)
|
||||||
|
..(user, candidate)
|
||||||
|
spawn(1)
|
||||||
|
for(var/mob/living/voice/V in contents)
|
||||||
|
V.universal_speak = 1
|
||||||
|
V.universal_understand = 1
|
||||||
|
|
||||||
|
// Verb: activate()
|
||||||
|
// Parameters: None
|
||||||
|
// Description: Lets synths use their communicators without hands.
|
||||||
/obj/item/device/communicator/integrated/verb/activate()
|
/obj/item/device/communicator/integrated/verb/activate()
|
||||||
set category = "AI IM"
|
set category = "AI IM"
|
||||||
set name = "Use Communicator"
|
set name = "Use Communicator"
|
||||||
|
|||||||
@@ -73,3 +73,17 @@
|
|||||||
"/obj/item/weapon/stock_parts/subspace/filter" = 1,
|
"/obj/item/weapon/stock_parts/subspace/filter" = 1,
|
||||||
"/obj/item/weapon/stock_parts/subspace/crystal" = 1,
|
"/obj/item/weapon/stock_parts/subspace/crystal" = 1,
|
||||||
"/obj/item/weapon/stock_parts/micro_laser/high" = 2)
|
"/obj/item/weapon/stock_parts/micro_laser/high" = 2)
|
||||||
|
|
||||||
|
//This isn't a real telecomms board but I don't want to make a whole file to hold only one circuitboard.
|
||||||
|
/obj/item/weapon/circuitboard/telecomms/exonet_node
|
||||||
|
name = T_BOARD("exonet node")
|
||||||
|
build_path = "/obj/machinery/exonet_node"
|
||||||
|
origin_tech = list(TECH_DATA = 5, TECH_ENGINEERING = 5, TECH_BLUESPACE = 4)
|
||||||
|
req_components = list(
|
||||||
|
"/obj/item/weapon/stock_parts/subspace/ansible" = 1,
|
||||||
|
"/obj/item/weapon/stock_parts/subspace/filter" = 1,
|
||||||
|
"/obj/item/weapon/stock_parts/manipulator" = 2,
|
||||||
|
"/obj/item/weapon/stock_parts/micro_laser" = 1,
|
||||||
|
"/obj/item/weapon/stock_parts/subspace/crystal" = 1,
|
||||||
|
"/obj/item/weapon/stock_parts/subspace/treatment" = 2,
|
||||||
|
"/obj/item/stack/cable_coil" = 2)
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
/datum/category_item/player_setup_item/player_global/communicators
|
||||||
|
name = "Communicators"
|
||||||
|
sort_order = 4
|
||||||
|
|
||||||
|
/datum/category_item/player_setup_item/player_global/communicators/load_preferences(var/savefile/S)
|
||||||
|
S["communicator_visibility"] >> pref.communicator_visibility
|
||||||
|
|
||||||
|
|
||||||
|
/datum/category_item/player_setup_item/player_global/communicators/save_preferences(var/savefile/S)
|
||||||
|
S["communicator_visibility"] << pref.communicator_visibility
|
||||||
|
|
||||||
|
|
||||||
|
/datum/category_item/player_setup_item/player_global/communicators/sanitize_preferences()
|
||||||
|
pref.communicator_visibility = sanitize_integer(pref.communicator_visibility, 0, 1, initial(pref.communicator_visibility))
|
||||||
|
|
||||||
|
/datum/category_item/player_setup_item/player_global/communicators/content(var/mob/user)
|
||||||
|
. += "<b>Communicator Identity:</b><br>"
|
||||||
|
. += "Visibility: <a href='?src=\ref[src];toggle_comm_visibility=1'><b>[(pref.communicator_visibility) ? "Yes" : "No"]</b></a><br>"
|
||||||
|
|
||||||
|
/datum/category_item/player_setup_item/player_global/communicators/OnTopic(var/href,var/list/href_list, var/mob/user)
|
||||||
|
if(href_list["toggle_comm_visibility"])
|
||||||
|
if(CanUseTopic(user))
|
||||||
|
pref.communicator_visibility = !pref.communicator_visibility
|
||||||
|
return TOPIC_REFRESH
|
||||||
|
|
||||||
|
return ..()
|
||||||
@@ -108,6 +108,9 @@ datum/preferences
|
|||||||
// OOC Metadata:
|
// OOC Metadata:
|
||||||
var/metadata = ""
|
var/metadata = ""
|
||||||
|
|
||||||
|
// Communicator identity data
|
||||||
|
var/communicator_visibility = 0
|
||||||
|
|
||||||
var/client/client = null
|
var/client/client = null
|
||||||
|
|
||||||
var/datum/category_collection/player_setup_collection/player_setup
|
var/datum/category_collection/player_setup_collection/player_setup
|
||||||
|
|||||||
@@ -18,3 +18,5 @@
|
|||||||
/datum/event/communications_blackout/start()
|
/datum/event/communications_blackout/start()
|
||||||
for(var/obj/machinery/telecomms/T in telecomms_list)
|
for(var/obj/machinery/telecomms/T in telecomms_list)
|
||||||
T.emp_act(1)
|
T.emp_act(1)
|
||||||
|
for(var/obj/machinery/exonet_node/N in machines)
|
||||||
|
N.emp_act(1)
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
endWhen = 36000
|
endWhen = 36000
|
||||||
var/last_spam_time = 0
|
var/last_spam_time = 0
|
||||||
var/obj/machinery/message_server/useMS
|
var/obj/machinery/message_server/useMS
|
||||||
|
var/obj/machinery/exonet_node/node
|
||||||
|
|
||||||
/datum/event/pda_spam/setup()
|
/datum/event/pda_spam/setup()
|
||||||
last_spam_time = world.time
|
last_spam_time = world.time
|
||||||
@@ -19,6 +20,11 @@
|
|||||||
//if there's no spam managed to get to receiver for five minutes, give up
|
//if there's no spam managed to get to receiver for five minutes, give up
|
||||||
kill()
|
kill()
|
||||||
return
|
return
|
||||||
|
if(!node)
|
||||||
|
node = get_exonet_node()
|
||||||
|
|
||||||
|
if(!node || !node.on || !node.allow_external_PDAs)
|
||||||
|
return
|
||||||
|
|
||||||
if(!useMS || !useMS.active)
|
if(!useMS || !useMS.active)
|
||||||
useMS = null
|
useMS = null
|
||||||
|
|||||||
22
code/modules/examine/descriptions/devices.dm
Normal file
22
code/modules/examine/descriptions/devices.dm
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
/obj/item/device/communicator
|
||||||
|
description_info = "This device allows someone to speak to another player sourced from the observer pool, as well as other communicators linked to it. \
|
||||||
|
To use the device, use it in your hand, and it will open an interface with various buttons. Near the bottom will be a list of devices currently available \
|
||||||
|
for you to call, both from communications on the station, and communicators very far away (observers). To call someone, send a communications request to \
|
||||||
|
someone and hope they respond, or receive one yourself and respond to them. Hanging up is also simple, and is handled in the 'Manage Connections' tab."
|
||||||
|
|
||||||
|
description_fluff = "As a concept, a device that allows long-distance communication has existed for over five hundred years. A device that can accomplish \
|
||||||
|
that while in space, across star systems, and that the consumer can afford and use without training, is much more recent, and is thanks to the backbone \
|
||||||
|
that is the Exonet.<br>\
|
||||||
|
<br>\
|
||||||
|
The Exonet is the predominant interstellar telecom system, servicing trillions of devices across a large portion of human-controlled space. \
|
||||||
|
It is distributed by a massive network of telecommunication satellites, some privately owned and others owned by the systems<6D> local governments, \
|
||||||
|
that utilize FTL technologies to bounce data between satellites at speeds that would not be possible at sub-light technology. This communicator \
|
||||||
|
uses a protocol called Exonet Protocol Version 2, generally shortened to EPv2.<br>\
|
||||||
|
<br>\
|
||||||
|
EPv2 is the most common communications protocol in the Exonet, and was specifically designed for it. It was designed to facilitate communication \
|
||||||
|
between any device in a star system, and have the ability to forward interstellar requests at the root node of that system<65>s Exonet. \
|
||||||
|
It is also built to cope with the reality that the numerous nodes in a system will likely have frequent outages. The protocol allows for \
|
||||||
|
up to 18,446,744,073,709,551,616 unique addresses, one of which is assigned to this device."
|
||||||
|
|
||||||
|
description_antag = "Electromagnetic pulses will cause the device to disconnect all linked communicators. Turning off the Exonet node at the Telecomms \
|
||||||
|
satellite will also accomplish this, but for all communicators on and near the station. This may be needed to allow for a quiet kill or capture."
|
||||||
36
code/modules/examine/descriptions/telecomms.dm
Normal file
36
code/modules/examine/descriptions/telecomms.dm
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
//This is not strictly a telecomms machine but conceptually it is.
|
||||||
|
/obj/machinery/exonet_node
|
||||||
|
description_info = "This machine is needed for several machines back at the colony to interact with systems beyond this region of space, such as \
|
||||||
|
communicators, external PDA messages, and even newscaster units, which host their content externally. You can fiddle with the device with \
|
||||||
|
just your hands, due to the integrated monitor and keyboard. Synthetic units can interface with it as well, just like most other machines."
|
||||||
|
|
||||||
|
description_fluff = "This is one of many nodes that make up the Exonet, which services trillions of devices across space. This particular node \
|
||||||
|
is referred to as a terminal node, servicing the Northern Star.<br>\
|
||||||
|
<br>\
|
||||||
|
In the beginning of humanity's ascend into space, the Exonet didn't exist. Instead, the Exonet is the evolution to a network called the Interplanetary \
|
||||||
|
Internet (sometimes referred to as the InterPlaNet), which was conceived and developed due to the limitations of the terrestrial Internet, mainly because \
|
||||||
|
the IP protocol was unsuitable for long range communications in space, due to the massive delays associated with lightspeed being unable to overcome \
|
||||||
|
the massive distances between planets in a timely manner. It was a store-and-forward network of smaller internets, distributed between various nodes, \
|
||||||
|
and was designed to be error, fault, and delay tolerant. The first nodes were put into space around the time when colonization had begun, to service humanity<74>s \
|
||||||
|
close holdings, such as Luna and Mars.<br>\
|
||||||
|
<br>\
|
||||||
|
By 2104, the Interplanetary Internet had coverage within most of Sol, but the network of networks were limited by the speed of light, and due \
|
||||||
|
to orbital mechanics, the delay for a request to be processed could vary. As an example, a direct message from Mars to Earth could take anywhere \
|
||||||
|
between three to twenty two minutes to get to Earth, and then the same amount of time to return to Mars. Coverage was also spotty due to the \
|
||||||
|
nature of operating at such vast distances.<br>\
|
||||||
|
<br>\
|
||||||
|
Fortunately, a method for traveling faster than light was discovered, which could allow data to be transmitted beyond the speed of light, \
|
||||||
|
and thus overcome the limitations of the Interplanetary Internet. One by one, each of the nodes were upgraded to utilize cutting edge \
|
||||||
|
(at the time) FTL technologies to rapidly increase response time.<br>\
|
||||||
|
<br>\
|
||||||
|
Once humanity had send colonists out beyond Sol, to other star systems such as Sirius and Alpha Centauri, they required their own interplanetary \
|
||||||
|
internet, so a new protocol had to be created, and another hardware upgrade for the separate interplanetary internets. The end result allowed \
|
||||||
|
communications between Sol and the various exosolar systems, and was dubbed the Exonet. It remains the most common way for consumers to engage \
|
||||||
|
in long range communications across planets and star systems. Generally, each system has their own Exonet, which is connected to all the other \
|
||||||
|
Exonets at the root node(s), and is typically arranged in a tree structure. The root node(s) are generally government-owned and are very secure \
|
||||||
|
and resilient to failure.<br>\
|
||||||
|
<br>\
|
||||||
|
This node is privately owned and maintained by Nanotrasen, and allows the colonists of the Northern Star to have access to the Exonet."
|
||||||
|
|
||||||
|
description_antag = "An EMP will disable this device for a short period of time. A longer downage can be achieved by turning it off, or rigging \
|
||||||
|
the APC it uses to turn off remotely, such as with a signaler in the right wire."
|
||||||
@@ -1,13 +1,11 @@
|
|||||||
//A very, very simple mob that exists inside other objects to talk and has zero influence on the world.
|
//A very, very simple mob that exists inside other objects to talk and has zero influence on the world.
|
||||||
/mob/living/voice
|
/mob/living/voice
|
||||||
name = "unknown"
|
name = "unknown person"
|
||||||
desc = "How are you examining me?"
|
desc = "How are you examining me?"
|
||||||
// default_language = "Galactic Common"
|
see_invisible = SEE_INVISIBLE_LIVING
|
||||||
var/obj/item/device/communicator/comm = null
|
var/obj/item/device/communicator/comm = null
|
||||||
|
|
||||||
emote_type = 2 //This lets them emote through containers. The communicator has a image feed of the person calling them so...
|
emote_type = 2 //This lets them emote through containers. The communicator has a image feed of the person calling them so...
|
||||||
var/stable_connection = 0 //If set to zero, makes the mob effectively deaf, mute, and blind, due to interferance.
|
|
||||||
|
|
||||||
|
|
||||||
/mob/living/voice/New(loc)
|
/mob/living/voice/New(loc)
|
||||||
add_language("Galactic Common")
|
add_language("Galactic Common")
|
||||||
@@ -20,11 +18,15 @@
|
|||||||
comm = loc
|
comm = loc
|
||||||
..()
|
..()
|
||||||
|
|
||||||
|
// Proc: transfer_identity()
|
||||||
|
// Parameters: 1 (speaker - the mob (usually an observer) to copy information from)
|
||||||
|
// Description: Copies the mob's icons, overlays, TOD, gender, currently loaded character slot, and languages, to src.
|
||||||
/mob/living/voice/proc/transfer_identity(mob/speaker)
|
/mob/living/voice/proc/transfer_identity(mob/speaker)
|
||||||
if(ismob(speaker))
|
if(ismob(speaker))
|
||||||
icon = speaker.icon
|
icon = speaker.icon
|
||||||
icon_state = speaker.icon_state
|
icon_state = speaker.icon_state
|
||||||
overlays = speaker.overlays
|
overlays = speaker.overlays
|
||||||
|
timeofdeath = speaker.timeofdeath
|
||||||
|
|
||||||
alpha = 127 //Maybe we'll have hologram calls later.
|
alpha = 127 //Maybe we'll have hologram calls later.
|
||||||
if(speaker.client && speaker.client.prefs)
|
if(speaker.client && speaker.client.prefs)
|
||||||
@@ -36,6 +38,9 @@
|
|||||||
for(var/language in p.alternate_languages)
|
for(var/language in p.alternate_languages)
|
||||||
add_language(language)
|
add_language(language)
|
||||||
|
|
||||||
|
// Proc: Login()
|
||||||
|
// Parameters: None
|
||||||
|
// Description: Adds a static overlay to the client's screen.
|
||||||
/mob/living/voice/Login()
|
/mob/living/voice/Login()
|
||||||
var/obj/screen/static_effect = new() //Since what the player sees is essentially a video feed, from a vast distance away, the view isn't going to be perfect.
|
var/obj/screen/static_effect = new() //Since what the player sees is essentially a video feed, from a vast distance away, the view isn't going to be perfect.
|
||||||
static_effect.screen_loc = ui_entire_screen
|
static_effect.screen_loc = ui_entire_screen
|
||||||
@@ -44,10 +49,23 @@
|
|||||||
static_effect.mouse_opacity = 0 //So the static doesn't get in the way of clicking.
|
static_effect.mouse_opacity = 0 //So the static doesn't get in the way of clicking.
|
||||||
client.screen.Add(static_effect)
|
client.screen.Add(static_effect)
|
||||||
|
|
||||||
|
// Proc: Destroy()
|
||||||
|
// Parameters: None
|
||||||
|
// Description: Removes reference to the communicator, so it can qdel() successfully.
|
||||||
/mob/living/voice/Destroy()
|
/mob/living/voice/Destroy()
|
||||||
comm = null
|
comm = null
|
||||||
..()
|
..()
|
||||||
|
|
||||||
|
// Proc: ghostize()
|
||||||
|
// Parameters: None
|
||||||
|
// Description: Sets a timeofdeath variable, to fix the free respawn bug.
|
||||||
|
/mob/living/voice/ghostize()
|
||||||
|
timeofdeath = world.time
|
||||||
|
. = ..()
|
||||||
|
|
||||||
|
// Verb: hang_up()
|
||||||
|
// Parameters: None
|
||||||
|
// Description: Disconnects the voice mob from the communicator.
|
||||||
/mob/living/voice/verb/hang_up()
|
/mob/living/voice/verb/hang_up()
|
||||||
set name = "Hang Up"
|
set name = "Hang Up"
|
||||||
set category = "Communicator"
|
set category = "Communicator"
|
||||||
@@ -55,40 +73,53 @@
|
|||||||
set src = usr
|
set src = usr
|
||||||
|
|
||||||
if(comm)
|
if(comm)
|
||||||
comm.close_connection(src, "[src] hung up")
|
comm.close_connection(user = src, target = src, reason = "[src] hung up")
|
||||||
else
|
else
|
||||||
src << "You appear to not be inside a communicator. This is a bug and you should report it."
|
src << "You appear to not be inside a communicator. This is a bug and you should report it."
|
||||||
|
|
||||||
|
// Verb: change_name()
|
||||||
|
// Parameters: None
|
||||||
|
// Description: Allows the voice mob to change their name, assuming it is valid.
|
||||||
|
/mob/living/voice/verb/change_name()
|
||||||
|
set name = "Change Name"
|
||||||
|
set category = "Communicator"
|
||||||
|
set desc = "Changes your name."
|
||||||
|
set src = usr
|
||||||
|
|
||||||
|
var/new_name = sanitizeSafe(input(src, "Who would you like to be now?", "Communicator", src.client.prefs.real_name) as text, MAX_NAME_LEN)
|
||||||
|
if(new_name)
|
||||||
|
if(comm)
|
||||||
|
comm.visible_message("<span class='notice'>\icon[comm] [src.name] has left, and now you see [new_name].</span>")
|
||||||
|
//Do a bit of logging in-case anyone tries to impersonate other characters for whatever reason.
|
||||||
|
var/msg = "[src.client.key] ([src]) has changed their communicator identity's name to [new_name]."
|
||||||
|
message_admins(msg)
|
||||||
|
log_game(msg)
|
||||||
|
src.name = new_name
|
||||||
|
else
|
||||||
|
src << "<span class='warning'>Invalid name. Rejected.</span>"
|
||||||
|
|
||||||
|
// Proc: Life()
|
||||||
|
// Parameters: None
|
||||||
|
// Description: Checks the active variable on the Exonet node, and kills the mob if it goes down or stops existing.
|
||||||
/mob/living/voice/Life()
|
/mob/living/voice/Life()
|
||||||
if(comm && comm.server && !comm.server.active)
|
if(comm)
|
||||||
comm.close_connection(src,"Connection to telecommunications array timed out")
|
if(!comm.node || !comm.node.on || !comm.node.allow_external_communicators)
|
||||||
|
comm.close_connection(user = src, target = src, reason = "Connection to telecommunications array timed out")
|
||||||
..()
|
..()
|
||||||
|
|
||||||
|
// Proc: say()
|
||||||
|
// Parameters: 4 (generic say() arguments)
|
||||||
|
// Description: Adds a speech bubble to the communicator device, then calls ..() to do the real work.
|
||||||
/mob/living/voice/say(var/message, var/datum/language/speaking = null, var/verb="says", var/alt_name="")
|
/mob/living/voice/say(var/message, var/datum/language/speaking = null, var/verb="says", var/alt_name="")
|
||||||
// world << "say() was called. Arguments: ([message], [speaking], [verb], [alt_name])."
|
|
||||||
if(!stable_connection)
|
|
||||||
return 0
|
|
||||||
|
|
||||||
//Speech bubbles.
|
//Speech bubbles.
|
||||||
if(comm)
|
if(comm)
|
||||||
var/speech_bubble_test = say_test(message)
|
var/speech_bubble_test = say_test(message)
|
||||||
var/image/speech_bubble = image('icons/mob/talk.dmi',src,"h[speech_bubble_test]")
|
var/image/speech_bubble = image('icons/mob/talk.dmi',comm,"h[speech_bubble_test]")
|
||||||
spawn(30)
|
spawn(30)
|
||||||
qdel(speech_bubble)
|
qdel(speech_bubble)
|
||||||
|
|
||||||
for(var/mob/M in hearers(world.view,comm)) //simplifed since it's just a speech bubble
|
for(var/mob/M in hearers(comm)) //simplifed since it's just a speech bubble
|
||||||
M << speech_bubble
|
M << speech_bubble
|
||||||
src << speech_bubble
|
src << speech_bubble
|
||||||
|
|
||||||
..(message, speaking, verb, alt_name) //mob/living/say() can do the actual talking.
|
..(message, speaking, verb, alt_name) //mob/living/say() can do the actual talking.
|
||||||
/*
|
|
||||||
/mob/verb/test_voice_mob()
|
|
||||||
set name = "Make Voice Mob"
|
|
||||||
set category = "Debug"
|
|
||||||
set desc = "For testing only!"
|
|
||||||
set src = usr
|
|
||||||
|
|
||||||
var/turf/T = get_turf(src)
|
|
||||||
var/obj/item/device/communicator/comm = new(T)
|
|
||||||
comm.open_connection(src, src)
|
|
||||||
*/
|
|
||||||
|
|||||||
@@ -1215,6 +1215,13 @@ CIRCUITS BELOW
|
|||||||
build_path = /obj/item/weapon/circuitboard/telecomms/receiver
|
build_path = /obj/item/weapon/circuitboard/telecomms/receiver
|
||||||
sort_string = "PAAAG"
|
sort_string = "PAAAG"
|
||||||
|
|
||||||
|
/datum/design/circuit/tcom/exonet_node
|
||||||
|
name = "exonet node"
|
||||||
|
id = "tcom-exonet_node"
|
||||||
|
req_tech = list(TECH_DATA = 5, TECH_ENGINEERING = 5, TECH_BLUESPACE = 4)
|
||||||
|
build_path = /obj/item/weapon/circuitboard/telecomms/broadcaster
|
||||||
|
sort_string = "PAAAH"
|
||||||
|
|
||||||
/datum/design/circuit/shield
|
/datum/design/circuit/shield
|
||||||
req_tech = list(TECH_BLUESPACE = 4, TECH_PHORON = 3)
|
req_tech = list(TECH_BLUESPACE = 4, TECH_PHORON = 3)
|
||||||
materials = list("glass" = 2000, "gold" = 1000)
|
materials = list("glass" = 2000, "gold" = 1000)
|
||||||
|
|||||||
38
html/changelogs/neerti-communicators2.yml
Normal file
38
html/changelogs/neerti-communicators2.yml
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
################################
|
||||||
|
# Example Changelog File
|
||||||
|
#
|
||||||
|
# Note: This file, and files beginning with ".", and files that don't end in ".yml" will not be read. If you change this file, you will look really dumb.
|
||||||
|
#
|
||||||
|
# Your changelog will be merged with a master changelog. (New stuff added only, and only on the date entry for the day it was merged.)
|
||||||
|
# When it is, any changes listed below will disappear.
|
||||||
|
#
|
||||||
|
# Valid Prefixes:
|
||||||
|
# bugfix
|
||||||
|
# wip (For works in progress)
|
||||||
|
# tweak
|
||||||
|
# soundadd
|
||||||
|
# sounddel
|
||||||
|
# rscadd (general adding of nice things)
|
||||||
|
# rscdel (general deleting of nice things)
|
||||||
|
# imageadd
|
||||||
|
# imagedel
|
||||||
|
# maptweak
|
||||||
|
# spellcheck (typo fixes)
|
||||||
|
# experiment
|
||||||
|
#################################
|
||||||
|
|
||||||
|
# Your name.
|
||||||
|
author: Neerti
|
||||||
|
|
||||||
|
# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again.
|
||||||
|
delete-after: True
|
||||||
|
|
||||||
|
# Any changes you've made. See valid prefix list above.
|
||||||
|
# INDENT WITH TWO SPACES. NOT TABS. SPACES.
|
||||||
|
# SCREW THIS UP AND IT WON'T WORK.
|
||||||
|
# Also, all entries are changed into a single [] after a master changelog generation. Just remove the brackets when you add new entries.
|
||||||
|
# Please surround your changes in double quotes ("), as certain characters otherwise screws up compiling. The quotes will not show up in the changelog.
|
||||||
|
changes:
|
||||||
|
- rscadd: "Adds NanoUI for communicators, the ability for communicators to call other communicators on the station, the ability for station-bound people to call ghosts, and for ghosts to toggle their visibility to communicators on or off in character preferences (defaults to off). Communicators can now also support more than one call at the same time, for both ghosts and normal communicators."
|
||||||
|
- rscadd: "Adds new machine to telecomms, the Exonet node. It is very basic in terms of functionality, but certain services can be selectively disabled from it, such as newscaster updates, communicators, or external PDA messages. Adds methods for building and deconstructing Exonet nodes."
|
||||||
|
- experiment: "Adds framework for a fake-networking system called EPv2 that communicators will now use, and perhaps other machines will in the future, for good or for evil."
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 86 KiB After Width: | Height: | Size: 95 KiB |
167
nano/templates/communicator.tmpl
Normal file
167
nano/templates/communicator.tmpl
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
<!--
|
||||||
|
Title: Communicator UI
|
||||||
|
Used In File(s): code\game\objects\items\devices\communicator\communicator.dm
|
||||||
|
-->
|
||||||
|
|
||||||
|
<h1>Status</h1>
|
||||||
|
<div class="item">
|
||||||
|
<div class="itemLabel">
|
||||||
|
Owner:
|
||||||
|
</div>
|
||||||
|
<div class="itemContent">
|
||||||
|
<div style="float: left; width: 180px;">{{:data.owner}}</div> {{:helper.link('Rename', 'pencil', {'rename' : 1})}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="item">
|
||||||
|
<div class="itemLabel">
|
||||||
|
Connection:
|
||||||
|
</div>
|
||||||
|
<div class="itemContent">
|
||||||
|
{{if data.connectionStatus == 1}}
|
||||||
|
<span class="good">Connected</span>
|
||||||
|
{{else}}
|
||||||
|
<span class="bad">Disconnected</span>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="item">
|
||||||
|
<div class="itemLabel">
|
||||||
|
Device EPv2 Address:
|
||||||
|
</div>
|
||||||
|
<div class="itemContent">
|
||||||
|
<div style="float: left; width: 180px;">{{:data.address}}</div>
|
||||||
|
</div>
|
||||||
|
<div class="itemContent">
|
||||||
|
<div style="float: left; width: 180px;">{{:helper.link('Visible', 'signal-diag', {'toggle_visibility' : 1}, data.visible ? 'selected' : null)}}{{:helper.link('Invisible', 'close', {'toggle_visibility' : 1}, data.visible ? null : 'selected')}}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- First tab -->
|
||||||
|
|
||||||
|
<div class="item">
|
||||||
|
<div class="itemContent">
|
||||||
|
<div style="float: left;">{{:helper.link('Dialing', 'gear', {'switch_tab' : 1}, data.currentTab == 1 ? 'selected' : null)}}{{:helper.link('Manage Connections', 'gear', {'switch_tab' : 2}, data.currentTab == 1 ? null : 'selected')}}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{if data.currentTab == 1}}
|
||||||
|
|
||||||
|
<h3>Manual Dial</h3>
|
||||||
|
|
||||||
|
<HR>
|
||||||
|
|
||||||
|
<div class="item">
|
||||||
|
<div class="itemLabel">
|
||||||
|
Target EPv2 Address:
|
||||||
|
</div>
|
||||||
|
<div class="itemContent">
|
||||||
|
<div style="float: left; width: 180px;">{{:data.targetAddress}}</div>
|
||||||
|
</div>
|
||||||
|
<div class="itemContent">
|
||||||
|
<div style="float: left; width: 180px;">{{:helper.link('Write', 'pencil', {'write_target_address' : 1})}} {{:helper.link('Clear', 'close', {'clear_target_address' : 1})}}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="item">
|
||||||
|
<div class="itemContent">
|
||||||
|
<div style="width: 100%;">
|
||||||
|
<div style="clear: both; padding-top: 1px;">
|
||||||
|
{{:helper.link('0', null, {'add_hex' : '0'})}}
|
||||||
|
{{:helper.link('1', null, {'add_hex' : '1'})}}
|
||||||
|
{{:helper.link('2', null, {'add_hex' : '2'})}}
|
||||||
|
{{:helper.link('3', null, {'add_hex' : '3'})}}
|
||||||
|
</div>
|
||||||
|
<div style="clear: both; padding-top: 1px;">
|
||||||
|
{{:helper.link('4', null, {'add_hex' : '4'})}}
|
||||||
|
{{:helper.link('5', null, {'add_hex' : '5'})}}
|
||||||
|
{{:helper.link('6', null, {'add_hex' : '6'})}}
|
||||||
|
{{:helper.link('7', null, {'add_hex' : '7'})}}
|
||||||
|
</div>
|
||||||
|
<div style="clear: both; padding-top: 1px;">
|
||||||
|
{{:helper.link('8', null, {'add_hex' : '8'})}}
|
||||||
|
{{:helper.link('9', null, {'add_hex' : '9'})}}
|
||||||
|
{{:helper.link('A', null, {'add_hex' : 'a'})}}
|
||||||
|
{{:helper.link('B', null, {'add_hex' : 'b'})}}
|
||||||
|
</div>
|
||||||
|
<div style="clear: both; padding-top: 1px;">
|
||||||
|
{{:helper.link('C', null, {'add_hex' : 'c'})}}
|
||||||
|
{{:helper.link('D', null, {'add_hex' : 'd'})}}
|
||||||
|
{{:helper.link('E', null, {'add_hex' : 'e'})}}
|
||||||
|
{{:helper.link('F', null, {'add_hex' : 'f'})}}
|
||||||
|
</div>
|
||||||
|
<div style="clear: both; padding-top: 1px;">
|
||||||
|
{{:helper.link('Dial', 'signal-diag', {'dial' : data.targetAddress})}}
|
||||||
|
{{:helper.link('Hang Up', 'close', {'hang_up' : '1'})}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<HR>
|
||||||
|
|
||||||
|
<h3>Known Devices</h3>
|
||||||
|
|
||||||
|
{{for data.knownDevices}}
|
||||||
|
<div class='item'>
|
||||||
|
<div class="itemLabel">
|
||||||
|
{{:value.name}}
|
||||||
|
</div>
|
||||||
|
<div class="itemContent">
|
||||||
|
<div style="float: left; width: 300px;">{{:value.address}}</div> {{:helper.link('Copy', 'pencil', {'copy' : value.address})}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/for}}
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Second tab, managing active invitations/requests/calls -->
|
||||||
|
{{else}}
|
||||||
|
<h3>Connection Management</h3>
|
||||||
|
<HR>
|
||||||
|
<h3>External Connections</h3>
|
||||||
|
{{for data.voice_mobs}}
|
||||||
|
<div class='item'>
|
||||||
|
<div class="itemLabel">
|
||||||
|
{{:value.name}}
|
||||||
|
</div>
|
||||||
|
<div class="itemContent">
|
||||||
|
<div style="float: left; width: 200px;">{{:helper.link('Disconnect', 'close', {'disconnect' : value.true_name}, null, 'redButton')}}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/for}}
|
||||||
|
<h3>Internal Connections</h3>
|
||||||
|
{{for data.communicating}}
|
||||||
|
<div class='item'>
|
||||||
|
<div class="itemLabel">
|
||||||
|
{{:value.name}}
|
||||||
|
</div>
|
||||||
|
<div class="itemContent">
|
||||||
|
<div style="float: left; width: 200px;">{{:helper.link('Disconnect', 'close', {'disconnect' : value.true_name}, null, 'redButton')}}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/for}}
|
||||||
|
<h3>Requests Received</h3>
|
||||||
|
{{for data.requestsReceived}}
|
||||||
|
<div class='item'>
|
||||||
|
<div class="itemLabel">
|
||||||
|
{{:value.name}}
|
||||||
|
</div>
|
||||||
|
<div class="itemContent">
|
||||||
|
<div style="float: left; width: 300px;">{{:value.address}}</div> {{:helper.link('Copy', 'pencil', {'copy' : value.address})}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/for}}
|
||||||
|
|
||||||
|
<h3>Invites Sent</h3>
|
||||||
|
{{for data.invitesSent}}
|
||||||
|
<div class='item'>
|
||||||
|
<div class="itemLabel">
|
||||||
|
{{:value.name}}
|
||||||
|
</div>
|
||||||
|
<div class="itemContent">
|
||||||
|
<div style="float: left; width: 300px;">{{:value.address}}</div> {{:helper.link('Copy', 'pencil', {'copy' : value.address})}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/for}}
|
||||||
|
<HR>
|
||||||
|
{{/if}}
|
||||||
40
nano/templates/exonet_node.tmpl
Normal file
40
nano/templates/exonet_node.tmpl
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
<!--
|
||||||
|
Title: Exonet Node UI
|
||||||
|
Used In File(s): code\game\machinery/exonet_node.dm
|
||||||
|
-->
|
||||||
|
|
||||||
|
<h1>Status</h1>
|
||||||
|
<div class="item">
|
||||||
|
<div class="itemLabel">
|
||||||
|
Power:
|
||||||
|
</div>
|
||||||
|
<div class="itemContent">
|
||||||
|
{{:helper.link('On', 'power', {'toggle_power' : 1}, data.on ? 'selected' : null)}}{{:helper.link('Off', 'close', {'toggle_power' : 1}, data.on ? null : 'selected', data.on ? 'redButton' : null)}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h1>Ports</h1>
|
||||||
|
<div class="item">
|
||||||
|
<div class="itemLabel">
|
||||||
|
Incoming PDA Messages:
|
||||||
|
</div>
|
||||||
|
<div class="itemContent">
|
||||||
|
{{:helper.link('Open', 'check', {'toggle_PDA_port' : 1}, data.allowPDAs ? 'selected' : null)}}{{:helper.link('Close', 'close', {'toggle_PDA_port' : 1}, data.allowPDAs ? null : 'selected')}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="item">
|
||||||
|
<div class="itemLabel">
|
||||||
|
Incoming Communicators:
|
||||||
|
</div>
|
||||||
|
<div class="itemContent">
|
||||||
|
{{:helper.link('Open', 'check', {'toggle_communicator_port' : 1}, data.allowCommunicators ? 'selected' : null)}}{{:helper.link('Close', 'close', {'toggle_communicator_port' : 1}, data.allowCommunicators ? null : 'selected')}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="item">
|
||||||
|
<div class="itemLabel">
|
||||||
|
Incoming Newscaster Content:
|
||||||
|
</div>
|
||||||
|
<div class="itemContent">
|
||||||
|
{{:helper.link('Open', 'check', {'toggle_newscaster_port' : 1}, data.allowNewscasters ? 'selected' : null)}}{{:helper.link('Close', 'close', {'toggle_newscaster_port' : 1}, data.allowNewscasters ? null : 'selected')}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -150,6 +150,7 @@
|
|||||||
#include "code\datums\crew.dm"
|
#include "code\datums\crew.dm"
|
||||||
#include "code\datums\datacore.dm"
|
#include "code\datums\datacore.dm"
|
||||||
#include "code\datums\disease.dm"
|
#include "code\datums\disease.dm"
|
||||||
|
#include "code\datums\EPv2.dm"
|
||||||
#include "code\datums\mind.dm"
|
#include "code\datums\mind.dm"
|
||||||
#include "code\datums\mixed.dm"
|
#include "code\datums\mixed.dm"
|
||||||
#include "code\datums\modules.dm"
|
#include "code\datums\modules.dm"
|
||||||
@@ -408,6 +409,7 @@
|
|||||||
#include "code\game\machinery\deployable.dm"
|
#include "code\game\machinery\deployable.dm"
|
||||||
#include "code\game\machinery\door_control.dm"
|
#include "code\game\machinery\door_control.dm"
|
||||||
#include "code\game\machinery\doppler_array.dm"
|
#include "code\game\machinery\doppler_array.dm"
|
||||||
|
#include "code\game\machinery\exonet_node.dm"
|
||||||
#include "code\game\machinery\flasher.dm"
|
#include "code\game\machinery\flasher.dm"
|
||||||
#include "code\game\machinery\floodlight.dm"
|
#include "code\game\machinery\floodlight.dm"
|
||||||
#include "code\game\machinery\floor_light.dm"
|
#include "code\game\machinery\floor_light.dm"
|
||||||
@@ -1022,6 +1024,7 @@
|
|||||||
#include "code\modules\client\preference_setup\global\01_ui.dm"
|
#include "code\modules\client\preference_setup\global\01_ui.dm"
|
||||||
#include "code\modules\client\preference_setup\global\02_settings.dm"
|
#include "code\modules\client\preference_setup\global\02_settings.dm"
|
||||||
#include "code\modules\client\preference_setup\global\03_pai.dm"
|
#include "code\modules\client\preference_setup\global\03_pai.dm"
|
||||||
|
#include "code\modules\client\preference_setup\global\04_communicators.dm"
|
||||||
#include "code\modules\client\preference_setup\occupation\occupation.dm"
|
#include "code\modules\client\preference_setup\occupation\occupation.dm"
|
||||||
#include "code\modules\client\preference_setup\skills\skills.dm"
|
#include "code\modules\client\preference_setup\skills\skills.dm"
|
||||||
#include "code\modules\clothing\chameleon.dm"
|
#include "code\modules\clothing\chameleon.dm"
|
||||||
@@ -1152,12 +1155,14 @@
|
|||||||
#include "code\modules\examine\stat_icons.dm"
|
#include "code\modules\examine\stat_icons.dm"
|
||||||
#include "code\modules\examine\descriptions\armor.dm"
|
#include "code\modules\examine\descriptions\armor.dm"
|
||||||
#include "code\modules\examine\descriptions\atmospherics.dm"
|
#include "code\modules\examine\descriptions\atmospherics.dm"
|
||||||
|
#include "code\modules\examine\descriptions\devices.dm"
|
||||||
#include "code\modules\examine\descriptions\engineering.dm"
|
#include "code\modules\examine\descriptions\engineering.dm"
|
||||||
#include "code\modules\examine\descriptions\medical.dm"
|
#include "code\modules\examine\descriptions\medical.dm"
|
||||||
#include "code\modules\examine\descriptions\mobs.dm"
|
#include "code\modules\examine\descriptions\mobs.dm"
|
||||||
#include "code\modules\examine\descriptions\paperwork.dm"
|
#include "code\modules\examine\descriptions\paperwork.dm"
|
||||||
#include "code\modules\examine\descriptions\stacks.dm"
|
#include "code\modules\examine\descriptions\stacks.dm"
|
||||||
#include "code\modules\examine\descriptions\structures.dm"
|
#include "code\modules\examine\descriptions\structures.dm"
|
||||||
|
#include "code\modules\examine\descriptions\telecomms.dm"
|
||||||
#include "code\modules\examine\descriptions\turfs.dm"
|
#include "code\modules\examine\descriptions\turfs.dm"
|
||||||
#include "code\modules\examine\descriptions\weapons.dm"
|
#include "code\modules\examine\descriptions\weapons.dm"
|
||||||
#include "code\modules\ext_scripts\irc.dm"
|
#include "code\modules\ext_scripts\irc.dm"
|
||||||
|
|||||||
Reference in New Issue
Block a user