diff --git a/code/game/machinery/alarm.dm b/code/game/machinery/alarm.dm
index 2cdfd0ee3d..a2e78a10f9 100644
--- a/code/game/machinery/alarm.dm
+++ b/code/game/machinery/alarm.dm
@@ -479,22 +479,29 @@
ui_interact(user)
wires.Interact(user)
-/obj/machinery/alarm/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1, remote = 0)
+/obj/machinery/alarm/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1, var/master_ui = null, var/datum/topic_state/custom_state = null)
var/data[0]
- var/is_locked = locked && !user.isAI()
+ var/remote_connection = 0
+ var/remote_access = 0
+ if(custom_state)
+ var/list/state = custom_state.href_list(user)
+ remote_connection = state["remote_connection"] // Remote connection means we're non-adjacent/connecting from another computer
+ remote_access = state["remote_access"] // Remote access means we also have the privilege to alter the air alarm.
- data["locked"] = is_locked
+ data["locked"] = locked && !user.isSilicon()
+ data["remote_connection"] = remote_connection
+ data["remote_access"] = remote_access
data["rcon"] = rcon_setting
data["screen"] = screen
populate_status(data)
- if(!is_locked)
+ if(!(locked && !remote_connection) || remote_access || user.isSilicon())
populate_controls(data)
ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open)
if(!ui)
- ui = new(user, src, ui_key, "air_alarm.tmpl", src.name, 625, 625)
+ ui = new(user, src, ui_key, "air_alarm.tmpl", src.name, 625, 625, master_ui = master_ui, custom_state = custom_state)
ui.set_initial_data(data)
ui.open()
ui.set_auto_update(1)
@@ -619,7 +626,7 @@
data["thresholds"] = thresholds
-/obj/machinery/alarm/CanUseTopic(var/mob/user)
+/obj/machinery/alarm/CanUseTopic(var/mob/user, href_list, var/datum/topic_state/custom_state)
if(buildstage != 2)
return STATUS_CLOSE
@@ -629,10 +636,18 @@
. = shorted ? STATUS_DISABLED : STATUS_INTERACTIVE
+ if(. == STATUS_INTERACTIVE)
+ var/extra_href = custom_state.href_list(usr)
+ // Prevent remote users from altering RCON settings unless they already have access (I realize the risks)
+ if(href_list["rcon"] && extra_href["remote_connection"] && !extra_href["remote_access"])
+ . = STATUS_UPDATE
+
+ //TODO: Move the rest of if(!locked || extra_href["remote_access"] || usr.isAI()) and hrefs here
+
return min(..(), .)
-/obj/machinery/alarm/Topic(href, href_list, var/nowindow = 0, var/remote = 0)
- if(..(href, href_list, nowindow, !remote))
+/obj/machinery/alarm/Topic(href, href_list, var/nowindow = 0, var/datum/topic_state/custom_state)
+ if(..(href, href_list, nowindow, custom_state))
return 1
// hrefs that can always be called -walter0o
@@ -661,8 +676,8 @@
return 1
// hrefs that need the AA unlocked -walter0o
- if(!locked || remote || istype(usr, /mob/living/silicon))
-
+ var/extra_href = custom_state.href_list(usr)
+ if(!(locked && !extra_href["remote_connection"]) || extra_href["remote_access"] || usr.isSilicon())
if(href_list["command"])
var/device_id = href_list["id_tag"]
switch(href_list["command"])
diff --git a/code/game/machinery/computer/atmos_control.dm b/code/game/machinery/computer/atmos_control.dm
index a722131102..a6eb8765f1 100644
--- a/code/game/machinery/computer/atmos_control.dm
+++ b/code/game/machinery/computer/atmos_control.dm
@@ -9,11 +9,11 @@
density = 1
anchored = 1.0
circuit = "/obj/item/weapon/circuitboard/atmoscontrol"
- var/obj/machinery/alarm/current
var/overridden = 0 //not set yet, can't think of a good way to do it
req_access = list(access_ce)
var/list/monitored_alarm_ids = null
var/list/monitored_alarms = null
+ var/ui_ref
/obj/machinery/computer/atmoscontrol/laptop
name = "Atmospherics Laptop"
@@ -32,36 +32,29 @@
monitored_alarms = dd_sortedObjectList(monitored_alarms)
/obj/machinery/computer/atmoscontrol/attack_ai(var/mob/user as mob)
- return interact(user)
+ return ui_interact(user)
/obj/machinery/computer/atmoscontrol/attack_hand(mob/user)
if(..())
return
- return interact(user)
+ return ui_interact(user)
-/obj/machinery/computer/atmoscontrol/interact(mob/user)
- user.set_machine(src)
- if(allowed(user))
- overridden = 1
- else if(!emagged)
- overridden = 0
- var/dat = "Main Menu
"
- if(monitored_alarms && monitored_alarms.len == 1)
- current = monitored_alarms[1]
- if(current)
- dat += specific()
- else
- for(var/obj/machinery/alarm/alarm in monitored_alarms ? monitored_alarms : machines)
- dat += ""
- switch(max(alarm.danger_level, alarm.alarm_area.atmosalm))
- if (0)
- dat += ""
- if (1)
- dat += ""
- if (2)
- dat += ""
- dat += "[sanitize(alarm.name)]
"
- user << browse(dat, "window=atmoscontrol")
+/obj/machinery/computer/atmoscontrol/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
+ var/data[0]
+ var/alarms[0]
+
+ // TODO: Move these to a cache, similar to cameras
+ for(var/obj/machinery/alarm/alarm in (monitored_alarms ? monitored_alarms : machines))
+ alarms[++alarms.len] = list("name" = sanitize(alarm.name), "ref"= "\ref[alarm]", "danger" = max(alarm.danger_level, alarm.alarm_area.atmosalm))
+ data["alarms"] = alarms
+
+ ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open)
+ if(!ui)
+ ui = new(user, src, ui_key, "atmos_control.tmpl", src.name, 625, 625)
+ ui.set_initial_data(data)
+ ui.open()
+ ui.set_auto_update(1)
+ ui_ref = ui
/obj/machinery/computer/atmoscontrol/attackby(var/obj/item/I as obj, var/mob/user as mob)
if(istype(I, /obj/item/weapon/card/emag) && !emagged)
@@ -73,23 +66,33 @@
return
return ..()
-/obj/machinery/computer/atmoscontrol/proc/specific()
- if(!current)
- return ""
- var/dat = "[current.name]
"
- dat += current.return_status()
- if(current.remote_control || overridden)
- dat += "
[current.return_controls(src)]"
- return dat
-
//a bunch of this is copied from atmos alarms
/obj/machinery/computer/atmoscontrol/Topic(href, href_list)
if(..())
- return
- if(href_list["reset"])
- current = null
+ return 1
+
if(href_list["alarm"])
- current = locate(href_list["alarm"])
- else if(current)
- current.Topic(href, href_list, 1, 1)
- interact(usr)
+ if(ui_ref)
+ var/obj/machinery/alarm/alarm = locate(href_list["alarm"]) in (monitored_alarms ? monitored_alarms : machines)
+ if(alarm)
+ var/datum/topic_state/TS = generate_state(alarm)
+ alarm.ui_interact(usr, master_ui = ui_ref, custom_state = TS)
+ return 1
+
+/obj/machinery/computer/atmoscontrol/proc/generate_state(var/alarm)
+ var/datum/topic_state/air_alarm/state = new()
+ state.atmos_control = src
+ state.air_alarm = alarm
+ return state
+
+/datum/topic_state/air_alarm
+ flags = NANO_IGNORE_DISTANCE
+ var/obj/machinery/computer/atmoscontrol/atmos_control = null
+ var/obj/machinery/alarm/air_alarm = null
+
+/datum/topic_state/air_alarm/href_list(var/mob/user)
+ var/list/extra_href = list()
+ extra_href["remote_connection"] = 1
+ extra_href["remote_access"] = user && (atmos_control.allowed(user) || atmos_control.emagged || air_alarm.rcon_setting == RCON_YES || (air_alarm.alarm_area.atmosalm && air_alarm.rcon_setting == RCON_AUTO))
+
+ return extra_href
diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm
index b0a01a04a0..b67ce9023e 100644
--- a/code/game/objects/objs.dm
+++ b/code/game/objects/objs.dm
@@ -16,7 +16,7 @@
var/damtype = "brute"
var/force = 0
-/obj/Topic(href, href_list, var/nowindow = 0, var/checkrange = 1)
+/obj/Topic(href, href_list, var/nowindow = 0, var/datum/topic_state/custom_state)
// Calling Topic without a corresponding window open causes runtime errors
if(!nowindow && ..())
return 1
@@ -24,7 +24,7 @@
// In the far future no checks are made in an overriding Topic() beyond if(..()) return
// Instead any such checks are made in CanUseTopic()
var/obj/host = nano_host()
- if(host.CanUseTopic(usr, checkrange) == STATUS_INTERACTIVE)
+ if(host.CanUseTopic(usr, href_list, custom_state) == STATUS_INTERACTIVE)
CouldUseTopic(usr)
return 0
diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm
index d205412297..bc9ba4a33d 100644
--- a/code/modules/mob/mob_helpers.dm
+++ b/code/modules/mob/mob_helpers.dm
@@ -80,6 +80,12 @@
return 1
return 0
+/mob/proc/isSilicon()
+ return 0
+
+/mob/living/silicon/isSilicon()
+ return 1
+
/mob/proc/isAI()
return 0
diff --git a/code/modules/nano/nanoexternal.dm b/code/modules/nano/nanoexternal.dm
index 50e2b706f8..b029019f1a 100644
--- a/code/modules/nano/nanoexternal.dm
+++ b/code/modules/nano/nanoexternal.dm
@@ -1,5 +1,5 @@
- // This file contains all Nano procs/definitions for external classes/objects
-
+ // This file contains all Nano procs/definitions for external classes/objects
+
/**
* Called when a Nano UI window is closed
* This is how Nano handles closed windows
@@ -15,7 +15,7 @@
if (istype(ui))
ui.close()
-
+
if(ui.ref)
var/href = "close=1"
src.Topic(href, params2list(href), ui.ref) // this will direct to the atom's Topic() proc via client.Topic()
@@ -31,14 +31,14 @@
* ui_interact is currently defined for /atom/movable
*
* @param user /mob The mob who is interacting with this ui
- * @param ui_key string A string key to use for this ui. Allows for multiple unique uis on one obj/mob (defaut value "main")
- * @param ui /datum/nanoui This parameter is passed by the nanoui process() proc when updating an open ui
+ * @param ui_key string A string key to use for this ui. Allows for multiple unique uis on one obj/mob (defaut value "main")
+ * @param ui /datum/nanoui This parameter is passed by the nanoui process() proc when updating an open ui
* @param force_open boolean Force the UI to (re)open, even if it's already open
*
* @return nothing
*/
-/atom/movable/proc/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
+/atom/movable/proc/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/nano_ui/master_ui = null, var/datum/topic_state/custom_state = null)
return
-
+
// Used by the Nano UI Manager (/datum/nanomanager) to track UIs opened by this mob
/mob/var/list/open_uis = list()
diff --git a/code/modules/nano/nanointeraction.dm b/code/modules/nano/nanointeraction.dm
index 42976554d9..09b46eb30e 100644
--- a/code/modules/nano/nanointeraction.dm
+++ b/code/modules/nano/nanointeraction.dm
@@ -5,11 +5,11 @@
return loc
-/atom/movable/proc/CanUseTopic(var/mob/user, var/be_close)
- return user.can_use_topic(nano_host(), be_close)
+/atom/movable/proc/CanUseTopic(var/mob/user, href_list, var/datum/topic_state/custom_state)
+ return user.can_use_topic(nano_host(), custom_state)
-/mob/proc/can_use_topic(var/src_object)
+/mob/proc/can_use_topic(var/mob/user, var/datum/topic_state/custom_state)
return STATUS_CLOSE // By default no mob can do anything with NanoUI
/mob/dead/observer/can_use_topic()
@@ -23,12 +23,13 @@
else
return ..()
-/mob/living/silicon/robot/can_use_topic(var/src_object)
+/mob/living/silicon/robot/can_use_topic(var/src_object, var/datum/topic_state/custom_state)
if(stat || !client)
return STATUS_CLOSE
if(lockcharge || stunned || weakened)
return STATUS_DISABLED
- if (src_object in view(src)) // robots can interact with things they can see within their view range
+ // robots can interact with things they can see within their view range
+ if(!(custom_state.flags & NANO_IGNORE_DISTANCE) && (src_object in view(src)))
return STATUS_INTERACTIVE // interactive (green visibility)
return STATUS_DISABLED // no updates, completely disabled (red visibility)
@@ -96,16 +97,22 @@
return STATUS_DISABLED // no updates, completely disabled (red visibility)
return STATUS_CLOSE
-/mob/living/can_use_topic(var/src_object, var/be_close = 1)
+/mob/living/can_use_topic(var/src_object, var/datum/topic_state/custom_state)
. = shared_living_nano_interaction(src_object)
- if(. == STATUS_INTERACTIVE && be_close)
+ if(. == STATUS_INTERACTIVE && !(custom_state.flags & NANO_IGNORE_DISTANCE))
. = shared_living_nano_distance(src_object)
if(STATUS_INTERACTIVE)
return STATUS_UPDATE
-/mob/living/carbon/human/can_use_topic(var/src_object, var/be_close = 1)
+/mob/living/carbon/human/can_use_topic(var/src_object, var/datum/topic_state/custom_state)
. = shared_living_nano_interaction(src_object)
- if(. == STATUS_INTERACTIVE && be_close)
+ if(. == STATUS_INTERACTIVE && !(custom_state.flags & NANO_IGNORE_DISTANCE))
. = shared_living_nano_distance(src_object)
if(. == STATUS_UPDATE && (TK in mutations)) // If we have telekinesis and remain close enough, allow interaction.
return STATUS_INTERACTIVE
+
+/datum/topic_state
+ var/flags = 0
+
+/datum/topic_state/proc/href_list(var/mob/user)
+ return list()
\ No newline at end of file
diff --git a/code/modules/nano/nanomanager.dm b/code/modules/nano/nanomanager.dm
index ddc3fcac9f..794d9c5727 100644
--- a/code/modules/nano/nanomanager.dm
+++ b/code/modules/nano/nanomanager.dm
@@ -186,7 +186,8 @@
return 0 // wasn't open
processing_uis.Remove(ui)
- ui.user.open_uis.Remove(ui)
+ if(ui.user) // Sanity check in case a user has been deleted (say a blown up borg watching the alarm interface)
+ ui.user.open_uis.Remove(ui)
var/list/uis = open_uis[src_object_key][ui.ui_key]
uis.Remove(ui)
diff --git a/code/modules/nano/nanoui.dm b/code/modules/nano/nanoui.dm
index e0047f8cfb..0a6b6ede8e 100644
--- a/code/modules/nano/nanoui.dm
+++ b/code/modules/nano/nanoui.dm
@@ -52,6 +52,11 @@ nanoui is used to open and update nano browser uis
// the current status/visibility of the ui
var/status = STATUS_INTERACTIVE
+ // Relationship between a master interface and its children. Used in update_status
+ var/datum/nanoui/master_ui
+ var/list/datum/nanoui/children = list()
+ var/datum/topic_state/custom_state = null
+
var/cached_data = null
/**
@@ -68,17 +73,22 @@ nanoui is used to open and update nano browser uis
*
* @return /nanoui new nanoui object
*/
-/datum/nanoui/New(nuser, nsrc_object, nui_key, ntemplate_filename, ntitle = 0, nwidth = 0, nheight = 0, var/atom/nref = null)
+/datum/nanoui/New(nuser, nsrc_object, nui_key, ntemplate_filename, ntitle = 0, nwidth = 0, nheight = 0, var/atom/nref = null, var/datum/nanoui/master_ui = null, var/datum/topic_state/custom_state = null)
user = nuser
src_object = nsrc_object
ui_key = nui_key
window_id = "[ui_key]\ref[src_object]"
+ src.master_ui = master_ui
+ if(master_ui)
+ master_ui.children += src
+ src.custom_state = custom_state ? custom_state : new/datum/topic_state()
+
// add the passed template filename as the "main" template, this is required
add_template("main", ntemplate_filename)
if (ntitle)
- title = ntitle
+ title = sanitize(ntitle)
if (nwidth)
width = nwidth
if (nheight)
@@ -132,16 +142,15 @@ nanoui is used to open and update nano browser uis
* @return nothing
*/
/datum/nanoui/proc/update_status(var/push_update = 0)
- var/new_status = get_status()
+ var/atom/movable/host = src_object.nano_host()
+ var/new_status = host.CanUseTopic(user, list(), custom_state)
+ if(master_ui)
+ new_status = min(new_status, master_ui.status)
if(new_status == STATUS_CLOSE)
close()
else
set_status(new_status, push_update)
-/datum/nanoui/proc/get_status()
- var/atom/movable/host = src_object.nano_host()
- return host.CanUseTopic(user)
-
/**
* Set the ui to auto update (every master_controller tick)
*
@@ -383,10 +392,6 @@ nanoui is used to open and update nano browser uis
* @return nothing
*/
/datum/nanoui/proc/open()
- var/new_status = get_status()
- if(new_status == STATUS_CLOSE)
- return
-
var/window_size = ""
if (width && height)
window_size = "size=[width]x[height];"
@@ -406,6 +411,8 @@ nanoui is used to open and update nano browser uis
is_auto_updating = 0
nanomanager.ui_closed(src)
user << browse(null, "window=[window_id]")
+ for(var/datum/nanoui/child in children)
+ child.close()
/**
* Set the UI window to call the nanoclose verb when the window is closed
@@ -470,7 +477,7 @@ nanoui is used to open and update nano browser uis
set_map_z_level(text2num(href_list["mapZLevel"]))
map_update = 1
- if ((src_object && src_object.Topic(href, href_list)) || map_update)
+ if ((src_object && src_object.Topic(href, href_list, 0, custom_state)) || map_update)
nanomanager.update_uis(src_object) // update all UIs attached to src_object
/**
@@ -497,5 +504,4 @@ nanoui is used to open and update nano browser uis
* @return nothing
*/
/datum/nanoui/proc/update(var/force_open = 0)
- src_object.ui_interact(user, ui_key, src, force_open)
-
+ src_object.ui_interact(user, ui_key, src, force_open, master_ui, custom_state)
diff --git a/code/setup.dm b/code/setup.dm
index 06235da7fc..fb54f3c067 100644
--- a/code/setup.dm
+++ b/code/setup.dm
@@ -759,3 +759,4 @@ var/list/be_special_flags = list(
#define STATUS_DISABLED 0 // RED Visability
#define STATUS_CLOSE -1 // Close the interface
+#define NANO_IGNORE_DISTANCE 1
\ No newline at end of file
diff --git a/nano/templates/air_alarm.tmpl b/nano/templates/air_alarm.tmpl
index 5b1ee24574..6ba6b14057 100644
--- a/nano/templates/air_alarm.tmpl
+++ b/nano/templates/air_alarm.tmpl
@@ -60,9 +60,9 @@ Used In File(s): \code\game\machinery\alarm.dm
- {{:helper.link('Off', null, { 'rcon' : 1}, null, data.rcon == 1 ? 'selected' : null)}}
- {{:helper.link('Auto', null, { 'rcon' : 2}, null, data.rcon == 2 ? 'selected' : null)}}
- {{:helper.link('On', null, { 'rcon' : 3}, null, data.rcon == 3 ? 'selected' : null)}}
+ {{:helper.link('Off', null, { 'rcon' : 1}, data.remote_connection && !data.remote_access ? (data.rcon == 1 ? 'yellowButton' : 'disabled') : null, data.rcon == 1 ? 'selected' : null)}}
+ {{:helper.link('Auto', null, { 'rcon' : 2}, data.remote_connection && !data.remote_access ? (data.rcon == 2 ? 'yellowButton' : 'disabled') : null, data.rcon == 2 ? 'selected' : null)}}
+ {{:helper.link('On', null, { 'rcon' : 3}, data.remote_connection && !data.remote_access ? (data.rcon == 3 ? 'yellowButton' : 'disabled') : null, data.rcon == 3 ? 'selected' : null)}}
|
@@ -74,8 +74,12 @@ Used In File(s): \code\game\machinery\alarm.dm
-{{if data.locked}}
- (Swipe ID card to unlock interface)
+{{if data.locked || (data.remote_connection && ! data.remote_access)}}
+ {{if data.remote_connection}}
+ (Current remote control settings and alarm status restricts access.)
+ {{else}}
+ (Swipe ID card to unlock interface.)
+ {{/if}}
{{else}}
{{if data.screen != 1}}
{{:helper.link('Main Menu', null, { 'screen' : 1})}}
diff --git a/nano/templates/atmos_control.tmpl b/nano/templates/atmos_control.tmpl
new file mode 100644
index 0000000000..5e9a1acfe4
--- /dev/null
+++ b/nano/templates/atmos_control.tmpl
@@ -0,0 +1,5 @@
+
+ {{for data.alarms}}
+ {{:helper.link(value.name, null, {'alarm' : value.ref}, null, value.danger == 2 ? 'redButton' : (value.danger == 1 ? 'yellowButton' : null))}}
+ {{/for}}
+
\ No newline at end of file
diff --git a/nano/templates/atmosphere_monitor.tmpl b/nano/templates/atmosphere_monitor.tmpl
new file mode 100644
index 0000000000..0089467e43
--- /dev/null
+++ b/nano/templates/atmosphere_monitor.tmpl
@@ -0,0 +1,37 @@
+
+
+{{for data.categories}}
+ {{:value.category}}
+ {{for value.alarms :alarmValue:alarmIndex}}
+ {{if alarmValue.origin_lost}}
+ {{:alarmValue.name}} Alarm Origin Lost
+ {{else}}
+ {{:alarmValue.name}}
+ {{/if}}
+ {{if alarmValue.has_cameras || alarmValue.lost_sources != ""}}
+
+ {{if alarmValue.has_cameras}}
+
+ {{for alarmValue.cameras :cameraValue:cameraIndex}}
+ {{if cameraValue.deact}}
+ {{:helper.link(cameraValue.name + " (deactivated)", '', {}, 'inactive')}}
+ {{else}}
+ {{:helper.link(cameraValue.name, '', {'switchTo' : cameraValue.camera})}}
+ {{/if}}
+ {{/for}}
+
+ {{/if}}
+ {{if alarmValue.lost_sources != ""}}
+
+
Lost Alarm Sources: {{:alarmValue.lost_sources}}
+
+ {{/if}}
+
+ {{/if}}
+ {{empty}}
+ --All Systems Nominal
+ {{/for}}
+{{/for}}
\ No newline at end of file