From b7b1d3b3542b405c51ce4660e3257ab35fb9434c Mon Sep 17 00:00:00 2001 From: zxaber <37497534+zxaber@users.noreply.github.com> Date: Fri, 19 Mar 2021 15:53:45 -0700 Subject: [PATCH 1/2] Siliconwithoutthebullshit title --- .vscode/settings.json | 9 +- .../mob/living/silicon/robot/robot_defense.dm | 2 + .../computers/item/computer.dm | 17 ++ .../modular_computers/file_system/program.dm | 15 ++ .../file_system/programs/borg_monitor.dm | 186 ++++++++++++++++++ hippiestation.dme | 1 + .../interfaces/NtosCyborgRemoteMonitor.js | 183 +++++++++++++++++ 7 files changed, 407 insertions(+), 6 deletions(-) create mode 100644 code/modules/modular_computers/file_system/programs/borg_monitor.dm create mode 100644 tgui/packages/tgui/interfaces/NtosCyborgRemoteMonitor.js diff --git a/.vscode/settings.json b/.vscode/settings.json index c1c58bc3287..a5f5ccd084c 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -3,10 +3,7 @@ "./tgui-next" ], - "workbench.editorAssociations": [ - { - "filenamePattern": "*.dmi", - "viewType": "imagePreview.previewEditor" - } - ] + "workbench.editorAssociations": { + "*.dmi": "imagePreview.previewEditor" + } } diff --git a/code/modules/mob/living/silicon/robot/robot_defense.dm b/code/modules/mob/living/silicon/robot/robot_defense.dm index ee953195925..698bfa58f63 100644 --- a/code/modules/mob/living/silicon/robot/robot_defense.dm +++ b/code/modules/mob/living/silicon/robot/robot_defense.dm @@ -142,6 +142,8 @@ 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 020da02382d..4eabdad24c5 100644 --- a/code/modules/modular_computers/computers/item/computer.dm +++ b/code/modules/modular_computers/computers/item/computer.dm @@ -69,6 +69,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/proc/add_verb(var/path) switch(path) diff --git a/code/modules/modular_computers/file_system/program.dm b/code/modules/modular_computers/file_system/program.dm index 95c635b771f..963bf6672f8 100644 --- a/code/modules/modular_computers/file_system/program.dm +++ b/code/modules/modular_computers/file_system/program.dm @@ -52,6 +52,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 new file mode 100644 index 00000000000..71011035aa1 --- /dev/null +++ b/code/modules/modular_computers/file_system/programs/borg_monitor.dm @@ -0,0 +1,186 @@ +/datum/computer_file/program/borg_monitor + filename = "siliconnect" + filedesc = "SiliConnect" + category = PROGRAM_CATEGORY_ROBO + ui_header = "borg_mon.gif" + program_icon_state = "generic" + extended_desc = "This program allows for remote monitoring of station cyborgs." + requires_ntnet = TRUE + transfer_access = ACCESS_ROBOTICS + size = 5 + tgui_id = "NtosCyborgRemoteMonitor" + program_icon = "project-diagram" + 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) + return FALSE + 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() + + data["card"] = FALSE + if(checkID()) + data["card"] = TRUE + + data["cyborgs"] = list() + for(var/mob/living/silicon/robot/R in GLOB.silicon_mobs) + if(!evaluate_borg(R)) + continue + + var/list/upgrade + for(var/obj/item/borg/upgrade/I in R.upgrades) + upgrade += "\[[I.name]\] " + + var/shell = FALSE + if(R.shell && !R.ckey) + shell = TRUE + + 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, + charge = R.cell ? round(R.cell.percent()) : null, + module = R.model ? "[R.model.name] Model" : "No Model Detected", + upgrades = upgrade, + 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) + . = ..() + if(.) + return + + switch(action) + if("messagebot") + var/mob/living/silicon/robot/R = locate(params["ref"]) in GLOB.silicon_mobs + if(!istype(R)) + return + var/ID = checkID() + if(!ID) + return + if(R.stat == DEAD) //Dead borgs will listen to you no longer + to_chat(usr, "Error -- Could not open a connection to unit:[R]") + var/message = stripped_input(usr, message = "Enter message to be sent to remote cyborg.", title = "Send Message") + if(!message) + return + to_chat(R, "

