diff --git a/code/modules/mob/living/silicon/robot/robot_defense.dm b/code/modules/mob/living/silicon/robot/robot_defense.dm index 8f2c07336d0..3e13c423cc4 100644 --- a/code/modules/mob/living/silicon/robot/robot_defense.dm +++ b/code/modules/mob/living/silicon/robot/robot_defense.dm @@ -369,6 +369,8 @@ GLOBAL_LIST_INIT(blacklisted_borg_hats, typecacheof(list( //Hats that don't real sleep(5) to_chat(src, "LAW SYNCHRONISATION ERROR") sleep(5) + if(user) + logevent("LOG: New user \[[replacetext(user.real_name," ","")]\], groups \[root\]") to_chat(src, "Would you like to send a report to NanoTraSoft? Y/N") sleep(10) to_chat(src, "> N") diff --git a/code/modules/modular_computers/computers/item/computer.dm b/code/modules/modular_computers/computers/item/computer.dm index d0d81e82914..edf69678825 100644 --- a/code/modules/modular_computers/computers/item/computer.dm +++ b/code/modules/modular_computers/computers/item/computer.dm @@ -78,6 +78,23 @@ physical = null return ..() +/obj/item/modular_computer/pre_attack_secondary(atom/A, mob/living/user, params) + if(active_program?.tap(A, user, params)) + user.do_attack_animation(A) //Emulate this animation since we kill the attack in three lines + playsound(loc, 'sound/weapons/tap.ogg', get_clamped_volume(), TRUE, -1) //Likewise for the tap sound + addtimer(CALLBACK(src, .proc/play_ping), 0.5 SECONDS, TIMER_UNIQUE) //Slightly delayed ping to indicate success + return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN + return ..() + + +/** + * Plays a ping sound. + * + * Timers runtime if you try to make them call playsound. Yep. + */ +/obj/item/modular_computer/proc/play_ping() + playsound(loc, 'sound/machines/ping.ogg', get_clamped_volume(), FALSE, -1) + /obj/item/modular_computer/AltClick(mob/user) ..() if(issilicon(user)) diff --git a/code/modules/modular_computers/file_system/program.dm b/code/modules/modular_computers/file_system/program.dm index 7462d180a02..dc485262f15 100644 --- a/code/modules/modular_computers/file_system/program.dm +++ b/code/modules/modular_computers/file_system/program.dm @@ -74,6 +74,21 @@ return computer.add_log(text) return 0 +/** + *Runs when the device is used to attack an atom in non-combat mode. + * + *Simulates using the device to read or scan something. Tap is called by the computer during pre_attack + *and sends us all of the related info. If we return TRUE, the computer will stop the attack process + *there. What we do with the info is up to us, but we should only return TRUE if we actually perform + *an action of some sort. + *Arguments: + *A is the atom being tapped + *user is the person making the attack action + *params is anything the pre_attack() proc had in the same-named variable. +*/ +/datum/computer_file/program/proc/tap(atom/A, mob/living/user, params) + return FALSE + /datum/computer_file/program/proc/is_supported_by_hardware(hardware_flag = 0, loud = 0, mob/user = null) if(!(hardware_flag & usage_flags)) if(loud && computer && user) diff --git a/code/modules/modular_computers/file_system/programs/borg_monitor.dm b/code/modules/modular_computers/file_system/programs/borg_monitor.dm index 4b350051030..71011035aa1 100644 --- a/code/modules/modular_computers/file_system/programs/borg_monitor.dm +++ b/code/modules/modular_computers/file_system/programs/borg_monitor.dm @@ -10,7 +10,21 @@ size = 5 tgui_id = "NtosCyborgRemoteMonitor" program_icon = "project-diagram" - var/emagged = FALSE + var/emagged = FALSE ///Bool of if this app has already been emagged + var/list/loglist = list() ///A list to copy a borg's IC log list into + var/mob/living/silicon/robot/DL_source ///reference of a borg if we're downloading a log, or null if not. + var/DL_progress = -1 ///Progress of current download, 0 to 100, -1 for no current download + +/datum/computer_file/program/borg_monitor/Destroy() + loglist = null + DL_source = null + return ..() + +/datum/computer_file/program/borg_monitor/kill_program(forced = FALSE) + loglist = null //Not everything is saved if you close an app + DL_source = null + DL_progress = 0 + return ..() /datum/computer_file/program/borg_monitor/run_emag() if(emagged) @@ -18,6 +32,49 @@ emagged = TRUE return TRUE +/datum/computer_file/program/borg_monitor/tap(atom/A, mob/living/user, params) + var/mob/living/silicon/robot/borgo = A + if(!istype(borgo) || !borgo.modularInterface) + return FALSE + DL_source = borgo + DL_progress = 0 + + var/username = "unknown user" + var/obj/item/card/id/stored_card = computer.GetID() + if(istype(stored_card) && stored_card.registered_name) + username = "user [stored_card.registered_name]" + to_chat(borgo, "Request received from [username] for the system log file. Upload in progress.")//Damning evidence may be contained, so warn the borg + borgo.logevent("File request by [username]: /var/logs/syslog") + return TRUE + +/datum/computer_file/program/borg_monitor/process_tick() + if(!DL_source) + DL_progress = -1 + return + + var/turf/here = get_turf(computer) + var/turf/there = get_turf(DL_source) + if(!here.Adjacent(there))//If someone walked away, cancel the download + to_chat(DL_source, "Log upload failed: general connection error.")//Let the borg know the upload stopped + DL_source = null + DL_progress = -1 + return + + if(DL_progress == 100) + if(!DL_source || !DL_source.modularInterface) //sanity check, in case the borg or their modular tablet poofs somehow + loglist = list("System log of unit [DL_source.name]") + loglist += "Error -- Download corrupted." + else + loglist = DL_source.modularInterface.borglog.Copy() + loglist.Insert(1,"System log of unit [DL_source.name]") + DL_progress = -1 + DL_source = null + for(var/datum/tgui/window in SStgui.open_uis_by_src[REF(src)]) + window.send_full_update() + return + + DL_progress += 25 + /datum/computer_file/program/borg_monitor/ui_data(mob/user) var/list/data = get_header_data() @@ -40,6 +97,7 @@ var/list/cyborg_data = list( name = R.name, + integ = round((R.health + 100) / 2), //mob heath is -100 to 100, we want to scale that to 0 - 100 locked_down = R.lockcharge, status = R.stat, shell_discon = shell, @@ -49,6 +107,12 @@ ref = REF(R) ) data["cyborgs"] += list(cyborg_data) + data["DL_progress"] = DL_progress + return data + +/datum/computer_file/program/borg_monitor/ui_static_data(mob/user) + var/list/data = list() + data["borglog"] = loglist return data /datum/computer_file/program/borg_monitor/ui_act(action, params) diff --git a/tgui/packages/tgui/interfaces/NtosCyborgRemoteMonitor.js b/tgui/packages/tgui/interfaces/NtosCyborgRemoteMonitor.js index 4aa273c6c6b..d03133d83d4 100644 --- a/tgui/packages/tgui/interfaces/NtosCyborgRemoteMonitor.js +++ b/tgui/packages/tgui/interfaces/NtosCyborgRemoteMonitor.js @@ -1,5 +1,5 @@ -import { useBackend } from '../backend'; -import { Box, Button, LabeledList, NoticeBox, Section } from '../components'; +import { useBackend, useSharedState } from '../backend'; +import { Box, Button, LabeledList, NoticeBox, ProgressBar, Section, Stack, Tabs } from '../components'; import { NtosWindow } from '../layouts'; export const NtosCyborgRemoteMonitor = (props, context) => { @@ -7,19 +7,39 @@ export const NtosCyborgRemoteMonitor = (props, context) => { - + ); }; +export const ProgressSwitch = param => { + switch (param) { + case -1: + return '_'; + case 0: + return 'Connecting'; + case 25: + return 'Starting Transfer'; + case 50: + return 'Downloading'; + case 75: + return 'Downloading'; + case 100: + return 'Formatting'; + } +}; + export const NtosCyborgRemoteMonitorContent = (props, context) => { const { act, data } = useBackend(context); + const [tab_main, setTab_main] = useSharedState(context, 'tab_main', 1); const { card, cyborgs = [], + DL_progress, } = data; + const storedlog = data.borglog || []; if (!cyborgs.length) { return ( @@ -30,64 +50,134 @@ export const NtosCyborgRemoteMonitorContent = (props, context) => { } return ( - <> - {!card && ( - - Certain features require an ID card login. - + + + + setTab_main(1)}> + Cyborgs + + setTab_main(2)}> + Stored Log File + + + + {tab_main === 1 && ( + <> + {!card && ( + + + Certain features require an ID card login. + + + )} + +
+ {cyborgs.map(cyborg => ( +
act('messagebot', { + ref: cyborg.ref, + })} /> + )}> + + + + {cyborg.status + ? "Not Responding" + : cyborg.locked_down + ? "Locked Down" + : cyborg.shell_discon + ? "Nominal/Disconnected" + : "Nominal"} + + + + + {cyborg.integ === 0 + ? "Hard Fault" + : cyborg.integ <= 25 + ? "Functionality Disrupted" + : cyborg.integ <= 75 + ? "Functionality Impaired" + : "Operational"} + + + + + {typeof cyborg.charge === 'number' + ? cyborg.charge + "%" + : "Not Found"} + + + + {cyborg.module} + + + {cyborg.upgrades} + + +
+ ))} +
+
+ )} - {cyborgs.map(cyborg => { - return ( -
act('messagebot', { - ref: cyborg.ref, - })} /> - )}> - - - - {cyborg.status - ? "Not Responding" - : cyborg.locked_down - ? "Locked Down" - : cyborg.shell_discon - ? "Nominal/Disconnected" - : "Nominal"} + {tab_main === 2 && ( + <> + +
+ Scan a cyborg to download stored logs. + + {ProgressSwitch(DL_progress)} + +
+
+ +
+ {storedlog.map(log => ( + + {log} - - - - {typeof cyborg.charge === 'number' - ? cyborg.charge + "%" - : "Not Found"} - - - - {cyborg.module} - - - {cyborg.upgrades} - - -
- ); - })} - + ))} +
+ + + )} +
); };