/obj/machinery/computer/robotics name = "robotics control console" desc = "Used to remotely lockdown or detonate linked Cyborgs." icon = 'icons/obj/computer.dmi' icon_keyboard = "tech_key" icon_screen = "robot" req_access = list(ACCESS_RD) circuit = /obj/item/circuitboard/robotics var/temp = null light_color = LIGHT_COLOR_PURPLE var/safety = 1 STATIC_COOLDOWN_DECLARE(detonate_cooldown) /obj/machinery/computer/robotics/attack_ai(mob/user as mob) return attack_hand(user) /obj/machinery/computer/robotics/attack_hand(mob/user as mob) if(..()) return if(stat & (NOPOWER|BROKEN)) return ui_interact(user) /obj/machinery/computer/robotics/proc/is_authenticated(mob/user) if(!istype(user)) return FALSE if(user.can_admin_interact()) return TRUE if(allowed(user)) return TRUE return FALSE /** * Does this borg show up in the console * * Returns TRUE if a robot will show up in the console * Returns FALSE if a robot will not show up in the console * Arguments: * * R - The [mob/living/silicon/robot] to be checked */ /obj/machinery/computer/robotics/proc/console_shows(mob/living/silicon/robot/R) if(!istype(R)) return FALSE if(isdrone(R)) return FALSE if(R.scrambledcodes) return FALSE if(!atoms_share_level(get_turf(src), get_turf(R))) return FALSE return TRUE /** * Check if a user can send a lockdown/detonate command to a specific borg * * Returns TRUE if a user can send the command (does not guarantee it will work) * Returns FALSE if a user cannot * Arguments: * * user - The [mob/user] to be checked * * R - The [mob/living/silicon/robot] to be checked * * telluserwhy - Bool of whether the user should be sent a to_chat message if they don't have access */ /obj/machinery/computer/robotics/proc/can_control(mob/user, mob/living/silicon/robot/R, telluserwhy = FALSE) if(!istype(user)) return FALSE if(!console_shows(R)) return FALSE if(isAI(user)) if(R.connected_ai != user) if(telluserwhy) to_chat(user, "AIs can only control cyborgs which are linked to them.") return FALSE if(isrobot(user)) if(R != user) if(telluserwhy) to_chat(user, "Cyborgs cannot control other cyborgs.") return FALSE return TRUE /// Checks if a user can detonate any cyborgs at all. /obj/machinery/computer/robotics/proc/can_detonate_any(mob/user, telluserwhy = FALSE) if(ispulsedemon(user)) if(telluserwhy) to_chat(user, "The console's authentication circuits reject your control!") return FALSE return TRUE /// Checks if a user can detonate a specific cyborg, does a can_control check first. /obj/machinery/computer/robotics/proc/can_detonate(mob/user, mob/living/silicon/robot/R, telluserwhy = FALSE) if(!can_control(user, R, telluserwhy)) return FALSE if(!can_detonate_any(user, telluserwhy)) return FALSE return TRUE /** * Check if the user is the right kind of entity to be able to hack borgs * * Returns TRUE if a user is a traitor AI, or aghost * Returns FALSE otherwise * Arguments: * * user - The [mob/user] to be checked */ /obj/machinery/computer/robotics/proc/can_hack_any(mob/user) if(!istype(user)) return FALSE if(user.can_admin_interact()) return TRUE if(!isAI(user)) return FALSE return (user.mind.special_role && user.mind.is_original_mob(user)) /** * Check if the user is allowed to hack a specific borg * * Returns TRUE if a user can hack the specific cyborg * Returns FALSE if a user cannot * Arguments: * * user - The [mob/user] to be checked * * R - The [mob/living/silicon/robot] to be checked */ /obj/machinery/computer/robotics/proc/can_hack(mob/user, mob/living/silicon/robot/R) if(!can_hack_any(user)) return FALSE if(!istype(R)) return FALSE if(R.emagged) return FALSE if(R.connected_ai != user) return FALSE return TRUE /obj/machinery/computer/robotics/ui_state(mob/user) return GLOB.default_state /obj/machinery/computer/robotics/ui_interact(mob/user, datum/tgui/ui = null) ui = SStgui.try_update_ui(user, src, ui) if(!ui) ui = new(user, src, "RoboticsControlConsole", name) ui.open() /obj/machinery/computer/robotics/ui_data(mob/user) var/list/data = list() data["auth"] = is_authenticated(user) data["can_hack"] = can_hack_any(user) data["cyborgs"] = list() data["safety"] = safety data["detonate_cooldown"] = round(COOLDOWN_TIMELEFT(src, detonate_cooldown) / 10) for(var/mob/living/silicon/robot/R in GLOB.mob_list) if(!console_shows(R)) continue var/area/A = get_area(R) var/turf/T = get_turf(R) var/list/cyborg_data = list( name = R.name, uid = R.UID(), locked_down = R.lockcharge, locstring = "[A.name] ([T.x], [T.y])", status = R.stat, health = round(R.health * 100 / R.maxHealth, 0.1), charge = R.cell ? round(R.cell.percent()) : null, cell_capacity = R.cell ? R.cell.maxcharge : null, module = R.module ? R.module.name : "No Module Detected", synchronization = R.connected_ai, is_hacked = R.connected_ai && R.emagged && can_hack_any(user), hackable = can_hack(user, R), ) data["cyborgs"] += list(cyborg_data) data["show_lock_all"] = (data["auth"] && length(data["cyborgs"]) > 0 && ishuman(user)) return data /obj/machinery/computer/robotics/ui_act(action, params) if(..()) return . = FALSE if(!is_authenticated(usr)) to_chat(usr, "Access denied.") return if(SSticker.current_state == GAME_STATE_FINISHED) to_chat(usr, "Access denied, borgs are no longer your station's property.") return switch(action) if("arm") // Arms the muli-lock system if(issilicon(usr)) to_chat(usr, "Access Denied (silicon detected)") return safety = !safety to_chat(usr, "You [safety ? "disarm" : "arm"] the emergency lockdown system.") . = TRUE if("masslock") // Locks down all accessible cyborgs if safety is disabled if(issilicon(usr)) to_chat(usr, "Access Denied (silicon detected)") return if(!can_detonate_any(usr, TRUE)) // Uses the same permissions as detonate. return if(safety) to_chat(usr, "Emergency lockdown aborted - safety active") return message_admins("[key_name_admin(usr)] locked all cyborgs!") log_game("\[key_name(usr)] locked all cyborgs!") for(var/mob/living/silicon/robot/R in GLOB.mob_list) if(isdrone(R)) continue // Ignore antagonistic cyborgs if(R.scrambledcodes) continue to_chat(R, "Emergency lockdown received.") if(R.connected_ai) to_chat(R.connected_ai, "

