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('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')} + /> + + +
+ )} + {tab_sub === 2 && ( +
+ {borgUpgrades.map((upgrade) => ( + + {upgrade} + + ))} +
+ )} + {tab_sub === 3 && ( +
+ + + {wireAI} + + + {wireLaw} + + + {wireCamera} + + + {wireModule} + + + {locomotion} + + + {cover} + + +
+ )} +
+
+ +
+
+
+
+ )} + {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 => ( + + +
+
+ )} +>>>>>>> 4f2ddd1916... [TG PORT] Removes Robot Commands and replaces it with an integrated control tablet program + silicons start with HUD on (#7351)
{programs.map(program => (