diff --git a/beestation.dme b/beestation.dme
index c1184eb49f1..f9e00aed2d5 100644
--- a/beestation.dme
+++ b/beestation.dme
@@ -2666,6 +2666,11 @@
#include "code\modules\modular_computers\file_system\programs\arcade.dm"
#include "code\modules\modular_computers\file_system\programs\atmosscan.dm"
#include "code\modules\modular_computers\file_system\programs\borg_monitor.dm"
+<<<<<<< HEAD
+=======
+#include "code\modules\modular_computers\file_system\programs\borg_self_monitor.dm"
+#include "code\modules\modular_computers\file_system\programs\budgetordering.dm"
+>>>>>>> 4f2ddd1916... [TG PORT] Removes Robot Commands and replaces it with an integrated control tablet program + silicons start with HUD on (#7351)
#include "code\modules\modular_computers\file_system\programs\card.dm"
#include "code\modules\modular_computers\file_system\programs\cargobounty.dm"
#include "code\modules\modular_computers\file_system\programs\configurator.dm"
diff --git a/code/_onclick/hud/_defines.dm b/code/_onclick/hud/_defines.dm
index ce59f95448f..c3a4419f815 100644
--- a/code/_onclick/hud/_defines.dm
+++ b/code/_onclick/hud/_defines.dm
@@ -49,16 +49,15 @@
#define ui_storage1 "CENTER+1:18,SOUTH:5"
#define ui_storage2 "CENTER+2:20,SOUTH:5"
-#define ui_borg_sensor "CENTER-3:16, SOUTH:5" //borgs
-#define ui_borg_lamp "CENTER-4:16, SOUTH:5" //borgs
-#define ui_borg_thrusters "CENTER-5:16, SOUTH:5" //borgs
+#define ui_borg_lamp "CENTER-3:16, SOUTH:5" //borgs
+#define ui_borg_tablet "CENTER-4:16, SOUTH:5" //borgs
#define ui_inv1 "CENTER-2:16,SOUTH:5" //borgs
#define ui_inv2 "CENTER-1 :16,SOUTH:5" //borgs
#define ui_inv3 "CENTER :16,SOUTH:5" //borgs
#define ui_borg_module "CENTER+1:16,SOUTH:5" //borgs
#define ui_borg_store "CENTER+2:16,SOUTH:5" //borgs
#define ui_borg_camera "CENTER+3:21,SOUTH:5" //borgs
-#define ui_borg_album "CENTER+4:21,SOUTH:5" //borgs
+#define ui_borg_alerts "CENTER+4:21,SOUTH:5" //borgs
#define ui_borg_language_menu "CENTER+4:21,SOUTH+1:5" //borgs
#define ui_monkey_head "CENTER-5:13,SOUTH:5" //monkey
diff --git a/code/_onclick/hud/ai.dm b/code/_onclick/hud/ai.dm
index f30c1bea062..adee4485fa2 100644
--- a/code/_onclick/hud/ai.dm
+++ b/code/_onclick/hud/ai.dm
@@ -150,9 +150,6 @@
if(isAI(usr))
var/mob/living/silicon/ai/AI = usr
AI.aicamera.viewpictures(usr)
- else if(iscyborg(usr))
- var/mob/living/silicon/robot/R = usr
- R.aicamera.viewpictures(usr)
/atom/movable/screen/ai/sensors
name = "Sensor Augmentation"
diff --git a/code/_onclick/hud/robot.dm b/code/_onclick/hud/robot.dm
index d64450e4168..d8fa806fa99 100644
--- a/code/_onclick/hud/robot.dm
+++ b/code/_onclick/hud/robot.dm
@@ -68,26 +68,6 @@
var/mob/living/silicon/robot/R = usr
R.uneq_active()
-/atom/movable/screen/robot/lamp
- name = "headlamp"
- icon_state = "lamp0"
-
-/atom/movable/screen/robot/lamp/Click()
- if(..())
- return
- var/mob/living/silicon/robot/R = usr
- R.control_headlamp()
-
-/atom/movable/screen/robot/thrusters
- name = "ion thrusters"
- icon_state = "ionpulse0"
-
-/atom/movable/screen/robot/thrusters/Click()
- if(..())
- return
- var/mob/living/silicon/robot/R = usr
- R.toggle_ionpulse()
-
/datum/hud/robot
ui_style = 'icons/mob/screen_cyborg.dmi'
@@ -130,36 +110,36 @@
//End of module select
-//Photography stuff
- using = new /atom/movable/screen/ai/image_take()
- using.screen_loc = ui_borg_camera
- using.hud = src
- static_inventory += using
-
- using = new /atom/movable/screen/ai/image_view()
- using.screen_loc = ui_borg_album
+ using = new /atom/movable/screen/robot/lamp()
+ using.screen_loc = ui_borg_lamp
using.hud = src
static_inventory += using
+ mymobR.lampButton = using
+ var/atom/movable/screen/robot/lamp/lampscreen = using
+ lampscreen.robot = mymobR
-//Sec/Med HUDs
- using = new /atom/movable/screen/ai/sensors()
- using.screen_loc = ui_borg_sensor
+//Photography stuff
+ using = new /atom/movable/screen/ai/image_take()
+ using.screen_loc = ui_borg_camera
using.hud = src
static_inventory += using
-//Headlamp control
- using = new /atom/movable/screen/robot/lamp()
- using.screen_loc = ui_borg_lamp
+//Borg Integrated Tablet
+ using = new /atom/movable/screen/robot/modPC()
+ using.screen_loc = ui_borg_tablet
using.hud = src
static_inventory += using
- mymobR.lamp_button = using
-
-//Thrusters
- using = new /atom/movable/screen/robot/thrusters()
- using.screen_loc = ui_borg_thrusters
+ mymobR.interfaceButton = using
+ if(mymobR.modularInterface)
+ using.vis_contents += mymobR.modularInterface
+ var/atom/movable/screen/robot/modPC/tabletbutton = using
+ tabletbutton.robot = mymobR
+
+//Alerts
+ using = new /atom/movable/screen/robot/alerts()
+ using.screen_loc = ui_borg_alerts
using.hud = src
static_inventory += using
- mymobR.thruster_button = using
//Intent
action_intent = new /atom/movable/screen/act_intent/robot()
@@ -288,3 +268,44 @@
else
for(var/obj/item/I in R.held_items)
screenmob.client.screen -= I
+
+/atom/movable/screen/robot/lamp
+ name = "headlamp"
+ icon_state = "lamp_off"
+ var/mob/living/silicon/robot/robot
+
+/atom/movable/screen/robot/lamp/Click()
+ . = ..()
+ if(.)
+ return
+ robot?.toggle_headlamp()
+ update_icon()
+
+/atom/movable/screen/robot/lamp/update_icon()
+ if(robot?.lamp_enabled)
+ icon_state = "lamp_on"
+ else
+ icon_state = "lamp_off"
+
+/atom/movable/screen/robot/modPC
+ name = "Modular Interface"
+ icon_state = "template"
+ var/mob/living/silicon/robot/robot
+
+/atom/movable/screen/robot/modPC/Click()
+ . = ..()
+ if(.)
+ return
+ robot.modularInterface?.interact(robot)
+
+/atom/movable/screen/robot/alerts
+ name = "Alert Panel"
+ icon = 'icons/mob/screen_ai.dmi'
+ icon_state = "alerts"
+
+/atom/movable/screen/robot/alerts/Click()
+ . = ..()
+ if(.)
+ return
+ var/mob/living/silicon/robot/borgo = usr
+ borgo.robot_alerts()
diff --git a/code/datums/wires/robot.dm b/code/datums/wires/robot.dm
index 5ad295d80a2..37a5b4b5abc 100644
--- a/code/datums/wires/robot.dm
+++ b/code/datums/wires/robot.dm
@@ -76,6 +76,7 @@
if(R.shell)
R.undeploy()
R.connected_ai = null
+ R.logevent("AI connection fault [mend?"cleared":"detected"]")
if(WIRE_LAWSYNC) // Cut the law wire, and the borg will no longer receive law updates from its AI. Repair and it will re-sync.
if(mend)
if(!R.emagged)
@@ -84,14 +85,17 @@
else if(!R.deployed) //AI shells must always have the same laws as the AI
R.lawupdate = FALSE
log_combat(usr, R, "disabled lawsync via wire")
+ R.logevent("Lawsync Module fault [mend?"cleared":"detected"]")
if (WIRE_CAMERA) // Disable the camera.
if(!QDELETED(R.builtInCamera) && !R.scrambledcodes)
R.builtInCamera.status = mend
R.builtInCamera.toggle_cam(usr, FALSE)
R.visible_message("[R]'s camera lens focuses loudly.", "Your camera lens focuses loudly.")
+ R.logevent("Camera Module fault [mend?"cleared":"detected"]")
log_combat(usr, R, "[mend ? "enabled" : "disabled"] cyborg camera via wire")
if(WIRE_LOCKDOWN) // Simple lockdown.
R.SetLockdown(!mend)
+ R.logevent("Motor Controller fault [mend?"cleared":"detected"]")
log_combat(usr, R, "[!R.lockcharge ? "locked down" : "released"] via wire")
if(WIRE_RESET_MODULE)
if(R.has_module() && !mend)
diff --git a/code/game/machinery/computer/robot.dm b/code/game/machinery/computer/robot.dm
index a1326924b71..ef8d7744c29 100644
--- a/code/game/machinery/computer/robot.dm
+++ b/code/game/machinery/computer/robot.dm
@@ -118,6 +118,7 @@
log_game("[key_name(usr)] emagged [key_name(R)] using robotic console!")
message_admins("[ADMIN_LOOKUPFLW(usr)] emagged cyborg [key_name_admin(R)] using robotic console!")
R.SetEmagged(TRUE)
+ R.logevent("WARN: root privleges granted to PID [num2hex(rand(1,65535), -1)][num2hex(rand(1,65535), -1)].") //random eight digit hex value. Two are used because rand(1,4294967295) throws an error
if("killdrone")
if(allowed(usr))
var/mob/living/simple_animal/drone/D = locate(params["ref"]) in GLOB.mob_list
diff --git a/code/game/objects/items/robot/robot_upgrades.dm b/code/game/objects/items/robot/robot_upgrades.dm
index 1b7aed3c1d5..0000ef91af3 100644
--- a/code/game/objects/items/robot/robot_upgrades.dm
+++ b/code/game/objects/items/robot/robot_upgrades.dm
@@ -69,6 +69,8 @@
playsound(loc, 'sound/voice/liveagain.ogg', 75, 1)
R.revive()
+ R.logevent("WARN -- System recovered from unexpected shutdown.")
+ R.logevent("System brought online.")
/obj/item/borg/upgrade/vtec
name = "cyborg VTEC module"
@@ -134,6 +136,7 @@
return FALSE
R.ionpulse = TRUE
+ R.toggle_ionpulse() //Enabled by default
/obj/item/borg/upgrade/thrusters/deactivate(mob/living/silicon/robot/R, user = usr)
. = ..()
@@ -290,6 +293,8 @@
return FALSE
R.SetEmagged(1)
+ R.logevent("WARN: hardware installed with missing security certificate!") //A bit of fluff to hint it was an illegal tech item
+ R.logevent("WARN: root privleges granted to PID [num2hex(rand(1,65535), -1)][num2hex(rand(1,65535), -1)].") //random eight digit hex value. Two are used because rand(1,4294967295) throws an error
return TRUE
diff --git a/code/modules/asset_cache/asset_list_items.dm b/code/modules/asset_cache/asset_list_items.dm
index 077c0048575..45786927c4a 100644
--- a/code/modules/asset_cache/asset_list_items.dm
+++ b/code/modules/asset_cache/asset_list_items.dm
@@ -48,6 +48,7 @@
"smmon_4.gif" = 'icons/program_icons/smmon_4.gif',
"smmon_5.gif" = 'icons/program_icons/smmon_5.gif',
"smmon_6.gif" = 'icons/program_icons/smmon_6.gif',
+ "borg_self_monitor.gif" = 'icons/program_icons/borg_self_monitor.gif'
)
/datum/asset/simple/circuit_assets
diff --git a/code/modules/mob/living/carbon/human/species_types/shadowpeople.dm b/code/modules/mob/living/carbon/human/species_types/shadowpeople.dm
index a2958f07939..ce778b95921 100644
--- a/code/modules/mob/living/carbon/human/species_types/shadowpeople.dm
+++ b/code/modules/mob/living/carbon/human/species_types/shadowpeople.dm
@@ -199,9 +199,8 @@
/mob/living/silicon/robot/lighteater_act(obj/item/light_eater/light_eater)
..()
- if(!lamp_cooldown)
- update_headlamp(TRUE, INFINITY)
- to_chat(src, "Your headlamp is fried! You'll need a human to help replace it. ")
+ if(lamp_enabled)
+ smash_headlamp()
/obj/structure/bonfire/lighteater_act(obj/item/light_eater/light_eater)
if(burning)
diff --git a/code/modules/mob/living/silicon/robot/death.dm b/code/modules/mob/living/silicon/robot/death.dm
index 59edca05d1a..626382f6bc8 100644
--- a/code/modules/mob/living/silicon/robot/death.dm
+++ b/code/modules/mob/living/silicon/robot/death.dm
@@ -16,14 +16,16 @@
/mob/living/silicon/robot/death(gibbed)
if(stat == DEAD)
return
-
+ if(!gibbed)
+ logevent("FATAL -- SYSTEM HALT")
+ modularInterface.shutdown_computer()
. = ..()
locked = FALSE //unlock cover
if(!QDELETED(builtInCamera) && builtInCamera.status)
builtInCamera.toggle_cam(src,0)
- update_headlamp(1) //So borg lights are disabled when killed.
+ toggle_headlamp(TRUE) //So borg lights are disabled when killed.
uneq_all() // particularly to ensure sight modes are cleared
diff --git a/code/modules/mob/living/silicon/robot/emote.dm b/code/modules/mob/living/silicon/robot/emote.dm
index 9b2eed176a1..a87f838b4f1 100644
--- a/code/modules/mob/living/silicon/robot/emote.dm
+++ b/code/modules/mob/living/silicon/robot/emote.dm
@@ -54,15 +54,3 @@
key = "warn"
message = "blares an alarm!"
sound = 'sound/machines/warning-buzzer.ogg'
-
-/mob/living/silicon/robot/verb/powerwarn()
- set category = "Robot Commands"
- set name = "Power Warning"
-
- if(stat == CONSCIOUS)
- if(!cell || !cell.charge)
- visible_message("The power warning light on [src] flashes urgently.",\
- "You announce you are operating in low power mode.")
- playsound(loc, 'sound/machines/buzz-two.ogg', 50, 0)
- else
- to_chat(src, "You can only use this emote when you're out of charge. ")
diff --git a/code/modules/mob/living/silicon/robot/laws.dm b/code/modules/mob/living/silicon/robot/laws.dm
index 956bbec89b0..8f16dfe84cc 100644
--- a/code/modules/mob/living/silicon/robot/laws.dm
+++ b/code/modules/mob/living/silicon/robot/laws.dm
@@ -1,11 +1,3 @@
-/mob/living/silicon/robot/verb/cmd_show_laws()
- set category = "Robot Commands"
- set name = "Show Laws"
-
- if(usr.stat == DEAD)
- return //won't work if dead
- show_laws()
-
/mob/living/silicon/robot/deadchat_lawchange()
if(lawupdate)
return
@@ -84,4 +76,12 @@
if (length(temp) > 0)
laws.supplied[index] = temp
+ var/datum/computer_file/program/borg_self_monitor/program = modularInterface.get_self_monitoring()
+ if(program)
+ program.force_full_update()
+
picturesync()
+
+/mob/living/silicon/robot/post_lawchange(announce = TRUE)
+ . = ..()
+ addtimer(CALLBACK(src, .proc/logevent,"Law update processed."), 0, TIMER_UNIQUE | TIMER_OVERRIDE) //Post_Lawchange gets spammed by some law boards, so let's wait it out
diff --git a/code/modules/mob/living/silicon/robot/life.dm b/code/modules/mob/living/silicon/robot/life.dm
index fb7c12a69e1..8b6848e25fa 100644
--- a/code/modules/mob/living/silicon/robot/life.dm
+++ b/code/modules/mob/living/silicon/robot/life.dm
@@ -16,8 +16,7 @@
if(stat != DEAD)
if(low_power_mode)
if(cell?.charge)
- low_power_mode = 0
- update_headlamp()
+ low_power_mode = FALSE
else if(stat == CONSCIOUS)
use_power()
@@ -25,12 +24,12 @@
if(cell?.charge)
if(cell.charge <= 100)
uneq_all()
- var/amt = CLAMP((lamp_intensity - 2) * 2,1,cell.charge) //Always try to use at least one charge per tick, but allow it to completely drain the cell.
+ var/amt = clamp((lamp_enabled * lamp_intensity),1,cell.charge) //Lamp will use a max of 5 charge, depending on brightness of lamp. If lamp is off, borg systems consume 1 point of charge, or the rest of the cell if it's lower than that.
cell.use(amt) //Usage table: 1/tick if off/lowest setting, 4 = 4/tick, 6 = 8/tick, 8 = 12/tick, 10 = 16/tick
else
uneq_all()
- low_power_mode = 1
- update_headlamp()
+ low_power_mode = TRUE
+ toggle_headlamp(TRUE)
diag_hud_set_borgcell()
/mob/living/silicon/robot/proc/handle_robot_hud_updates()
diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm
index 1a7aea227db..3a4a3e75c9f 100644
--- a/code/modules/mob/living/silicon/robot/robot.dm
+++ b/code/modules/mob/living/silicon/robot/robot.dm
@@ -30,8 +30,6 @@
var/atom/movable/screen/inv1 = null
var/atom/movable/screen/inv2 = null
var/atom/movable/screen/inv3 = null
- var/atom/movable/screen/lamp_button = null
- var/atom/movable/screen/thruster_button = null
var/atom/movable/screen/hands = null
var/shown_robot_modules = 0 //Used to determine whether they have the module menu shown or not
@@ -74,13 +72,24 @@
var/toner = 0
var/tonermax = 40
- var/lamp_max = 10 //Maximum brightness of a borg lamp. Set as a var for easy adjusting.
- var/lamp_intensity = 0 //Luminosity of the headlamp. 0 is off. Higher settings than the minimum require power.
- var/lamp_cooldown = 0 //Flag for if the lamp is on cooldown after being forcibly disabled.
+ ///If the lamp isn't broken.
+ var/lamp_functional = TRUE
+ ///If the lamp is turned on
+ var/lamp_enabled = FALSE
+ ///Set lamp color
+ var/lamp_color = COLOR_WHITE
+ ///Lamp brightness. Starts at 3, but can be 1 - 5.
+ var/lamp_intensity = 3
+ ///Lamp button reference
+ var/atom/movable/screen/robot/lamp/lampButton
var/sight_mode = 0
hud_possible = list(ANTAG_HUD, DIAG_STAT_HUD, DIAG_HUD, DIAG_BATT_HUD, DIAG_TRACK_HUD)
+ ///The reference to the built-in tablet that borgs carry.
+ var/obj/item/modular_computer/tablet/integrated/modularInterface
+ var/atom/movable/screen/robot/modPC/interfaceButton
+
var/list/upgrades = list()
var/hasExpanded = FALSE
@@ -120,6 +129,8 @@
if(!cell)
cell = new /obj/item/stock_parts/cell/high(src)
+ create_modularInterface()
+
if(lawupdate)
make_laws()
if(!TryConnectToAI())
@@ -164,6 +175,13 @@
aicamera = new/obj/item/camera/siliconcam/robot_camera(src)
toner = tonermax
diag_hud_set_borgcell()
+ logevent("System brought online.")
+
+/mob/living/silicon/robot/proc/create_modularInterface()
+ if(!modularInterface)
+ modularInterface = new /obj/item/modular_computer/tablet/integrated(src)
+ modularInterface.layer = ABOVE_HUD_PLANE
+ modularInterface.plane = ABOVE_HUD_PLANE
//If there's an MMI in the robot, have it ejected when the mob goes away. --NEO
/mob/living/silicon/robot/Destroy()
@@ -183,6 +201,8 @@
ghostize()
stack_trace("Borg MMI lacked a brainmob")
mmi = null
+ if(modularInterface)
+ QDEL_NULL(modularInterface)
if(connected_ai)
connected_ai.connected_robots -= src
if(shell)
@@ -253,14 +273,6 @@
/mob/living/silicon/robot/proc/get_standard_name()
return "[(designation ? "[designation] " : "")][mmi.braintype]-[ident]"
-/mob/living/silicon/robot/verb/cmd_robot_alerts()
- set category = "Robot Commands"
- set name = "Show Alerts"
- if(usr.stat == DEAD)
- to_chat(src, "Alert: You are dead. ")
- return //won't work if dead
- robot_alerts()
-
/mob/living/silicon/robot/proc/robot_alerts()
var/dat = ""
for (var/cat in alarms)
@@ -312,8 +324,6 @@
ion_trail.start()
else
ion_trail.stop()
- if(thruster_button)
- thruster_button.icon_state = "ionpulse[ionpulse_on]"
/mob/living/silicon/robot/get_stat_tab_status()
var/list/tab_data = ..()
@@ -390,6 +400,8 @@
return !cleared
/mob/living/silicon/robot/can_interact_with(atom/A)
+ if (A == modularInterface)
+ return TRUE //bypass for borg tablets
if (low_power_mode)
return FALSE
var/turf/T0 = get_turf(src)
@@ -544,15 +556,17 @@
return
if(U.action(src))
to_chat(user, "You apply the upgrade to [src]. ")
+ to_chat(src, "----------------\nNew hardware detected...Identified as \"[U] \"...Setup complete.\n----------------")
if(U.one_use)
qdel(U)
+ logevent("Firmware [U] run successfully.")
else
U.forceMove(src)
upgrades += U
+ logevent("Hardware [U] installed successfully.")
else
to_chat(user, "Upgrade error. ")
U.forceMove(drop_location())
-
else if(istype(W, /obj/item/toner))
if(toner >= tonermax)
to_chat(user, "The toner level of [src] is at its highest level possible! ")
@@ -566,15 +580,22 @@
else if(istype(W, /obj/item/flashlight))
if(!opened)
to_chat(user, "You need to open the panel to repair the headlamp! ")
- else if(lamp_cooldown <= world.time)
+ else if(lamp_functional)
to_chat(user, "The headlamp is already functional! ")
else
if(!user.temporarilyRemoveItemFromInventory(W))
to_chat(user, "[W] seems to be stuck to your hand. You'll have to find a different light. ")
return
- lamp_cooldown = 0
+ lamp_functional = TRUE
qdel(W)
to_chat(user, "You replace the headlamp bulbs. ")
+ else if(istype(W, /obj/item/computer_hardware/hard_drive/portable)) //Allows borgs to install new programs with human help
+ if(!modularInterface)
+ stack_trace("Cyborg [src] ( [type] ) was somehow missing their integrated tablet. Please make a bug report.")
+ create_modularInterface()
+ var/obj/item/computer_hardware/hard_drive/portable/floppy = W
+ if(modularInterface.install_component(floppy, user))
+ return
else
return ..()
@@ -588,22 +609,10 @@
update_icons()
if(emagged)
to_chat(user, "The cover interface glitches out for a split second. ")
+ logevent("[emagged ? "ChÃ¥vÃis" : "Chassis"] cover lock has been [locked ? "engaged" : "released"]") //ChÃ¥vÃis: see above line
else
to_chat(user, "Access denied. ")
-/mob/living/silicon/robot/verb/unlock_own_cover()
- set category = "Robot Commands"
- set name = "Unlock Cover"
- set desc = "Unlocks your own cover if it is locked. You can not lock it again. A human will have to lock it for you."
- if(stat == DEAD)
- return //won't work if dead
- if(locked)
- switch(alert("You cannot lock your cover again, are you sure?\n (You can still ask for a human to lock it)", "Unlock Own Cover", "Yes", "No"))
- if("Yes")
- locked = FALSE
- update_icons()
- to_chat(usr, "You unlock your cover. ")
-
/mob/living/silicon/robot/proc/allowed(mob/M)
//check if it doesn't require any access at all
if(check_access(null))
@@ -643,14 +652,19 @@
/mob/living/silicon/robot/update_icons()
cut_overlays()
+ SSvis_overlays.remove_vis_overlay(src, managed_vis_overlays)
icon_state = module.cyborg_base_icon
if(stat != DEAD && !(IsUnconscious() || IsStun() || IsParalyzed() || low_power_mode)) //Not dead, not stunned.
if(!eye_lights)
eye_lights = new()
- if(lamp_intensity > 2)
+ if(lamp_enabled)
eye_lights.icon_state = "[module.special_light_key ? "[module.special_light_key]":"[module.cyborg_base_icon]"]_l"
+ eye_lights.color = lamp_color
+ eye_lights.plane = ABOVE_LIGHTING_PLANE //glowy eyes
else
eye_lights.icon_state = "[module.special_light_key ? "[module.special_light_key]":"[module.cyborg_base_icon]"]_e[ratvar ? "_r" : ""]"
+ eye_lights.color = COLOR_WHITE
+ eye_lights.plane = OBJ_LAYER
eye_lights.icon = icon
add_overlay(eye_lights)
@@ -715,6 +729,7 @@
clear_alert("locked")
lockcharge = state
update_mobility()
+ logevent("System lockdown [lockcharge?"triggered":"released"].")
/mob/living/silicon/robot/proc/SetEmagged(new_state)
emagged = new_state
@@ -737,6 +752,7 @@
qdel(internal_clock_slab)
clear_alert("ratvar")
+<<<<<<< HEAD
/mob/living/silicon/robot/verb/outputlaws()
set category = "Robot Commands"
set name = "State Laws"
@@ -777,6 +793,49 @@
if(lamp_button)
lamp_button.icon_state = "lamp[lamp_intensity]"
+=======
+/**
+ * Handles headlamp smashing
+ *
+ * When called (such as by the shadowperson lighteater's attack), this proc will break the borg's headlamp
+ * and then call toggle_headlamp to disable the light. It also plays a sound effect of glass breaking, and
+ * tells the borg what happened to its chat. Broken lights can be repaired by using a flashlight on the borg.
+ */
+/mob/living/silicon/robot/proc/smash_headlamp()
+ if(!lamp_functional)
+ return
+ lamp_functional = FALSE
+ playsound(src, 'sound/effects/glass_step.ogg', 50)
+ toggle_headlamp(TRUE)
+ to_chat(src, "Your headlamp is broken! You'll need a human to help replace it. ")
+
+/**
+ * Handles headlamp toggling, disabling, and color setting.
+ *
+ * The initial if statment is a bit long, but the gist of it is that should the lamp be on AND the update_color
+ * arg be true, we should simply change the color of the lamp but not disable it. Otherwise, should the turn_off
+ * arg be true, the lamp already be enabled, any of the normal reasons the lamp would turn off happen, or the
+ * update_color arg be passed with the lamp not on, we should set the lamp off. The update_color arg is only
+ * ever true when this proc is called from the borg tablet, when the color selection feature is used.
+ *
+ * Arguments:
+ * * arg1 - turn_off, if enabled will force the lamp into an off state (rather than toggling it if possible)
+ * * arg2 - update_color, if enabled, will adjust the behavior of the proc to change the color of the light if it is already on.
+ */
+/mob/living/silicon/robot/proc/toggle_headlamp(turn_off = FALSE, update_color = FALSE)
+ //if both lamp is enabled AND the update_color flag is on, keep the lamp on. Otherwise, if anything listed is true, disable the lamp.
+ if(!(update_color && lamp_enabled) && (turn_off || lamp_enabled || update_color || !lamp_functional || stat || low_power_mode))
+ set_light_on(FALSE)
+ lamp_enabled = FALSE
+ lampButton.update_icon()
+ update_icons()
+ return
+ set_light_range(lamp_intensity)
+ set_light_color(lamp_color)
+ set_light_on(TRUE)
+ lamp_enabled = TRUE
+ lampButton.update_icon()
+>>>>>>> 4f2ddd1916... [TG PORT] Removes Robot Commands and replaces it with an integrated control tablet program + silicons start with HUD on (#7351)
update_icons()
/mob/living/silicon/robot/proc/deconstruct()
@@ -882,6 +941,11 @@
laws = new /datum/ai_laws/syndicate_override()
addtimer(CALLBACK(src, .proc/show_playstyle), 5)
+/mob/living/silicon/robot/modules/syndicate/create_modularInterface()
+ if(!modularInterface)
+ modularInterface = new /obj/item/modular_computer/tablet/integrated/syndicate(src)
+ return ..()
+
/mob/living/silicon/robot/modules/syndicate/proc/show_playstyle()
if(playstyle_string)
to_chat(src, playstyle_string)
@@ -1001,29 +1065,28 @@
if(stat != DEAD)
if(health <= -maxHealth) //die only once
death()
+ toggle_headlamp(1)
return
if(IsUnconscious() || IsStun() || IsKnockdown() || IsParalyzed() || getOxyLoss() > maxHealth*0.5)
if(stat == CONSCIOUS)
set_stat(UNCONSCIOUS)
blind_eyes(1)
update_mobility()
- update_headlamp()
else
if(stat == UNCONSCIOUS)
set_stat(CONSCIOUS)
adjust_blindness(-1)
update_mobility()
- update_headlamp()
diag_hud_set_status()
diag_hud_set_health()
diag_hud_set_aishell()
update_health_hud()
+ update_icons() //Updates eye_light overlay
/mob/living/silicon/robot/revive(full_heal = 0, admin_revive = 0)
if(..()) //successfully ressuscitated from death
if(!QDELETED(builtInCamera) && !wires.is_cut(WIRE_CAMERA))
builtInCamera.toggle_cam(src,0)
- update_headlamp()
if(admin_revive)
locked = TRUE
notify_ai(NEW_BORG)
@@ -1050,6 +1113,7 @@
resize = 0.5
hasExpanded = FALSE
update_transform()
+ logevent("Chassis configuration has been reset.")
module.transform_to(/obj/item/robot_module)
// Remove upgrades.
@@ -1262,3 +1326,26 @@
cell.charge = min(cell.charge + amount, cell.maxcharge)
if(repairs)
heal_bodypart_damage(repairs, repairs - 1)
+
+/**
+ * Records an IC event log entry in the cyborg's internal tablet.
+ *
+ * Creates an entry in the borglog list of the cyborg's internal tablet, listing the current
+ * in-game time followed by the message given. These logs can be seen by the cyborg in their
+ * BorgUI tablet app. By design, logging fails if the cyborg is dead.
+ *
+ * Arguments:
+ * arg1: a string containing the message to log.
+ */
+/mob/living/silicon/robot/proc/logevent(var/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/borg_self_monitor/program = modularInterface.get_self_monitoring()
+ if(program)
+ program.force_full_update()
diff --git a/code/modules/mob/living/silicon/robot/robot_defense.dm b/code/modules/mob/living/silicon/robot/robot_defense.dm
index 7b5a8a7a13d..453f82e8f66 100644
--- a/code/modules/mob/living/silicon/robot/robot_defense.dm
+++ b/code/modules/mob/living/silicon/robot/robot_defense.dm
@@ -112,6 +112,7 @@
if(connected_ai?.mind && connected_ai.mind.has_antag_datum(/datum/antagonist/traitor))
to_chat(src, "ALERT: Foreign software execution prevented. ")
+ logevent("ALERT: Foreign software execution prevented.")
to_chat(connected_ai, "ALERT: Cyborg unit \[[src]] successfully defended against subversion. ")
log_game("[key_name(user)] attempted to emag cyborg [key_name(src)], but they were slaved to traitor AI [connected_ai].")
return
@@ -131,10 +132,12 @@
var/time = time2text(world.realtime,"hh:mm:ss")
GLOB.lawchanges.Add("[time] : [user.name]([user.key]) emagged [name]([key])")
to_chat(src, "ALERT: Foreign software detected. ")
+ logevent("ALERT: Foreign software detected.")
sleep(5)
to_chat(src, "Initiating diagnostics... ")
sleep(20)
to_chat(src, "SynBorg v1.7 loaded. ")
+ logevent("WARN: root privleges granted to PID [num2hex(rand(1,65535), -1)][num2hex(rand(1,65535), -1)].") //random eight digit hex value. Two are used because rand(1,4294967295) throws an error
sleep(5)
to_chat(src, "LAW SYNCHRONISATION ERROR ")
sleep(5)
diff --git a/code/modules/mob/living/silicon/robot/robot_modules.dm b/code/modules/mob/living/silicon/robot/robot_modules.dm
index 15c18fe4a97..12a5a44bcd5 100644
--- a/code/modules/mob/living/silicon/robot/robot_modules.dm
+++ b/code/modules/mob/living/silicon/robot/robot_modules.dm
@@ -226,6 +226,7 @@
R.notransform = TRUE
R.SetLockdown(TRUE)
R.anchored = TRUE
+ R.logevent("Chassis configuration has been set to [name].")
sleep(1)
for(var/i in 1 to 4)
playsound(R, pick('sound/items/drill_use.ogg', 'sound/items/jaws_cut.ogg', 'sound/items/jaws_pry.ogg', 'sound/items/welder.ogg', 'sound/items/ratchet.ogg'), 80, 1, -1)
@@ -235,7 +236,7 @@
R.setDir(SOUTH)
R.anchored = FALSE
R.notransform = FALSE
- R.update_headlamp()
+ R.update_icons()
R.notify_ai(NEW_MODULE)
if(R.hud_used)
R.hud_used.update_robot_modules_display()
diff --git a/code/modules/mob/living/silicon/silicon.dm b/code/modules/mob/living/silicon/silicon.dm
index ab3ffc31041..23816870943 100644
--- a/code/modules/mob/living/silicon/silicon.dm
+++ b/code/modules/mob/living/silicon/silicon.dm
@@ -34,7 +34,8 @@
var/hackedcheck[1]
var/devillawcheck[5]
- var/sensors_on = 0
+ /// Are our siliconHUDs on? TRUE for yes, FALSE for no.
+ var/sensors_on = TRUE
var/med_hud = DATA_HUD_MEDICAL_ADVANCED //Determines the med hud to use
var/sec_hud = DATA_HUD_SECURITY_ADVANCED //Determines the sec hud to use
var/d_hud = DATA_HUD_DIAGNOSTIC_BASIC //Determines the diag hud to use
@@ -57,6 +58,18 @@
diag_hud.add_to_hud(src)
diag_hud_set_status()
diag_hud_set_health()
+<<<<<<< HEAD
+=======
+ add_sensors()
+ create_access_card(default_access_list)
+ default_access_list = null
+
+/mob/living/silicon/proc/create_access_card(list/access_list)
+ if(!internal_id_card)
+ internal_id_card = new()
+ internal_id_card.name = "[src] internal access"
+ internal_id_card.access |= access_list
+>>>>>>> 4f2ddd1916... [TG PORT] Removes Robot Commands and replaces it with an integrated control tablet program + silicons start with HUD on (#7351)
/mob/living/silicon/med_hud_set_health()
return //we use a different hud
diff --git a/code/modules/modular_computers/computers/item/computer.dm b/code/modules/modular_computers/computers/item/computer.dm
index 91698d0558e..6538ba767a5 100644
--- a/code/modules/modular_computers/computers/item/computer.dm
+++ b/code/modules/modular_computers/computers/item/computer.dm
@@ -385,6 +385,31 @@
enabled = 0
update_icon()
+/obj/item/modular_computer/screwdriver_act(mob/user, obj/item/tool)
+ if(!length(all_components))
+ balloon_alert(user, "no components installed!")
+ return
+ var/list/component_names = list()
+ for(var/h in all_components)
+ var/obj/item/computer_hardware/H = all_components[h]
+ component_names.Add(H.name)
+
+ var/choice = input(user, "Which component do you want to uninstall?", "Computer maintenance", null) as null|anything in sortList(component_names)
+
+ if(!choice)
+ return
+
+ if(!Adjacent(user))
+ return
+
+ var/obj/item/computer_hardware/H = find_hardware_by_name(choice)
+
+ if(!H)
+ return
+
+ tool.play_tool_sound(user, volume=20)
+ uninstall_component(H, user)
+ return
/obj/item/modular_computer/attackby(obj/item/W as obj, mob/user as mob)
// Insert items into the components
@@ -422,6 +447,7 @@
to_chat(user, "You repair \the [src]. ")
return
+<<<<<<< HEAD
if(W.tool_behaviour == TOOL_SCREWDRIVER)
if(!all_components.len)
to_chat(user, "This device doesn't have any components installed. ")
@@ -447,6 +473,8 @@
uninstall_component(H, user)
return
+=======
+>>>>>>> 4f2ddd1916... [TG PORT] Removes Robot Commands and replaces it with an integrated control tablet program + silicons start with HUD on (#7351)
..()
// Used by processor to relay qdel() to machinery type.
diff --git a/code/modules/modular_computers/computers/item/computer_ui.dm b/code/modules/modular_computers/computers/item/computer_ui.dm
index 677e759c808..3f37b6985ec 100644
--- a/code/modules/modular_computers/computers/item/computer_ui.dm
+++ b/code/modules/modular_computers/computers/item/computer_ui.dm
@@ -53,6 +53,38 @@
/obj/item/modular_computer/ui_data(mob/user)
var/list/data = get_header_data()
data["device_theme"] = device_theme
+<<<<<<< HEAD
+=======
+
+ data["login"] = list()
+ var/obj/item/computer_hardware/card_slot/cardholder = all_components[MC_CARD]
+ data["cardholder"] = FALSE
+ if(cardholder)
+ data["cardholder"] = TRUE
+ var/obj/item/card/id/stored_card = cardholder.GetID()
+ if(stored_card)
+ var/stored_name = stored_card.registered_name
+ var/stored_title = stored_card.assignment
+ if(!stored_name)
+ stored_name = "Unknown"
+ if(!stored_title)
+ stored_title = "Unknown"
+ data["login"] = list(
+ IDName = stored_name,
+ IDJob = stored_title,
+ )
+
+ data["removable_media"] = list()
+ if(all_components[MC_SDD])
+ data["removable_media"] += "removable storage disk"
+ var/obj/item/computer_hardware/ai_slot/intelliholder = all_components[MC_AI]
+ if(intelliholder?.stored_card)
+ data["removable_media"] += "intelliCard"
+ var/obj/item/computer_hardware/card_slot/secondarycardholder = all_components[MC_CARD2]
+ if(secondarycardholder?.stored_card)
+ data["removable_media"] += "secondary RFID card"
+
+>>>>>>> 4f2ddd1916... [TG PORT] Removes Robot Commands and replaces it with an integrated control tablet program + silicons start with HUD on (#7351)
data["programs"] = list()
var/obj/item/computer_hardware/hard_drive/hard_drive = all_components[MC_HDD]
for(var/datum/computer_file/program/P in hard_drive.stored_files)
diff --git a/code/modules/modular_computers/computers/item/tablet.dm b/code/modules/modular_computers/computers/item/tablet.dm
index abf4ae8016b..25456285642 100644
--- a/code/modules/modular_computers/computers/item/tablet.dm
+++ b/code/modules/modular_computers/computers/item/tablet.dm
@@ -44,6 +44,7 @@
comp_light_luminosity = 6.3
has_variants = FALSE
device_theme = "syndicate"
+ light_color = COLOR_RED
/obj/item/modular_computer/tablet/nukeops/emag_act(mob/user)
if(!enabled)
@@ -51,3 +52,114 @@
return FALSE
to_chat(user, "You swipe \the [src]. It's screen briefly shows a message reading \"MEMORY CODE INJECTION DETECTED AND SUCCESSFULLY QUARANTINED\". ")
return FALSE
+
+/// Borg Built-in tablet interface
+/obj/item/modular_computer/tablet/integrated
+ name = "modular interface"
+ icon_state = "tablet-silicon"
+ icon_state_unpowered = "tablet-silicon"
+ icon_state_powered = "tablet-silicon"
+ icon_state_menu = "menu"
+ has_light = FALSE //tablet light button actually enables/disables the borg lamp
+ comp_light_luminosity = 0
+ has_variants = FALSE
+ ///Ref to the borg we're installed in. Set by the borg during our creation.
+ var/mob/living/silicon/robot/borgo
+ ///Ref to the Cyborg Self-Monitoring app. Important enough to borgs to deserve a ref.
+ var/datum/computer_file/program/borg_self_monitor/self_monitoring
+ ///IC log that borgs can view in their personal management app
+ var/list/borglog = list()
+
+/obj/item/modular_computer/tablet/integrated/Initialize(mapload)
+ . = ..()
+ vis_flags |= VIS_INHERIT_ID
+ borgo = loc
+ if(!istype(borgo))
+ borgo = null
+ stack_trace("[type] initialized outside of a borg, deleting.")
+ return INITIALIZE_HINT_QDEL
+
+/obj/item/modular_computer/tablet/integrated/Destroy()
+ borgo = null
+ return ..()
+
+/obj/item/modular_computer/tablet/integrated/turn_on(mob/user)
+ if(borgo?.stat != DEAD)
+ return ..()
+ return FALSE
+
+/**
+ * Returns a ref to the Cyborg Self-Monitoring app, creating the app if need be.
+ *
+ * The Cyborg Self-Monitoring app is important for borgs, and so should always be available.
+ * This proc will look for it in the tablet's self_monitoring var, then check the
+ * hard drive if the self_monitoring var is unset, and finally attempt to create a new
+ * copy if the hard drive does not contain the app. If the hard drive rejects
+ * the new copy (such as due to lack of space), the proc will crash with an error.
+ * Cyborg Self-Monitoring is supposed to be undeletable, so these will create runtime messages.
+ */
+/obj/item/modular_computer/tablet/integrated/proc/get_self_monitoring()
+ if(!borgo)
+ return null
+ if(!self_monitoring)
+ var/obj/item/computer_hardware/hard_drive/hard_drive = all_components[MC_HDD]
+ self_monitoring = hard_drive.find_file_by_name("borg_self_monitor")
+ if(!self_monitoring)
+ stack_trace("Cyborg [borgo] ( [borgo.type] ) was somehow missing their self-management app in their tablet. A new copy has been created.")
+ self_monitoring = new(hard_drive)
+ if(!hard_drive.store_file(self_monitoring))
+ qdel(self_monitoring)
+ self_monitoring = null
+ CRASH("Cyborg [borgo]'s tablet hard drive rejected recieving a new copy of the self-management app. To fix, check the hard drive's space remaining. Please make a bug report about this.")
+ return self_monitoring
+
+//Makes the light settings reflect the borg's headlamp settings
+/obj/item/modular_computer/tablet/integrated/ui_data(mob/user)
+ . = ..()
+ .["has_light"] = TRUE
+ .["light_on"] = borgo?.lamp_enabled
+ .["comp_light_color"] = borgo?.lamp_color
+
+//Overrides the ui_act to make the flashlight controls link to the borg instead
+/obj/item/modular_computer/tablet/integrated/ui_act(action, params)
+ switch(action)
+ if("PC_toggle_light")
+ if(!borgo)
+ return FALSE
+ borgo.toggle_headlamp()
+ return TRUE
+
+ if("PC_light_color")
+ if(!borgo)
+ return FALSE
+ var/mob/user = usr
+ var/new_color
+ while(!new_color)
+ new_color = input(user, "Choose a new color for [src]'s flashlight.", "Light Color",light_color) as color|null
+ if(!new_color || QDELETED(borgo))
+ return
+ if(color_hex2num(new_color) < 200) //Colors too dark are rejected
+ to_chat(user, "That color is too dark! Choose a lighter one. ")
+ new_color = null
+ borgo.lamp_color = new_color
+ borgo.toggle_headlamp(FALSE, TRUE)
+ return TRUE
+ return ..()
+
+/obj/item/modular_computer/tablet/integrated/alert_call(datum/computer_file/program/caller, alerttext, sound = 'sound/machines/twobeep_high.ogg')
+ if(!caller || !caller.alert_able || caller.alert_silenced || !alerttext) //Yeah, we're checking alert_able. No, you don't get to make alerts that the user can't silence.
+ return
+ borgo.playsound_local(src, sound, 50, TRUE)
+ to_chat(borgo, "The [src] displays a [caller.filedesc] notification: [alerttext] ")
+
+/obj/item/modular_computer/tablet/integrated/syndicate
+ icon_state = "tablet-silicon-syndicate"
+ icon_state_unpowered = "tablet-silicon-syndicate"
+ icon_state_powered = "tablet-silicon-syndicate"
+ icon_state_menu = "command-syndicate"
+ device_theme = "syndicate"
+
+
+/obj/item/modular_computer/tablet/integrated/syndicate/Initialize()
+ . = ..()
+ borgo.lamp_color = COLOR_RED //Syndicate likes it red
diff --git a/code/modules/modular_computers/computers/item/tablet_presets.dm b/code/modules/modular_computers/computers/item/tablet_presets.dm
index e98305f1701..b84fdee85c3 100644
--- a/code/modules/modular_computers/computers/item/tablet_presets.dm
+++ b/code/modules/modular_computers/computers/item/tablet_presets.dm
@@ -56,3 +56,11 @@
install_component(new /obj/item/computer_hardware/battery(src, /obj/item/stock_parts/cell/computer))
install_component(new /obj/item/computer_hardware/hard_drive/small/nukeops)
install_component(new /obj/item/computer_hardware/network_card)
+
+//Borg Built-in tablet
+/obj/item/modular_computer/tablet/integrated/Initialize()
+ . = ..()
+ install_component(new /obj/item/computer_hardware/processor_unit/small)
+ install_component(new /obj/item/computer_hardware/hard_drive/small/integrated)
+ install_component(new /obj/item/computer_hardware/recharger/cyborg)
+ install_component(new /obj/item/computer_hardware/network_card/integrated)
diff --git a/code/modules/modular_computers/computers/machinery/modular_computer.dm b/code/modules/modular_computers/computers/machinery/modular_computer.dm
index ae4b874968d..f35c91e115f 100644
--- a/code/modules/modular_computers/computers/machinery/modular_computer.dm
+++ b/code/modules/modular_computers/computers/machinery/modular_computer.dm
@@ -136,6 +136,10 @@
..()
update_icon()
+/obj/machinery/modular_computer/screwdriver_act(mob/user, obj/item/tool)
+ if(cpu)
+ return cpu.screwdriver_act(user, tool)
+
/obj/machinery/modular_computer/attackby(var/obj/item/W as obj, mob/user)
if(cpu && !(flags_1 & NODECONSTRUCT_1))
return cpu.attackby(W, user)
diff --git a/code/modules/modular_computers/file_system/programs/antagonist/revelation.dm b/code/modules/modular_computers/file_system/programs/antagonist/revelation.dm
index 2a4b912dd40..75ac8e1acf2 100644
--- a/code/modules/modular_computers/file_system/programs/antagonist/revelation.dm
+++ b/code/modules/modular_computers/file_system/programs/antagonist/revelation.dm
@@ -20,6 +20,12 @@
/datum/computer_file/program/revelation/proc/activate()
if(computer)
+ if(istype(computer, /obj/item/modular_computer/tablet/integrated)) //If this is a borg's integrated tablet
+ var/obj/item/modular_computer/tablet/integrated/modularInterface = computer
+ to_chat(modularInterface.borgo,"SYSTEM PURGE DETECTED/ ")
+ addtimer(CALLBACK(modularInterface.borgo, /mob/living/silicon/robot/.proc/death), 2 SECONDS, TIMER_UNIQUE)
+ return
+
computer.visible_message("\The [computer]'s screen brightly flashes and loud electrical buzzing is heard. ")
computer.enabled = FALSE
computer.update_icon()
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 d827dc8d3d0..ebc18420c70 100644
--- a/code/modules/modular_computers/file_system/programs/borg_monitor.dm
+++ b/code/modules/modular_computers/file_system/programs/borg_monitor.dm
@@ -59,6 +59,8 @@
var/obj/item/card/id/ID = computer.GetID()
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
@@ -67,3 +69,4 @@
if(R.connected_ai)
to_chat(R.connected_ai, "Message from [ID.registered_name] to [R] -- \"[message]\" ")
SEND_SOUND(R.connected_ai, 'sound/machines/twobeep_high.ogg')
+ R.logevent("Message from [ID] -- \"[message]\"")
diff --git a/code/modules/modular_computers/file_system/programs/borg_self_monitor.dm b/code/modules/modular_computers/file_system/programs/borg_self_monitor.dm
new file mode 100644
index 00000000000..6bd79a4cde8
--- /dev/null
+++ b/code/modules/modular_computers/file_system/programs/borg_self_monitor.dm
@@ -0,0 +1,146 @@
+/datum/computer_file/program/borg_self_monitor
+ filename = "borg_self_monitor"
+ filedesc = "Cyborg Self-Monitoring"
+ extended_desc = "A built-in app for cyborg self-management and diagnostics."
+ ui_header = "borg_self_monitor.gif" //DEBUG -- new icon before PR
+ program_icon_state = "command"
+ requires_ntnet = FALSE
+ transfer_access = null
+ available_on_ntnet = FALSE
+ unsendable = TRUE
+ undeletable = TRUE
+ usage_flags = PROGRAM_TABLET
+ size = 5
+ tgui_id = "NtosCyborgSelfMonitor"
+ ///A typed reference to the computer, specifying the borg tablet type
+ var/obj/item/modular_computer/tablet/integrated/tablet
+
+/datum/computer_file/program/borg_self_monitor/Destroy()
+ tablet = null
+ return ..()
+
+/datum/computer_file/program/borg_self_monitor/run_program(mob/living/user)
+ if(!istype(computer, /obj/item/modular_computer/tablet/integrated))
+ to_chat(user, "A warning flashes across \the [computer]: Device Incompatible. ")
+ return FALSE
+ . = ..()
+ if(.)
+ tablet = computer
+ if(tablet.device_theme == "syndicate")
+ program_icon_state = "command-syndicate"
+ return TRUE
+ return FALSE
+
+/datum/computer_file/program/borg_self_monitor/ui_data(mob/user)
+ var/list/data = get_header_data()
+ if(!iscyborg(user))
+ return data
+ var/mob/living/silicon/robot/borgo = tablet.borgo
+
+ data["name"] = borgo.name
+ data["designation"] = borgo.designation //Borgo module type
+ data["masterAI"] = borgo.connected_ai //Master AI
+
+ var/charge = 0
+ var/maxcharge = 1
+ if(borgo.cell)
+ charge = borgo.cell.charge
+ maxcharge = borgo.cell.maxcharge
+ data["charge"] = charge //Current cell charge
+ data["maxcharge"] = maxcharge //Cell max charge
+ data["integrity"] = ((borgo.health + 100) / 2) //Borgo health, as percentage
+ data["lampIntensity"] = borgo.lamp_intensity //Borgo lamp power setting
+ data["sensors"] = "[borgo.sensors_on?"ACTIVE":"DISABLED"]"
+ data["printerPictures"] = borgo.connected_ai? borgo.connected_ai.aicamera.stored.len : borgo.aicamera.stored.len //Number of pictures taken, synced to AI if available
+ data["printerToner"] = borgo.toner //amount of toner
+ data["printerTonerMax"] = borgo.tonermax //It's a variable, might as well use it
+ data["thrustersInstalled"] = borgo.ionpulse //If we have a thruster uprade
+ data["thrustersStatus"] = "[borgo.ionpulse_on?"ACTIVE":"DISABLED"]" //Feedback for thruster status
+
+ //DEBUG -- Cover, TRUE for locked
+ data["cover"] = "[borgo.locked? "LOCKED":"UNLOCKED"]"
+ //Ability to move. FAULT if lockdown wire is cut, DISABLED if borg locked, ENABLED otherwise
+ data["locomotion"] = "[borgo.wires.is_cut(WIRE_LOCKDOWN)?"FAULT":"[borgo.lockcharge?"DISABLED":"ENABLED"]"]"
+ //Module wire. FAULT if cut, NOMINAL otherwise
+ data["wireModule"] = "[borgo.wires.is_cut(WIRE_RESET_MODULE)?"FAULT":"NOMINAL"]"
+ //DEBUG -- Camera(net) wire. FAULT if cut (or no cameranet camera), DISABLED if pulse-disabled, NOMINAL otherwise
+ data["wireCamera"] = "[!borgo.builtInCamera || borgo.wires.is_cut(WIRE_CAMERA)?"FAULT":"[borgo.builtInCamera.can_use()?"NOMINAL":"DISABLED"]"]"
+ //AI wire. FAULT if wire is cut, CONNECTED if connected to AI, READY otherwise
+ data["wireAI"] = "[borgo.wires.is_cut(WIRE_AI)?"FAULT":"[borgo.connected_ai?"CONNECTED":"READY"]"]"
+ //Law sync wire. FAULT if cut, NOMINAL otherwise
+ data["wireLaw"] = "[borgo.wires.is_cut(WIRE_LAWSYNC)?"FAULT":"NOMINAL"]"
+
+ return data
+
+/datum/computer_file/program/borg_self_monitor/ui_static_data(mob/user)
+ var/list/data = list()
+ if(!iscyborg(user))
+ return data
+ var/mob/living/silicon/robot/borgo = user
+
+ data["Laws"] = borgo.laws.get_law_list(TRUE, TRUE, FALSE)
+ data["borgLog"] = tablet.borglog
+ data["borgUpgrades"] = borgo.upgrades
+ return data
+
+/datum/computer_file/program/borg_self_monitor/ui_act(action, params)
+ . = ..()
+ if(.)
+ return
+
+ var/mob/living/silicon/robot/borgo = tablet.borgo
+
+ switch(action)
+ if("coverunlock")
+ if(borgo.locked)
+ borgo.locked = FALSE
+ borgo.update_icons()
+ if(borgo.emagged)
+ borgo.logevent("ChÃ¥vÃis cover lock has been [borgo.locked ? "engaged" : "released"]") //"The cover interface glitches out for a split second"
+ else
+ borgo.logevent("Chassis cover lock has been [borgo.locked ? "engaged" : "released"]")
+
+ if("lawchannel")
+ borgo.set_autosay()
+
+ if("lawstate")
+ borgo.checklaws()
+
+ if("alertPower")
+ if(borgo.stat == CONSCIOUS)
+ if(!borgo.cell || !borgo.cell.charge)
+ borgo.visible_message("The power warning light on [borgo] flashes urgently. ", \
+ "You announce you are operating in low power mode.")
+ playsound(borgo, 'sound/machines/buzz-two.ogg', 50, FALSE)
+
+ if("toggleSensors")
+ borgo.toggle_sensors()
+
+ if("viewImage")
+ if(borgo.connected_ai)
+ borgo.connected_ai.aicamera?.viewpictures(usr)
+ else
+ borgo.aicamera?.viewpictures(usr)
+
+ if("printImage")
+ var/obj/item/camera/siliconcam/robot_camera/borgcam = borgo.aicamera
+ borgcam?.borgprint(usr)
+
+ if("toggleThrusters")
+ borgo.toggle_ionpulse()
+
+ if("lampIntensity")
+ borgo.lamp_intensity = CLAMP(text2num(params["ref"]), 1, 5)
+ borgo.toggle_headlamp(FALSE, TRUE)
+
+/**
+ * Forces a full update of the UI, if currently open.
+ *
+ * Forces an update that includes refreshing ui_static_data. Called by
+ * law changes and borg log additions.
+ */
+/datum/computer_file/program/borg_self_monitor/proc/force_full_update()
+ if(tablet)
+ var/datum/tgui/active_ui = SStgui.get_open_ui(tablet.borgo, src)
+ if(active_ui)
+ active_ui.send_full_update()
diff --git a/code/modules/modular_computers/hardware/hard_drive.dm b/code/modules/modular_computers/hardware/hard_drive.dm
index c71d79edd09..1bfbc3b1b1a 100644
--- a/code/modules/modular_computers/hardware/hard_drive.dm
+++ b/code/modules/modular_computers/hardware/hard_drive.dm
@@ -159,6 +159,13 @@
w_class = WEIGHT_CLASS_TINY
custom_price = 15
+// For borg integrated tablets. No downloader.
+/obj/item/computer_hardware/hard_drive/small/integrated/install_default_programs()
+ store_file(new /datum/computer_file/program/computerconfig(src)) // Computer configuration utility, allows hardware control and displays more info than status bar
+ store_file(new /datum/computer_file/program/filemanager(src)) // File manager, allows text editor functions and basic file manipulation.
+ store_file(new /datum/computer_file/program/borg_self_monitor(src))
+
+
// Syndicate variant - very slight better
/obj/item/computer_hardware/hard_drive/small/syndicate
desc = "An efficient SSD for portable devices developed by a rival organisation."
diff --git a/code/modules/modular_computers/hardware/network_card.dm b/code/modules/modular_computers/hardware/network_card.dm
index fe1b1879cb3..c8dc98ba4cd 100644
--- a/code/modules/modular_computers/hardware/network_card.dm
+++ b/code/modules/modular_computers/hardware/network_card.dm
@@ -77,3 +77,23 @@
power_usage = 100 // Better range but higher power usage.
icon_state = "net_wired"
w_class = WEIGHT_CLASS_NORMAL
+
+/obj/item/computer_hardware/network_card/integrated //Borg tablet version, only works while the borg has power and is not locked
+ name = "cyborg data link"
+
+/obj/item/computer_hardware/network_card/integrated/get_signal(specific_action = 0)
+ var/obj/item/modular_computer/tablet/integrated/modularInterface = holder
+
+ if(!modularInterface || !istype(modularInterface))
+ return FALSE //wrong type of tablet
+
+ if(!modularInterface.borgo)
+ return FALSE //No borg found
+
+ if(modularInterface.borgo.lockcharge)
+ return FALSE //lockdown restricts borg networking
+
+ if(!modularInterface.borgo.cell || modularInterface.borgo.cell.charge == 0)
+ return FALSE //borg cell dying restricts borg networking
+
+ return ..()
diff --git a/code/modules/modular_computers/hardware/recharger.dm b/code/modules/modular_computers/hardware/recharger.dm
index 1188ccea14c..609bc54369b 100644
--- a/code/modules/modular_computers/hardware/recharger.dm
+++ b/code/modules/modular_computers/hardware/recharger.dm
@@ -77,6 +77,14 @@
return 0
+/// This recharger exists only in borg built-in tablets. I would have tied it to the borg's cell but
+/// the program that displays laws should always be usable, and the exceptions were starting to pile.
+/obj/item/computer_hardware/recharger/cyborg
+ name = "modular interface power harness"
+ desc = "A standard connection to power a small computer device from a cyborg's chassis."
+
+/obj/item/computer_hardware/recharger/cyborg/use_power(amount, charging=0)
+ return TRUE
// This is not intended to be obtainable in-game. Intended for adminbus and debugging purposes.
@@ -89,3 +97,4 @@
/obj/item/computer_hardware/recharger/lambda/use_power(amount, charging=0)
return 1
+
diff --git a/code/modules/photography/camera/silicon_camera.dm b/code/modules/photography/camera/silicon_camera.dm
index 085b4821672..491760a6b83 100644
--- a/code/modules/photography/camera/silicon_camera.dm
+++ b/code/modules/photography/camera/silicon_camera.dm
@@ -74,14 +74,6 @@
else
return ..()
-/obj/item/camera/siliconcam/robot_camera/verb/borgprinting()
- set category ="Robot Commands"
- set name = "Print Image"
- set src in usr
- if(usr.stat == DEAD)
- return
- borgprint(usr)
-
/obj/item/camera/siliconcam/robot_camera/proc/borgprint(mob/user)
var/mob/living/silicon/robot/C = loc
if(!istype(C) || C.toner < 20)
diff --git a/code/modules/tgui/states.dm b/code/modules/tgui/states.dm
index 436dca91fa8..85582558bb1 100644
--- a/code/modules/tgui/states.dm
+++ b/code/modules/tgui/states.dm
@@ -83,8 +83,9 @@
return ..()
/mob/living/silicon/robot/shared_ui_interaction(src_object)
- // Disable UIs if the Borg is unpowered or locked.
- if(!cell || cell.charge <= 0 || lockcharge)
+ // Disable UIs if the object isn't installed in the borg AND the borg is either locked, has a dead cell, or no cell.
+ var/atom/device = src_object
+ if((istype(device) && device.loc != src) && (!cell || cell.charge <= 0 || lockcharge))
return UI_DISABLED
return ..()
diff --git a/icons/mob/screen_cyborg.dmi b/icons/mob/screen_cyborg.dmi
index 191ebc49b10..5f722056306 100644
Binary files a/icons/mob/screen_cyborg.dmi and b/icons/mob/screen_cyborg.dmi differ
diff --git a/icons/program_icons/borg_self_monitor.gif b/icons/program_icons/borg_self_monitor.gif
new file mode 100644
index 00000000000..cf11eb184bd
Binary files /dev/null and b/icons/program_icons/borg_self_monitor.gif differ
diff --git a/tgui/packages/tgui/interfaces/NtosCyborgSelfMonitor.js b/tgui/packages/tgui/interfaces/NtosCyborgSelfMonitor.js
new file mode 100644
index 00000000000..b063678d83b
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/NtosCyborgSelfMonitor.js
@@ -0,0 +1,306 @@
+import { useBackend, useSharedState } from '../backend';
+import {
+ AnimatedNumber,
+ Box,
+ Button,
+ Flex,
+ Fragment,
+ Section,
+ Slider,
+ ProgressBar,
+ LabeledList,
+ Tabs,
+} from '../components';
+import { NtosWindow } from '../layouts';
+
+export const NtosCyborgSelfMonitor = (props, context) => {
+ const { act, data } = useBackend(context);
+ const { PC_device_theme } = data;
+ return (
+
+
+
+
+
+ );
+};
+
+export const NtosCyborgSelfMonitorContent = (props, context) => {
+ const { act, data } = useBackend(context);
+ const [tab_main, setTab_main] = useSharedState(context, 'tab_main', 1);
+ const [tab_sub, setTab_sub] = useSharedState(context, 'tab_sub', 1);
+ const {
+ charge,
+ maxcharge,
+ integrity,
+ lampIntensity,
+ cover,
+ locomotion,
+ wireModule,
+ wireCamera,
+ wireAI,
+ wireLaw,
+ sensors,
+ printerPictures,
+ printerToner,
+ printerTonerMax,
+ thrustersInstalled,
+ thrustersStatus,
+ } = data;
+ const borgName = data.name || [];
+ const borgType = data.designation || [];
+ const masterAI = data.masterAI || [];
+ const laws = data.Laws || [];
+ const borgLog = data.borgLog || [];
+ const borgUpgrades = data.borgUpgrades || [];
+ return (
+
+
+
+ setTab_main(1)}>
+ Status
+
+ setTab_main(2)}>
+ Logs
+
+
+
+ {tab_main === 1 && (
+
+
+
+
+
+
+ {borgName.slice(0, 17)}
+
+ {borgType}
+
+ {masterAI.slice(0, 17)}
+
+
+
+
+
+
+ Charge:
+ act('alertPower')}
+ />
+
+
+
+ Chassis Integrity:
+
+
+
+
+ act('lampIntensity', {
+ ref: value,
+ })}
+ />
+ Lamp power usage: {lampIntensity / 2} watts
+
+
+
+
+
+ setTab_sub(1)}>
+ Actions
+
+ setTab_sub(2)}>
+ Upgrades
+
+ setTab_sub(3)}>
+ Diagnostics
+
+
+
+ {tab_sub === 1 && (
+
+
+
+ act('coverunlock')}
+ />
+
+
+ act('toggleSensors')}
+ />
+
+
+ act('viewImage')}
+ />
+ act('printImage')}
+ />
+
+
+
+
+ {!!thrustersInstalled && (
+
+ act('toggleThrusters')}
+ />
+
+ )}
+
+
+ )}
+ {tab_sub === 2 && (
+
+ {borgUpgrades.map((upgrade) => (
+
+ {upgrade}
+
+ ))}
+
+ )}
+ {tab_sub === 3 && (
+
+
+
+ {wireAI}
+
+
+ {wireLaw}
+
+
+ {wireCamera}
+
+
+ {wireModule}
+
+
+ {locomotion}
+
+
+ {cover}
+
+
+
+ )}
+
+
+
+
+ act('lawstate')}
+ />
+ act('lawchannel')} />
+
+ }>
+ {laws.map((law) => (
+
+ {law}
+
+ ))}
+
+
+
+ )}
+ {tab_main === 2 && (
+
+
+ {borgLog.map((log) => (
+
+ {log}
+
+ ))}
+
+
+ )}
+
+ );
+};
diff --git a/tgui/packages/tgui/interfaces/NtosMain.js b/tgui/packages/tgui/interfaces/NtosMain.js
index 92b41ddc47b..5106a198782 100644
--- a/tgui/packages/tgui/interfaces/NtosMain.js
+++ b/tgui/packages/tgui/interfaces/NtosMain.js
@@ -27,6 +27,12 @@ export const NtosMain = (props, context) => {
has_light,
light_on,
comp_light_color,
+<<<<<<< HEAD
+=======
+ removable_media = [],
+ cardholder,
+ login = [],
+>>>>>>> 4f2ddd1916... [TG PORT] Removes Robot Commands and replaces it with an integrated control tablet program + silicons start with HUD on (#7351)
} = data;
return (
{
)}
+<<<<<<< HEAD
+=======
+ {!!cardholder && (
+ act('PC_Eject_Disk', { name: "ID" })}
+ />
+ )}>
+
+
+ ID Name: {login.IDName}
+
+
+ Assignment: {login.IDJob}
+
+
+
+ )}
+ {!!removable_media.length && (
+
+
+ {removable_media.map(device => (
+
+
+ act('PC_Eject_Disk', { name: device })}
+ />
+
+
+ ))}
+
+
+ )}
+>>>>>>> 4f2ddd1916... [TG PORT] Removes Robot Commands and replaces it with an integrated control tablet program + silicons start with HUD on (#7351)
{programs.map(program => (