[MIRROR] Adds borg tablets, and RoboTact as a borg self-management app (#959)

* tgui: Borg tablets and RoboTact as a borg self-management app (#53373)

* Automatic changelog compile [ci skip]

* Automatic changelog compile [ci skip]

* Automatic changelog compile [ci skip]

* Automatic changelog compile [ci skip]

* Automatic changelog compile [ci skip]

* Automatic changelog compile [ci skip]

* Automatic changelog compile [ci skip]

* Automatic changelog compile [ci skip]

* Automatic changelog compile [ci skip]

* Automatic changelog compile [ci skip]

* Automatic changelog compile [ci skip]

* Automatic changelog compile [ci skip]

* Automatic changelog compile [ci skip]

* Automatic changelog compile [ci skip]

* Automatic changelog compile [ci skip]

* Automatic changelog compile [ci skip]

* Automatic changelog compile [ci skip]

* Automatic changelog compile [ci skip]

* Automatic changelog compile [ci skip]

* Automatic changelog compile [ci skip]

* Automatic changelog compile [ci skip]

* Automatic changelog compile [ci skip]

* Automatic changelog compile [ci skip]

* Automatic changelog compile [ci skip]

* Automatic changelog compile [ci skip]

* Automatic changelog compile [ci skip]

* Automatic changelog compile [ci skip]

* Automatic changelog compile [ci skip]

* Automatic changelog compile [ci skip]

* Automatic changelog compile [ci skip]

* Automatic changelog compile [ci skip]

* Automatic changelog compile [ci skip]

* Automatic changelog compile [ci skip]

* Automatic changelog compile [ci skip]

* Automatic changelog compile [ci skip]

* Automatic changelog compile [ci skip]

* Automatic changelog compile [ci skip]

* Automatic changelog compile [ci skip]

* Automatic changelog compile [ci skip]

* Automatic changelog compile [ci skip]

* Automatic changelog compile [ci skip]

* Automatic changelog compile [ci skip]

* Automatic changelog compile [ci skip]

* Automatic changelog compile [ci skip]

* Automatic changelog compile [ci skip]

* Automatic changelog compile [ci skip]

* Automatic changelog compile [ci skip]

* Automatic changelog compile [ci skip]

* Automatic changelog compile [ci skip]

* Automatic changelog compile [ci skip]

* Automatic changelog compile [ci skip]

* Automatic changelog compile [ci skip]

* Automatic changelog compile [ci skip]

* Automatic changelog compile [ci skip]

* Automatic changelog compile [ci skip]

* Automatic changelog compile [ci skip]

* Automatic changelog compile [ci skip]

* Automatic changelog compile [ci skip]

* Automatic changelog compile [ci skip]

* Automatic changelog compile [ci skip]

* Automatic changelog compile [ci skip]

* Automatic changelog compile [ci skip]

* Automatic changelog compile [ci skip]

* Automatic changelog compile [ci skip]

* Automatic changelog compile [ci skip]

* Automatic changelog compile [ci skip]

* Automatic changelog compile [ci skip]

* Automatic changelog compile [ci skip]

* Automatic changelog compile [ci skip]

* Automatic changelog compile [ci skip]

* Automatic changelog compile [ci skip]

* Automatic changelog compile [ci skip]

* Automatic changelog compile [ci skip]

* Automatic changelog compile [ci skip]

* Automatic changelog compile [ci skip]

* Automatic changelog compile [ci skip]

* Automatic changelog compile [ci skip]

* Automatic changelog compile [ci skip]

* Automatic changelog compile [ci skip]

* Automatic changelog compile [ci skip]

* Automatic changelog compile [ci skip]

* Automatic changelog compile [ci skip]

* Automatic changelog compile [ci skip]

* Automatic changelog compile [ci skip]

* Automatic changelog compile [ci skip]

* Automatic changelog compile [ci skip]

* take one

* Automatic changelog compile [ci skip]

* Automatic changelog compile [ci skip]

* Automatic changelog compile [ci skip]

* part 2

* Automatic changelog compile [ci skip]

* part 3

* Automatic changelog compile [ci skip]

* Automatic changelog compile [ci skip]

* Automatic changelog compile [ci skip]

* part three I think

* Automatic changelog compile [ci skip]

* Automatic changelog compile [ci skip]

* Automatic changelog compile [ci skip]

* Automatic changelog compile [ci skip]

* who even knows what step I'm on anymore

* and another one

* Automatic changelog compile [ci skip]

* PR time

* Automatic changelog compile [ci skip]

* Automatic changelog compile [ci skip]

* Automatic changelog compile [ci skip]

* Forward Progress

* Automatic changelog compile [ci skip]

* Merge remote-tracking branch 'upstream/master' into SiliScreen

* more foward progress

* RoboTact

* bad tab

* Improvements all around

Plus some changes to vis_overlay to allow for color

* Lights and things

* borg network card

borg tablets lose networking when borg is locked or has no power

* hud changes

not done yet, but getting close

* better modPC screen button

* updoot

* more useful colors

* linter

* Do I finally get a green ✓

* better law sync catching

* hate linters

Co-authored-by: Changelogs <action@ github.com>

* Adds borg tablets, and RoboTact as a borg self-management app

Co-authored-by: zxaber <37497534+zxaber@users.noreply.github.com>
Co-authored-by: Changelogs <action@ github.com>
This commit is contained in:
SkyratBot
2020-09-24 20:29:31 +02:00
committed by GitHub
co-authored by Changelogs zxaber
parent 948d4886ee
commit fd11d35489
38 changed files with 887 additions and 216 deletions
+1 -1
View File
@@ -14,7 +14,7 @@
if(check_click_intercept(params,A))
return
if(stat || lockcharge || IsParalyzed() || IsStun())
if(stat || (lockcharge) || IsParalyzed() || IsStun())
return
var/list/modifiers = params2list(params)
+3 -4
View File
@@ -105,16 +105,15 @@
#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_sensor "CENTER-3:16, SOUTH:5"
#define ui_borg_lamp "CENTER-4:16, SOUTH:5"
#define ui_borg_thrusters "CENTER-5:16, SOUTH:5"
#define ui_borg_lamp "CENTER-3:16, SOUTH:5"
#define ui_borg_tablet "CENTER-4:16, SOUTH:5"
#define ui_inv1 "CENTER-2:16,SOUTH:5"
#define ui_inv2 "CENTER-1 :16,SOUTH:5"
#define ui_inv3 "CENTER :16,SOUTH:5"
#define ui_borg_module "CENTER+1:16,SOUTH:5"
#define ui_borg_store "CENTER+2:16,SOUTH:5"
#define ui_borg_camera "CENTER+3:21,SOUTH:5"
#define ui_borg_album "CENTER+4:21,SOUTH:5"
#define ui_borg_alerts "CENTER+4:21,SOUTH:5"
#define ui_borg_language_menu "CENTER+4:21,SOUTH+1:5"
//Aliens
-3
View File
@@ -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"
+60 -39
View File
@@ -68,26 +68,6 @@
var/mob/living/silicon/robot/R = usr
R.uneq_active()
/obj/screen/robot/lamp
name = "headlamp"
icon_state = "lamp0"
/obj/screen/robot/lamp/Click()
if(..())
return
var/mob/living/silicon/robot/R = usr
R.control_headlamp()
/obj/screen/robot/thrusters
name = "ion thrusters"
icon_state = "ionpulse0"
/obj/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'
@@ -131,37 +111,37 @@
//End of module select
using = new /obj/screen/robot/lamp()
using.screen_loc = ui_borg_lamp
using.hud = src
static_inventory += using
robit.lampButton = using
var/obj/screen/robot/lamp/lampscreen = using
lampscreen.robot = robit
//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
robit.interfaceButton = using
if(robit.modularInterface)
using.vis_contents += robit.modularInterface
var/obj/screen/robot/modPC/tabletbutton = using
tabletbutton.robot = robit
//Sec/Med HUDs
using = new /obj/screen/ai/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
robit.lamp_button = using
//Thrusters
using = new /obj/screen/robot/thrusters()
using.screen_loc = ui_borg_thrusters
using.hud = src
static_inventory += using
robit.thruster_button = using
//Intent
action_intent = new /obj/screen/act_intent/robot()
action_intent.icon_state = mymob.a_intent
@@ -289,3 +269,44 @@
else
for(var/obj/item/I in R.held_items)
screenmob.client.screen -= I
/obj/screen/robot/lamp
name = "headlamp"
icon_state = "lamp_off"
var/mob/living/silicon/robot/robot
/obj/screen/robot/lamp/Click()
. = ..()
if(.)
return
robot?.toggle_headlamp()
update_icon()
/obj/screen/robot/lamp/update_icon()
if(robot?.lamp_enabled)
icon_state = "lamp_on"
else
icon_state = "lamp_off"
/obj/screen/robot/modPC
name = "Modular Interface"
icon_state = "template"
var/mob/living/silicon/robot/robot
/obj/screen/robot/modPC/Click()
. = ..()
if(.)
return
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()
+4
View File
@@ -69,19 +69,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("<span class='notice'>[R]'s camera lens focuses loudly.</span>", "<span class='notice'>Your camera lens focuses loudly.</span>")
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()
+1
View File
@@ -108,6 +108,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
@@ -70,6 +70,8 @@
playsound(loc, 'sound/voice/liveagain.ogg', 75, TRUE)
R.revive(full_heal = FALSE, admin_revive = FALSE)
R.logevent("WARN -- System recovered from unexpected shutdown.")
R.logevent("System brought online.")
/obj/item/borg/upgrade/disablercooler
name = "cyborg rapid disabler cooling module"
@@ -114,6 +116,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)
. = ..()
@@ -271,6 +274,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
+2 -1
View File
@@ -48,7 +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'
"borg_mon.gif" = 'icons/program_icons/borg_mon.gif',
"robotact.gif" = 'icons/program_icons/robotact.gif'
)
/datum/asset/simple/radar_assets
@@ -195,13 +195,12 @@
if(isethereal(L))
AM.emp_act(EMP_LIGHT)
else if(iscyborg(L))
var/mob/living/silicon/robot/borg = L
if(borg.lamp_intensity)
borg.update_headlamp(TRUE, INFINITY)
to_chat(borg, "<span class='danger'>Your headlamp is fried! You'll need a human to help replace it.</span>")
else if(ishuman(L))
var/mob/living/carbon/human/H = L
else if(iscyborg(AM))
var/mob/living/silicon/robot/borg = AM
if(borg.lamp_enabled)
borg.smash_headlamp()
else if(ishuman(AM))
var/mob/living/carbon/human/H = AM
for(var/obj/item/O in H.get_all_gear()) //less expensive than getallcontents
light_item_check(O, H)
else
@@ -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
@@ -48,15 +48,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("<span class='notice'>The power warning light on <span class='name'>[src]</span> flashes urgently.</span>", \
"You announce you are operating in low power mode.")
playsound(loc, 'sound/machines/buzz-two.ogg', 50, FALSE)
else
to_chat(src, "<span class='warning'>You can only use this emote when you're out of charge.</span>")
@@ -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
@@ -78,4 +70,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
@@ -11,7 +11,6 @@
if(low_power_mode)
if(cell && cell.charge)
low_power_mode = FALSE
update_headlamp()
else if(stat == CONSCIOUS)
use_power()
@@ -19,12 +18,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),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 = TRUE
update_headlamp()
toggle_headlamp(TRUE)
diag_hud_set_borgcell()
/mob/living/silicon/robot/proc/handle_robot_hud_updates()
+99 -76
View File
@@ -33,8 +33,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
@@ -80,13 +78,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/obj/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/obj/screen/robot/modPC/interfaceButton
var/list/upgrades = list()
var/hasExpanded = FALSE
@@ -165,6 +174,14 @@
aicamera = new/obj/item/camera/siliconcam/robot_camera(src)
toner = tonermax
diag_hud_set_borgcell()
create_modularInterface()
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()
@@ -184,6 +201,8 @@
ghostize()
stack_trace("Borg MMI lacked a brainmob")
mmi = null
if(modularInterface)
QDEL_NULL(modularInterface)
if(connected_ai)
set_connected_ai(null)
if(shell)
@@ -255,14 +274,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, "<span class='userdanger'>Alert: You are dead.</span>")
return //won't work if dead
robot_alerts()
/mob/living/silicon/robot/proc/robot_alerts()
var/dat = ""
for (var/cat in alarms)
@@ -309,8 +320,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()
. = ..()
@@ -379,19 +388,6 @@
return FALSE
return ISINRANGE(T1.x, T0.x - interaction_range, T0.x + interaction_range) && ISINRANGE(T1.y, T0.y - interaction_range, T0.y + interaction_range)
/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, "<span class='notice'>You unlock your cover.</span>")
/mob/living/silicon/robot/proc/allowed(mob/M)
//check if it doesn't require any access at all
if(check_access(null))
@@ -431,14 +427,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 && !(HAS_TRAIT(src, TRAIT_KNOCKEDOUT) || 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 = 19 //glowy eyes
else
eye_lights.icon_state = "[module.special_light_key ? "[module.special_light_key]":"[module.cyborg_base_icon]"]_e"
eye_lights.color = COLOR_WHITE
eye_lights.plane = -1
eye_lights.icon = icon
add_overlay(eye_lights)
@@ -511,6 +512,7 @@
else if(.)
REMOVE_TRAIT(src, TRAIT_IMMOBILIZED, LOCKED_BORG_TRAIT)
update_mobility()
logevent("System lockdown [lockcharge?"triggered":"released"].")
/mob/living/silicon/robot/proc/SetEmagged(new_state)
@@ -522,56 +524,49 @@
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, "<span class='danger'>This function is currently offline.</span>")
/**
* 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, "<span class='danger'>Your headlamp is broken! You'll need a human to help replace it.</span>")
if(lamp_intensity == 0) //We'll skip intensity of 2, since every mob already has such a see-darkness range, so no much need for it.
lamp_intensity = 4
else //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, "<span class='notice'>[lamp_intensity > 2 ? "Headlamp power set to Level [lamp_intensity * 0.5]" : "Headlamp disabled"].</span>")
update_headlamp()
/mob/living/silicon/robot/proc/update_headlamp(turn_off = FALSE, cooldown = 10 SECONDS)
if(lamp_intensity > 2)
if(turn_off || stat || low_power_mode)
to_chat(src, "<span class='danger'>Your headlamp has been deactivated.</span>")
lamp_intensity = 0
lamp_cooldown = cooldown == BORG_LAMP_CD_RESET ? 0 : max(world.time + cooldown, lamp_cooldown)
set_light_on(FALSE)
else
set_light_range(lamp_intensity * 0.5)
set_light_on(TRUE)
else
/**
* 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)
if(lamp_button)
lamp_button.icon_state = "lamp[lamp_intensity]"
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()
update_icons()
/mob/living/silicon/robot/proc/deconstruct()
SEND_SIGNAL(src, COMSIG_BORG_SAFE_DECONSTRUCT)
var/turf/T = get_turf(src)
@@ -676,6 +671,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)
@@ -805,13 +805,13 @@
if(stat != DEAD)
if(health <= -maxHealth) //die only once
death()
toggle_headlamp(1)
return
if(HAS_TRAIT(src, TRAIT_KNOCKEDOUT) || IsStun() || IsKnockdown() || IsParalyzed())
set_stat(UNCONSCIOUS)
else
set_stat(CONSCIOUS)
update_mobility()
update_headlamp()
diag_hud_set_status()
diag_hud_set_health()
diag_hud_set_aishell()
@@ -821,7 +821,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)
@@ -847,6 +846,7 @@
resize = 0.5
hasExpanded = FALSE
update_transform()
logevent("Chassis configuration has been reset.")
module.transform_to(/obj/item/robot_module)
// Remove upgrades.
@@ -1100,3 +1100,26 @@
old_ai.connected_robots -= src
if(connected_ai)
connected_ai.connected_robots |= src
/**
* 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/robotact/program = modularInterface.get_robotact()
if(program)
program.force_full_update()
@@ -128,6 +128,7 @@ GLOBAL_LIST_INIT(blacklisted_borg_hats, typecacheof(list( //Hats that don't real
to_chat(user, "<span class='notice'>You apply the upgrade to [src].</span>")
to_chat(src, "----------------\nNew hardware detected...Identified as \"<b>[D]</b>\"...Setup complete.\n----------------")
upgrades += B
logevent("Hardware [D] detected and installed successfully.")
qdel(D)
return
@@ -171,6 +172,9 @@ GLOBAL_LIST_INIT(blacklisted_borg_hats, typecacheof(list( //Hats that don't real
update_icons()
if(emagged)
to_chat(user, "<span class='notice'>The cover interface glitches out for a split second.</span>")
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, "<span class='danger'>Access denied.</span>")
return
@@ -190,10 +194,13 @@ GLOBAL_LIST_INIT(blacklisted_borg_hats, typecacheof(list( //Hats that don't real
to_chat(user, "<span class='notice'>You apply the upgrade to [src].</span>")
to_chat(src, "----------------\nNew hardware detected...Identified as \"<b>[U]</b>\"...Setup complete.\n----------------")
if(U.one_use)
logevent("Firmware [U] run successfully.")
qdel(U)
else
U.forceMove(src)
upgrades += U
logevent("Hardware [U] installed successfully.")
else
to_chat(user, "<span class='danger'>Upgrade error.</span>")
U.forceMove(drop_location())
@@ -214,17 +221,25 @@ GLOBAL_LIST_INIT(blacklisted_borg_hats, typecacheof(list( //Hats that don't real
if(!opened)
to_chat(user, "<span class='warning'>You need to open the panel to repair the headlamp!</span>")
return
if(lamp_cooldown <= world.time)
if(lamp_functional)
to_chat(user, "<span class='warning'>The headlamp is already functional!</span>")
return
if(!user.temporarilyRemoveItemFromInventory(W))
to_chat(user, "<span class='warning'>[W] seems to be stuck to your hand. You'll have to find a different light.</span>")
return
lamp_cooldown = 0
lamp_functional = TRUE
qdel(W)
to_chat(user, "<span class='notice'>You replace the headlamp bulbs.</span>")
return
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
if(W.force && W.damtype != STAMINA && stat != DEAD) //only sparks if real damage is dealt.
spark_system.start()
return ..()
@@ -328,6 +343,7 @@ GLOBAL_LIST_INIT(blacklisted_borg_hats, typecacheof(list( //Hats that don't real
if(connected_ai && connected_ai.mind && connected_ai.mind.has_antag_datum(/datum/antagonist/traitor))
to_chat(src, "<span class='danger'>ALERT: Foreign software execution prevented.</span>")
logevent("ALERT: Foreign software execution prevented.")
to_chat(connected_ai, "<span class='danger'>ALERT: Cyborg unit \[[src]] successfully defended against subversion.</span>")
log_game("[key_name(user)] attempted to emag cyborg [key_name(src)], but they were slaved to traitor AI [connected_ai].")
return
@@ -350,10 +366,12 @@ GLOBAL_LIST_INIT(blacklisted_borg_hats, typecacheof(list( //Hats that don't real
else
GLOB.lawchanges.Add("[time] <B>:</B> [name]([key]) emagged by external event.")
to_chat(src, "<span class='danger'>ALERT: Foreign software detected.</span>")
logevent("ALERT: Foreign software detected.")
sleep(5)
to_chat(src, "<span class='danger'>Initiating diagnostics...</span>")
sleep(20)
to_chat(src, "<span class='danger'>SynBorg v1.7 loaded.</span>")
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, "<span class='danger'>LAW SYNCHRONISATION ERROR</span>")
sleep(5)
@@ -223,6 +223,7 @@
R.notransform = TRUE
R.SetLockdown(1)
R.set_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, TRUE, -1)
@@ -233,7 +234,7 @@
R.set_anchored(FALSE)
R.notransform = FALSE
R.updatehealth()
R.update_headlamp(FALSE, BORG_LAMP_CD_RESET)
R.update_icons()
R.notify_ai(NEW_MODULE)
if(R.hud_used)
R.hud_used.update_robot_modules_display()
@@ -346,6 +346,31 @@
enabled = 0
update_icon()
/obj/item/modular_computer/screwdriver_act(mob/user, obj/item/tool)
if(!all_components.len)
to_chat(user, "<span class='warning'>This device doesn't have any components installed.</span>")
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
uninstall_component(H, user)
return
/obj/item/modular_computer/attackby(obj/item/W as obj, mob/user as mob)
// Check for ID first
@@ -387,31 +412,6 @@
to_chat(user, "<span class='notice'>You repair \the [src].</span>")
return
if(W.tool_behaviour == TOOL_SCREWDRIVER)
if(!all_components.len)
to_chat(user, "<span class='warning'>This device doesn't have any components installed.</span>")
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
uninstall_component(H, user)
return
..()
// Used by processor to relay qdel() to machinery type.
@@ -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
@@ -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)
@@ -48,3 +49,102 @@
return FALSE
to_chat(user, "<span class='notice'>You swipe \the [src]. It's screen briefly shows a message reading \"MEMORY CODE INJECTION DETECTED AND SUCCESSFULLY QUARANTINED\".</span>")
return FALSE
/// 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, "<span class='warning'>That color is too dark! Choose a lighter one.</span>")
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
@@ -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)
@@ -111,6 +111,10 @@
return
. = ..()
/obj/machinery/modular_computer/screwdriver_act(mob/user, obj/item/tool)
if(cpu)
return cpu.screwdriver_act(user, tool)
/obj/machinery/modular_computer/attackby(obj/item/W as obj, mob/user)
if(cpu && !(flags_1 & NODECONSTRUCT_1))
return cpu.attackby(W, user)
@@ -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,"<span class='userdanger'>SYSTEM PURGE DETECTED/</span>")
addtimer(CALLBACK(modularInterface.borgo, /mob/living/silicon/robot/.proc/death), 2 SECONDS, TIMER_UNIQUE)
return
computer.visible_message("<span class='notice'>\The [computer]'s screen brightly flashes and loud electrical buzzing is heard.</span>")
computer.enabled = FALSE
computer.update_icon()
@@ -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, "<span class='warn'>Error -- Could not open a connection to unit:[R]</span>")
var/message = stripped_input(usr, message = "Enter message to be sent to remote cyborg.", title = "Send Message")
if(!message)
return
to_chat(R, "<br><br><span class='notice'>Message from [ID] -- \"[message]\"</span><br>")
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, "<br><br><span class='notice'>Message from [ID] to [R] -- \"[message]\"</span><br>")
@@ -0,0 +1,143 @@
/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, "<span class='warning'>A warning flashes across \the [computer]: Device Incompatible.</span>")
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.aicamera.stored.len //Number of pictures taken
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/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("<span class='notice'>The power warning light on <span class='name'>[borgo]</span> flashes urgently.</span>", \
"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")
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()
@@ -159,6 +159,13 @@
w_class = WEIGHT_CLASS_TINY
custom_price = 150
// 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))
// 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."
@@ -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 ..()
@@ -76,6 +76,14 @@
return TRUE
return FALSE
/// 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.
@@ -88,3 +96,4 @@
/obj/item/computer_hardware/recharger/lambda/use_power(amount, charging=0)
return 1
@@ -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)
+3 -2
View File
@@ -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 ..()
Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.4 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 B

+1
View File
@@ -2559,6 +2559,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"
+22 -19
View File
@@ -29,6 +29,7 @@ export const NtosMain = (props, context) => {
light_on,
comp_light_color,
removable_media = [],
cardholder,
login = [],
} = data;
return (
@@ -58,25 +59,27 @@ export const NtosMain = (props, context) => {
</Button>
</Section>
)}
<Section
title="User Login"
buttons={(
<Button
icon="eject"
content="Eject ID"
disabled={!login.IDName}
onClick={() => act('PC_Eject_Disk', { name: "ID" })}
/>
)}>
<Table>
<Table.Row>
ID Name: {login.IDName}
</Table.Row>
<Table.Row>
Assignment: {login.IDJob}
</Table.Row>
</Table>
</Section>
{!!cardholder && (
<Section
title="User Login"
buttons={(
<Button
icon="eject"
content="Eject ID"
disabled={!login.IDName}
onClick={() => act('PC_Eject_Disk', { name: "ID" })}
/>
)}>
<Table>
<Table.Row>
ID Name: {login.IDName}
</Table.Row>
<Table.Row>
Assignment: {login.IDJob}
</Table.Row>
</Table>
</Section>
)}
{!!removable_media.length && (
<Section title="Media Eject">
<Table>
@@ -0,0 +1,313 @@
import { classes } from 'common/react';
import { resolveAsset } from '../assets';
import { useBackend, useSharedState } from '../backend';
import { AnimatedNumber, Box, Button, Flex, Fragment, Icon, NoticeBox, Section, Slider, ProgressBar, LabeledList, Table, Tabs } from '../components';
import { NtosWindow } from '../layouts';
export const NtosRobotact = (props, context) => {
const { act, data } = useBackend(context);
const { PC_device_theme } = data;
return (
<NtosWindow
width={800}
height={600}
theme={PC_device_theme}>
<NtosWindow.Content>
<NtosRobotactContent />
</NtosWindow.Content>
</NtosWindow>
);
};
export const NtosRobotactContent = (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 (
<Flex
direction={"column"}>
<Flex.Item
position="relative"
mb={1}>
<Tabs>
<Tabs.Tab
icon="list"
lineHeight="23px"
selected={tab_main === 1}
onClick={() => setTab_main(1)}>
Status
</Tabs.Tab>
<Tabs.Tab
icon="list"
lineHeight="23px"
selected={tab_main === 2}
onClick={() => setTab_main(2)}>
Logs
</Tabs.Tab>
</Tabs>
</Flex.Item>
{tab_main === 1 && (
<Fragment>
<Flex
direction={"row"}>
<Flex.Item
width="30%">
<Section
title="Configuration"
fill>
<LabeledList>
<LabeledList.Item
label="Unit">
{borgName.slice(0, 17)}
</LabeledList.Item>
<LabeledList.Item
label="Type">
{borgType}
</LabeledList.Item>
<LabeledList.Item
label="AI">
{masterAI.slice(0, 17)}
</LabeledList.Item>
</LabeledList>
</Section>
</Flex.Item>
<Flex.Item
grow={1}
ml={1}>
<Section
title="Status">
Charge:
<Button
content="Power Alert"
disabled={charge}
onClick={() => act('alertPower')} />
<ProgressBar
value={charge / maxcharge}
ranges={{
good: [0.5, Infinity],
average: [0.1, 0.5],
bad: [-Infinity, 0.1],
}}>
<AnimatedNumber value={charge} />
</ProgressBar>
Chassis Integrity:
<ProgressBar
value={integrity}
minValue={0}
maxValue={100}
ranges={{
bad: [-Infinity, 25],
average: [25, 75],
good: [75, Infinity],
}} />
</Section>
<Section
title="Lamp Power">
<Slider
value={lampIntensity}
step={1}
stepPixelSize={25}
maxValue={5}
minValue={1}
onChange={(e, value) => act('lampIntensity', {
ref: value,
})} />
Lamp power usage: {lampIntensity/2} watts
</Section>
</Flex.Item>
<Flex.Item
width="50%"
ml={1}>
<Section
fitted>
<Tabs
fluid={1}
textAlign="center">
<Tabs.Tab
icon=""
lineHeight="23px"
selected={tab_sub === 1}
onClick={() => setTab_sub(1)}>
Actions
</Tabs.Tab>
<Tabs.Tab
icon=""
lineHeight="23px"
selected={tab_sub === 2}
onClick={() => setTab_sub(2)}>
Upgrades
</Tabs.Tab>
<Tabs.Tab
icon=""
lineHeight="23px"
selected={tab_sub === 3}
onClick={() => setTab_sub(3)}>
Diagnostics
</Tabs.Tab>
</Tabs>
</Section>
{tab_sub === 1 && (
<Section>
<LabeledList>
<LabeledList.Item
label="Maintenance Cover">
<Button.Confirm
content="Unlock"
disabled={cover==="UNLOCKED"}
onClick={() => act('coverunlock')} />
</LabeledList.Item>
<LabeledList.Item
label="Sensor Overlay">
<Button
content={sensors}
onClick={() => act('toggleSensors')} />
</LabeledList.Item>
<LabeledList.Item
label={"Stored Photos (" + printerPictures + ")"}>
<Button
content="View"
disabled={!printerPictures}
onClick={() => act('viewImage')} />
<Button
content="Print"
disabled={!printerPictures}
onClick={() => act('printImage')} />
</LabeledList.Item>
<LabeledList.Item
label="Printer Toner">
<ProgressBar
value={printerToner / printerTonerMax} />
</LabeledList.Item>
{!!thrustersInstalled && (
<LabeledList.Item
label="Toggle Thrusters">
<Button
content={thrustersStatus}
onClick={() => act('toggleThrusters')} />
</LabeledList.Item>
)}
</LabeledList>
</Section>
)}
{tab_sub === 2 && (
<Section>
{borgUpgrades.map(upgrade => (
<Box
mb={1}
key={upgrade}>
{upgrade}
</Box>
))}
</Section>
)}
{tab_sub === 3 && (
<Section>
<LabeledList>
<LabeledList.Item
label="AI Connection"
color={wireAI==="FAULT"?'red': wireAI==="READY"?'yellow': 'green'}>
{wireAI}
</LabeledList.Item>
<LabeledList.Item
label="LawSync"
color={wireLaw==="FAULT"?"red":"green"}>
{wireLaw}
</LabeledList.Item>
<LabeledList.Item
label="Camera"
color={wireCamera==="FAULT"?'red': wireCamera==="DISABLED"?'yellow': 'green'}>
{wireCamera}
</LabeledList.Item>
<LabeledList.Item
label="Module Controller"
color={wireModule==="FAULT"?"red":"green"}>
{wireModule}
</LabeledList.Item>
<LabeledList.Item
label="Motor Controller"
color={locomotion==="FAULT"?'red': locomotion==="DISABLED"?'yellow': 'green'}>
{locomotion}
</LabeledList.Item>
<LabeledList.Item
label="Maintenance Cover"
color={cover==="UNLOCKED"?"red":"green"}>
{cover}
</LabeledList.Item>
</LabeledList>
</Section>
)}
</Flex.Item>
</Flex>
<Flex.Item
height={21}
mt={1}>
<Section
title="Laws"
fill
scrollable
buttons={(
<Fragment>
<Button
content="State Laws"
onClick={() => act('lawstate')} />
<Button
icon="volume-off"
onClick={() => act('lawchannel')} />
</Fragment>
)}>
{laws.map(law => (
<Box
mb={1}
key={law}>
{law}
</Box>
))}
</Section>
</Flex.Item>
</Fragment>
)}
{tab_main === 2 && (
<Flex.Item>
<Section
backgroundColor="black"
height={40}>
<NtosWindow.Content scrollable>
{borgLog.map(log => (
<Box
mb={1}
key={log}>
<font color="green">{log}</font>
</Box>
))}
</NtosWindow.Content>
</Section>
</Flex.Item>
)}
</Flex>
);
};
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long