From 7870ee529cfa1ab7d5d24ca79d8e1baa58cdc31c Mon Sep 17 00:00:00 2001 From: Atermonera Date: Thu, 28 Dec 2017 21:33:11 -0800 Subject: [PATCH] Adds a weather app to the communicator Splits communicator.dm into multiple files Also a few bits from my PDA->communicator project, should be commented out --- .../objects/items/devices/communicator/UI.dm | 238 +++++ .../items/devices/communicator/cartridge.dm | 0 .../devices/communicator/communicator.dm | 831 +----------------- .../items/devices/communicator/integrated.dm | 32 + .../items/devices/communicator/messaging.dm | 162 ++++ .../items/devices/communicator/phone.dm | 380 ++++++++ html/changelogs/Atermonera - Weatherapp.yml | 5 + nano/css/icons.css | 363 ++++---- nano/images/source/uiIcons64.xcf | Bin 64086 -> 154502 bytes nano/images/uiIcons64.png | Bin 11743 -> 14179 bytes nano/templates/communicator.tmpl | 42 +- polaris.dme | 5 + 12 files changed, 1068 insertions(+), 990 deletions(-) create mode 100644 code/game/objects/items/devices/communicator/UI.dm create mode 100644 code/game/objects/items/devices/communicator/cartridge.dm create mode 100644 code/game/objects/items/devices/communicator/integrated.dm create mode 100644 code/game/objects/items/devices/communicator/messaging.dm create mode 100644 code/game/objects/items/devices/communicator/phone.dm create mode 100644 html/changelogs/Atermonera - Weatherapp.yml diff --git a/code/game/objects/items/devices/communicator/UI.dm b/code/game/objects/items/devices/communicator/UI.dm new file mode 100644 index 00000000000..77ce5b03c13 --- /dev/null +++ b/code/game/objects/items/devices/communicator/UI.dm @@ -0,0 +1,238 @@ +// 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, var/key_state = null) + // 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. + + var/im_contacts_ui[0] //List of communicators that have been messaged. + var/im_list_ui[0] //List of messages. + + var/weather[0] + var/modules_ui[0] //Home screen info. + + //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/observer/dead/O in known_devices) + if(O.client && O.client.prefs.communicator_visibility == 1 && O.exonet) + communicators[++communicators.len] = list("name" = sanitize("[O.client.prefs.real_name]'s communicator"), "address" = O.exonet.address, "ref" = "\ref[O]") + + //Lists all the other communicators that we invited. + for(var/obj/item/device/communicator/comm in voice_invites) + if(comm.exonet) + invites[++invites.len] = list("name" = sanitize(comm.name), "address" = comm.exonet.address, "ref" = "\ref[comm]") + + //Ghosts we invited. + for(var/mob/observer/dead/O in voice_invites) + if(O.exonet && O.client) + invites[++invites.len] = list("name" = sanitize("[O.client.prefs.real_name]'s communicator"), "address" = O.exonet.address, "ref" = "\ref[O]") + + //Communicators that want to talk to us. + for(var/obj/item/device/communicator/comm in voice_requests) + if(comm.exonet) + requests[++requests.len] = list("name" = sanitize(comm.name), "address" = comm.exonet.address, "ref" = "\ref[comm]") + + //Ghosts that want to talk to us. + for(var/mob/observer/dead/O in voice_requests) + if(O.exonet && O.client) + requests[++requests.len] = list("name" = sanitize("[O.client.prefs.real_name]'s communicator"), "address" = O.exonet.address, "ref" = "\ref[O]") + + //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), "ref" = "\ref[comm]") + + //Devices that have been messaged or recieved messages from. + for(var/obj/item/device/communicator/comm in im_contacts) + if(comm.exonet) + im_contacts_ui[++im_contacts_ui.len] = list("name" = sanitize(comm.name), "address" = comm.exonet.address, "ref" = "\ref[comm]") + + for(var/mob/observer/dead/ghost in im_contacts) + if(ghost.exonet) + im_contacts_ui[++im_contacts_ui.len] = list("name" = sanitize(ghost.name), "address" = ghost.exonet.address, "ref" = "\ref[ghost]") + + //Actual messages. + for(var/I in im_list) + im_list_ui[++im_list_ui.len] = list("address" = I["address"], "to_address" = I["to_address"], "im" = I["im"]) + + //Weather reports. + for(var/datum/planet/planet in planet_controller.planets) + if(planet.weather_holder && planet.weather_holder.current_weather) + weather[++weather.len] = list("Planet" = planet.name, "Weather" = planet.weather_holder.current_weather.name, "Temperature" = (round(planet.weather_holder.temperature*1000)/1000),\ + "High" = planet.weather_holder.current_weather.temp_high, "Low" = planet.weather_holder.current_weather.temp_low, "Wind" = "[planet.weather_holder.wind_speed]|[dir2text(planet.weather_holder.wind_dir)]") + + //Modules for homescreen. + for(var/list/R in modules) + modules_ui[++modules_ui.len] = R + + data["owner"] = owner ? owner : "Unset" + data["occupation"] = occupation ? occupation : "Swipe ID to set." + data["connectionStatus"] = get_connection_to_tcomms() + data["visible"] = network_visibility + data["address"] = exonet.address ? exonet.address : "Unallocated" + data["targetAddress"] = target_address + data["targetAddressName"] = target_address_name + data["currentTab"] = selected_tab + data["knownDevices"] = communicators + data["invitesSent"] = invites + data["requestsReceived"] = requests + data["voice_mobs"] = voices + data["communicating"] = connected_communicators + data["video_comm"] = video_source ? "\ref[video_source.loc]" : null + data["imContacts"] = im_contacts_ui + data["imList"] = im_list_ui + data["time"] = stationtime2text() + data["ring"] = ringer + data["homeScreen"] = modules_ui + data["note"] = note // current notes + data["weather"] = weather + + // 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", 475, 700, state = key_state) + // 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(camera) + camera.name = name + camera.c_tag = name + + if(href_list["toggle_visibility"]) + switch(network_visibility) + if(1) //Visible, becoming invisbile + network_visibility = 0 + if(camera) + camera.remove_network(NETWORK_COMMUNICATORS) + if(0) //Invisible, becoming visible + network_visibility = 1 + if(camera) + camera.add_network(NETWORK_COMMUNICATORS) + + if(href_list["toggle_ringer"]) + ringer = !ringer + + 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 << "Error: Cannot connect to Exonet node." + return + var/their_address = href_list["dial"] + exonet.send_message(their_address, "voice") + + if(href_list["decline"]) + var/ref_to_remove = href_list["decline"] + var/atom/decline = locate(ref_to_remove) + if(decline) + del_request(decline) + + if(href_list["message"]) + if(!get_connection_to_tcomms()) + usr << "Error: Cannot connect to Exonet node." + return + var/their_address = href_list["message"] + var/text = sanitizeSafe(input(usr,"Enter your message.","Text Message")) + if(text) + exonet.send_message(their_address, "text", text) + im_list += list(list("address" = exonet.address, "to_address" = their_address, "im" = text)) + log_pda("[usr] (COMM: [src]) sent \"[text]\" to [exonet.get_atom_from_address(their_address)]") + for(var/mob/M in player_list) + if(M.stat == DEAD && M.is_preference_enabled(/datum/client_preference/ghost_ears)) + if(istype(M, /mob/new_player) || M.forbid_seeing_deadchat) + continue + if(exonet.get_atom_from_address(their_address) == M) + continue + M.show_message("Comm IM - [src] -> [exonet.get_atom_from_address(their_address)]: [text]") + + 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(href_list["startvideo"]) + var/ref_to_video = href_list["startvideo"] + var/obj/item/device/communicator/comm = locate(ref_to_video) + if(comm) + connect_video(usr, comm) + + if(href_list["endvideo"]) + if(video_source) + end_video() + + if(href_list["watchvideo"]) + if(video_source) + watch_video(usr,video_source.loc) + + if(href_list["copy"]) + target_address = href_list["copy"] + + if(href_list["copy_name"]) + target_address_name = href_list["copy_name"] + + if(href_list["hang_up"]) + for(var/mob/living/voice/V in contents) + close_connection(usr, V, "[usr] hung up") + for(var/obj/item/device/communicator/comm in communicating) + close_connection(usr, comm, "[usr] hung up") + + if(href_list["switch_tab"]) + selected_tab = href_list["switch_tab"] + + if(href_list["edit"]) + var/n = input(usr, "Please enter message", name, notehtml) + n = sanitizeSafe(n, extra = 0) + if(n) + note = html_decode(n) + notehtml = note + note = replacetext(note, "\n", "
") + else + note = "" + notehtml = note + + nanomanager.update_uis(src) + add_fingerprint(usr) \ No newline at end of file diff --git a/code/game/objects/items/devices/communicator/cartridge.dm b/code/game/objects/items/devices/communicator/cartridge.dm new file mode 100644 index 00000000000..e69de29bb2d diff --git a/code/game/objects/items/devices/communicator/communicator.dm b/code/game/objects/items/devices/communicator/communicator.dm index 9e31cb38bb0..d5a4114aff1 100644 --- a/code/game/objects/items/devices/communicator/communicator.dm +++ b/code/game/objects/items/devices/communicator/communicator.dm @@ -37,7 +37,8 @@ var/global/list/obj/item/device/communicator/all_communicators = list() list("module" = "Contacts", "icon" = "person64", "number" = 3), list("module" = "Messaging", "icon" = "comment64", "number" = 4), list("module" = "Note", "icon" = "note64", "number" = 5), - list("module" = "Settings", "icon" = "gear64", "number" = 6) + list("module" = "Weather", "icon" = "sun64", "number" = 6), + list("module" = "Settings", "icon" = "gear64", "number" = 7) ) //list("module" = "Name of Module", "icon" = "icon name64", "number" = "what tab is the module") var/selected_tab = 1 @@ -191,16 +192,19 @@ var/global/list/obj/item/device/communicator/all_communicators = list() if(istype(C, /obj/item/weapon/card/id)) var/obj/item/weapon/card/id/idcard = C if(!idcard.registered_name || !idcard.assignment) - user << "\The [src] rejects the ID." - return - if(!owner) - user << "\The [src] rejects the ID." - return - if(owner == idcard.registered_name) + to_chat(user, "\The [src] rejects the ID.") + else if(!owner) + to_chat(user, "\The [src] rejects the ID.") + else if(owner == idcard.registered_name) occupation = idcard.assignment - user << "Occupation updated." - return - else return + to_chat(user, "Occupation updated.") +// else if(istype(C, /obj/item/weapon/cartridge)) +// if(cartridge) +// to_chat(user, "\The [src] already has an external device attached!") +// else +// modules.Add(list("module" = "External Device", "icon = external64", "number" = 8)) +// cartridge = C + return // Proc: attack_self() // Parameters: 1 (user - the mob that clicked the device in their hand) @@ -258,288 +262,6 @@ var/global/list/obj/item/device/communicator/all_communicators = list() exonet = null return ..() -// 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, var/key_state = null) - // 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. - - var/im_contacts_ui[0] //List of communicators that have been messaged. - var/im_list_ui[0] //List of messages. - - var/modules_ui[0] //Home screen info. - - //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/observer/dead/O in known_devices) - if(O.client && O.client.prefs.communicator_visibility == 1 && O.exonet) - communicators[++communicators.len] = list("name" = sanitize("[O.client.prefs.real_name]'s communicator"), "address" = O.exonet.address, "ref" = "\ref[O]") - - //Lists all the other communicators that we invited. - for(var/obj/item/device/communicator/comm in voice_invites) - if(comm.exonet) - invites[++invites.len] = list("name" = sanitize(comm.name), "address" = comm.exonet.address, "ref" = "\ref[comm]") - - //Ghosts we invited. - for(var/mob/observer/dead/O in voice_invites) - if(O.exonet && O.client) - invites[++invites.len] = list("name" = sanitize("[O.client.prefs.real_name]'s communicator"), "address" = O.exonet.address, "ref" = "\ref[O]") - - //Communicators that want to talk to us. - for(var/obj/item/device/communicator/comm in voice_requests) - if(comm.exonet) - requests[++requests.len] = list("name" = sanitize(comm.name), "address" = comm.exonet.address, "ref" = "\ref[comm]") - - //Ghosts that want to talk to us. - for(var/mob/observer/dead/O in voice_requests) - if(O.exonet && O.client) - requests[++requests.len] = list("name" = sanitize("[O.client.prefs.real_name]'s communicator"), "address" = O.exonet.address, "ref" = "\ref[O]") - - //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), "ref" = "\ref[comm]") - - //Devices that have been messaged or recieved messages from. - for(var/obj/item/device/communicator/comm in im_contacts) - if(comm.exonet) - im_contacts_ui[++im_contacts_ui.len] = list("name" = sanitize(comm.name), "address" = comm.exonet.address, "ref" = "\ref[comm]") - - for(var/mob/observer/dead/ghost in im_contacts) - if(ghost.exonet) - im_contacts_ui[++im_contacts_ui.len] = list("name" = sanitize(ghost.name), "address" = ghost.exonet.address, "ref" = "\ref[ghost]") - - //Actual messages. - for(var/I in im_list) - im_list_ui[++im_list_ui.len] = list("address" = I["address"], "to_address" = I["to_address"], "im" = I["im"]) - - //Modules for homescreen. - for(var/list/R in modules) - modules_ui[++modules_ui.len] = R - - data["owner"] = owner ? owner : "Unset" - data["occupation"] = occupation ? occupation : "Swipe ID to set." - data["connectionStatus"] = get_connection_to_tcomms() - data["visible"] = network_visibility - data["address"] = exonet.address ? exonet.address : "Unallocated" - data["targetAddress"] = target_address - data["targetAddressName"] = target_address_name - data["currentTab"] = selected_tab - data["knownDevices"] = communicators - data["invitesSent"] = invites - data["requestsReceived"] = requests - data["voice_mobs"] = voices - data["communicating"] = connected_communicators - data["video_comm"] = video_source ? "\ref[video_source.loc]" : null - data["imContacts"] = im_contacts_ui - data["imList"] = im_list_ui - data["time"] = stationtime2text() - data["ring"] = ringer - data["homeScreen"] = modules_ui - data["note"] = note // current notes - - // 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", 475, 700, state = key_state) - // 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(camera) - camera.name = name - camera.c_tag = name - - if(href_list["toggle_visibility"]) - switch(network_visibility) - if(1) //Visible, becoming invisbile - network_visibility = 0 - if(camera) - camera.remove_network(NETWORK_COMMUNICATORS) - if(0) //Invisible, becoming visible - network_visibility = 1 - if(camera) - camera.add_network(NETWORK_COMMUNICATORS) - - if(href_list["toggle_ringer"]) - ringer = !ringer - - 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 << "Error: Cannot connect to Exonet node." - return - var/their_address = href_list["dial"] - exonet.send_message(their_address, "voice") - - if(href_list["decline"]) - var/ref_to_remove = href_list["decline"] - var/atom/decline = locate(ref_to_remove) - if(decline) - del_request(decline) - - if(href_list["message"]) - if(!get_connection_to_tcomms()) - usr << "Error: Cannot connect to Exonet node." - return - var/their_address = href_list["message"] - var/text = sanitizeSafe(input(usr,"Enter your message.","Text Message")) - if(text) - exonet.send_message(their_address, "text", text) - im_list += list(list("address" = exonet.address, "to_address" = their_address, "im" = text)) - log_pda("[usr] (COMM: [src]) sent \"[text]\" to [exonet.get_atom_from_address(their_address)]") - for(var/mob/M in player_list) - if(M.stat == DEAD && M.is_preference_enabled(/datum/client_preference/ghost_ears)) - if(istype(M, /mob/new_player) || M.forbid_seeing_deadchat) - continue - if(exonet.get_atom_from_address(their_address) == M) - continue - M.show_message("Comm IM - [src] -> [exonet.get_atom_from_address(their_address)]: [text]") - - 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(href_list["startvideo"]) - var/ref_to_video = href_list["startvideo"] - var/obj/item/device/communicator/comm = locate(ref_to_video) - if(comm) - connect_video(usr, comm) - - if(href_list["endvideo"]) - if(video_source) - end_video() - - if(href_list["watchvideo"]) - if(video_source) - watch_video(usr,video_source.loc) - - if(href_list["copy"]) - target_address = href_list["copy"] - - if(href_list["copy_name"]) - target_address_name = href_list["copy_name"] - - if(href_list["hang_up"]) - for(var/mob/living/voice/V in contents) - close_connection(usr, V, "[usr] hung up") - for(var/obj/item/device/communicator/comm in communicating) - close_connection(usr, comm, "[usr] hung up") - - if(href_list["switch_tab"]) - selected_tab = href_list["switch_tab"] - - if(href_list["edit"]) - var/n = input(usr, "Please enter message", name, notehtml) - n = sanitizeSafe(n, extra = 0) - if(n) - note = html_decode(n) - notehtml = note - note = replacetext(note, "\n", "
") - else - note = "" - notehtml = note - - nanomanager.update_uis(src) - add_fingerprint(usr) - -// Proc: receive_exonet_message() -// Parameters: 4 (origin atom - the source of the message's holder, origin_address - where the message came from, message - the message received, -// text - message text to send if message is of type "text") -// Description: Handles voice requests and invite messages originating from both real communicators and ghosts. Also includes a ping response and IM function. -/obj/item/device/communicator/receive_exonet_message(var/atom/origin_atom, origin_address, message, text) - 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 - 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") - if(message == "text") - request_im(origin_atom, origin_address, text) - return - -// 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. -/mob/observer/dead/receive_exonet_message(origin_atom, origin_address, message, text) - if(message == "voice") - if(istype(origin_atom, /obj/item/device/communicator)) - var/obj/item/device/communicator/comm = origin_atom - if(src in comm.voice_invites) - comm.open_connection(src) - return - src << "\icon[origin_atom] Receiving communicator request from [origin_atom]. To answer, use the Call Communicator \ - verb, and select that name to answer the call." - src << 'sound/machines/defib_SafetyOn.ogg' - 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") - if(message == "text") - src << "\icon[origin_atom] Received text message from [origin_atom]: \"[text]\"" - src << 'sound/machines/defib_safetyOff.ogg' - exonet_messages.Add("From [origin_atom]:
[text]") - return - // Proc: register_device() // Parameters: 1 (user - the person to use their name for) // Description: Updates the owner's name and the device's name. @@ -553,252 +275,13 @@ var/global/list/obj/item/device/communicator/all_communicators = list() camera.name = name camera.c_tag = name -// Proc: add_communicating() -// Parameters: 1 (comm - the communicator to add to communicating) -// Description: Used when this communicator gets a new communicator to relay say/me messages to -/obj/item/device/communicator/proc/add_communicating(obj/item/device/communicator/comm) - if(!comm || !istype(comm)) return - - communicating |= comm - listening_objects |= src - update_icon() - -// Proc: del_communicating() -// Parameters: 1 (comm - the communicator to remove from communicating) -// Description: Used when this communicator is being asked to stop relaying say/me messages to another -/obj/item/device/communicator/proc/del_communicating(obj/item/device/communicator/comm) - if(!comm || !istype(comm)) return - - communicating.Remove(comm) - update_icon() - -// Proc: open_connection() -// Parameters: 2 (user - the person who initiated the connecting being opened, candidate - the communicator or observer that will connect to the device) -// 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 - if(istype(candidate, /obj/item/device/communicator)) - open_connection_to_communicator(user, candidate) - -// Proc: open_connection_to_communicator() -// Parameters: 2 (user - the person who initiated this and will be receiving feedback information, candidate - someone else's communicator) -// Description: Adds the candidate and src to each other's communicating lists, allowing messages seen by the devices to be relayed. -/obj/item/device/communicator/proc/open_connection_to_communicator(mob/user, var/atom/candidate) - if(!istype(candidate, /obj/item/device/communicator)) - return - var/obj/item/device/communicator/comm = candidate - voice_invites.Remove(candidate) - comm.voice_requests.Remove(src) - - if(user) - comm.visible_message("\icon[src] Connecting to [src].") - user << "\icon[src] Attempting to call [comm]." - sleep(10) - user << "\icon[src] Dialing internally from [station_name()], Kara Subsystem, [system_name()]." - sleep(20) //If they don't have an exonet something is very wrong and we want a runtime. - user << "\icon[src] Connection re-routed to [comm] at [comm.exonet.address]." - sleep(40) - user << "\icon[src] Connection to [comm] at [comm.exonet.address] established." - comm.visible_message("\icon[src] Connection to [src] at [exonet.address] established.") - sleep(20) - - src.add_communicating(comm) - comm.add_communicating(src) - -// 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)) - return - //Handle moving the ghost into the new shell. - announce_ghost_joinleave(candidate, 0, "They are occupying a personal communications device now.") - voice_requests.Remove(candidate) - 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. - var/msg = "[candidate && candidate.client ? "[candidate.client.key]" : "*no key*"] ([candidate]) has entered [src], triggered by \ - [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) - log_game(msg) - new_voice.mind = candidate.mind //Transfer the mind, if any. - new_voice.ckey = candidate.ckey //Finally, bring the client over. - voice_mobs.Add(new_voice) - listening_objects |= src - - 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.icon = 'icons/effects/effects.dmi' - blackness.icon_state = "1" - blackness.mouse_opacity = 2 //Can't see anything! - new_voice.client.screen.Add(blackness) - - update_icon() - - //Now for some connection fluff. - if(user) - user << "\icon[src] Connecting to [candidate]." - new_voice << "\icon[src] Attempting to call [src]." - sleep(10) - new_voice << "\icon[src] Dialing to [station_name()], Kara Subsystem, [system_name()]." - sleep(20) - new_voice << "\icon[src] Connecting to [station_name()] telecommunications array." - sleep(40) - new_voice << "\icon[src] Connection to [station_name()] telecommunications array established. Redirecting signal to [src]." - sleep(20) - - //We're connected, no need to hide everything. - new_voice.client.screen.Remove(blackness) - qdel(blackness) - - new_voice << "\icon[src] Connection to [src] established." - new_voice << "To talk to the person on the other end of the call, just talk normally." - new_voice << "If you want to end the call, use the 'Hang Up' verb. The other person can also hang up at any time." - new_voice << "Remember, your character does not know anything you've learned from observing!" - if(new_voice.mind) - new_voice.mind.assigned_role = "Disembodied Voice" - if(user) - user << "\icon[src] Your communicator is now connected to [candidate]'s communicator." - -// Proc: close_connection() -// 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 - - 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 << "\icon[src] [reason]." - visible_message("\icon[src] [reason].") - voice_mobs.Remove(voice) - qdel(voice) - update_icon() - - for(var/obj/item/device/communicator/comm in communicating) //Now we handle real communicators. - if(target && comm != target) - continue - src.del_communicating(comm) - comm.del_communicating(src) - comm.visible_message("\icon[src] [reason].") - visible_message("\icon[src] [reason].") - if(comm.camera && video_source == comm.camera) //We hung up on the person on video - end_video() - if(camera && comm.video_source == camera) //We hung up on them while they were watching us - comm.end_video() - - if(voice_mobs.len == 0 && communicating.len == 0) - listening_objects.Remove(src) - -// Proc: request() -// Parameters: 1 (candidate - the ghost or communicator wanting to call the device) -// 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) - return - 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 - - if(ringer) - playsound(loc, 'sound/machines/twobeep.ogg', 50, 1) - for (var/mob/O in hearers(2, loc)) - O.show_message(text("\icon[src] *beep*")) - - alert_called = 1 - update_icon() - - //Search for holder of the device. - var/mob/living/L = null - if(loc && isliving(loc)) - L = loc - - if(L) - L << "\icon[src] Communications request from [who]." - -// Proc: del_request() -// Parameters: 1 (candidate - the ghost or communicator to be declined) -// Description: Declines a request and cleans up both ends -/obj/item/device/communicator/proc/del_request(var/atom/candidate) - if(!(candidate in voice_requests)) - return - - if(isobserver(candidate)) - candidate << "Your communicator call request was declined." - else if(istype(candidate, /obj/item/device/communicator)) - var/obj/item/device/communicator/comm = candidate - comm.voice_invites -= src - - voice_requests -= candidate - - //Search for holder of our device. - var/mob/living/us = null - if(loc && isliving(loc)) - us = loc - - if(us) - us << "\icon[src] Declined request." - -// Proc: request_im() -// Parameters: 3 (candidate - the communicator wanting to message the device, origin_address - the address of the sender, text - the message) -// Description: Response to a communicator trying to message the device. -// Adds them to the list of people that have messaged this device and adds the message to the message list. -/obj/item/device/communicator/proc/request_im(var/atom/candidate, var/origin_address, var/text) - var/who = null - if(isobserver(candidate)) - var/mob/observer/dead/ghost = candidate - who = ghost - im_list += list(list("address" = origin_address, "to_address" = exonet.address, "im" = text)) - else if(istype(candidate, /obj/item/device/communicator)) - var/obj/item/device/communicator/comm = candidate - who = comm.owner - comm.im_contacts |= src - im_list += list(list("address" = origin_address, "to_address" = exonet.address, "im" = text)) - else return - - im_contacts |= candidate - - if(!who) - return - - if(ringer) - playsound(loc, 'sound/machines/twobeep.ogg', 50, 1) - for (var/mob/O in hearers(2, loc)) - O.show_message(text("\icon[src] *beep*")) - - alert_called = 1 - update_icon() - - //Search for holder of the device. - var/mob/living/L = null - if(loc && isliving(loc)) - L = loc - - if(L) - L << "\icon[src] Message from [who]." - // 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() for(var/mob/living/voice/voice in contents) voice_mobs.Remove(voice) - voice << "\icon[src] Connection timed out with remote host." + to_chat(voice, "\icon[src] Connection timed out with remote host.") qdel(voice) close_connection(reason = "Connection timed out") communicating.Cut() @@ -832,290 +315,6 @@ var/global/list/obj/item/device/communicator/all_communicators = list() 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) - var/rendered = "\icon[src] [text]" - for(var/obj/item/device/communicator/comm in communicating) - var/turf/T = get_turf(comm) - if(!T) return - var/list/in_range = get_mobs_and_objs_in_view_fast(T,world.view,0) //Range of 3 since it's a tiny video display - var/list/mobs_to_relay = in_range["mobs"] - - for(var/mob/mob in mobs_to_relay) //We can't use visible_message(), or else we will get an infinite loop if two communicators hear each other. - var/dst = get_dist(get_turf(mob),get_turf(comm)) - if(dst <= video_range) - mob.show_message(rendered) - else - mob << "You can barely see some movement on \the [src]'s display." - - ..() - -// Proc: hear_talk() -// 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 -// being used) -// Description: Relays the speech to all linked communicators. -/obj/item/device/communicator/hear_talk(mob/living/M, text, verb, datum/language/speaking) - for(var/obj/item/device/communicator/comm in communicating) - - var/turf/T = get_turf(comm) - if(!T) return - var/list/in_range = get_mobs_and_objs_in_view_fast(T,world.view,0) - var/list/mobs_to_relay = in_range["mobs"] - - for(var/mob/mob in mobs_to_relay) - //Can whoever is hearing us understand? - if(!mob.say_understands(M, speaking)) - if(speaking) - text = speaking.scramble(text) - else - text = stars(text) - var/name_used = M.GetVoice() - var/rendered = null - if(speaking) //Language being used - rendered = "\icon[src] [name_used] [speaking.format_message(text, verb)]" - else - rendered = "\icon[src] [name_used] [verb], \"[text]\"" - 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] [msg]" - for(var/obj/item/device/communicator/comm in communicating) - var/turf/T = get_turf(comm) - if(!T) return - var/list/in_range = get_mobs_and_objs_in_view_fast(T,world.view,0) - var/list/mobs_to_relay = in_range["mobs"] - - for(var/mob/mob in mobs_to_relay) - mob.show_message(rendered) - ..() - -// Verb: join_as_voice() -// Parameters: None -// Description: Allows ghosts to call communicators, if they meet all the requirements. -/mob/observer/dead/verb/join_as_voice() - set category = "Ghost" - set name = "Call Communicator" - set desc = "If there is a communicator available, send a request to speak through it. This will reset your respawn timer, if someone picks up." - - if(ticker.current_state < GAME_STATE_PLAYING) - src << "The game hasn't started yet!" - return - - if (!src.stat) - return - - if (usr != src) - return //something is terribly wrong - - var/confirm = alert(src, "Would you like to talk as [src.client.prefs.real_name], over a communicator? \ - This will reset your respawn timer, if someone answers.", "Join as Voice?", "Yes","No") - if(confirm == "No") - return - - for(var/mob/living/L in mob_list) //Simple check so you don't have dead people calling. - if(src.client.prefs.real_name == L.real_name) - src << "Your identity is already present in the game world. Please load in a different character first." - return - - var/obj/machinery/exonet_node/E = get_exonet_node() - if(!E || !E.on || !E.allow_external_communicators) - src << "The Exonet node at telecommunications is down at the moment, or is actively blocking you, so your call can't go through." - return - - var/list/choices = list() - for(var/obj/item/device/communicator/comm in all_communicators) - if(!comm.network_visibility || !comm.exonet || !comm.exonet.address) - continue - choices.Add(comm) - - if(!choices.len) - src << "There are no available communicators, sorry." - return - - var/choice = input(src,"Send a voice request to whom?") as null|anything in choices - if(choice) - var/obj/item/device/communicator/chosen_communicator = choice - var/mob/observer/dead/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." - -// Verb: text_communicator() -// Parameters: None -// Description: Allows a ghost to send a text message to a communicator. -/mob/observer/dead/verb/text_communicator() - set category = "Ghost" - set name = "Text Communicator" - set desc = "If there is a communicator available, send a text message to it." - - if(ticker.current_state < GAME_STATE_PLAYING) - src << "The game hasn't started yet!" - return - - if (!src.stat) - return - - if (usr != src) - return //something is terribly wrong - - for(var/mob/living/L in mob_list) //Simple check so you don't have dead people calling. - if(src.client.prefs.real_name == L.real_name) - src << "Your identity is already present in the game world. Please load in a different character first." - return - - var/obj/machinery/exonet_node/E = get_exonet_node() - if(!E || !E.on || !E.allow_external_communicators) - src << "The Exonet node at telecommunications is down at the moment, or is actively blocking you, so your call can't go through." - return - - var/list/choices = list() - for(var/obj/item/device/communicator/comm in all_communicators) - if(!comm.network_visibility || !comm.exonet || !comm.exonet.address) - continue - choices.Add(comm) - - if(!choices.len) - src << "There are no available communicators, sorry." - return - - var/choice = input(src,"Send a text message to whom?") as null|anything in choices - if(choice) - var/obj/item/device/communicator/chosen_communicator = choice - var/mob/observer/dead/O = src - var/text_message = sanitize(input(src, "What do you want the message to say?")) as message - if(text_message && O.exonet) - O.exonet.send_message(chosen_communicator.exonet.address, "text", text_message) - - src << "You have sent '[text_message]' to [chosen_communicator]." - exonet_messages.Add("To [chosen_communicator]:
[text_message]") - log_pda("[usr] (COMM: [src]) sent \"[text_message]\" to [chosen_communicator]") - for(var/mob/M in player_list) - if(M.stat == DEAD && M.is_preference_enabled(/datum/client_preference/ghost_ears)) - if(istype(M, /mob/new_player) || M.forbid_seeing_deadchat) - continue - if(M == src) - continue - M.show_message("Comm IM - [src] -> [chosen_communicator]: [text_message]") - - - -// Verb: show_text_messages() -// Parameters: None -// Description: Lets ghosts review messages they've sent or received. -/mob/observer/dead/verb/show_text_messages() - set category = "Ghost" - set name = "Show Text Messages" - set desc = "Allows you to see exonet text messages you've sent and received." - - var/HTML = "Exonet Message Log" - for(var/line in exonet_messages) - HTML += line + "
" - HTML +="" - usr << browse(HTML, "window=log;size=400x444;border=1;can_resize=1;can_close=1;can_minimize=0") - -// Proc: connect_video() -// Parameters: user - the mob doing the viewing of video, comm - the communicator at the far end -// Description: Sets up a videocall and puts the first view into it using watch_video, and updates the icon -/obj/item/device/communicator/proc/connect_video(mob/user,obj/item/device/communicator/comm) - if((!user) || (!comm) || user.stat) return //KO or dead, or already in a video - - if(video_source) //Already in a video - user << "You are already connected to a video call!" - - if(user.blinded) //User is blinded - user << "You cannot see well enough to do that!" - - if(!(src in comm.communicating) || !comm.camera) //You called someone with a broken communicator or one that's fake or yourself or something - user << "\icon[src]ERROR: Video failed. Either bandwidth is too low, or the other communicator is malfunctioning." - - user << "\icon[src] Attempting to start video over existing call." - sleep(30) - user << "\icon[src] Please wait..." - - video_source = comm.camera - comm.visible_message("\icon[src] New video connection from [comm].") - watch_video(user) - update_icon() - -// Proc: watch_video() -// Parameters: user - the mob doing the viewing of video -// Description: Moves a mob's eye to the far end for the duration of viewing the far end -/obj/item/device/communicator/proc/watch_video(mob/user) - if(!Adjacent(user) || !video_source) return - user.set_machine(video_source) - user.reset_view(video_source) - to_chat(user,"Now viewing video session. To leave camera view, close the communicator window OR: OOC -> Cancel Camera View") - to_chat(user,"To return to an active video session, use the communicator in your hand.") - spawn(0) - while(user.machine == video_source && Adjacent(user)) - var/turf/T = get_turf(video_source) - if(!T || !is_on_same_plane_or_station(T.z, user.z) || !video_source.can_use()) - user << "The screen bursts into static, then goes black." - video_cleanup(user) - return - sleep(10) - - video_cleanup(user) - -// Proc: video_cleanup() -// Parameters: user - the mob who doesn't want to see video anymore -// Description: Cleans up mob's client when they stop watching a video -/obj/item/device/communicator/proc/video_cleanup(mob/user) - if(!user) return - - user.reset_view(null) - user.unset_machine() - -// Proc: end_video() -// Parameters: reason - the text reason to print for why it ended -// Description: Ends the video call by clearing video_source -/obj/item/device/communicator/proc/end_video(var/reason) - video_source = null - - . = "\icon[src] [reason ? reason : "Video session ended"]." - - visible_message(.) - update_icon() - -//For synths who have no hands. -/obj/item/device/communicator/integrated - name = "integrated communicator" - desc = "A circuit used for long-range communications, able to be integrated into a system." - -//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() - set category = "AI IM" - set name = "Use Communicator" - set desc = "Utilizes your built-in communicator." - set src in usr - - if(usr.stat == 2) - usr << "You can't do that because you are dead!" - return - - src.attack_self(usr) - // A camera preset for spawning in the communicator /obj/machinery/camera/communicator network = list(NETWORK_COMMUNICATORS) diff --git a/code/game/objects/items/devices/communicator/integrated.dm b/code/game/objects/items/devices/communicator/integrated.dm new file mode 100644 index 00000000000..f464e15e0b4 --- /dev/null +++ b/code/game/objects/items/devices/communicator/integrated.dm @@ -0,0 +1,32 @@ +//For synths who have no hands. +/obj/item/device/communicator/integrated + name = "integrated communicator" + desc = "A circuit used for long-range communications, able to be integrated into a system." + +//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() + set category = "AI IM" + set name = "Use Communicator" + set desc = "Utilizes your built-in communicator." + set src in usr + + if(usr.stat == 2) + to_chat(usr, "You can't do that because you are dead!") + return + + src.attack_self(usr) \ No newline at end of file diff --git a/code/game/objects/items/devices/communicator/messaging.dm b/code/game/objects/items/devices/communicator/messaging.dm new file mode 100644 index 00000000000..3419985a710 --- /dev/null +++ b/code/game/objects/items/devices/communicator/messaging.dm @@ -0,0 +1,162 @@ +// Proc: receive_exonet_message() +// Parameters: 4 (origin atom - the source of the message's holder, origin_address - where the message came from, message - the message received, +// text - message text to send if message is of type "text") +// Description: Handles voice requests and invite messages originating from both real communicators and ghosts. Also includes a ping response and IM function. +/obj/item/device/communicator/receive_exonet_message(var/atom/origin_atom, origin_address, message, text) + 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 + 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") + if(message == "text") + request_im(origin_atom, origin_address, text) + return + +// 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. +/mob/observer/dead/receive_exonet_message(origin_atom, origin_address, message, text) + if(message == "voice") + if(istype(origin_atom, /obj/item/device/communicator)) + var/obj/item/device/communicator/comm = origin_atom + if(src in comm.voice_invites) + comm.open_connection(src) + return + to_chat(src, "\icon[origin_atom] Receiving communicator request from [origin_atom]. To answer, use the Call Communicator \ + verb, and select that name to answer the call.") + src << 'sound/machines/defib_SafetyOn.ogg' + 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") + if(message == "text") + to_chat(src, "\icon[origin_atom] Received text message from [origin_atom]: \"[text]\"") + src << 'sound/machines/defib_safetyOff.ogg' + exonet_messages.Add("From [origin_atom]:
[text]") + return + +// Proc: request_im() +// Parameters: 3 (candidate - the communicator wanting to message the device, origin_address - the address of the sender, text - the message) +// Description: Response to a communicator trying to message the device. +// Adds them to the list of people that have messaged this device and adds the message to the message list. +/obj/item/device/communicator/proc/request_im(var/atom/candidate, var/origin_address, var/text) + var/who = null + if(isobserver(candidate)) + var/mob/observer/dead/ghost = candidate + who = ghost + im_list += list(list("address" = origin_address, "to_address" = exonet.address, "im" = text)) + else if(istype(candidate, /obj/item/device/communicator)) + var/obj/item/device/communicator/comm = candidate + who = comm.owner + comm.im_contacts |= src + im_list += list(list("address" = origin_address, "to_address" = exonet.address, "im" = text)) + else return + + im_contacts |= candidate + + if(!who) + return + + if(ringer) + playsound(loc, 'sound/machines/twobeep.ogg', 50, 1) + for (var/mob/O in hearers(2, loc)) + O.show_message(text("\icon[src] *beep*")) + + alert_called = 1 + update_icon() + + //Search for holder of the device. + var/mob/living/L = null + if(loc && isliving(loc)) + L = loc + + if(L) + to_chat(L, "\icon[src] Message from [who].") + +// Verb: text_communicator() +// Parameters: None +// Description: Allows a ghost to send a text message to a communicator. +/mob/observer/dead/verb/text_communicator() + set category = "Ghost" + set name = "Text Communicator" + set desc = "If there is a communicator available, send a text message to it." + + if(ticker.current_state < GAME_STATE_PLAYING) + to_chat(src, "The game hasn't started yet!") + return + + if (!src.stat) + return + + if (usr != src) + return //something is terribly wrong + + for(var/mob/living/L in mob_list) //Simple check so you don't have dead people calling. + if(src.client.prefs.real_name == L.real_name) + to_chat(src, "Your identity is already present in the game world. Please load in a different character first.") + return + + var/obj/machinery/exonet_node/E = get_exonet_node() + if(!E || !E.on || !E.allow_external_communicators) + to_chat(src, "The Exonet node at telecommunications is down at the moment, or is actively blocking you, \ + so your call can't go through.") + return + + var/list/choices = list() + for(var/obj/item/device/communicator/comm in all_communicators) + if(!comm.network_visibility || !comm.exonet || !comm.exonet.address) + continue + choices.Add(comm) + + if(!choices.len) + to_chat(src, "There are no available communicators, sorry.") + return + + var/choice = input(src,"Send a text message to whom?") as null|anything in choices + if(choice) + var/obj/item/device/communicator/chosen_communicator = choice + var/mob/observer/dead/O = src + var/text_message = sanitize(input(src, "What do you want the message to say?")) as message + if(text_message && O.exonet) + O.exonet.send_message(chosen_communicator.exonet.address, "text", text_message) + + to_chat(src, "You have sent '[text_message]' to [chosen_communicator].") + exonet_messages.Add("To [chosen_communicator]:
[text_message]") + log_pda("[usr] (COMM: [src]) sent \"[text_message]\" to [chosen_communicator]") + for(var/mob/M in player_list) + if(M.stat == DEAD && M.is_preference_enabled(/datum/client_preference/ghost_ears)) + if(istype(M, /mob/new_player) || M.forbid_seeing_deadchat) + continue + if(M == src) + continue + M.show_message("Comm IM - [src] -> [chosen_communicator]: [text_message]") + + + +// Verb: show_text_messages() +// Parameters: None +// Description: Lets ghosts review messages they've sent or received. +/mob/observer/dead/verb/show_text_messages() + set category = "Ghost" + set name = "Show Text Messages" + set desc = "Allows you to see exonet text messages you've sent and received." + + var/HTML = "Exonet Message Log" + for(var/line in exonet_messages) + HTML += line + "
" + HTML +="" + usr << browse(HTML, "window=log;size=400x444;border=1;can_resize=1;can_close=1;can_minimize=0") \ No newline at end of file diff --git a/code/game/objects/items/devices/communicator/phone.dm b/code/game/objects/items/devices/communicator/phone.dm new file mode 100644 index 00000000000..d120bac21a9 --- /dev/null +++ b/code/game/objects/items/devices/communicator/phone.dm @@ -0,0 +1,380 @@ +// Proc: add_communicating() +// Parameters: 1 (comm - the communicator to add to communicating) +// Description: Used when this communicator gets a new communicator to relay say/me messages to +/obj/item/device/communicator/proc/add_communicating(obj/item/device/communicator/comm) + if(!comm || !istype(comm)) return + + communicating |= comm + listening_objects |= src + update_icon() + +// Proc: del_communicating() +// Parameters: 1 (comm - the communicator to remove from communicating) +// Description: Used when this communicator is being asked to stop relaying say/me messages to another +/obj/item/device/communicator/proc/del_communicating(obj/item/device/communicator/comm) + if(!comm || !istype(comm)) return + + communicating.Remove(comm) + update_icon() + +// Proc: open_connection() +// Parameters: 2 (user - the person who initiated the connecting being opened, candidate - the communicator or observer that will connect to the device) +// 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 + if(istype(candidate, /obj/item/device/communicator)) + open_connection_to_communicator(user, candidate) + +// Proc: open_connection_to_communicator() +// Parameters: 2 (user - the person who initiated this and will be receiving feedback information, candidate - someone else's communicator) +// Description: Adds the candidate and src to each other's communicating lists, allowing messages seen by the devices to be relayed. +/obj/item/device/communicator/proc/open_connection_to_communicator(mob/user, var/atom/candidate) + if(!istype(candidate, /obj/item/device/communicator)) + return + var/obj/item/device/communicator/comm = candidate + voice_invites.Remove(candidate) + comm.voice_requests.Remove(src) + + if(user) + comm.visible_message("\icon[src] Connecting to [src].") + to_chat(user, "\icon[src] Attempting to call [comm].") + sleep(10) + to_chat(user, "\icon[src] Dialing internally from [station_name()], Kara Subsystem, [system_name()].") + sleep(20) //If they don't have an exonet something is very wrong and we want a runtime. + to_chat(user, "\icon[src] Connection re-routed to [comm] at [comm.exonet.address].") + sleep(40) + to_chat(user, "\icon[src] Connection to [comm] at [comm.exonet.address] established.") + comm.visible_message("\icon[src] Connection to [src] at [exonet.address] established.") + sleep(20) + + src.add_communicating(comm) + comm.add_communicating(src) + +// 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)) + return + //Handle moving the ghost into the new shell. + announce_ghost_joinleave(candidate, 0, "They are occupying a personal communications device now.") + voice_requests.Remove(candidate) + 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. + var/msg = "[candidate && candidate.client ? "[candidate.client.key]" : "*no key*"] ([candidate]) has entered [src], triggered by \ + [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) + log_game(msg) + new_voice.mind = candidate.mind //Transfer the mind, if any. + new_voice.ckey = candidate.ckey //Finally, bring the client over. + voice_mobs.Add(new_voice) + listening_objects |= src + + 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.icon = 'icons/effects/effects.dmi' + blackness.icon_state = "1" + blackness.mouse_opacity = 2 //Can't see anything! + new_voice.client.screen.Add(blackness) + + update_icon() + + //Now for some connection fluff. + if(user) + to_chat(user, "\icon[src] Connecting to [candidate].") + to_chat(new_voice, "\icon[src] Attempting to call [src].") + sleep(10) + to_chat(new_voice, "\icon[src] Dialing to [station_name()], Kara Subsystem, [system_name()].") + sleep(20) + to_chat(new_voice, "\icon[src] Connecting to [station_name()] telecommunications array.") + sleep(40) + to_chat(new_voice, "\icon[src] Connection to [station_name()] telecommunications array established. Redirecting signal to [src].") + sleep(20) + + //We're connected, no need to hide everything. + new_voice.client.screen.Remove(blackness) + qdel(blackness) + + to_chat(new_voice, "\icon[src] Connection to [src] established.") + to_chat(new_voice, "To talk to the person on the other end of the call, just talk normally.") + to_chat(new_voice, "If you want to end the call, use the 'Hang Up' verb. The other person can also hang up at any time.") + to_chat(new_voice, "Remember, your character does not know anything you've learned from observing!") + if(new_voice.mind) + new_voice.mind.assigned_role = "Disembodied Voice" + if(user) + to_chat(user, "\icon[src] Your communicator is now connected to [candidate]'s communicator.") + +// Proc: close_connection() +// 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 + + 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 + to_chat(voice, "\icon[src] [reason].") + visible_message("\icon[src] [reason].") + voice_mobs.Remove(voice) + qdel(voice) + update_icon() + + for(var/obj/item/device/communicator/comm in communicating) //Now we handle real communicators. + if(target && comm != target) + continue + src.del_communicating(comm) + comm.del_communicating(src) + comm.visible_message("\icon[src] [reason].") + visible_message("\icon[src] [reason].") + if(comm.camera && video_source == comm.camera) //We hung up on the person on video + end_video() + if(camera && comm.video_source == camera) //We hung up on them while they were watching us + comm.end_video() + + if(voice_mobs.len == 0 && communicating.len == 0) + listening_objects.Remove(src) + +// Proc: request() +// Parameters: 1 (candidate - the ghost or communicator wanting to call the device) +// 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) + return + 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 + + if(ringer) + playsound(loc, 'sound/machines/twobeep.ogg', 50, 1) + for (var/mob/O in hearers(2, loc)) + O.show_message(text("\icon[src] *beep*")) + + alert_called = 1 + update_icon() + + //Search for holder of the device. + var/mob/living/L = null + if(loc && isliving(loc)) + L = loc + + if(L) + to_chat(L, "\icon[src] Communications request from [who].") + +// Proc: del_request() +// Parameters: 1 (candidate - the ghost or communicator to be declined) +// Description: Declines a request and cleans up both ends +/obj/item/device/communicator/proc/del_request(var/atom/candidate) + if(!(candidate in voice_requests)) + return + + if(isobserver(candidate)) + to_chat(candidate, "Your communicator call request was declined.") + else if(istype(candidate, /obj/item/device/communicator)) + var/obj/item/device/communicator/comm = candidate + comm.voice_invites -= src + + voice_requests -= candidate + + //Search for holder of our device. + var/mob/living/us = null + if(loc && isliving(loc)) + us = loc + + if(us) + to_chat(us, "\icon[src] Declined request.") + +// 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) + var/rendered = "\icon[src] [text]" + for(var/obj/item/device/communicator/comm in communicating) + var/turf/T = get_turf(comm) + if(!T) return + var/list/in_range = get_mobs_and_objs_in_view_fast(T,world.view,0) //Range of 3 since it's a tiny video display + var/list/mobs_to_relay = in_range["mobs"] + + for(var/mob/mob in mobs_to_relay) //We can't use visible_message(), or else we will get an infinite loop if two communicators hear each other. + var/dst = get_dist(get_turf(mob),get_turf(comm)) + if(dst <= video_range) + mob.show_message(rendered) + else + to_chat(mob, "You can barely see some movement on \the [src]'s display.") + + ..() + +// Proc: hear_talk() +// 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 +// being used) +// Description: Relays the speech to all linked communicators. +/obj/item/device/communicator/hear_talk(mob/living/M, text, verb, datum/language/speaking) + for(var/obj/item/device/communicator/comm in communicating) + + var/turf/T = get_turf(comm) + if(!T) return + var/list/in_range = get_mobs_and_objs_in_view_fast(T,world.view,0) + var/list/mobs_to_relay = in_range["mobs"] + + for(var/mob/mob in mobs_to_relay) + //Can whoever is hearing us understand? + if(!mob.say_understands(M, speaking)) + if(speaking) + text = speaking.scramble(text) + else + text = stars(text) + var/name_used = M.GetVoice() + var/rendered = null + if(speaking) //Language being used + rendered = "\icon[src] [name_used] [speaking.format_message(text, verb)]" + else + rendered = "\icon[src] [name_used] [verb], \"[text]\"" + 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] [msg]" + for(var/obj/item/device/communicator/comm in communicating) + var/turf/T = get_turf(comm) + if(!T) return + var/list/in_range = get_mobs_and_objs_in_view_fast(T,world.view,0) + var/list/mobs_to_relay = in_range["mobs"] + + for(var/mob/mob in mobs_to_relay) + mob.show_message(rendered) + ..() + +// Verb: join_as_voice() +// Parameters: None +// Description: Allows ghosts to call communicators, if they meet all the requirements. +/mob/observer/dead/verb/join_as_voice() + set category = "Ghost" + set name = "Call Communicator" + set desc = "If there is a communicator available, send a request to speak through it. This will reset your respawn timer, if someone picks up." + + if(ticker.current_state < GAME_STATE_PLAYING) + to_chat(src, "The game hasn't started yet!") + return + + if (!src.stat) + return + + if (usr != src) + return //something is terribly wrong + + var/confirm = alert(src, "Would you like to talk as [src.client.prefs.real_name], over a communicator? \ + This will reset your respawn timer, if someone answers.", "Join as Voice?", "Yes","No") + if(confirm == "No") + return + + for(var/mob/living/L in mob_list) //Simple check so you don't have dead people calling. + if(src.client.prefs.real_name == L.real_name) + to_chat(src, "Your identity is already present in the game world. Please load in a different character first.") + return + + var/obj/machinery/exonet_node/E = get_exonet_node() + if(!E || !E.on || !E.allow_external_communicators) + to_chat(src, "The Exonet node at telecommunications is down at the moment, or is actively blocking you, \ + so your call can't go through.") + return + + var/list/choices = list() + for(var/obj/item/device/communicator/comm in all_communicators) + if(!comm.network_visibility || !comm.exonet || !comm.exonet.address) + continue + choices.Add(comm) + + if(!choices.len) + to_chat(src , "There are no available communicators, sorry.") + return + + var/choice = input(src,"Send a voice request to whom?") as null|anything in choices + if(choice) + var/obj/item/device/communicator/chosen_communicator = choice + var/mob/observer/dead/O = src + if(O.exonet) + O.exonet.send_message(chosen_communicator.exonet.address, "voice") + + to_chat(src, "A communications request has been sent to [chosen_communicator]. Now you need to wait until someone answers.") + +// Proc: connect_video() +// Parameters: user - the mob doing the viewing of video, comm - the communicator at the far end +// Description: Sets up a videocall and puts the first view into it using watch_video, and updates the icon +/obj/item/device/communicator/proc/connect_video(mob/user,obj/item/device/communicator/comm) + if((!user) || (!comm) || user.stat) return //KO or dead, or already in a video + + if(video_source) //Already in a video + to_chat(user, "You are already connected to a video call!") + + if(user.blinded) //User is blinded + to_chat(user, "You cannot see well enough to do that!") + + if(!(src in comm.communicating) || !comm.camera) //You called someone with a broken communicator or one that's fake or yourself or something + to_chat(user, "\icon[src]ERROR: Video failed. Either bandwidth is too low, or the other communicator is malfunctioning.") + + to_chat(user, "\icon[src] Attempting to start video over existing call.") + sleep(30) + to_chat(user, "\icon[src] Please wait...") + + video_source = comm.camera + comm.visible_message("\icon[src] New video connection from [comm].") + watch_video(user) + update_icon() + +// Proc: watch_video() +// Parameters: user - the mob doing the viewing of video +// Description: Moves a mob's eye to the far end for the duration of viewing the far end +/obj/item/device/communicator/proc/watch_video(mob/user) + if(!Adjacent(user) || !video_source) return + user.set_machine(video_source) + user.reset_view(video_source) + to_chat(user,"Now viewing video session. To leave camera view, close the communicator window OR: OOC -> Cancel Camera View") + to_chat(user,"To return to an active video session, use the communicator in your hand.") + spawn(0) + while(user.machine == video_source && Adjacent(user)) + var/turf/T = get_turf(video_source) + if(!T || !is_on_same_plane_or_station(T.z, user.z) || !video_source.can_use()) + user << "The screen bursts into static, then goes black." + video_cleanup(user) + return + sleep(10) + + video_cleanup(user) + +// Proc: video_cleanup() +// Parameters: user - the mob who doesn't want to see video anymore +// Description: Cleans up mob's client when they stop watching a video +/obj/item/device/communicator/proc/video_cleanup(mob/user) + if(!user) return + + user.reset_view(null) + user.unset_machine() + +// Proc: end_video() +// Parameters: reason - the text reason to print for why it ended +// Description: Ends the video call by clearing video_source +/obj/item/device/communicator/proc/end_video(var/reason) + video_source = null + + . = "\icon[src] [reason ? reason : "Video session ended"]." + + visible_message(.) + update_icon() + diff --git a/html/changelogs/Atermonera - Weatherapp.yml b/html/changelogs/Atermonera - Weatherapp.yml new file mode 100644 index 00000000000..2a513366f1d --- /dev/null +++ b/html/changelogs/Atermonera - Weatherapp.yml @@ -0,0 +1,5 @@ +author: Atermonera + +delete-after: True + + - rscadd: "Communicators now have a weather app." diff --git a/nano/css/icons.css b/nano/css/icons.css index b2eacc0ab89..68ac412abaa 100644 --- a/nano/css/icons.css +++ b/nano/css/icons.css @@ -50,222 +50,241 @@ /* positioning */ .uiIcon16.icon-blank { background-position: 16px 16px; } -.uiIcon16.icon-carat-1-n { background-position: 0 0; } -.uiIcon16.icon-carat-1-ne { background-position: -16px 0; } -.uiIcon16.icon-carat-1-e { background-position: -32px 0; } -.uiIcon16.icon-carat-1-se { background-position: -48px 0; } -.uiIcon16.icon-carat-1-s { background-position: -64px 0; } -.uiIcon16.icon-carat-1-sw { background-position: -80px 0; } -.uiIcon16.icon-carat-1-w { background-position: -96px 0; } -.uiIcon16.icon-carat-1-nw { background-position: -112px 0; } + +.uiIcon16.icon-carat-1-n { background-position: 0 0; } +.uiIcon16.icon-carat-1-ne { background-position: -16px 0; } +.uiIcon16.icon-carat-1-e { background-position: -32px 0; } +.uiIcon16.icon-carat-1-se { background-position: -48px 0; } +.uiIcon16.icon-carat-1-s { background-position: -64px 0; } +.uiIcon16.icon-carat-1-sw { background-position: -80px 0; } +.uiIcon16.icon-carat-1-w { background-position: -96px 0; } +.uiIcon16.icon-carat-1-nw { background-position: -112px 0; } .uiIcon16.icon-carat-2-n-s { background-position: -128px 0; } .uiIcon16.icon-carat-2-e-w { background-position: -144px 0; } -.uiIcon16.icon-triangle-1-n { background-position: 0 -16px; } -.uiIcon16.icon-triangle-1-ne { background-position: -16px -16px; } -.uiIcon16.icon-triangle-1-e { background-position: -32px -16px; } -.uiIcon16.icon-triangle-1-se { background-position: -48px -16px; } -.uiIcon16.icon-triangle-1-s { background-position: -64px -16px; } -.uiIcon16.icon-triangle-1-sw { background-position: -80px -16px; } -.uiIcon16.icon-triangle-1-w { background-position: -96px -16px; } -.uiIcon16.icon-triangle-1-nw { background-position: -112px -16px; } + +.uiIcon16.icon-triangle-1-n { background-position: 0 -16px; } +.uiIcon16.icon-triangle-1-ne { background-position: -16px -16px; } +.uiIcon16.icon-triangle-1-e { background-position: -32px -16px; } +.uiIcon16.icon-triangle-1-se { background-position: -48px -16px; } +.uiIcon16.icon-triangle-1-s { background-position: -64px -16px; } +.uiIcon16.icon-triangle-1-sw { background-position: -80px -16px; } +.uiIcon16.icon-triangle-1-w { background-position: -96px -16px; } +.uiIcon16.icon-triangle-1-nw { background-position: -112px -16px; } .uiIcon16.icon-triangle-2-n-s { background-position: -128px -16px; } .uiIcon16.icon-triangle-2-e-w { background-position: -144px -16px; } -.uiIcon16.icon-arrow-1-n { background-position: 0 -32px; } -.uiIcon16.icon-arrow-1-ne { background-position: -16px -32px; } -.uiIcon16.icon-arrow-1-e { background-position: -32px -32px; } -.uiIcon16.icon-arrow-1-se { background-position: -48px -32px; } -.uiIcon16.icon-arrow-1-s { background-position: -64px -32px; } -.uiIcon16.icon-arrow-1-sw { background-position: -80px -32px; } -.uiIcon16.icon-arrow-1-w { background-position: -96px -32px; } -.uiIcon16.icon-arrow-1-nw { background-position: -112px -32px; } -.uiIcon16.icon-arrow-2-n-s { background-position: -128px -32px; } + +.uiIcon16.icon-arrow-1-n { background-position: 0 -32px; } +.uiIcon16.icon-arrow-1-ne { background-position: -16px -32px; } +.uiIcon16.icon-arrow-1-e { background-position: -32px -32px; } +.uiIcon16.icon-arrow-1-se { background-position: -48px -32px; } +.uiIcon16.icon-arrow-1-s { background-position: -64px -32px; } +.uiIcon16.icon-arrow-1-sw { background-position: -80px -32px; } +.uiIcon16.icon-arrow-1-w { background-position: -96px -32px; } +.uiIcon16.icon-arrow-1-nw { background-position: -112px -32px; } +.uiIcon16.icon-arrow-2-n-s { background-position: -128px -32px; } .uiIcon16.icon-arrow-2-ne-sw { background-position: -144px -32px; } -.uiIcon16.icon-arrow-2-e-w { background-position: -160px -32px; } +.uiIcon16.icon-arrow-2-e-w { background-position: -160px -32px; } .uiIcon16.icon-arrow-2-se-nw { background-position: -176px -32px; } + .uiIcon16.icon-arrowstop-1-n { background-position: -192px -32px; } .uiIcon16.icon-arrowstop-1-e { background-position: -208px -32px; } .uiIcon16.icon-arrowstop-1-s { background-position: -224px -32px; } .uiIcon16.icon-arrowstop-1-w { background-position: -240px -32px; } -.uiIcon16.icon-arrowthick-1-n { background-position: 0 -48px; } -.uiIcon16.icon-arrowthick-1-ne { background-position: -16px -48px; } -.uiIcon16.icon-arrowthick-1-e { background-position: -32px -48px; } -.uiIcon16.icon-arrowthick-1-se { background-position: -48px -48px; } -.uiIcon16.icon-arrowthick-1-s { background-position: -64px -48px; } -.uiIcon16.icon-arrowthick-1-sw { background-position: -80px -48px; } -.uiIcon16.icon-arrowthick-1-w { background-position: -96px -48px; } -.uiIcon16.icon-arrowthick-1-nw { background-position: -112px -48px; } -.uiIcon16.icon-arrowthick-2-n-s { background-position: -128px -48px; } + +.uiIcon16.icon-arrowthick-1-n { background-position: 0 -48px; } +.uiIcon16.icon-arrowthick-1-ne { background-position: -16px -48px; } +.uiIcon16.icon-arrowthick-1-e { background-position: -32px -48px; } +.uiIcon16.icon-arrowthick-1-se { background-position: -48px -48px; } +.uiIcon16.icon-arrowthick-1-s { background-position: -64px -48px; } +.uiIcon16.icon-arrowthick-1-sw { background-position: -80px -48px; } +.uiIcon16.icon-arrowthick-1-w { background-position: -96px -48px; } +.uiIcon16.icon-arrowthick-1-nw { background-position: -112px -48px; } +.uiIcon16.icon-arrowthick-2-n-s { background-position: -128px -48px; } .uiIcon16.icon-arrowthick-2-ne-sw { background-position: -144px -48px; } -.uiIcon16.icon-arrowthick-2-e-w { background-position: -160px -48px; } +.uiIcon16.icon-arrowthick-2-e-w { background-position: -160px -48px; } .uiIcon16.icon-arrowthick-2-se-nw { background-position: -176px -48px; } + .uiIcon16.icon-arrowthickstop-1-n { background-position: -192px -48px; } .uiIcon16.icon-arrowthickstop-1-e { background-position: -208px -48px; } .uiIcon16.icon-arrowthickstop-1-s { background-position: -224px -48px; } .uiIcon16.icon-arrowthickstop-1-w { background-position: -240px -48px; } + .uiIcon16.icon-arrowreturnthick-1-w { background-position: 0 -64px; } .uiIcon16.icon-arrowreturnthick-1-n { background-position: -16px -64px; } .uiIcon16.icon-arrowreturnthick-1-e { background-position: -32px -64px; } .uiIcon16.icon-arrowreturnthick-1-s { background-position: -48px -64px; } -.uiIcon16.icon-arrowreturn-1-w { background-position: -64px -64px; } -.uiIcon16.icon-arrowreturn-1-n { background-position: -80px -64px; } -.uiIcon16.icon-arrowreturn-1-e { background-position: -96px -64px; } -.uiIcon16.icon-arrowreturn-1-s { background-position: -112px -64px; } + +.uiIcon16.icon-arrowreturn-1-w { background-position: -64px -64px; } +.uiIcon16.icon-arrowreturn-1-n { background-position: -80px -64px; } +.uiIcon16.icon-arrowreturn-1-e { background-position: -96px -64px; } +.uiIcon16.icon-arrowreturn-1-s { background-position: -112px -64px; } .uiIcon16.icon-arrowrefresh-1-w { background-position: -128px -64px; } .uiIcon16.icon-arrowrefresh-1-n { background-position: -144px -64px; } .uiIcon16.icon-arrowrefresh-1-e { background-position: -160px -64px; } .uiIcon16.icon-arrowrefresh-1-s { background-position: -176px -64px; } -.uiIcon16.icon-arrow-4 { background-position: 0 -80px; } -.uiIcon16.icon-arrow-4-diag { background-position: -16px -80px; } -.uiIcon16.icon-extlink { background-position: -32px -80px; } -.uiIcon16.icon-newwin { background-position: -48px -80px; } -.uiIcon16.icon-refresh { background-position: -64px -80px; } -.uiIcon16.icon-shuffle { background-position: -80px -80px; } -.uiIcon16.icon-transfer-e-w { background-position: -96px -80px; } + +.uiIcon16.icon-arrow-4 { background-position: 0 -80px; } +.uiIcon16.icon-arrow-4-diag { background-position: -16px -80px; } +.uiIcon16.icon-extlink { background-position: -32px -80px; } +.uiIcon16.icon-newwin { background-position: -48px -80px; } +.uiIcon16.icon-refresh { background-position: -64px -80px; } +.uiIcon16.icon-shuffle { background-position: -80px -80px; } +.uiIcon16.icon-transfer-e-w { background-position: -96px -80px; } .uiIcon16.icon-transferthick-e-w { background-position: -112px -80px; } -.uiIcon16.icon-radiation { background-position: -128px -80px; } -.uiIcon16.icon-folder-collapsed { background-position: 0 -96px; } -.uiIcon16.icon-folder-open { background-position: -16px -96px; } -.uiIcon16.icon-document { background-position: -32px -96px; } -.uiIcon16.icon-document-b { background-position: -48px -96px; } -.uiIcon16.icon-note { background-position: -64px -96px; } -.uiIcon16.icon-mail-closed { background-position: -80px -96px; } -.uiIcon16.icon-mail-open { background-position: -96px -96px; } -.uiIcon16.icon-suitcase { background-position: -112px -96px; } -.uiIcon16.icon-comment { background-position: -128px -96px; } -.uiIcon16.icon-person { background-position: -144px -96px; } -.uiIcon16.icon-print { background-position: -160px -96px; } -.uiIcon16.icon-trash { background-position: -176px -96px; } -.uiIcon16.icon-locked { background-position: -192px -96px; } -.uiIcon16.icon-unlocked { background-position: -208px -96px; } -.uiIcon16.icon-bookmark { background-position: -224px -96px; } -.uiIcon16.icon-tag { background-position: -240px -96px; } -.uiIcon16.icon-home { background-position: 0 -112px; } -.uiIcon16.icon-flag { background-position: -16px -112px; } -.uiIcon16.icon-calendar { background-position: -32px -112px; } -.uiIcon16.icon-cart { background-position: -48px -112px; } -.uiIcon16.icon-pencil { background-position: -64px -112px; } -.uiIcon16.icon-clock { background-position: -80px -112px; } -.uiIcon16.icon-disk { background-position: -96px -112px; } -.uiIcon16.icon-calculator { background-position: -112px -112px; } -.uiIcon16.icon-zoomin { background-position: -128px -112px; } -.uiIcon16.icon-zoomout { background-position: -144px -112px; } -.uiIcon16.icon-search { background-position: -160px -112px; } -.uiIcon16.icon-wrench { background-position: -176px -112px; } -.uiIcon16.icon-gear { background-position: -192px -112px; } -.uiIcon16.icon-heart { background-position: -208px -112px; } -.uiIcon16.icon-star { background-position: -224px -112px; } -.uiIcon16.icon-link { background-position: -240px -112px; } -.uiIcon16.icon-cancel { background-position: 0 -128px; } -.uiIcon16.icon-plus { background-position: -16px -128px; } -.uiIcon16.icon-plusthick { background-position: -32px -128px; } -.uiIcon16.icon-minus { background-position: -48px -128px; } -.uiIcon16.icon-minusthick { background-position: -64px -128px; } -.uiIcon16.icon-close { background-position: -80px -128px; } -.uiIcon16.icon-closethick { background-position: -96px -128px; } -.uiIcon16.icon-key { background-position: -112px -128px; } -.uiIcon16.icon-lightbulb { background-position: -128px -128px; } -.uiIcon16.icon-scissors { background-position: -144px -128px; } -.uiIcon16.icon-clipboard { background-position: -160px -128px; } -.uiIcon16.icon-copy { background-position: -176px -128px; } -.uiIcon16.icon-contact { background-position: -192px -128px; } -.uiIcon16.icon-image { background-position: -208px -128px; } -.uiIcon16.icon-video { background-position: -224px -128px; } -.uiIcon16.icon-script { background-position: -240px -128px; } -.uiIcon16.icon-alert { background-position: 0 -144px; } -.uiIcon16.icon-alert-red { background-image: url(uiIcons16Red.png); background-position: 0 -144px; } -.uiIcon16.icon-info { background-position: -16px -144px; } -.uiIcon16.icon-notice { background-position: -32px -144px; } -.uiIcon16.icon-help { background-position: -48px -144px; } -.uiIcon16.icon-check { background-position: -64px -144px; } -.uiIcon16.icon-bullet { background-position: -80px -144px; } -.uiIcon16.icon-radio-on { background-position: -96px -144px; } -.uiIcon16.icon-radio-off { background-position: -112px -144px; } -.uiIcon16.icon-pin-w { background-position: -128px -144px; } -.uiIcon16.icon-pin-s { background-position: -144px -144px; } -.uiIcon16.icon-phone { background-position: -160px -144px; } -.uiIcon16.icon-list { background-position: -176px -144px; } -.uiIcon16.icon-syringe { background-position: -192px -144px; } -.uiIcon16.icon-play { background-position: 0 -160px; } -.uiIcon16.icon-pause { background-position: -16px -160px; } -.uiIcon16.icon-seek-next { background-position: -32px -160px; } -.uiIcon16.icon-seek-prev { background-position: -48px -160px; } -.uiIcon16.icon-seek-end { background-position: -64px -160px; } -.uiIcon16.icon-seek-start { background-position: -80px -160px; } +.uiIcon16.icon-radiation { background-position: -128px -80px; } +.uiIcon16.icon-folder-collapsed { background-position: 0 -96px; } +.uiIcon16.icon-folder-open { background-position: -16px -96px; } +.uiIcon16.icon-document { background-position: -32px -96px; } +.uiIcon16.icon-document-b { background-position: -48px -96px; } +.uiIcon16.icon-note { background-position: -64px -96px; } +.uiIcon16.icon-mail-closed { background-position: -80px -96px; } +.uiIcon16.icon-mail-open { background-position: -96px -96px; } +.uiIcon16.icon-suitcase { background-position: -112px -96px; } +.uiIcon16.icon-comment { background-position: -128px -96px; } +.uiIcon16.icon-person { background-position: -144px -96px; } +.uiIcon16.icon-print { background-position: -160px -96px; } +.uiIcon16.icon-trash { background-position: -176px -96px; } +.uiIcon16.icon-locked { background-position: -192px -96px; } +.uiIcon16.icon-unlocked { background-position: -208px -96px; } +.uiIcon16.icon-bookmark { background-position: -224px -96px; } +.uiIcon16.icon-tag { background-position: -240px -96px; } +.uiIcon16.icon-home { background-position: 0 -112px; } +.uiIcon16.icon-flag { background-position: -16px -112px; } +.uiIcon16.icon-calendar { background-position: -32px -112px; } +.uiIcon16.icon-cart { background-position: -48px -112px; } +.uiIcon16.icon-pencil { background-position: -64px -112px; } +.uiIcon16.icon-clock { background-position: -80px -112px; } +.uiIcon16.icon-disk { background-position: -96px -112px; } +.uiIcon16.icon-calculator { background-position: -112px -112px; } +.uiIcon16.icon-zoomin { background-position: -128px -112px; } +.uiIcon16.icon-zoomout { background-position: -144px -112px; } +.uiIcon16.icon-search { background-position: -160px -112px; } +.uiIcon16.icon-wrench { background-position: -176px -112px; } +.uiIcon16.icon-gear { background-position: -192px -112px; } +.uiIcon16.icon-heart { background-position: -208px -112px; } +.uiIcon16.icon-star { background-position: -224px -112px; } +.uiIcon16.icon-link { background-position: -240px -112px; } +.uiIcon16.icon-cancel { background-position: 0 -128px; } +.uiIcon16.icon-plus { background-position: -16px -128px; } +.uiIcon16.icon-plusthick { background-position: -32px -128px; } +.uiIcon16.icon-minus { background-position: -48px -128px; } +.uiIcon16.icon-minusthick { background-position: -64px -128px; } +.uiIcon16.icon-close { background-position: -80px -128px; } +.uiIcon16.icon-closethick { background-position: -96px -128px; } +.uiIcon16.icon-key { background-position: -112px -128px; } +.uiIcon16.icon-lightbulb { background-position: -128px -128px; } +.uiIcon16.icon-scissors { background-position: -144px -128px; } +.uiIcon16.icon-clipboard { background-position: -160px -128px; } +.uiIcon16.icon-copy { background-position: -176px -128px; } +.uiIcon16.icon-contact { background-position: -192px -128px; } +.uiIcon16.icon-image { background-position: -208px -128px; } +.uiIcon16.icon-video { background-position: -224px -128px; } +.uiIcon16.icon-script { background-position: -240px -128px; } +.uiIcon16.icon-alert { background-position: 0 -144px; } +.uiIcon16.icon-alert-red { background-image: url(uiIcons16Red.png); background-position: 0 -144px; } +.uiIcon16.icon-info { background-position: -16px -144px; } +.uiIcon16.icon-notice { background-position: -32px -144px; } +.uiIcon16.icon-help { background-position: -48px -144px; } +.uiIcon16.icon-check { background-position: -64px -144px; } +.uiIcon16.icon-bullet { background-position: -80px -144px; } +.uiIcon16.icon-radio-on { background-position: -96px -144px; } +.uiIcon16.icon-radio-off { background-position: -112px -144px; } +.uiIcon16.icon-pin-w { background-position: -128px -144px; } +.uiIcon16.icon-pin-s { background-position: -144px -144px; } +.uiIcon16.icon-phone { background-position: -160px -144px; } +.uiIcon16.icon-list { background-position: -176px -144px; } +.uiIcon16.icon-syringe { background-position: -192px -144px; } +.uiIcon16.icon-play { background-position: 0 -160px; } +.uiIcon16.icon-pause { background-position: -16px -160px; } +.uiIcon16.icon-seek-next { background-position: -32px -160px; } +.uiIcon16.icon-seek-prev { background-position: -48px -160px; } +.uiIcon16.icon-seek-end { background-position: -64px -160px; } +.uiIcon16.icon-seek-start { background-position: -80px -160px; } + /* uiIcon-seek-first is deprecated, use uiIcon-seek-start instead */ -.uiIcon16.icon-seek-first { background-position: -80px -160px; } -.uiIcon16.icon-stop { background-position: -96px -160px; } -.uiIcon16.icon-eject { background-position: -112px -160px; } -.uiIcon16.icon-volume-off { background-position: -128px -160px; } -.uiIcon16.icon-volume-on { background-position: -144px -160px; } -.uiIcon16.icon-power { background-position: 0 -176px; } -.uiIcon16.icon-signal-diag { background-position: -16px -176px; } -.uiIcon16.icon-signal { background-position: -32px -176px; } +.uiIcon16.icon-seek-first { background-position: -80px -160px; } +.uiIcon16.icon-stop { background-position: -96px -160px; } +.uiIcon16.icon-eject { background-position: -112px -160px; } +.uiIcon16.icon-volume-off { background-position: -128px -160px; } +.uiIcon16.icon-volume-on { background-position: -144px -160px; } +.uiIcon16.icon-power { background-position: 0 -176px; } +.uiIcon16.icon-signal-diag { background-position: -16px -176px; } +.uiIcon16.icon-signal { background-position: -32px -176px; } .uiIcon16.icon-signal-green { background-image: url(uiIcons16Green.png); background-position: -32px -176px; } + .uiIcon16.icon-battery-0 { background-position: -48px -176px; } .uiIcon16.icon-battery-1 { background-position: -64px -176px; } .uiIcon16.icon-battery-2 { background-position: -80px -176px; } .uiIcon16.icon-battery-3 { background-position: -96px -176px; } -.uiIcon16.icon-circle-plus { background-position: 0 -192px; } -.uiIcon16.icon-circle-minus { background-position: -16px -192px; } -.uiIcon16.icon-circle-close { background-position: -32px -192px; } + +.uiIcon16.icon-circle-plus { background-position: 0 -192px; } +.uiIcon16.icon-circle-minus { background-position: -16px -192px; } +.uiIcon16.icon-circle-close { background-position: -32px -192px; } .uiIcon16.icon-circle-triangle-e { background-position: -48px -192px; } .uiIcon16.icon-circle-triangle-s { background-position: -64px -192px; } .uiIcon16.icon-circle-triangle-w { background-position: -80px -192px; } .uiIcon16.icon-circle-triangle-n { background-position: -96px -192px; } -.uiIcon16.icon-circle-arrow-e { background-position: -112px -192px; } -.uiIcon16.icon-circle-arrow-s { background-position: -128px -192px; } -.uiIcon16.icon-circle-arrow-w { background-position: -144px -192px; } -.uiIcon16.icon-circle-arrow-n { background-position: -160px -192px; } -.uiIcon16.icon-circle-zoomin { background-position: -176px -192px; } -.uiIcon16.icon-circle-zoomout { background-position: -192px -192px; } -.uiIcon16.icon-circle-check { background-position: -208px -192px; } -.uiIcon16.icon-circlesmall-plus { background-position: 0 -208px; } +.uiIcon16.icon-circle-arrow-e { background-position: -112px -192px; } +.uiIcon16.icon-circle-arrow-s { background-position: -128px -192px; } +.uiIcon16.icon-circle-arrow-w { background-position: -144px -192px; } +.uiIcon16.icon-circle-arrow-n { background-position: -160px -192px; } +.uiIcon16.icon-circle-zoomin { background-position: -176px -192px; } +.uiIcon16.icon-circle-zoomout { background-position: -192px -192px; } +.uiIcon16.icon-circle-check { background-position: -208px -192px; } + +.uiIcon16.icon-circlesmall-plus { background-position: 0 -208px; } .uiIcon16.icon-circlesmall-minus { background-position: -16px -208px; } .uiIcon16.icon-circlesmall-close { background-position: -32px -208px; } -.uiIcon16.icon-squaresmall-plus { background-position: -48px -208px; } + +.uiIcon16.icon-squaresmall-plus { background-position: -48px -208px; } .uiIcon16.icon-squaresmall-minus { background-position: -64px -208px; } .uiIcon16.icon-squaresmall-close { background-position: -80px -208px; } -.uiIcon16.icon-grip-dotted-vertical { background-position: 0 -224px; } + +.uiIcon16.icon-grip-dotted-vertical { background-position: 0 -224px; } .uiIcon16.icon-grip-dotted-horizontal { background-position: -16px -224px; } -.uiIcon16.icon-grip-solid-vertical { background-position: -32px -224px; } -.uiIcon16.icon-grip-solid-horizontal { background-position: -48px -224px; } -.uiIcon16.icon-gripsmall-diagonal-se { background-position: -64px -224px; } -.uiIcon16.icon-grip-diagonal-se { background-position: -80px -224px; } +.uiIcon16.icon-grip-solid-vertical { background-position: -32px -224px; } +.uiIcon16.icon-grip-solid-horizontal { background-position: -48px -224px; } +.uiIcon16.icon-gripsmall-diagonal-se { background-position: -64px -224px; } +.uiIcon16.icon-grip-diagonal-se { background-position: -80px -224px; } + .uiIcon16.icon-batt_full { background-image: url(c_max.gif); background-position: 0px 0px } .uiIcon16.icon-batt_disc { background-image: url(c_discharging.gif); background-position: 0px 0px } .uiIcon16.icon-batt_chrg { background-image: url(c_charging.gif); background-position: 0px 0px } -/*yes, defining these all like this is awful, but unless the javascript for icon handling is sorted out this will have to do*/ -.uiIcon16.icon-empty64 { background-image: url(uiIcons64.png); background-position: 0 0; width: 64px; height: 64px; } -.uiIcon16.icon-phone64 { background-image: url(uiIcons64.png); background-position: -64px 0; width: 64px; height: 64px; } -.uiIcon16.icon-comment64 { background-image: url(uiIcons64.png); background-position: -128px 0; width: 64px; height: 64px; } -.uiIcon16.icon-list64 { background-image: url(uiIcons64.png); background-position: -192px 0; width: 64px; height: 64px; } -.uiIcon16.icon-gear64 { background-image: url(uiIcons64.png); background-position: -256px 0; width: 64px; height: 64px; } -.uiIcon16.icon-person64 { background-image: url(uiIcons64.png); background-position: -320px 0; width: 64px; height: 64px; } -.uiIcon16.icon-newspaper64 { background-image: url(uiIcons64.png); background-position: 0 -64px; width: 64px; height: 64px; } -.uiIcon16.icon-note64 { background-image: url(uiIcons64.png); background-position: -64px -64px; width: 64px; height: 64px; } -.uiIcon16.icon-close64 { background-image: url(uiIcons64.png); background-position: -128px -64px; width: 64px; height: 64px; } -.uiIcon16.icon-pill { background-image: url(pills32.png); width: 32px; height: 32px;} -.uiIcon16.icon-pill.pill1 { background-position: 0 0; } -.uiIcon16.icon-pill.pill2 { background-position: -32px 0; } -.uiIcon16.icon-pill.pill3 { background-position: -64px 0; } -.uiIcon16.icon-pill.pill4 { background-position: -96px 0; } -.uiIcon16.icon-pill.pill5 { background-position: -128px 0; } -.uiIcon16.icon-pill.pill6 { background-position: 0 -32px; } -.uiIcon16.icon-pill.pill7 { background-position: -32px -32px; } -.uiIcon16.icon-pill.pill8 { background-position: -64px -32px; } -.uiIcon16.icon-pill.pill9 { background-position: -96px -32px; } -.uiIcon16.icon-pill.pill10 { background-position: -128px -32px; } -.uiIcon16.icon-pill.pill11 { background-position: 0 -64px; } -.uiIcon16.icon-pill.pill12 { background-position: -32px -64px; } -.uiIcon16.icon-pill.pill13 { background-position: -64px -64px; } -.uiIcon16.icon-pill.pill14 { background-position: -96px -64px; } -.uiIcon16.icon-pill.pill15 { background-position: -128px -64px; } -.uiIcon16.icon-pill.pill16 { background-position: 0 -96px; } -.uiIcon16.icon-pill.pill17 { background-position: -32px -96px; } -.uiIcon16.icon-pill.pill18 { background-position: -64px -96px; } -.uiIcon16.icon-pill.pill19 { background-position: -96px -96px; } -.uiIcon16.icon-pill.pill20 { background-position: -128px -96px; } +/*yes, defining these all like this is awful, but unless the javascript for icon handling is sorted out this will have to do*/ +.uiIcon16.icon-empty64 { background-image: url(uiIcons64.png); background-position: 0 0; width: 64px; height: 64px; } +.uiIcon16.icon-phone64 { background-image: url(uiIcons64.png); background-position: -64px 0; width: 64px; height: 64px; } +.uiIcon16.icon-comment64 { background-image: url(uiIcons64.png); background-position: -128px 0; width: 64px; height: 64px; } +.uiIcon16.icon-list64 { background-image: url(uiIcons64.png); background-position: -192px 0; width: 64px; height: 64px; } +.uiIcon16.icon-gear64 { background-image: url(uiIcons64.png); background-position: -256px 0; width: 64px; height: 64px; } +.uiIcon16.icon-person64 { background-image: url(uiIcons64.png); background-position: -320px 0; width: 64px; height: 64px; } +.uiIcon16.icon-newspaper64 { background-image: url(uiIcons64.png); background-position: 0 -64px; width: 64px; height: 64px; } +.uiIcon16.icon-note64 { background-image: url(uiIcons64.png); background-position: -64px -64px; width: 64px; height: 64px; } +.uiIcon16.icon-close64 { background-image: url(uiIcons64.png); background-position: -128px -64px; width: 64px; height: 64px; } +.uiIcon16.icon-sun64 { background-image: url(uiIcons64.png); background-position: -192px -64px; width: 64px; height: 64px; } +.uiIcon16.icon-external64 { background-image: url(uiIcons64.png); background-position: -256px -64px; width: 64px; height: 64px; } + +.uiIcon16.icon-pill { background-image: url(pills32.png); width: 32px; height: 32px;} +.uiIcon16.icon-pill.pill1 { background-position: 0 0; } +.uiIcon16.icon-pill.pill2 { background-position: -32px 0; } +.uiIcon16.icon-pill.pill3 { background-position: -64px 0; } +.uiIcon16.icon-pill.pill4 { background-position: -96px 0; } +.uiIcon16.icon-pill.pill5 { background-position: -128px 0; } +.uiIcon16.icon-pill.pill6 { background-position: 0 -32px; } +.uiIcon16.icon-pill.pill7 { background-position: -32px -32px; } +.uiIcon16.icon-pill.pill8 { background-position: -64px -32px; } +.uiIcon16.icon-pill.pill9 { background-position: -96px -32px; } +.uiIcon16.icon-pill.pill10 { background-position: -128px -32px; } +.uiIcon16.icon-pill.pill11 { background-position: 0 -64px; } +.uiIcon16.icon-pill.pill12 { background-position: -32px -64px; } +.uiIcon16.icon-pill.pill13 { background-position: -64px -64px; } +.uiIcon16.icon-pill.pill14 { background-position: -96px -64px; } +.uiIcon16.icon-pill.pill15 { background-position: -128px -64px; } +.uiIcon16.icon-pill.pill16 { background-position: 0 -96px; } +.uiIcon16.icon-pill.pill17 { background-position: -32px -96px; } +.uiIcon16.icon-pill.pill18 { background-position: -64px -96px; } +.uiIcon16.icon-pill.pill19 { background-position: -96px -96px; } +.uiIcon16.icon-pill.pill20 { background-position: -128px -96px; } .uiIcon16.icon-pill.bottle1 { background-position: 0 -128px; } .uiIcon16.icon-pill.bottle2 { background-position: -32px -128px; } .uiIcon16.icon-pill.bottle3 { background-position: -64px -128px; } diff --git a/nano/images/source/uiIcons64.xcf b/nano/images/source/uiIcons64.xcf index c167e3b686075379370c7404b70a6918dfe47f8b..76c06ea1b6fcdaf9f07f74e5b85c8ed9e8aa5f52 100644 GIT binary patch literal 154502 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 b;6cE-WdFU1$7&}$8_#b!;q&nv&EfwCv*IYP diff --git a/nano/images/uiIcons64.png b/nano/images/uiIcons64.png index b58eb78f62bb5284e567325b2ddccb986f4db2f9..d849b3e8699c330de85a8ccd8ecc3b7207972852 100644 GIT binary patch literal 14179 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 diff --git a/nano/templates/communicator.tmpl b/nano/templates/communicator.tmpl index 6157f45d614..00f58378be5 100644 --- a/nano/templates/communicator.tmpl +++ b/nano/templates/communicator.tmpl @@ -234,9 +234,39 @@ Used In File(s): code\game\objects\items\devices\communicator\communicator.dm - + {{else data.currentTab == 6}} +

