diff --git a/code/_onclick/hud/_defines.dm b/code/_onclick/hud/_defines.dm index 467c67e0c3..7dcfad01b5 100644 --- a/code/_onclick/hud/_defines.dm +++ b/code/_onclick/hud/_defines.dm @@ -49,16 +49,12 @@ #define ui_storage1 "CENTER+1:18,SOUTH:5" #define ui_storage2 "CENTER+2:20,SOUTH:5" -#define ui_borg_sensor "CENTER-3:15, SOUTH:5" //borgs -#define ui_borg_lamp "CENTER-4:15, SOUTH:5" //borgs -#define ui_borg_thrusters "CENTER-5:15, 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_language_menu "EAST-1:27,SOUTH+2:8" //borgs #define ui_monkey_head "CENTER-5:13,SOUTH:5" //monkey @@ -91,7 +87,9 @@ #define ui_borg_pull "EAST-2:26,SOUTH+1:7" #define ui_borg_radio "EAST-1:28,SOUTH+1:7" #define ui_borg_intents "EAST-2:26,SOUTH:5" - +#define ui_borg_lamp "CENTER-3:16, SOUTH:5" +#define ui_borg_tablet "CENTER-4:16, SOUTH:5" +#define ui_borg_alerts "CENTER+4:21,SOUTH:5" //Upper-middle right (alerts) #define ui_alert1 "EAST-1:28,CENTER+5:27" diff --git a/code/_onclick/hud/ai.dm b/code/_onclick/hud/ai.dm index 93c3b63d1e..f54d229c5e 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) /obj/screen/ai/sensors name = "Sensor Augmentation" diff --git a/code/_onclick/hud/robot.dm b/code/_onclick/hud/robot.dm index 9859e7acdf..6a9a670214 100644 --- a/code/_onclick/hud/robot.dm +++ b/code/_onclick/hud/robot.dm @@ -70,23 +70,46 @@ /obj/screen/robot/lamp name = "headlamp" - icon_state = "lamp0" + icon = 'icons/mob/screen_cyborg.dmi' + icon_state = "lamp_off" + var/mob/living/silicon/robot/robot /obj/screen/robot/lamp/Click() - if(..()) + . = ..() + if(.) return - var/mob/living/silicon/robot/R = usr - R.control_headlamp() + robot?.toggle_headlamp() + update_icon() -/obj/screen/robot/thrusters - name = "ion thrusters" - icon_state = "ionpulse0" +/obj/screen/robot/lamp/update_icon() + if(robot?.lamp_enabled) + icon_state = "lamp_on" + else + icon_state = "lamp_off" -/obj/screen/robot/thrusters/Click() - if(..()) +/obj/screen/robot/modPC + name = "Modular Interface" + icon = 'icons/mob/screen_cyborg.dmi' + icon_state = "template" + var/mob/living/silicon/robot/robot + +/obj/screen/robot/modPC/Click() + . = ..() + if(.) return - var/mob/living/silicon/robot/R = usr - R.toggle_ionpulse() + robot.modularInterface?.interact(robot) + +/obj/screen/robot/alerts + name = "Alert Panel" + icon = 'icons/mob/screen_ai.dmi' + icon_state = "alerts" + +/obj/screen/robot/alerts/Click() + . = ..() + if(.) + return + var/mob/living/silicon/robot/borgo = usr + borgo.robot_alerts() /obj/screen/robot/sensors name = "Sensor Augmentation" @@ -148,37 +171,37 @@ //End of module select + using = new /obj/screen/robot/lamp() + using.screen_loc = ui_borg_lamp + using.hud = src + static_inventory += using + mymobR.lampButton = using + var/obj/screen/robot/lamp/lampscreen = using + lampscreen.robot = mymobR + //Photography stuff using = new /obj/screen/ai/image_take() using.screen_loc = ui_borg_camera using.hud = src static_inventory += using - using = new /obj/screen/ai/image_view() - using.screen_loc = ui_borg_album +//Borg Integrated Tablet + using = new /obj/screen/robot/modPC() + using.screen_loc = ui_borg_tablet using.hud = src static_inventory += using + mymobR.interfaceButton = using + if(mymobR.modularInterface) + using.vis_contents += mymobR.modularInterface + var/obj/screen/robot/modPC/tabletbutton = using + tabletbutton.robot = mymobR -//Sec/Med HUDs - using = new /obj/screen/robot/sensors() - using.screen_loc = ui_borg_sensor +//Alerts + using = new /obj/screen/robot/alerts() + using.screen_loc = ui_borg_alerts using.hud = src static_inventory += using -//Headlamp control - using = new /obj/screen/robot/lamp() - using.screen_loc = ui_borg_lamp - using.hud = src - static_inventory += using - mymobR.lamp_button = using - -//Thrusters - using = new /obj/screen/robot/thrusters() - using.screen_loc = ui_borg_thrusters - using.hud = src - static_inventory += using - mymobR.thruster_button = using - //Intent action_intent = new /obj/screen/act_intent/robot() action_intent.icon_state = mymob.a_intent diff --git a/code/datums/wires/robot.dm b/code/datums/wires/robot.dm index 593579c469..8d5847d811 100644 --- a/code/datums/wires/robot.dm +++ b/code/datums/wires/robot.dm @@ -70,19 +70,23 @@ if(R.shell) R.undeploy() R.set_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) R.lawupdate = TRUE else if(!R.deployed) //AI shells must always have the same laws as the AI R.lawupdate = FALSE + 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, 0) - R.visible_message("[R]'s camera lens focuses loudly.", "Your camera lens focuses loudly.") + R.visible_message("[R]'s camera lens focuses loudly.", "Your camera lens focuses loudly.") + R.logevent("Camera Module fault [mend?"cleared":"detected"]") if(WIRE_LOCKDOWN) // Simple lockdown. R.SetLockdown(!mend) + R.logevent("Motor Controller fault [mend?"cleared":"detected"]") if(WIRE_RESET_MODULE) if(R.has_module() && !mend) R.ResetModule() diff --git a/code/game/machinery/computer/robot.dm b/code/game/machinery/computer/robot.dm index 9b911e9c15..932a2fe455 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("convert") if(isAI(usr) && is_servant_of_ratvar(usr)) var/mob/living/silicon/robot/R = locate(params["ref"]) in GLOB.silicon_mobs diff --git a/code/game/objects/items/robot/robot_upgrades.dm b/code/game/objects/items/robot/robot_upgrades.dm index 291fc30aa2..bbdf80c308 100644 --- a/code/game/objects/items/robot/robot_upgrades.dm +++ b/code/game/objects/items/robot/robot_upgrades.dm @@ -74,6 +74,8 @@ as performing this in action() will cause the upgrade to end up in the borg inst 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" @@ -149,6 +151,7 @@ as performing this in action() will cause the upgrade to end up in the borg inst return FALSE R.ionpulse = TRUE + R.toggle_ionpulse() //Enabled by default /obj/item/borg/upgrade/thrusters/deactivate(mob/living/silicon/robot/R, user = usr) . = ..() @@ -285,6 +288,8 @@ as performing this in action() will cause the upgrade to end up in the borg inst 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/antagonists/traitor/equipment/Malf_Modules.dm b/code/modules/antagonists/traitor/equipment/Malf_Modules.dm index 0659655da5..5722276159 100644 --- a/code/modules/antagonists/traitor/equipment/Malf_Modules.dm +++ b/code/modules/antagonists/traitor/equipment/Malf_Modules.dm @@ -354,9 +354,14 @@ GLOBAL_LIST_INIT(blacklisted_malf_machines, typecacheof(list( var/obj/effect/countdown/doomsday/countdown var/detonation_timer var/next_announce + var/mob/living/silicon/ai/owner /obj/machinery/doomsday_device/Initialize() . = ..() + owner = loc + if(!istype(owner)) + stack_trace("Doomsday created outside an AI somehow, shit's fucking broke. Anyway, we're just gonna qdel now. Go make a github issue report.") + qdel(src) countdown = new(src) /obj/machinery/doomsday_device/Destroy() @@ -364,6 +369,9 @@ GLOBAL_LIST_INIT(blacklisted_malf_machines, typecacheof(list( STOP_PROCESSING(SSfastprocess, src) SSshuttle.clearHostileEnvironment(src) SSmapping.remove_nuke_threat(src) + for(var/mob/living/silicon/robot/borg in owner.connected_robots) + borg.lamp_doom = FALSE + borg.toggle_headlamp(FALSE, TRUE) //forces borg lamp to update for(var/A in GLOB.ai_list) var/mob/living/silicon/ai/AI = A if(AI.doomsday_device == src) @@ -378,6 +386,9 @@ GLOBAL_LIST_INIT(blacklisted_malf_machines, typecacheof(list( START_PROCESSING(SSfastprocess, src) SSshuttle.registerHostileEnvironment(src) SSmapping.add_nuke_threat(src) //This causes all blue "circuit" tiles on the map to change to animated red icon state. + for(var/mob/living/silicon/robot/borg in owner.connected_robots) + borg.lamp_doom = TRUE + borg.toggle_headlamp(FALSE, TRUE) //forces borg lamp to update /obj/machinery/doomsday_device/proc/seconds_remaining() . = max(0, (round((detonation_timer - world.time) / 10))) diff --git a/code/modules/asset_cache/asset_list_items.dm b/code/modules/asset_cache/asset_list_items.dm index 22fb266033..a5290a5752 100644 --- a/code/modules/asset_cache/asset_list_items.dm +++ b/code/modules/asset_cache/asset_list_items.dm @@ -48,8 +48,8 @@ "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_mon.gif" = 'icons/program_icons/borg_mon.gif', - // "robotact.gif" = 'icons/program_icons/robotact.gif' + "borg_mon.gif" = 'icons/program_icons/borg_mon.gif', + "robotact.gif" = 'icons/program_icons/robotact.gif' ) /datum/asset/simple/radar_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 33a2a09b70..329fa5ee65 100644 --- a/code/modules/mob/living/carbon/human/species_types/shadowpeople.dm +++ b/code/modules/mob/living/carbon/human/species_types/shadowpeople.dm @@ -187,12 +187,8 @@ AM.emp_act(50) if(iscyborg(AM)) var/mob/living/silicon/robot/borg = AM - if(borg.lamp_intensity) - borg.update_headlamp(TRUE, INFINITY) - to_chat(borg, "Your headlamp is fried! You'll need a human to help replace it.") - for(var/obj/item/assembly/flash/cyborg/F in borg.held_items) - if(!F.crit_fail) - F.burn_out() + if(borg.lamp_enabled) + borg.smash_headlamp() else for(var/obj/item/O in AM) if(O.light_range && O.light_power) diff --git a/code/modules/mob/living/silicon/robot/death.dm b/code/modules/mob/living/silicon/robot/death.dm index 6a5338c1f5..b5fa78a3fd 100644 --- a/code/modules/mob/living/silicon/robot/death.dm +++ b/code/modules/mob/living/silicon/robot/death.dm @@ -17,6 +17,9 @@ if(stat == DEAD) return + if(!gibbed) + logevent("FATAL -- SYSTEM HALT") + modularInterface.shutdown_computer() . = ..() locked = FALSE //unlock cover @@ -24,7 +27,7 @@ update_mobility() 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 d499c2abae..2ef6f6490e 100644 --- a/code/modules/mob/living/silicon/robot/emote.dm +++ b/code/modules/mob/living/silicon/robot/emote.dm @@ -64,15 +64,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 995ad5a5ee..e8d3b76927 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/show_laws(everyone = 0) laws_sanity_check() var/who @@ -79,4 +71,12 @@ if (length(temp) > 0) laws.supplied[index] = temp + var/datum/computer_file/program/robotact/program = modularInterface.get_robotact() + 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 0feb8b98b7..1b3b1a8d94 100644 --- a/code/modules/mob/living/silicon/robot/life.dm +++ b/code/modules/mob/living/silicon/robot/life.dm @@ -9,8 +9,7 @@ if(stat != DEAD) if(low_power_mode) if(cell && cell.charge) - low_power_mode = 0 - update_headlamp() + low_power_mode = FALSE else if(stat == CONSCIOUS) use_power() @@ -18,12 +17,12 @@ if(cell && 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 - 2) : 0 * 2,1,cell.charge) //Always try to use at least one charge per tick, but allow it to completely drain the cell. 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 7db417b8fa..03e59408c6 100644 --- a/code/modules/mob/living/silicon/robot/robot.dm +++ b/code/modules/mob/living/silicon/robot/robot.dm @@ -33,6 +33,8 @@ if(!TryConnectToAI()) lawupdate = FALSE + create_modularInterface() + radio = new /obj/item/radio/borg(src) if(!scrambledcodes && !builtInCamera) builtInCamera = new (src) @@ -71,6 +73,7 @@ aicamera = new/obj/item/camera/siliconcam/robot_camera(src) toner = tonermax diag_hud_set_borgcell() + logevent("System brought online.") add_verb(src, /mob/living/proc/lay_down) //CITADEL EDIT gimmie rest verb kthx add_verb(src, /mob/living/silicon/robot/proc/rest_style) @@ -93,6 +96,8 @@ ghostize() stack_trace("Borg MMI lacked a brainmob") mmi = null + if(modularInterface) + QDEL_NULL(modularInterface) //CITADEL EDIT: Cyborgs drop encryption keys on destroy if(istype(radio) && istype(radio.keyslot)) radio.keyslot.forceMove(T) @@ -165,14 +170,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) @@ -219,8 +216,6 @@ ion_trail.start() else ion_trail.stop() - if(thruster_button) - thruster_button.icon_state = "ionpulse[ionpulse_on]" /mob/living/silicon/robot/get_status_tab_items() . = ..() @@ -281,6 +276,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) @@ -428,8 +425,6 @@ to_chat(user, "Unable to locate a radio!") else if (istype(W, /obj/item/card/id)||istype(W, /obj/item/pda)) // trying to unlock the interface with an ID card - if(emagged)//still allow them to open the cover - to_chat(user, "The interface seems slightly damaged.") if(opened) to_chat(user, "You must close the cover to swipe an ID card!") else @@ -437,6 +432,11 @@ locked = !locked to_chat(user, "You [ locked ? "lock" : "unlock"] [src]'s cover.") update_icons() + if(emagged) + to_chat(user, "The cover interface glitches out for a split second.") + logevent("ChÃ¥vÃis cover lock has been [locked ? "engaged" : "released"]") //ChÃ¥vÃis: see above line + else + logevent("Chassis cover lock has been [locked ? "engaged" : "released"]") else to_chat(user, "Access denied.") @@ -454,11 +454,13 @@ if(U.action(src)) to_chat(user, "You apply the upgrade to [src].") if(U.one_use) + logevent("Firmware [U] run successfully.") U.afterInstall(src) qdel(U) else U.forceMove(src) upgrades += U + logevent("Hardware [U] installed successfully.") U.afterInstall(src) else to_chat(user, "Upgrade error.") @@ -477,15 +479,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 ..() @@ -502,19 +511,6 @@ return TRUE return ..() -/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)) @@ -596,6 +592,7 @@ else clear_alert("locked") locked_down = state + logevent("System lockdown [locked_down?"triggered":"released"].") update_mobility() /mob/living/silicon/robot/proc/SetEmagged(new_state) @@ -607,48 +604,6 @@ else clear_alert("hacked") -/mob/living/silicon/robot/verb/outputlaws() - set category = "Robot Commands" - set name = "State Laws" - - if(usr.stat == DEAD) - return //won't work if dead - checklaws() - -/mob/living/silicon/robot/verb/set_automatic_say_channel() //Borg version of setting the radio for autosay messages. - set name = "Set Auto Announce Mode" - set desc = "Modify the default radio setting for stating your laws." - set category = "Robot Commands" - - if(usr.stat == DEAD) - return //won't work if dead - set_autosay() - -/mob/living/silicon/robot/proc/control_headlamp() - if(stat || lamp_cooldown > world.time || low_power_mode) - to_chat(src, "This function is currently offline.") - return - -//Some sort of magical "modulo" thing which somehow increments lamp power by 2, until it hits the max and resets to 0. - lamp_intensity = (lamp_intensity+2) % (lamp_max+2) - to_chat(src, "[lamp_intensity ? "Headlamp power set to Level [lamp_intensity/2]" : "Headlamp disabled."]") - update_headlamp() - -/mob/living/silicon/robot/proc/update_headlamp(var/turn_off = 0, var/cooldown = 100) - set_light(0) - - if(lamp_intensity && (turn_off || stat || low_power_mode)) - to_chat(src, "Your headlamp has been deactivated.") - lamp_intensity = 0 - lamp_cooldown = world.time + cooldown - else - set_light(lamp_intensity) - - if(lamp_button) - lamp_button.icon_state = "lamp[lamp_intensity]" - - update_icons() - /mob/living/silicon/robot/proc/deconstruct() var/turf/T = get_turf(src) if (robot_suit) @@ -867,13 +822,11 @@ if(!eye_blind) blind_eyes(1) update_mobility() - update_headlamp() else if(stat == UNCONSCIOUS) stat = CONSCIOUS adjust_blindness(-1) update_mobility() - update_headlamp() diag_hud_set_status() diag_hud_set_health() diag_hud_set_aishell() @@ -883,7 +836,6 @@ 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) @@ -908,6 +860,7 @@ resize = 0.5 hasExpanded = FALSE update_transform() + logevent("Chassis configuration has been reset.") module.transform_to(/obj/item/robot_module) // Remove upgrades. @@ -1173,8 +1126,91 @@ return . = connected_ai connected_ai = new_ai + lamp_doom = FALSE if(.) var/mob/living/silicon/ai/old_ai = . old_ai.connected_robots -= src if(connected_ai) connected_ai.connected_robots |= src + toggle_headlamp(FALSE, TRUE) + +/** + * 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)) + if(lamp_functional && stat != DEAD && lamp_doom) + set_light(l_power = TRUE) //If the lamp isn't broken and borg isn't dead, doomsday borgs cannot disable their light fully. + set_light(l_color = "#FF0000") //This should only matter for doomsday borgs, as any other time the lamp will be off and the color not seen + set_light(l_range = 1) //Again, like above, this only takes effect when the light is forced on by doomsday mode. + set_light(l_power = FALSE) + lamp_enabled = FALSE + lampButton?.update_icon() + update_icons() + return + set_light(l_range = lamp_intensity) + set_light(l_color = (lamp_doom? "#FF0000" : lamp_color)) //Red for doomsday killborgs, borg's choice otherwise + set_light(l_power = TRUE) + lamp_enabled = TRUE + lampButton?.update_icon() + update_icons() + +/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 + +/mob/living/silicon/robot/modules/syndicate/create_modularInterface() + if(!modularInterface) + modularInterface = new /obj/item/modular_computer/tablet/integrated/syndicate(src) + return ..() + +/** + * 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("hh:mm:ss", world.time)] - [string]" + var/datum/computer_file/program/robotact/program = modularInterface.get_robotact() + 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 0ed380040b..69497b1f8b 100644 --- a/code/modules/mob/living/silicon/robot/robot_defense.dm +++ b/code/modules/mob/living/silicon/robot/robot_defense.dm @@ -118,6 +118,7 @@ if(connected_ai && connected_ai.mind && connected_ai.mind.has_antag_datum(/datum/antagonist/traitor)) to_chat(src, "ALERT: Foreign software execution prevented.") to_chat(connected_ai, "ALERT: Cyborg unit \[[src]] successfully defended against subversion.") + logevent("ALERT: Foreign software execution prevented.") log_game("[key_name(user)] attempted to emag cyborg [key_name(src)], but they were slaved to traitor AI [connected_ai].") return TRUE @@ -140,10 +141,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_defines.dm b/code/modules/mob/living/silicon/robot/robot_defines.dm index 4f7ae41969..f9d56e9caa 100644 --- a/code/modules/mob/living/silicon/robot/robot_defines.dm +++ b/code/modules/mob/living/silicon/robot/robot_defines.dm @@ -26,8 +26,6 @@ var/obj/screen/inv1 = null var/obj/screen/inv2 = null var/obj/screen/inv3 = null - var/obj/screen/lamp_button = null - var/obj/screen/thruster_button = null var/obj/screen/hands = null var/shown_robot_modules = 0 //Used to determine whether they have the module menu shown or not @@ -70,11 +68,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. - light_color = "#FFCC66" - light_power = 0.8 - 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 = "#FFFFFF" + ///Lamp brightness. Starts at 3, but can be 1 - 5. + var/lamp_intensity = 3 + ///Lamp button reference + var/obj/screen/robot/lamp/lampButton + ///Set to true if a doomsday event is locking our lamp to on and RED + var/lamp_doom = FALSE + //Maximum brightness of a borg lamp. Set as a var for easy adjusting. + var/lamp_max = 10 + + ///The reference to the built-in tablet that borgs carry. + var/obj/item/modular_computer/tablet/integrated/modularInterface + var/obj/screen/robot/modPC/interfaceButton var/sight_mode = 0 hud_possible = list(ANTAG_HUD, DIAG_STAT_HUD, DIAG_HUD, DIAG_BATT_HUD, DIAG_TRACK_HUD) diff --git a/code/modules/mob/living/silicon/robot/robot_modules.dm b/code/modules/mob/living/silicon/robot/robot_modules.dm index 4c83841809..241efcca6c 100644 --- a/code/modules/mob/living/silicon/robot/robot_modules.dm +++ b/code/modules/mob/living/silicon/robot/robot_modules.dm @@ -262,6 +262,7 @@ R.mob_transforming = TRUE R.SetLockdown(1) 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) @@ -271,7 +272,7 @@ R.setDir(SOUTH) R.anchored = FALSE R.mob_transforming = 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/robot/update_icons.dm b/code/modules/mob/living/silicon/robot/update_icons.dm index a567446e9e..cc54ea7af2 100644 --- a/code/modules/mob/living/silicon/robot/update_icons.dm +++ b/code/modules/mob/living/silicon/robot/update_icons.dm @@ -1,6 +1,7 @@ /// this is bad code /mob/living/silicon/robot/update_icons() cut_overlays() + SSvis_overlays.remove_vis_overlay(src, managed_vis_overlays) icon_state = module.cyborg_base_icon //Citadel changes start here - Allows modules to use different icon files, and allows modules to specify a pixel offset icon = (module.cyborg_icon_override ? module.cyborg_icon_override : initial(icon)) @@ -26,10 +27,14 @@ if(stat != DEAD && !(IsUnconscious() ||IsStun() || IsKnockdown() || 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_doom? COLOR_RED : lamp_color + eye_lights.plane = 19 //glowy eyes else eye_lights.icon_state = "[module.special_light_key ? "[module.special_light_key]":"[module.cyborg_base_icon]"]_e[is_servant_of_ratvar(src) ? "_r" : ""]" + eye_lights.color = "#FFFFFF" + eye_lights.plane = -1 eye_lights.icon = icon add_overlay(eye_lights) diff --git a/code/modules/modular_computers/computers/item/computer.dm b/code/modules/modular_computers/computers/item/computer.dm index 63cb1cc5fa..a1fa8c7e1a 100644 --- a/code/modules/modular_computers/computers/item/computer.dm +++ b/code/modules/modular_computers/computers/item/computer.dm @@ -386,32 +386,32 @@ to_chat(user, "You repair \the [src].") return - if(W.tool_behaviour == TOOL_SCREWDRIVER) - if(!all_components.len) - to_chat(user, "This device doesn't have any 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) +/obj/item/modular_computer/screwdriver_act(mob/user, obj/item/tool) + if(!all_components.len) + to_chat(user, "This device doesn't have any 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) - if(!choice) - return + var/choice = input(user, "Which component do you want to uninstall?", "Computer maintenance", null) as null|anything in sortList(component_names) - if(!Adjacent(user)) - return - - var/obj/item/computer_hardware/H = find_hardware_by_name(choice) - - if(!H) - return - - uninstall_component(H, user) + if(!choice) return - ..() + if(!Adjacent(user)) + return + + var/obj/item/computer_hardware/H = find_hardware_by_name(choice) + + if(!H) + return + + uninstall_component(H, user) + return // Used by processor to relay qdel() to machinery type. /obj/item/modular_computer/proc/relay_qdel() diff --git a/code/modules/modular_computers/computers/item/computer_ui.dm b/code/modules/modular_computers/computers/item/computer_ui.dm index 4a985b93c1..88fab58859 100644 --- a/code/modules/modular_computers/computers/item/computer_ui.dm +++ b/code/modules/modular_computers/computers/item/computer_ui.dm @@ -47,7 +47,9 @@ 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 diff --git a/code/modules/modular_computers/computers/item/tablet-silicon.dm b/code/modules/modular_computers/computers/item/tablet-silicon.dm new file mode 100644 index 0000000000..df224fd71c --- /dev/null +++ b/code/modules/modular_computers/computers/item/tablet-silicon.dm @@ -0,0 +1,98 @@ +/// Borg Built-in tablet interface +/obj/item/modular_computer/tablet/integrated + name = "modular interface" + icon_state = "tablet-silicon" + 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 RoboTact app. Important enough to borgs to deserve a ref. + var/datum/computer_file/program/robotact/robotact + ///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 RoboTact app, creating the app if need be. + * + * The RoboTact app is important for borgs, and so should always be available. + * This proc will look for it in the tablet's robotact var, then check the + * hard drive if the robotact 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. + * RoboTact is supposed to be undeletable, so these will create runtime messages. + */ +/obj/item/modular_computer/tablet/integrated/proc/get_robotact() + if(!borgo) + return null + if(!robotact) + var/obj/item/computer_hardware/hard_drive/hard_drive = all_components[MC_HDD] + robotact = hard_drive.find_file_by_name("robotact") + if(!robotact) + stack_trace("Cyborg [borgo] ( [borgo.type] ) was somehow missing their self-manage app in their tablet. A new copy has been created.") + robotact = new(hard_drive) + if(!hard_drive.store_file(robotact)) + qdel(robotact) + robotact = null + CRASH("Cyborg [borgo]'s tablet hard drive rejected recieving a new copy of the self-manage app. To fix, check the hard drive's space remaining. Please make a bug report about this.") + return robotact + +//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/syndicate + icon_state = "tablet-silicon-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.dm b/code/modules/modular_computers/computers/item/tablet.dm index 67e8118c7a..ffbd5c9936 100644 --- a/code/modules/modular_computers/computers/item/tablet.dm +++ b/code/modules/modular_computers/computers/item/tablet.dm @@ -41,6 +41,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) diff --git a/code/modules/modular_computers/computers/item/tablet_presets.dm b/code/modules/modular_computers/computers/item/tablet_presets.dm index 8ac669d2bf..fac6d8fef7 100644 --- a/code/modules/modular_computers/computers/item/tablet_presets.dm +++ b/code/modules/modular_computers/computers/item/tablet_presets.dm @@ -67,3 +67,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 0e6f4d161a..91e51b8066 100644 --- a/code/modules/modular_computers/computers/machinery/modular_computer.dm +++ b/code/modules/modular_computers/computers/machinery/modular_computer.dm @@ -146,3 +146,7 @@ /obj/machinery/modular_computer/bullet_act(obj/item/projectile/Proj) if(cpu) cpu.bullet_act(Proj) + +/obj/machinery/modular_computer/screwdriver_act(mob/user, obj/item/tool) + if(cpu) + return cpu.screwdriver_act(user, tool) 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 4f1c488b9e..a4408346e5 100644 --- a/code/modules/modular_computers/file_system/programs/antagonist/revelation.dm +++ b/code/modules/modular_computers/file_system/programs/antagonist/revelation.dm @@ -17,6 +17,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 13caab27ef..938d8a17de 100644 --- a/code/modules/modular_computers/file_system/programs/borg_monitor.dm +++ b/code/modules/modular_computers/file_system/programs/borg_monitor.dm @@ -54,10 +54,14 @@ var/ID = checkID() if(!ID) return + if(R.stat == DEAD) //Dead borgs will listen to you no longer + to_chat(usr, "Error -- Could not open a connection to unit:[R]") var/message = stripped_input(usr, message = "Enter message to be sent to remote cyborg.", title = "Send Message") if(!message) return to_chat(R, "

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