Message from [ID] -- \"[message]\"
") + to_chat(usr, "Message sent to [R]: [message]") + R.logevent("Message from [ID] -- \"[message]\"") + SEND_SOUND(R, 'sound/machines/twobeep_high.ogg') + if(R.connected_ai) + to_chat(R.connected_ai, "

Message from [ID] to [R] -- \"[message]\"
") + SEND_SOUND(R.connected_ai, 'sound/machines/twobeep_high.ogg') + usr.log_talk(message, LOG_PDA, tag="Cyborg Monitor Program: ID name \"[ID]\" to [R]") + +///This proc is used to determin if a borg should be shown in the list (based on the borg's scrambledcodes var). Syndicate version overrides this to show only syndicate borgs. +/datum/computer_file/program/borg_monitor/proc/evaluate_borg(mob/living/silicon/robot/R) + if((get_turf(computer)).z != (get_turf(R)).z) + return FALSE + if(R.scrambledcodes) + return FALSE + return TRUE + +///Gets the ID's name, if one is inserted into the device. This is a seperate proc solely to be overridden by the syndicate version of the app. +/datum/computer_file/program/borg_monitor/proc/checkID() + var/obj/item/card/id/ID = computer.GetID() + if(!ID) + if(emagged) + return "STDERR:UNDF" + return FALSE + return ID.registered_name + +/datum/computer_file/program/borg_monitor/syndicate + filename = "roboverlord" + filedesc = "Roboverlord" + category = PROGRAM_CATEGORY_ROBO + ui_header = "borg_mon.gif" + program_icon_state = "generic" + extended_desc = "This program allows for remote monitoring of mission-assigned cyborgs." + requires_ntnet = FALSE + available_on_ntnet = FALSE + available_on_syndinet = TRUE + transfer_access = null + tgui_id = "NtosCyborgRemoteMonitorSyndicate" + +/datum/computer_file/program/borg_monitor/syndicate/run_emag() + return FALSE + +/datum/computer_file/program/borg_monitor/syndicate/evaluate_borg(mob/living/silicon/robot/R) + if((get_turf(computer)).z != (get_turf(R)).z) + return FALSE + if(!R.scrambledcodes) + return FALSE + return TRUE + +/datum/computer_file/program/borg_monitor/syndicate/checkID() + return "\[CLASSIFIED\]" //no ID is needed for the syndicate version's message function, and the borg will see "[CLASSIFIED]" as the message sender. diff --git a/hippiestation.dme b/hippiestation.dme index 3f434174fd1..5c5a991f6a1 100644 --- a/hippiestation.dme +++ b/hippiestation.dme @@ -2353,6 +2353,7 @@ #include "code\modules\modular_computers\hardware\network_card.dm" #include "code\modules\modular_computers\hardware\portable_disk.dm" #include "code\modules\modular_computers\hardware\printer.dm" +#include "code\modules\modular_computers\file_system\programs\borg_monitor.dm" #include "code\modules\modular_computers\hardware\recharger.dm" #include "code\modules\modular_computers\NTNet\NTNRC\conversation.dm" #include "code\modules\ninja\__ninjaDefines.dm" diff --git a/tgui/packages/tgui/interfaces/NtosCyborgRemoteMonitor.js b/tgui/packages/tgui/interfaces/NtosCyborgRemoteMonitor.js new file mode 100644 index 00000000000..d03133d83d4 --- /dev/null +++ b/tgui/packages/tgui/interfaces/NtosCyborgRemoteMonitor.js @@ -0,0 +1,183 @@ +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) => { + return ( + + + + + + ); +}; + +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 ( + + No cyborg units detected. + + ); + } + + return ( + + + + 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} + + +
+ ))} +
+
+ + )} + {tab_main === 2 && ( + <> + +
+ Scan a cyborg to download stored logs. + + {ProgressSwitch(DL_progress)} + +
+
+ +
+ {storedlog.map(log => ( + + {log} + + ))} +
+
+ + )} +
+ ); +}; From 1b195840fbf55a0ad611796cb3f8b26cf314094d Mon Sep 17 00:00:00 2001 From: Acensti <37810260+Acensti@users.noreply.github.com> Date: Thu, 23 Sep 2021 02:50:17 +0200 Subject: [PATCH 2/2] fix more fix --- code/__DEFINES/combat.dm | 3 +++ code/__DEFINES/machines.dm | 2 ++ code/modules/mob/living/silicon/robot/robot.dm | 12 ++++++++++++ .../modular_computers/file_system/program.dm | 17 +++++++++++++++++ 4 files changed, 34 insertions(+) diff --git a/code/__DEFINES/combat.dm b/code/__DEFINES/combat.dm index c2f2aa48080..24ca9b3a2ae 100644 --- a/code/__DEFINES/combat.dm +++ b/code/__DEFINES/combat.dm @@ -208,3 +208,6 @@ GLOBAL_LIST_INIT(shove_disarming_types, typecacheof(list( #define BULLET_ACT_BLOCK "BLOCK" //It's a blocked hit, whatever that means in the context of the thing it's hitting. #define BULLET_ACT_FORCE_PIERCE "PIERCE" //It pierces through the object regardless of the bullet being piercing by default. #define BULLET_ACT_TURF "TURF" //It hit us but it should hit something on the same turf too. Usually used for turfs. + +/// Cancels the attack chain entirely. +#define SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN 2 diff --git a/code/__DEFINES/machines.dm b/code/__DEFINES/machines.dm index 16682ad605e..c861f144edc 100644 --- a/code/__DEFINES/machines.dm +++ b/code/__DEFINES/machines.dm @@ -123,3 +123,5 @@ //cloning defines. These are flags. #define CLONING_SUCCESS (1<<0) #define CLONING_DELETE_RECORD (1<<1) +//Program categories +#define PROGRAM_CATEGORY_ROBO "Robotics" diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm index cc80f1cd66d..94c820732ec 100644 --- a/code/modules/mob/living/silicon/robot/robot.dm +++ b/code/modules/mob/living/silicon/robot/robot.dm @@ -1200,3 +1200,15 @@ cell.charge = min(cell.charge + amount, cell.maxcharge) if(repairs) heal_bodypart_damage(repairs, repairs - 1) +/mob/living/silicon/robot/proc/logevent(string = "") + if(!string) + return + if(stat == DEAD) //Dead borgs log no longer + return + if(!modularInterface) + stack_trace("Cyborg [src] ( [type] ) was somehow missing their integrated tablet. Please make a bug report.") + create_modularInterface() + modularInterface.borglog += "[station_time_timestamp()] - [string]" + var/datum/computer_file/program/robotact/program = modularInterface.get_robotact() + if(program) + program.force_full_update() diff --git a/code/modules/modular_computers/file_system/program.dm b/code/modules/modular_computers/file_system/program.dm index 963bf6672f8..3dccb2b013c 100644 --- a/code/modules/modular_computers/file_system/program.dm +++ b/code/modules/modular_computers/file_system/program.dm @@ -21,6 +21,9 @@ var/ui_x = 575 // Default size of TGUI window, in pixels var/ui_y = 700 var/ui_header = null // Example: "something.gif" - a header image that will be rendered in computer's UI when this program is running at background. Images are taken from /icons/program_icons. Be careful not to use too large images! + var/program_icon = "window-maximize-o" // Font Awesome icon to use as this program's icon in the modular computer main menu. Defaults to a basic program maximize window icon if not overridden. + + /datum/computer_file/program/New(obj/item/modular_computer/comp = null) ..() @@ -211,3 +214,17 @@ if(program_state != PROGRAM_STATE_ACTIVE) // Our program was closed. Close the ui if it exists. return UI_CLOSE return ..() + +/** + * + *Called by the device when it is emagged. + * + *Emagging the device allows certain programs to unlock new functions. However, the program will + *need to be downloaded first, and then handle the unlock on their own in their run_emag() proc. + *The device will allow an emag to be run multiple times, so the user can re-emag to run the + *override again, should they download something new. The run_emag() proc should return TRUE if + *the emagging affected anything, and FALSE if no change was made (already emagged, or has no + *emag functions). +**/ +/datum/computer_file/program/proc/run_emag() + return FALSE