diff --git a/code/modules/alarm/alarm_handler.dm b/code/modules/alarm/alarm_handler.dm
index 9878a4b5e5e..cd09ebe4427 100644
--- a/code/modules/alarm/alarm_handler.dm
+++ b/code/modules/alarm/alarm_handler.dm
@@ -47,6 +47,11 @@
existing.clear(source)
return check_alarm_cleared(existing)
+/datum/alarm_handler/proc/has_major_alarms()
+ if(alarms && alarms.len)
+ return 1
+ return 0
+
/datum/alarm_handler/proc/major_alarms()
return alarms
diff --git a/code/modules/modular_computers/file_system/programs/airestorer.dm b/code/modules/modular_computers/file_system/programs/airestorer.dm
new file mode 100644
index 00000000000..0f662b427ce
--- /dev/null
+++ b/code/modules/modular_computers/file_system/programs/airestorer.dm
@@ -0,0 +1,138 @@
+
+
+/datum/computer_file/program/aidiag
+ filename = "aidiag"
+ filedesc = "AI Maintenance Utility"
+ program_icon_state = "generic"
+ extended_desc = "This program is capable of reconstructing damaged AI systems. Requires direct AI connection via intellicard slot."
+ size = 12
+ requires_ntnet = 0
+ usage_flags = PROGRAM_CONSOLE
+ transfer_access = access_heads
+ available_on_ntnet = 1
+ var/restoring = FALSE
+
+/datum/computer_file/program/aidiag/proc/get_ai(cardcheck)
+
+ var/obj/item/weapon/computer_hardware/ai_slot/ai_slot
+
+ if(computer)
+ ai_slot = computer.all_components[MC_AI]
+
+ if(computer && ai_slot && ai_slot.check_functionality())
+ if(cardcheck == 1)
+ return ai_slot
+ if(ai_slot.enabled && ai_slot.stored_card)
+ if(cardcheck == 2)
+ return ai_slot.stored_card
+ if(locate(/mob/living/silicon/ai) in ai_slot.stored_card)
+ return locate(/mob/living/silicon/ai) in ai_slot.stored_card
+
+ return null
+
+/datum/computer_file/program/aidiag/Topic(href, list/href_list)
+ if(..())
+ return TRUE
+
+ var/mob/living/silicon/ai/A = get_ai()
+ if(!A)
+ restoring = FALSE
+
+ switch(href_list["action"])
+ if("PRG_beginReconstruction")
+ if(A && A.health < 100)
+ restoring = TRUE
+ return TRUE
+ if("PRG_eject")
+ if(computer.all_components[MC_AI])
+ var/obj/item/weapon/computer_hardware/ai_slot/ai_slot = computer.all_components[MC_AI]
+ if(ai_slot && ai_slot.stored_card)
+ ai_slot.try_eject(0,usr)
+ return TRUE
+
+/datum/computer_file/program/aidiag/process_tick()
+ ..()
+ if(!restoring) //Put the check here so we don't check for an ai all the time
+ return
+ var/obj/item/device/aicard/cardhold = get_ai(2)
+
+ var/obj/item/weapon/computer_hardware/ai_slot/ai_slot = get_ai(1)
+
+
+ var/mob/living/silicon/ai/A = get_ai()
+ if(!A || !cardhold)
+ restoring = FALSE // If the AI was removed, stop the restoration sequence.
+ if(ai_slot)
+ ai_slot.locked = FALSE
+ return
+
+ if(cardhold.flush)
+ ai_slot.locked = FALSE
+ restoring = FALSE
+ return
+ ai_slot.locked = TRUE
+ A.adjustOxyLoss(-1)
+ A.adjustFireLoss(-1)
+ A.adjustToxLoss(-1)
+ A.adjustBruteLoss(-1)
+ A.updatehealth()
+ if(A.health >= 0 && A.stat == DEAD)
+ A.stat = CONSCIOUS
+ A.lying = 0
+ dead_mob_list -= A
+ living_mob_list += A
+ // Finished restoring
+ if(A.health >= 100)
+ ai_slot.locked = FALSE
+ restoring = FALSE
+
+ return TRUE
+
+
+/datum/computer_file/program/aidiag/ui_data(mob/user)
+ var/list/data = get_header_data()
+ var/mob/living/silicon/ai/AI
+ // A shortcut for getting the AI stored inside the computer. The program already does necessary checks.
+ AI = get_ai()
+
+ var/obj/item/device/aicard/aicard = get_ai(2)
+
+ if(!aicard)
+ data["nocard"] = TRUE
+ data["error"] = "Please insert an intelliCard."
+ else
+ if(!AI)
+ data["error"] = "No AI located"
+ else
+ var/obj/item/device/aicard/cardhold = AI.loc
+ if(cardhold.flush)
+ data["error"] = "Flush in progress"
+ else
+ data["name"] = AI.name
+ data["restoring"] = restoring
+ data["health"] = (AI.health + 100) / 2
+ data["isDead"] = AI.stat == DEAD
+
+ var/list/all_laws[0]
+ for(var/datum/ai_law/L in AI.laws.all_laws())
+ all_laws.Add(list(list(
+ "index" = L.index,
+ "text" = L.law
+ )))
+
+ data["ai_laws"] = all_laws
+
+ return data
+
+/datum/computer_file/program/aidiag/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
+ ui = nanomanager.try_update_ui(user, src, ui_key, ui, force_open)
+ if(!ui)
+ var/datum/asset/assets = get_asset_datum(/datum/asset/simple/headers)
+ assets.send(user)
+ ui = new(user, src, ui_key, "ai_restorer.tmpl", "Integrity Restorer", 600, 400)
+ ui.set_auto_update(1)
+ ui.open()
+
+/datum/computer_file/program/aidiag/kill_program(forced)
+ restoring = FALSE
+ return ..(forced)
\ No newline at end of file
diff --git a/code/modules/modular_computers/file_system/programs/alarm.dm b/code/modules/modular_computers/file_system/programs/alarm.dm
new file mode 100644
index 00000000000..17e0a8ad2fb
--- /dev/null
+++ b/code/modules/modular_computers/file_system/programs/alarm.dm
@@ -0,0 +1,70 @@
+/datum/computer_file/program/alarm_monitor
+ filename = "alarmmonitor"
+ filedesc = "Alarm Monitoring"
+ ui_header = "alarm_green.gif"
+ program_icon_state = "alert-green"
+ extended_desc = "This program provides visual interface for station's alarm system."
+ requires_ntnet = 1
+ network_destination = "alarm monitoring network"
+ size = 5
+ var/list/datum/alarm_handler/alarm_handlers
+
+/datum/computer_file/program/alarm_monitor/New()
+ ..()
+ alarm_handlers = list(atmosphere_alarm, fire_alarm, power_alarm)
+ for(var/datum/alarm_handler/AH in alarm_handlers)
+ AH.register(src, /datum/computer_file/program/alarm_monitor/proc/update_icon)
+
+/datum/computer_file/program/alarm_monitor/Destroy()
+ ..()
+ alarm_handlers.unregister(src)
+ qdel(alarm_handlers)
+ alarm_handlers = null
+
+/datum/computer_file/program/alarm_monitor/proc/update_icon()
+ for(var/datum/alarm_handler/AH in alarm_handlers)
+ if(AH.has_major_alarms())
+ program_icon_state = "alert-red"
+ ui_header = "alarm_red.gif"
+ update_computer_icon()
+ return 1
+ program_icon_state = "alert-green"
+ ui_header = "alarm_green.gif"
+ update_computer_icon()
+ return 0
+
+/datum/computer_file/program/alarm_monitor/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
+ ui = nanomanager.try_update_ui(user, src, ui_key, ui, force_open)
+ if(!ui)
+ var/datum/asset/assets = get_asset_datum(/datum/asset/simple/headers)
+ assets.send(user)
+ ui = new(user, src, ui_key, "alarm_monitor.tmpl", "Alarm Monitoring", 575, 700)
+ ui.set_auto_update(1)
+ ui.open()
+
+/datum/computer_file/program/alarm_monitor/ui_data(mob/user)
+ var/list/data = get_header_data()
+
+ var/categories[0]
+ for(var/datum/alarm_handler/AH in alarm_handlers)
+ categories[++categories.len] = list("category" = AH.category, "alarms" = list())
+ for(var/datum/alarm/A in AH.major_alarms())
+ var/cameras[0]
+ var/lost_sources[0]
+
+ if(isAI(user))
+ for(var/obj/machinery/camera/C in A.cameras())
+ cameras[++cameras.len] = C.nano_structure()
+ for(var/datum/alarm_source/AS in A.sources)
+ if(!AS.source)
+ lost_sources[++lost_sources.len] = AS.source_name
+
+ categories[categories.len]["alarms"] += list(list(
+ "name" = sanitize(A.alarm_name()),
+ "origin_lost" = A.origin == null,
+ "has_cameras" = cameras.len,
+ "cameras" = cameras,
+ "lost_sources" = lost_sources.len ? sanitize(english_list(lost_sources, nothing_text = "", and_text = ", ")) : ""))
+ data["categories"] = categories
+
+ return data
\ No newline at end of file
diff --git a/nano/templates/ai_restorer.tmpl b/nano/templates/ai_restorer.tmpl
new file mode 100644
index 00000000000..bfb23901e19
--- /dev/null
+++ b/nano/templates/ai_restorer.tmpl
@@ -0,0 +1,45 @@
+{{if data.restoring}}
+
Reconstruction in progress!
+{{/if}}
+
+
+
Inserted AI:
+
{{:helper.link(data.name ? data.name : "-----", 'eject', {'action' : 'PRG_eject'}, data.nocard ? 'disabled' : null)}}
+
+
+{{if data.error}}
+ ERROR: {{:data.error}}
+{{else}}
+ System Status
+
+
+
Current AI:
+
{{:data.name}}
+
+
+
Status:
+
{{if data.isDead}}Nonfunctional{{else}}Functional{{/if}}
+
+
+
System Integrity:
+ {{:helper.displayBar(data.health, 0, 100, (data.health > 75) ? 'good' : (health > 50) ? 'average' : 'bad')}}
+
+ {{:helper.smoothRound(data.health)}}%
+
+
+
+
+ Active Laws:
+
+
+
+ {{for data.ai_laws}}
+ | {{:value.index}}: {{:value.text}}
+ {{/for}}
+ |
+
+
+
+ Operations
+ {{:helper.link('Begin Reconstruction', 'plus', {'action' : 'PRG_beginReconstruction'}, data.restoring ? 'disabled' : null)}}
+{{/if}}
\ No newline at end of file
diff --git a/nano/templates/file_manager.tmpl b/nano/templates/file_manager.tmpl
index 7cf1dd8a676..1bdb0cd95c4 100644
--- a/nano/templates/file_manager.tmpl
+++ b/nano/templates/file_manager.tmpl
@@ -28,12 +28,12 @@
.{{:value.type}} |
{{:value.size}}GQ |
- {{:helper.link('VIEW', null, {'action' : 'PRG_openfile', 'name' : value.name})}}
- {{:helper.link('DELETE', null, {'action' : 'PRG_deletefile', 'name' : value.name}, value.undeletable ? 'disabled' : null)}}
- {{:helper.link('RENAME', null, {'action' : 'PRG_rename', 'name' : value.name}, value.undeletable ? 'disabled' : null)}}
- {{:helper.link('CLONE', null, {'action' : 'PRG_clone', 'name' : value.name}, value.undeletable ? 'disabled' : null)}}
+ {{:helper.link('VIEW', 'eye', {'action' : 'PRG_openfile', 'name' : value.name})}}
+ {{:helper.link('DELETE', 'trash', {'action' : 'PRG_deletefile', 'name' : value.name}, value.undeletable ? 'disabled' : null)}}
+ {{:helper.link('RENAME', 'pencil', {'action' : 'PRG_rename', 'name' : value.name}, value.undeletable ? 'disabled' : null)}}
+ {{:helper.link('CLONE', 'files-o', {'action' : 'PRG_clone', 'name' : value.name}, value.undeletable ? 'disabled' : null)}}
{{if data.usbconnected}}
- {{:helper.link('EXPORT', null, {'action' : 'PRG_copytousb', 'name' : value.name}, value.undeletable ? 'disabled' : null)}}
+ {{:helper.link('EXPORT', 'upload', {'action' : 'PRG_copytousb', 'name' : value.name}, value.undeletable ? 'disabled' : null)}}
{{/if}}
|
@@ -56,9 +56,9 @@
.{{:value.type}} |
{{:value.size}}GQ |
- {{:helper.link('DELETE', null, {'action' : 'PRG_usbdeletefile', 'name' : value.name}, value.undeletable ? 'disabled' : null)}}
+ {{:helper.link('DELETE', 'trash', {'action' : 'PRG_usbdeletefile', 'name' : value.name}, value.undeletable ? 'disabled' : null)}}
{{if data.usbconnected}}
- {{:helper.link('IMPORT', null, {'action' : 'PRG_copyfromusb', 'name' : value.name}, value.undeletable ? 'disabled' : null)}}
+ {{:helper.link('IMPORT', 'download', {'action' : 'PRG_copyfromusb', 'name' : value.name}, value.undeletable ? 'disabled' : null)}}
{{/if}}
|
@@ -66,6 +66,6 @@
{{/if}}
- {{:helper.link('NEW DATA FILE', null, {'action' : 'PRG_newtextfile'})}}
+ {{:helper.link('NEW DATA FILE', 'file-text', {'action' : 'PRG_newtextfile'})}}
{{/if}}
{{/if}}
\ No newline at end of file
diff --git a/nano/templates/ntnet_dos.tmpl b/nano/templates/ntnet_dos.tmpl
index 93faa096bfe..db92c2e84b7 100644
--- a/nano/templates/ntnet_dos.tmpl
+++ b/nano/templates/ntnet_dos.tmpl
@@ -14,12 +14,12 @@
{{if data.focus}}{{:data.focus}}{{else}}None{{/if}}
- {{:helper.link('EXECUTE', null, { 'PRG_execute' : 1 })}}
+ {{:helper.link('EXECUTE', null, {'action' : 'PRG_execute'})}}
Detected devices on network:
{{for data.relays}}
- | {{:helper.link(value.id, null, {'action' : 'PRG_target_relay', 'targid' : value.id})}}
+ |
| {{:helper.link(value.id, null, {'action' : 'PRG_target_relay', 'targid' : value.id})}}
{{/for}}
|
{{/if}}
\ No newline at end of file
diff --git a/paradise.dme b/paradise.dme
index 996c56dedfe..901804277da 100644
--- a/paradise.dme
+++ b/paradise.dme
@@ -1769,6 +1769,8 @@
#include "code\modules\modular_computers\file_system\data.dm"
#include "code\modules\modular_computers\file_system\program.dm"
#include "code\modules\modular_computers\file_system\program_events.dm"
+#include "code\modules\modular_computers\file_system\programs\airestorer.dm"
+#include "code\modules\modular_computers\file_system\programs\alarm.dm"
#include "code\modules\modular_computers\file_system\programs\configurator.dm"
#include "code\modules\modular_computers\file_system\programs\file_browser.dm"
#include "code\modules\modular_computers\file_system\programs\ntdownloader.dm"