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