From 5b299e0104878fdd896ef3eb92c75ed47569febf Mon Sep 17 00:00:00 2001 From: PsiOmega Date: Thu, 19 Feb 2015 18:23:47 +0100 Subject: [PATCH] NanoUI atmosphere control and remote air alarm support. --- code/game/machinery/alarm.dm | 35 +++++--- code/game/machinery/computer/atmos_control.dm | 87 ++++++++++--------- code/game/objects/objs.dm | 4 +- code/modules/mob/mob_helpers.dm | 6 ++ code/modules/nano/nanoexternal.dm | 14 +-- code/modules/nano/nanointeraction.dm | 25 ++++-- code/modules/nano/nanomanager.dm | 3 +- code/modules/nano/nanoui.dm | 34 +++++--- code/setup.dm | 1 + nano/templates/air_alarm.tmpl | 14 +-- nano/templates/atmos_control.tmpl | 5 ++ nano/templates/atmosphere_monitor.tmpl | 37 ++++++++ 12 files changed, 175 insertions(+), 90 deletions(-) create mode 100644 nano/templates/atmos_control.tmpl create mode 100644 nano/templates/atmosphere_monitor.tmpl 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