Message from [ID] to [R] -- \"[message]\"
") diff --git a/code/modules/modular_computers/file_system/programs/robotact.dm b/code/modules/modular_computers/file_system/programs/robotact.dm new file mode 100644 index 0000000000..066c9a6744 --- /dev/null +++ b/code/modules/modular_computers/file_system/programs/robotact.dm @@ -0,0 +1,147 @@ +/datum/computer_file/program/robotact + filename = "robotact" + filedesc = "RoboTact" + extended_desc = "A built-in app for cyborg self-management and diagnostics." + ui_header = "robotact.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 = "NtosRobotact" + ///A typed reference to the computer, specifying the borg tablet type + var/obj/item/modular_computer/tablet/integrated/tablet + +/datum/computer_file/program/robotact/Destroy() + tablet = null + return ..() + +/datum/computer_file/program/robotact/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/robotact/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 + data["lampPowerUse"] = clamp(borgo.lamp_enabled ? (borgo.lamp_intensity - 2) : 0 * 2,1,borgo.cell.charge) + + //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.locked_down?"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/robotact/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/robotact/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 = params["ref"] + 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/robotact/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 e5c133de20..63d3162579 100644 --- a/code/modules/modular_computers/hardware/hard_drive.dm +++ b/code/modules/modular_computers/hardware/hard_drive.dm @@ -184,3 +184,9 @@ max_capacity = 32 icon_state = "ssd_micro" w_class = WEIGHT_CLASS_TINY + +// 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/robotact(src)) diff --git a/code/modules/modular_computers/hardware/network_card.dm b/code/modules/modular_computers/hardware/network_card.dm index 04bf494fe4..625ead6ed7 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.locked_down) + 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 13ae6c1f39..2fee94c5f3 100644 --- a/code/modules/modular_computers/hardware/recharger.dm +++ b/code/modules/modular_computers/hardware/recharger.dm @@ -89,3 +89,12 @@ /obj/item/computer_hardware/recharger/lambda/use_power(amount, charging=0) return 1 + +/// 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 diff --git a/code/modules/photography/camera/silicon_camera.dm b/code/modules/photography/camera/silicon_camera.dm index 28a080d5f1..a6b8093f27 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 fa88cc1338..1410dc6221 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 || locked_down) + // 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 || locked_down)) return UI_DISABLED return ..() diff --git a/icons/mob/screen_cyborg.dmi b/icons/mob/screen_cyborg.dmi index 632443036e..3d69a52d32 100644 Binary files a/icons/mob/screen_cyborg.dmi and b/icons/mob/screen_cyborg.dmi differ diff --git a/icons/obj/modular_tablet.dmi b/icons/obj/modular_tablet.dmi index 32edb57475..b5de2701e9 100644 Binary files a/icons/obj/modular_tablet.dmi and b/icons/obj/modular_tablet.dmi differ diff --git a/icons/program_icons/robotact.gif b/icons/program_icons/robotact.gif new file mode 100644 index 0000000000..cf11eb184b Binary files /dev/null and b/icons/program_icons/robotact.gif differ diff --git a/tgstation.dme b/tgstation.dme index f733aee27c..eadf0983b0 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -2821,6 +2821,7 @@ #include "code\modules\modular_computers\computers\item\laptop.dm" #include "code\modules\modular_computers\computers\item\laptop_presets.dm" #include "code\modules\modular_computers\computers\item\processor.dm" +#include "code\modules\modular_computers\computers\item\tablet-silicon.dm" #include "code\modules\modular_computers\computers\item\tablet.dm" #include "code\modules\modular_computers\computers\item\tablet_presets.dm" #include "code\modules\modular_computers\computers\machinery\console_presets.dm" @@ -2847,6 +2848,7 @@ #include "code\modules\modular_computers\file_system\programs\powermonitor.dm" #include "code\modules\modular_computers\file_system\programs\radar.dm" #include "code\modules\modular_computers\file_system\programs\robocontrol.dm" +#include "code\modules\modular_computers\file_system\programs\robotact.dm" #include "code\modules\modular_computers\file_system\programs\sm_monitor.dm" #include "code\modules\modular_computers\file_system\programs\antagonist\contract_uplink.dm" #include "code\modules\modular_computers\file_system\programs\antagonist\dos.dm"