ALERT - Cyborg lockdown detected: [R.name]
") R.SetLockdown(!R.lockcharge) . = TRUE if("killbot") // destroys one specific cyborg if(!COOLDOWN_FINISHED(src, detonate_cooldown)) to_chat(usr, "Detonation Safety Cooldown Active. Please Stand By!") return var/mob/living/silicon/robot/R = locateUID(params["uid"]) if(!can_detonate(usr, R, TRUE)) return if(R.mind && R.mind.special_role && R.emagged) to_chat(R, "Extreme danger! Termination codes detected. Scrambling security codes and automatic AI unlink triggered.") R.ResetSecurityCodes() . = TRUE return var/turf/T = get_turf(R) message_admins("[key_name_admin(usr)] detonated [key_name_admin(R)] ([ADMIN_COORDJMP(T)])!") log_game("\[key_name(usr)] detonated [key_name(R)]!") to_chat(R, "Self-destruct command received.") if(R.connected_ai) to_chat(R.connected_ai, "

ALERT - Cyborg detonation detected: [R.name]
") R.self_destruct() COOLDOWN_START(src, detonate_cooldown, 60 SECONDS) . = TRUE if("stopbot") // lock or unlock the borg if(isrobot(usr)) to_chat(usr, "Access Denied.") return var/mob/living/silicon/robot/R = locateUID(params["uid"]) if(!can_control(usr, R, TRUE)) return message_admins("[ADMIN_LOOKUPFLW(usr)] [!R.lockcharge ? "locked down" : "released"] [ADMIN_LOOKUPFLW(R)]!") log_game("[key_name(usr)] [!R.lockcharge ? "locked down" : "released"] [key_name(R)]!") R.SetLockdown(!R.lockcharge) to_chat(R, "[!R.lockcharge ? "Your lockdown has been lifted!" : "You have been locked down!"]") if(R.connected_ai) to_chat(R.connected_ai, "[!R.lockcharge ? "NOTICE - Cyborg lockdown lifted" : "ALERT - Cyborg lockdown detected"]: [R.name]
") . = TRUE if("hackbot") // AIs hacking/emagging a borg var/mob/living/silicon/robot/R = locateUID(params["uid"]) if(!can_hack(usr, R)) return var/choice = alert(usr, "Really hack [R.name]? This cannot be undone.", "Do you want to hack this borg?", "Yes", "No") if(choice != "Yes") return log_game("[key_name(usr)] emagged [key_name(R)] using robotic console!") message_admins("[key_name_admin(usr)] emagged [key_name_admin(R)] using robotic console!") R.emagged = TRUE R.module.emag_act(usr) R.module.module_type = "Malf" R.update_module_icon() R.module.rebuild_modules() to_chat(R, "Failsafe protocols overridden. New tools available.") . = TRUE