Weather

+ +
+
{{:helper.link('Home', 'home', {'switch_tab' : 1})}}

+ + {{for data.weather}} +
+
+ {{:value.Planet}}: +
+
+ Weather: {{:value.Weather}}, {{:value.Temperature - 273.15}}℃
+ High: {{:value.High - 273.15}}℃ | Low: {{:value.Low - 273.15}}℃
+ Wind: {{:value.Wind}} +
+
+ {{empty}} +
+
+ Error +
+
+ No weather reports available. Please try again later. +
+
+ {{/for}} + + + +{{else data.currentTab == 7}}

Settings

@@ -295,4 +325,12 @@ Used In File(s): code\game\objects\items\devices\communicator\communicator.dm -{{/if}} + + +{{else data.currentTab == 8}} + + + + + +{{/if}} \ No newline at end of file diff --git a/polaris.dme b/polaris.dme index f0c98f0ec08..16fc68c53c2 100644 --- a/polaris.dme +++ b/polaris.dme @@ -848,7 +848,12 @@ #include "code\game\objects\items\devices\uplink_random_lists.dm" #include "code\game\objects\items\devices\violin.dm" #include "code\game\objects\items\devices\whistle.dm" +#include "code\game\objects\items\devices\communicator\cartridge.dm" #include "code\game\objects\items\devices\communicator\communicator.dm" +#include "code\game\objects\items\devices\communicator\integrated.dm" +#include "code\game\objects\items\devices\communicator\messaging.dm" +#include "code\game\objects\items\devices\communicator\phone.dm" +#include "code\game\objects\items\devices\communicator\UI.dm" #include "code\game\objects\items\devices\PDA\cart.dm" #include "code\game\objects\items\devices\PDA\chatroom.dm" #include "code\game\objects\items\devices\PDA\PDA.dm"