diff --git a/code/game/machinery/computer/robot.dm b/code/game/machinery/computer/robot.dm
index 84a6b7730c7..d0d72fc53f2 100644
--- a/code/game/machinery/computer/robot.dm
+++ b/code/game/machinery/computer/robot.dm
@@ -1,5 +1,3 @@
-//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31
-
/obj/machinery/computer/robotics
name = "robotics control console"
desc = "Used to remotely lockdown or detonate linked cyborgs."
@@ -9,218 +7,205 @@
req_access = list(access_robotics)
circuit = "/obj/item/weapon/circuitboard/robotics"
- var/id = 0.0
- var/temp = null
- var/status = 0
- var/timeleft = 60
- var/stop = 0.0
- var/screen = 0 // 0 - Main Menu, 1 - Cyborg Status, 2 - Kill 'em All! -- In text
-
+ var/safety = 1
/obj/machinery/computer/robotics/attack_ai(var/mob/user as mob)
- return src.attack_hand(user)
+ ui_interact(user)
/obj/machinery/computer/robotics/attack_hand(var/mob/user as mob)
- if(..())
- return
- if (src.z > 6)
- user << "\red Unable to establish a connection: \black You're too far away from the station!"
- return
- user.set_machine(src)
- var/dat
- if (src.temp)
- dat = "[src.temp]
Clear Screen"
- else
- if(screen == 0)
- dat += "
Cyborg Control Console
"
- dat += "1. Cyborg Status
"
- dat += "2. Emergency Full Destruct
"
- if(screen == 1)
- for(var/mob/living/silicon/robot/R in mob_list)
- if(istype(R, /mob/living/silicon/robot/drone))
- continue //There's a specific console for drones.
- if(istype(user, /mob/living/silicon/ai))
- if (R.connected_ai != user)
- continue
- if(istype(user, /mob/living/silicon/robot))
- if (R != user)
- continue
- if(R.scrambledcodes)
- continue
+ ui_interact(user)
- dat += "[R.name] |"
- if(R.stat)
- dat += " Not Responding |"
- else if (!R.canmove)
- dat += " Locked Down |"
- else
- dat += " Operating Normally |"
- if (!R.canmove)
- else if(R.cell)
- dat += " Battery Installed ([R.cell.charge]/[R.cell.maxcharge]) |"
- else
- dat += " No Cell Installed |"
- if(R.module)
- dat += " Module Installed ([R.module.name]) |"
- else
- dat += " No Module Installed |"
- if(R.connected_ai)
- dat += " Slaved to [R.connected_ai.name] |"
- else
- dat += " Independent from AI |"
- if (istype(user, /mob/living/silicon))
- if((user.mind.special_role && user.mind.original == user) && !R.emagged)
- dat += "(Hack) "
- dat += "([R.canmove ? "Lockdown" : "Release"]) "
- dat += "(Destroy)"
- dat += "
"
- dat += "(Return to Main Menu)
"
- if(screen == 2)
- if(!src.status)
- dat += {"
Emergency Robot Self-Destruct
\nStatus: Off
- \n
- \nCountdown: [src.timeleft]/60 \[Reset\]
- \n
- \nStart Sequence
- \n
- \nClose"}
- else
- dat = {"Emergency Robot Self-Destruct
\nStatus: Activated
- \n
- \nCountdown: [src.timeleft]/60 \[Reset\]
- \n
\nStop Sequence
- \n
- \nClose"}
- dat += "(Return to Main Menu)
"
+/obj/machinery/computer/robotics/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
+ var/data[0]
+ data["robots"] = get_cyborgs(user)
+ data["safety"] = safety
+ // Also applies for cyborgs. Hides the manual self-destruct button.
+ data["is_ai"] = user.isSilicon()
- user << browse(dat, "window=computer;size=400x500")
- onclose(user, "computer")
- return
+
+ ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open)
+ if (!ui)
+ ui = new(user, src, ui_key, "robot_control.tmpl", "Robotic Control Console", 400, 500)
+ ui.set_initial_data(data)
+ ui.open()
+ ui.set_auto_update(1)
/obj/machinery/computer/robotics/Topic(href, href_list)
if(..())
- return 1
- if ((usr.contents.Find(src) || (in_range(src, usr) && istype(src.loc, /turf))) || (istype(usr, /mob/living/silicon)))
- usr.set_machine(src)
+ return
+ var/mob/user = usr
+ if(!src.allowed(user))
+ user << "Access Denied"
+ return
- if (href_list["eject"])
- src.temp = {"Destroy Robots?
-
\[Swipe ID to initiate destruction sequence\]
- Cancel"}
-
- else if (href_list["eject2"])
- var/obj/item/weapon/card/id/I = usr.get_active_hand()
- if (istype(I, /obj/item/device/pda))
- var/obj/item/device/pda/pda = I
- I = pda.id
- if (istype(I))
- if(src.check_access(I))
- if (!status)
- message_admins("\blue [key_name_admin(usr)] has initiated the global cyborg killswitch!")
- log_game("\blue [key_name(usr)] has initiated the global cyborg killswitch!")
- src.status = 1
- src.start_sequence()
- src.temp = null
-
- else
- usr << "\red Access Denied."
-
- else if (href_list["stop"])
- src.temp = {"
- Stop Robot Destruction Sequence?
-
Yes
- No"}
-
- else if (href_list["stop2"])
- src.stop = 1
- src.temp = null
- src.status = 0
-
- else if (href_list["reset"])
- src.timeleft = 60
-
- else if (href_list["temp"])
- src.temp = null
- else if (href_list["screen"])
- switch(href_list["screen"])
- if("0")
- screen = 0
- if("1")
- screen = 1
- if("2")
- screen = 2
- else if (href_list["killbot"])
- if(src.allowed(usr))
- var/mob/living/silicon/robot/R = locate(href_list["killbot"])
- if(R)
- var/choice = input("Are you certain you wish to detonate [R.name]?") in list("Confirm", "Abort")
- if(choice == "Confirm")
- if(R && istype(R))
- if(R.mind && R.mind.special_role && R.emagged)
- R << "Extreme danger. Termination codes detected. Scrambling security codes and automatic AI unlink triggered."
- R.ResetSecurityCodes()
-
- else
- message_admins("\blue [key_name_admin(usr)] detonated [R.name]!")
- log_game("\blue [key_name_admin(usr)] detonated [R.name]!")
- R.self_destruct()
- else
- usr << "\red Access Denied."
-
- else if (href_list["stopbot"])
- if(src.allowed(usr))
- var/mob/living/silicon/robot/R = locate(href_list["stopbot"])
- if(R && istype(R)) // Extra sancheck because of input var references
- var/choice = input("Are you certain you wish to [R.canmove ? "lock down" : "release"] [R.name]?") in list("Confirm", "Abort")
- if(choice == "Confirm")
- if(R && istype(R))
- message_admins("\blue [key_name_admin(usr)] [R.canmove ? "locked down" : "released"] [R.name]!")
- log_game("[key_name(usr)] [R.canmove ? "locked down" : "released"] [R.name]!")
- R.canmove = !R.canmove
- if (R.lockcharge)
- // R.cell.charge = R.lockcharge
- R.lockcharge = !R.lockcharge
- R << "Your lockdown has been lifted!"
- else
- R.lockcharge = !R.lockcharge
- // R.cell.charge = 0
- R << "You have been locked down!"
-
- else
- usr << "\red Access Denied."
-
- else if (href_list["magbot"])
- if(src.allowed(usr))
- var/mob/living/silicon/robot/R = locate(href_list["magbot"])
-
- // whatever weirdness this is supposed to be, but that is how the href gets added, so here it is again
- if(istype(R) && istype(usr, /mob/living/silicon) && usr.mind.special_role && (usr.mind.original == usr) && !R.emagged)
-
- var/choice = input("Are you certain you wish to hack [R.name]?") in list("Confirm", "Abort")
- if(choice == "Confirm")
- if(R && istype(R))
-// message_admins("\blue [key_name_admin(usr)] emagged [R.name] using robotic console!")
- log_game("[key_name(usr)] emagged [R.name] using robotic console!")
- R.emagged = 1
- if(R.mind.special_role)
- R.verbs += /mob/living/silicon/robot/proc/ResetSecurityCodes
-
- src.add_fingerprint(usr)
- src.updateUsrDialog()
- return
-
-/obj/machinery/computer/robotics/proc/start_sequence()
-
- do
- if(src.stop)
- src.stop = 0
+ // Destroys the cyborg
+ if(href_list["detonate"])
+ var/mob/living/silicon/robot/target = get_cyborg_by_name(href_list["detonate"])
+ if(!target || !istype(target))
return
- src.timeleft--
- sleep(10)
- while(src.timeleft)
+ if(user.isMobAI() && (target.connected_ai != user))
+ user << "Access Denied. This robot is not linked to you."
+ return
+ // Cyborgs may blow up themselves via the console
+ if(isrobot(user) && user != target)
+ user << "Access Denied."
+ return
+ var/choice = input("Really detonate [target.name]?") in list ("Yes", "No")
+ if(choice != "Yes")
+ return
+ if(!target || !istype(target))
+ return
+
+ // Antagonistic cyborgs? Left here for downstream
+ if(target.mind && target.mind.special_role && target.emagged)
+ target << "Extreme danger. Termination codes detected. Scrambling security codes and automatic AI unlink triggered."
+ target.ResetSecurityCodes()
+ else
+ message_admins("\blue [key_name_admin(usr)] detonated [target.name]!")
+ log_game("\blue [key_name_admin(usr)] detonated [target.name]!")
+ target << "Self-destruct command received."
+ spawn(10)
+ target.self_destruct()
+
+
+
+ // Locks or unlocks the cyborg
+ else if (href_list["lockdown"])
+ var/mob/living/silicon/robot/target = get_cyborg_by_name(href_list["lockdown"])
+ if(!target || !istype(target))
+ return
+
+ if(user.isMobAI() && (target.connected_ai != user))
+ user << "Access Denied. This robot is not linked to you."
+ return
+
+ if(isrobot(user))
+ user << "Access Denied."
+ return
+
+ var/choice = input("Really [target.lockcharge ? "unlock" : "lockdown"] [target.name] ?") in list ("Yes", "No")
+ if(choice != "Yes")
+ return
+
+ if(!target || !istype(target))
+ return
+
+ message_admins("\blue [key_name_admin(usr)] [target.canmove ? "locked down" : "released"] [target.name]!")
+ log_game("[key_name(usr)] [target.canmove ? "locked down" : "released"] [target.name]!")
+ target.canmove = !target.canmove
+ if (target.lockcharge)
+ target.lockcharge = !target.lockcharge
+ target << "Your lockdown has been lifted!"
+ else
+ target.lockcharge = !target.lockcharge
+ target << "You have been locked down!"
+
+ // Remotely hacks the cyborg. Only antag AIs can do this and only to linked cyborgs.
+ else if (href_list["hack"])
+ var/mob/living/silicon/robot/target = get_cyborg_by_name(href_list["hack"])
+ if(!target || !istype(target))
+ return
+
+ // Antag AI checks
+ if(!istype(user, /mob/living/silicon/ai) || !(user.mind.special_role && user.mind.original == user))
+ user << "Access Denied"
+ return
+
+ if(target.emagged)
+ user << "Robot is already hacked."
+ return
+
+ var/choice = input("Really hack [target.name]? This cannot be undone.") in list("Yes", "No")
+ if(choice != "Yes")
+ return
+
+ if(!target || !istype(target))
+ return
+
+ message_admins("\blue [key_name_admin(usr)] emagged [target.name] using robotic console!")
+ log_game("[key_name(usr)] emagged [target.name] using robotic console!")
+ target.emagged = 1
+ target << "Failsafe protocols overriden. New tools available."
+
+ // Arms the emergency self-destruct system
+ else if(href_list["arm"])
+ if(istype(user, /mob/living/silicon))
+ user << "Access Denied"
+ return
+
+ safety = !safety
+ user << "You [safety ? "disarm" : "arm"] the emergency self destruct"
+
+ // Destroys all accessible cyborgs if safety is disabled
+ else if(href_list["nuke"])
+ if(istype(user, /mob/living/silicon))
+ user << "Access Denied"
+ return
+ if(safety)
+ user << "Self-destruct aborted - safety active"
+ return
+
+ message_admins("\blue [key_name_admin(usr)] detonated all cyborgs!")
+ log_game("[key_name(usr)] detonated all cyborgs!")
+
+ for(var/mob/living/silicon/robot/R in mob_list)
+ if(istype(R, /mob/living/silicon/robot/drone))
+ continue
+ // Ignore antagonistic cyborgs
+ if(R.scrambledcodes)
+ continue
+ R << "Self-destruct command received."
+ spawn(10)
+ R.self_destruct()
+
+
+// Proc: get_cyborgs()
+// Parameters: 1 (operator - mob which is operating the console.)
+// Description: Returns NanoUI-friendly list of accessible cyborgs.
+/obj/machinery/computer/robotics/proc/get_cyborgs(var/mob/operator)
+ var/list/robots = list()
for(var/mob/living/silicon/robot/R in mob_list)
- if(!R.scrambledcodes && !istype(R, /mob/living/silicon/robot/drone))
- R.self_destruct()
+ // Ignore drones
+ if(istype(R, /mob/living/silicon/robot/drone))
+ continue
+ // Ignore antagonistic cyborgs
+ if(R.scrambledcodes)
+ continue
- return
+ var/list/robot = list()
+ robot["name"] = R.name
+ if(R.stat)
+ robot["status"] = "Not Responding"
+ else if (!R.canmove)
+ robot["status"] = "Lockdown"
+ else
+ robot["status"] = "Operational"
+
+ if(R.cell)
+ robot["cell"] = 1
+ robot["cell_capacity"] = R.cell.maxcharge
+ robot["cell_current"] = R.cell.charge
+ robot["cell_percentage"] = round(R.cell.percent())
+ else
+ robot["cell"] = 0
+
+ robot["module"] = R.module ? R.module.name : "None"
+ robot["master_ai"] = R.connected_ai ? R.connected_ai.name : "None"
+ robot["hackable"] = 0
+ // Antag AIs know whether linked cyborgs are hacked or not.
+ if(operator && istype(operator, /mob/living/silicon/ai) && (R.connected_ai == operator) && (operator.mind.special_role && operator.mind.original == operator))
+ robot["hacked"] = R.emagged ? 1 : 0
+ robot["hackable"] = R.emagged? 0 : 1
+ robots.Add(list(robot))
+ return robots
+
+// Proc: get_cyborg_by_name()
+// Parameters: 1 (name - Cyborg we are trying to find)
+// Description: Helper proc for finding cyborg by name
+/obj/machinery/computer/robotics/proc/get_cyborg_by_name(var/name)
+ if (!name)
+ return
+ for(var/mob/living/silicon/robot/R in mob_list)
+ if(R.name == name)
+ return R
diff --git a/code/modules/supermatter/supermatter.dm b/code/modules/supermatter/supermatter.dm
index bf5d6957a6a..bbeb1603b7e 100644
--- a/code/modules/supermatter/supermatter.dm
+++ b/code/modules/supermatter/supermatter.dm
@@ -120,10 +120,15 @@
if(lum != light_range || clr != light_color)
set_light(lum, l_color = clr)
-/obj/machinery/power/supermatter/proc/announce_warning()
+/obj/machinery/power/supermatter/proc/get_integrity()
var/integrity = damage / explosion_point
integrity = round(100 - integrity * 100)
integrity = integrity < 0 ? 0 : integrity
+ return integrity
+
+
+/obj/machinery/power/supermatter/proc/announce_warning()
+ var/integrity = get_integrity()
var/alert_msg = " Integrity at [integrity]%"
if(damage > emergency_point)
@@ -267,11 +272,11 @@
if(Adjacent(user))
return attack_hand(user)
else
- user << "You attempt to interface with the control circuits but find they are not connected to your network. Maybe in a future firmware update."
+ ui_interact(user)
return
/obj/machinery/power/supermatter/attack_ai(mob/user as mob)
- user << "You attempt to interface with the control circuits but find they are not connected to your network. Maybe in a future firmware update."
+ ui_interact(user)
/obj/machinery/power/supermatter/attack_hand(mob/user as mob)
user.visible_message("\The [user] reaches out and touches \the [src], inducing a resonance... \his body starts to glow and bursts into flames before flashing into ash.",\
@@ -280,6 +285,31 @@
Consume(user)
+// This is purely informational UI that may be accessed by AIs or robots
+/obj/machinery/power/supermatter/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
+ var/data[0]
+
+ data["integrity_percentage"] = round(get_integrity())
+ var/datum/gas_mixture/env = null
+ if(!istype(src.loc, /turf/space))
+ env = src.loc.return_air()
+
+ if(!env)
+ data["ambient_temp"] = 0
+ data["ambient_pressure"] = 0
+ else
+ data["ambient_temp"] = round(env.temperature)
+ data["ambient_pressure"] = round(env.return_pressure())
+ data["detonating"] = grav_pulling
+
+ ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open)
+ if (!ui)
+ ui = new(user, src, ui_key, "supermatter_crystal.tmpl", "Supermatter Crystal", 500, 300)
+ ui.set_initial_data(data)
+ ui.open()
+ ui.set_auto_update(1)
+
+
/*
/obj/machinery/power/supermatter/proc/transfer_energy()
for(var/obj/machinery/power/rad_collector/R in rad_collectors)
diff --git a/html/changelogs/Atlantis-PR-8816.yml b/html/changelogs/Atlantis-PR-8816.yml
new file mode 100644
index 00000000000..f3d30f9abec
--- /dev/null
+++ b/html/changelogs/Atlantis-PR-8816.yml
@@ -0,0 +1,6 @@
+author: Atlantis
+delete-after: True
+
+changes:
+ - rscadd: "NanoUI for Robotics Control Console"
+ - rscdel: "NanoUI for Supermatter Crystal - AI/Robot only, purely informational"
diff --git a/nano/templates/robot_control.tmpl b/nano/templates/robot_control.tmpl
new file mode 100644
index 00000000000..57bbebd490b
--- /dev/null
+++ b/nano/templates/robot_control.tmpl
@@ -0,0 +1,83 @@
+{{if !data.is_ai}}
+
+
+ Emergency Self-Destruct:
+
+
+ {{if data.safety}}
+ {{:helper.link('ARM', 'unlocked', {'arm' : 1})}}
+ {{:helper.link('DETONATE', 'radiation', {'nuke' : 1}, 'disabled')}}
+ {{else}}
+ {{:helper.link('DISARM', 'locked', {'arm' : 1})}}
+ {{:helper.link('DETONATE', 'radiation', {'nuke' : 1}, null, 'redButton')}}
+ {{/if}}
+
+
+
+{{/if}}
+{{for data.robots}}
+
+
+
{{:value.name}}
+ Information
+
+ Status:
+
+
+ {{:value.status}}
+
+
+ Master AI:
+
+
+ {{:value.master_ai}}
+
+
+ Module:
+
+
+ {{:value.module}}
+
+
+ {{if value.hackable}}
+
+ Safeties:
+
+
+ ENABLED
+
+ {{else value.hacked}}
+
+ Safeties:
+
+
+ DISABLED
+
+ {{/if}}
+ Power Cell
+ {{if value.cell}}
+
+ Rating :
+
+
+ {{:value.cell_capacity}}
+
+ {{:helper.displayBar(value.cell_percentage, 0, 100, (value.cell_percentage >= 50) ? 'good' : (value.cell_percentage >= 25) ? 'average' : 'bad')}}
+ {{:value.cell_percentage}} %
+ {{else}}
+ Not Installed
+ {{:helper.displayBar(100, 0, 100, 'bad')}}
+ N/A %
+ {{/if}}
+ Actions
+ {{if value.status == "Operational"}}
+ {{:helper.link('Lockdown', 'locked', {'lockdown' : value.name})}}
+ {{else}}
+ {{:helper.link('Unlock', 'unlocked', {'lockdown' : value.name})}}
+ {{/if}}
+ {{:helper.link('Self-Destruct', 'radiation', {'detonate' : value.name}, null, 'redButton')}}
+ {{if value.hackable}}
+ {{:helper.link('Hack', 'calculator', {'hack' : value.name}, null, 'redButton')}}
+ {{/if}}
+
+{{/for}}
\ No newline at end of file
diff --git a/nano/templates/supermatter_crystal.tmpl b/nano/templates/supermatter_crystal.tmpl
new file mode 100644
index 00000000000..b06aaae7466
--- /dev/null
+++ b/nano/templates/supermatter_crystal.tmpl
@@ -0,0 +1,25 @@
+{{if data.detonating}}
+
+
CRYSTAL DELAMINATING
+
Evacuate area immediately
+
+
+{{else}}
+ Crystal Integrity
+ {{:helper.displayBar(data.integrity_percentage, 0, 100, (data.integrity_percentage >= 90) ? 'good' : (data.integrity_percentage >= 25) ? 'average' : 'bad')}}
+ {{:data.integrity_percentage}} %
+ Environment
+
+ Temperature:
+
+
+ {{:helper.displayBar(data.ambient_temp, 0, 10000, (data.ambient_temp >= 5000) ? 'bad' : (data.ambient_temp >= 4000) ? 'average' : 'good')}}
+ {{:data.ambient_temp}} K
+
+
+ Pressure:
+
+
+ {{:data.ambient_pressure}} kPa
+
+{{/if}}
\ No newline at end of file