mirror of
https://github.com/Aurorastation/Aurora.3.git
synced 2026-07-19 20:06:28 +01:00
Rock the UI Away - Removes VueUI and adds TGUI (#16509)
* tgui the beginning * binaries and the like * Bring in the last of it * Example radio UI * delete example * NTOS Main Menu, start on manifest, tgui states * tasks.json * gunnery ui pt 1 * okay * fix everything * scss update * oops * manifest gigablast * downloader part 1 * download prt 2 * NTOSDownloader final * mfw committing to_worlds * gunnery console pt2 * i cooked * targeting (finished) * one vueui down * voting ui almost done * MY MIND FEELS LIKE AN ARCH ENEMYYYY * voting ui down * photocopier * ntos config + download fixes * photocopier 2 * refactor define * NTOS client manager + fixes * fax machine final (it also uses toner now) * marching forwards... left behind... * ntnrc part 1 * canister * add quotes * portable pumps pt1 + more backgrounds * oops * finish the portable pump * freezers so I'll keep on pushing forward... you haven't seen the last of me... oooooooh... * doors ui pt1 * finish doors UI (forgive me wildkins it's a bit of shitcode) * vitals monitor, make things use labeled lists, new backgrounds * mais j'envoyé aucun mayday... * maglock pt1 * pour ça je me suis perdu... * infrared * fix that * prox sensor pt1 * prox sensor * signaler (this was actually pretty hard) * atmos control pt1 * atmos control pt1.1 * atmos pt 2 * fuel injector * multitool UI * jammer * list viewer * APC * portgen * targeting console updates + SMES ui * new themes, shield generator * supermatter * Add ore detector and (shitty) NTNet Relay * orderterminal pt1 * orderterminal pt2 * smartfridge * Add (air-)tank GUI update ore detector size * Adds Transfer Valves * Add AtmoScrubber * analyzer pt1 * weapons analyzer pt2 * bodyscanner pt1 * bodyscanner pt2 * fix this shitcode * seed storage * appearance changer * appearance changer final * sleeper pt1 * sleeper * gps * vehicles * chem dispenser * lily request * holopad * tgui modules pt1 * ppanel * damage menu * fixes * im here too now * follow menu, search bars * quikpay * quikpay fixes * circuit printer * ppanel * ppanel updates * pai * turret controls (i want to kill myself) * tweak * remove the boardgame * guntracker * implant tracker * penal mechs come close to me, come close to me * chem codex * pai radio * doorjack * pai directives * signaler removal, sensors * ghost spawner * spawnpoint * fixes * teleporter * one more to the chopping block * account database * remove divider * scanner, atmos * latejoin ui pt1 * latejoin * records pt1 * RECORDS UI DONE * delete interpreter & records * CHAT FUCKING CLIENT * data updates * fix some things * final UI, log * basic nanoui fix * antag panel * remove vueui * atm update * vending update * warrants, cameras * ntmonitor * time comes for all * preserve this legacy * bring that back (oops) * rcon, ui auto update for computer UIs, remove rcon computers * alarm monitoring (a bit broke and also todo: add custom alarm monitoring programs to a few consoles) * A LIKE SUPREME * a * power monitor * lights on * fuck this code, fuck nanoui modules, and fuck nanoui * LEAVE IT OH SO FAR BEHIND * fix alarm monitoring for synths * I SAW IN YOU WHAT LIFE WAS MISSING * comms console * idcard and record updates * turn the light on * arcade * pt2 * news browser * static * crusher * f * COULD I JUST SLEIGH THE GOLD FROM THE BALLS? I'M SO FRUSTRATED OH COULD YOU TELL? IF I HEAR ONE MORE VUEUI OR ONE NANOUI I'M GONNA LOSE IT SO LET ME GOOOOOOOOOOOOOOOOO * codeowners & suit sensors * html ui style removal * make lint happy * resist and disorder * i slowly get up and turn off the noise, already fed up... * pleaseeeeeeeeeeeeeee * THE CREDIT LARP IS NECESSARY * i hold the keys * RISE UP * fix that? * harry's suggestions xoxo * runtime fix pt2 * You are the only thing that I still care about * adds build workflow * Update update_tgui.yml * adds some needed steps * ATM * misc fixes and tweaks * fixes 2 * make newscasters usable and fix use power on freezers * turret control is clearer --------- Co-authored-by: John Wildkins <john.wildkins@gmail.com> Co-authored-by: Matt Atlas <liermattia@gmail.com> Co-authored-by: harryob <55142896+harryob@users.noreply.github.com> Co-authored-by: Werner <Arrow768@users.noreply.github.com> Co-authored-by: Geeves <ggrobler447@gmail.com> Co-authored-by: harryob <me@harryob.live>
This commit is contained in:
@@ -195,7 +195,6 @@
|
||||
// Relays kill program request to currently active program. Use this to quit current program.
|
||||
/obj/item/modular_computer/proc/kill_program(var/forced = FALSE)
|
||||
if(active_program && active_program.kill_program(forced))
|
||||
src.vueui_transfer(active_program)
|
||||
active_program = null
|
||||
else
|
||||
return FALSE
|
||||
@@ -217,7 +216,7 @@
|
||||
return ntnet_global.add_log(text, network_card)
|
||||
|
||||
/obj/item/modular_computer/proc/shutdown_computer(var/loud = TRUE)
|
||||
SSvueui.close_uis(active_program)
|
||||
SStgui.close_uis(active_program)
|
||||
kill_program(TRUE)
|
||||
for(var/datum/computer_file/program/P in idle_threads)
|
||||
P.kill_program(TRUE)
|
||||
@@ -231,7 +230,7 @@
|
||||
|
||||
if(loud)
|
||||
visible_message(SPAN_NOTICE("\The [src] shuts down."))
|
||||
SSvueui.close_uis(src)
|
||||
SStgui.close_uis(src)
|
||||
enabled = FALSE
|
||||
if(looping_sound)
|
||||
soundloop.stop(src)
|
||||
@@ -263,8 +262,6 @@
|
||||
|
||||
idle_threads.Add(active_program)
|
||||
active_program.program_state = PROGRAM_STATE_BACKGROUND // Should close any existing UIs
|
||||
SSnanoui.close_uis(active_program.NM ? active_program.NM : active_program)
|
||||
src.vueui_transfer(active_program)
|
||||
active_program = null
|
||||
update_icon()
|
||||
if(istype(user))
|
||||
@@ -291,8 +288,7 @@
|
||||
active_program = P
|
||||
idle_threads.Remove(P)
|
||||
update_icon()
|
||||
if(!P.vueui_transfer(src))
|
||||
SSvueui.close_uis(src)
|
||||
ui_interact(user)
|
||||
return
|
||||
|
||||
if(idle_threads.len >= processor_unit.max_idle_programs+1)
|
||||
@@ -308,19 +304,18 @@
|
||||
|
||||
if(P.run_program(user))
|
||||
active_program = P
|
||||
if(!P.vueui_transfer(src))
|
||||
SSvueui.close_uis(src)
|
||||
ui_interact(user)
|
||||
update_icon()
|
||||
return TRUE
|
||||
|
||||
/obj/item/modular_computer/proc/update_uis()
|
||||
if(active_program) //Should we update program ui or computer ui?
|
||||
SSnanoui.update_uis(active_program)
|
||||
SSvueui.check_uis_for_change(active_program)
|
||||
SStgui.update_uis(src)
|
||||
if(active_program.NM)
|
||||
SSnanoui.update_uis(active_program.NM)
|
||||
else
|
||||
SSvueui.check_uis_for_change(src)
|
||||
SStgui.update_uis(src)
|
||||
SSnanoui.update_uis(src)
|
||||
|
||||
/obj/item/modular_computer/proc/check_update_ui_need()
|
||||
|
||||
@@ -215,7 +215,7 @@
|
||||
if(!hard_drive)
|
||||
to_chat(user, SPAN_WARNING("You cannot reset a device that has no hard drive."))
|
||||
return TRUE
|
||||
enrolled = 0
|
||||
enrolled = DEVICE_UNSET
|
||||
hard_drive.reset_drive()
|
||||
audible_message("[icon2html(src, viewers(get_turf(src)))] <b>[src]</b> pings, <span class='notice'>\"Enrollment status reset! Have a NanoTrasen day.\"</span>")
|
||||
return TRUE
|
||||
|
||||
@@ -1,180 +1,185 @@
|
||||
// Opens VueUI
|
||||
/obj/item/modular_computer/ui_interact(mob/user)
|
||||
var/datum/vueui/ui = SSvueui.get_open_ui(user, src)
|
||||
if(!screen_on || !enabled)
|
||||
if(ui)
|
||||
ui.close()
|
||||
return
|
||||
if(!computer_use_power())
|
||||
if(ui)
|
||||
ui.close()
|
||||
/obj/item/modular_computer/ui_interact(mob/user, datum/tgui/ui)
|
||||
ui = SStgui.try_update_ui(user, src, ui)
|
||||
if(ui && (!screen_on || !enabled || !computer_use_power()))
|
||||
ui.close()
|
||||
return
|
||||
|
||||
// If we have an active program switch to it now.
|
||||
if(active_program)
|
||||
active_program.ui_interact(user)
|
||||
return
|
||||
|
||||
// We are still here, that means there is no program loaded. Load the BIOS/ROM/OS/whatever you want to call it.
|
||||
// This screen simply lists available programs and user may select them.
|
||||
if(!hard_drive || !hard_drive.stored_files || !hard_drive.stored_files.len)
|
||||
audible_message(SPAN_WARNING("\The [src] beeps three times, its screen displaying, \"DISK ERROR!\"."))
|
||||
return // No HDD, No HDD files list or no stored files. Something is very broken.
|
||||
|
||||
if(!ui)
|
||||
ui = new /datum/vueui/modularcomputer(user, src, "mcomputer-system-main", 400, 500, "NTOS Main Menu")
|
||||
ui.header = "modular-computer"
|
||||
ui.open()
|
||||
if(active_program)
|
||||
ui = new(user, src, active_program.tgui_id, active_program.filedesc)
|
||||
ui.autoupdate = active_program.ui_auto_update
|
||||
else
|
||||
ui = new(user, src, "NTOSMain")
|
||||
ui.open()
|
||||
return
|
||||
|
||||
/obj/item/modular_computer/vueui_transfer(oldobj)
|
||||
. = FALSE
|
||||
var/uis = SSvueui.transfer_uis(oldobj, src, "mcomputer-system-main", 400, 500, "NTOS Main Menu")
|
||||
for(var/tui in uis)
|
||||
var/datum/vueui/ui = tui
|
||||
ui.auto_update_content = FALSE
|
||||
. = TRUE
|
||||
var/old_open_ui = ui.interface
|
||||
if(active_program)
|
||||
ui.interface = active_program.tgui_id
|
||||
ui.autoupdate = active_program.ui_auto_update
|
||||
ui.title = active_program.filedesc
|
||||
else
|
||||
ui.interface = "NTOSMain"
|
||||
|
||||
// Gaters data for ui
|
||||
/obj/item/modular_computer/vueui_data_change(var/list/data, var/mob/user, var/datum/vueui/ui)
|
||||
. = ..()
|
||||
data = . || data || list()
|
||||
// Gather data for computer header
|
||||
var/headerdata = get_header_data(data["_PC"])
|
||||
if(headerdata)
|
||||
data["_PC"] = headerdata
|
||||
. = data
|
||||
if(old_open_ui != ui.interface)
|
||||
// We've switched UIs!
|
||||
update_static_data(user, ui)
|
||||
ui.send_assets()
|
||||
|
||||
/obj/item/modular_computer/ui_assets(mob/user)
|
||||
return list(
|
||||
get_asset_datum(/datum/asset/simple/ntos),
|
||||
)
|
||||
|
||||
/obj/item/modular_computer/proc/get_header_data(list/data)
|
||||
LAZYINITLIST(data)
|
||||
data["PC_batteryicon"] = get_battery_icon()
|
||||
data["PC_showbatteryicon"] = !!battery_module
|
||||
data["PC_batterypercent"] = battery_module ? "[round(battery_module.battery.percent())] %" : "N/C"
|
||||
data["PC_apclinkicon"] = (tesla_link?.enabled && apc_powered) ? "charging.gif" : ""
|
||||
data["PC_device_theme"] = active_program ? active_program.tgui_theme : "scc"
|
||||
data["PC_ntneticon"] = get_ntnet_status_icon()
|
||||
data["PC_stationtime"] = worldtime2text()
|
||||
data["PC_stationdate"] = "[time2text(world.realtime, "DDD, Month DD")], [game_year]"
|
||||
data["PC_showexitprogram"] = !!active_program
|
||||
data["PC_haslight"] = !!flashlight
|
||||
data["PC_lighton"] = flashlight?.enabled ? TRUE : FALSE
|
||||
data["PC_programheaders"] = list()
|
||||
if(idle_threads.len)
|
||||
var/list/program_headers = list()
|
||||
for(var/datum/computer_file/program/P as anything in idle_threads)
|
||||
if(!P.ui_header)
|
||||
continue
|
||||
program_headers.Add(list(list("icon" = P.ui_header)))
|
||||
data["PC_programheaders"] = program_headers
|
||||
return data
|
||||
|
||||
/obj/item/modular_computer/ui_static_data(mob/user)
|
||||
var/list/data = ..()
|
||||
if(active_program)
|
||||
data += active_program.ui_static_data(user)
|
||||
return data
|
||||
|
||||
return data
|
||||
|
||||
/obj/item/modular_computer/ui_data(mob/user)
|
||||
var/list/data = get_header_data()
|
||||
if(active_program)
|
||||
data += active_program.ui_data(user)
|
||||
return data
|
||||
|
||||
var/datum/computer_file/data/autorun = hard_drive.find_file_by_name("autorun")
|
||||
VUEUI_SET_CHECK_IFNOTSET(data["programs"], list(), ., data)
|
||||
data["programs"] = list()
|
||||
data["services"] = list()
|
||||
for(var/datum/computer_file/program/P in hard_drive.stored_files)
|
||||
if(P.program_hidden())
|
||||
continue
|
||||
VUEUI_SET_CHECK_IFNOTSET(data["programs"][P.filename], list(), ., data)
|
||||
VUEUI_SET_CHECK(data["programs"][P.filename]["desc"], P.filedesc, ., data)
|
||||
VUEUI_SET_CHECK(data["programs"][P.filename]["autorun"], (istype(autorun) && (autorun.stored_data == P.filename)), ., data)
|
||||
VUEUI_SET_CHECK(data["programs"][P.filename]["running"], (P in idle_threads), ., data)
|
||||
VUEUI_SET_CHECK(data["programs"][P.filename]["type"], P.program_type, ., data)
|
||||
VUEUI_SET_CHECK_IFNOTSET(data["programs"][P.filename]["service"], list(), ., data)
|
||||
VUEUI_SET_CHECK(data["programs"][P.filename]["service"]["enabled"], (P in enabled_services), ., data)
|
||||
VUEUI_SET_CHECK(data["programs"][P.filename]["service"]["online"], (P.service_state == PROGRAM_STATE_ACTIVE), ., data)
|
||||
data["programs"] += list(list(
|
||||
"filename" = P.filename,
|
||||
"desc" = P.filedesc,
|
||||
"autorun" = istype(autorun) && (autorun.stored_data == P.filename),
|
||||
"running" = (P in idle_threads)
|
||||
))
|
||||
if(P.program_type & PROGRAM_SERVICE)
|
||||
data["services"] += list(list(
|
||||
"filename" = P.filename,
|
||||
"desc" = P.filedesc,
|
||||
"running" = (P in enabled_services) && (P.service_state > PROGRAM_STATE_KILLED)
|
||||
))
|
||||
|
||||
// Handles user's GUI input
|
||||
/obj/item/modular_computer/Topic(href, href_list)
|
||||
if(..())
|
||||
return TRUE
|
||||
if(href_list["PC_exit"])
|
||||
return data
|
||||
|
||||
/obj/item/modular_computer/proc/get_battery_icon()
|
||||
if(battery_module)
|
||||
switch(battery_module.battery.percent())
|
||||
if(80 to 200)
|
||||
return "batt_100.gif"
|
||||
if(60 to 80)
|
||||
return "batt_80.gif"
|
||||
if(40 to 60)
|
||||
return "batt_60.gif"
|
||||
if(20 to 40)
|
||||
return "batt_40.gif"
|
||||
if(5 to 20)
|
||||
return "batt_20.gif"
|
||||
|
||||
return "batt_5.gif"
|
||||
|
||||
/obj/item/modular_computer/proc/get_ntnet_status_icon()
|
||||
switch(get_ntnet_status())
|
||||
if(0)
|
||||
return "sig_none.gif"
|
||||
if(1)
|
||||
return "sig_low.gif"
|
||||
if(2)
|
||||
return "sig_high.gif"
|
||||
if(3)
|
||||
return "sig_lan.gif"
|
||||
else
|
||||
return ""
|
||||
|
||||
/obj/item/modular_computer/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
|
||||
. = ..()
|
||||
if(.)
|
||||
return
|
||||
|
||||
if(active_program)
|
||||
. = active_program.ui_act(action, params, ui, state)
|
||||
|
||||
if(action == "PC_exit")
|
||||
kill_program()
|
||||
return TRUE
|
||||
if(href_list["PC_enable_component"])
|
||||
var/obj/item/computer_hardware/H = find_hardware_by_name(href_list["PC_enable_component"])
|
||||
if(H && istype(H) && !H.enabled)
|
||||
H.enable()
|
||||
if(action == "PC_toggle_component")
|
||||
var/obj/item/computer_hardware/H = find_hardware_by_name(params["component"])
|
||||
if(istype(H))
|
||||
if(H.enabled)
|
||||
H.disable()
|
||||
else
|
||||
H.enable()
|
||||
. = TRUE
|
||||
if(href_list["PC_disable_component"])
|
||||
var/obj/item/computer_hardware/H = find_hardware_by_name(href_list["PC_disable_component"])
|
||||
if(H && istype(H) && H.enabled)
|
||||
H.disable()
|
||||
. = TRUE
|
||||
if(href_list["PC_togglelight"])
|
||||
if(action == "PC_togglelight")
|
||||
if(flashlight)
|
||||
flashlight.toggle()
|
||||
. = TRUE
|
||||
if(href_list["PC_shutdown"])
|
||||
if(action == "PC_shutdown")
|
||||
shutdown_computer()
|
||||
return TRUE
|
||||
if(href_list["PC_minimize"])
|
||||
if(action == "PC_minimize")
|
||||
var/mob/user = usr
|
||||
if(user)
|
||||
minimize_program(user)
|
||||
. = TRUE
|
||||
if(action == "PC_killprogram")
|
||||
var/mob/user = usr
|
||||
minimize_program(user)
|
||||
|
||||
if(href_list["PC_killprogram"])
|
||||
var/prog = href_list["PC_killprogram"]
|
||||
var/datum/computer_file/program/P
|
||||
var/mob/user = usr
|
||||
if(hard_drive)
|
||||
P = hard_drive.find_file_by_name(prog)
|
||||
P = hard_drive.find_file_by_name(params["filename"])
|
||||
|
||||
if(!istype(P) || P.program_state == PROGRAM_STATE_KILLED)
|
||||
return
|
||||
return TRUE
|
||||
|
||||
if(P.kill_program())
|
||||
to_chat(user, SPAN_NOTICE("Program [P.filename].[P.filetype] with PID [rand(100,999)] has been killed."))
|
||||
update_uis()
|
||||
|
||||
if(href_list["PC_runprogram"] )
|
||||
. = run_program(href_list["PC_runprogram"], usr)
|
||||
ui_interact(usr)
|
||||
|
||||
if(href_list["PC_setautorun"])
|
||||
if(!hard_drive)
|
||||
return
|
||||
set_autorun(href_list["PC_setautorun"])
|
||||
|
||||
if(href_list["PC_register"])
|
||||
. = TRUE
|
||||
if(action == "PC_runprogram")
|
||||
run_program(params["filename"], usr)
|
||||
. = TRUE
|
||||
if(action == "PC_setautorun")
|
||||
if(hard_drive)
|
||||
set_autorun(params["filename"])
|
||||
. = TRUE
|
||||
if(action == "PC_register")
|
||||
if(registered_id)
|
||||
unregister_account()
|
||||
else if(GetID())
|
||||
register_account()
|
||||
. = TRUE
|
||||
if(action == "PC_toggleservice")
|
||||
toggle_service(params["service_to_toggle"], usr)
|
||||
. = TRUE
|
||||
|
||||
if( href_list["PC_toggleservice"] )
|
||||
toggle_service(href_list["PC_toggleservice"], usr)
|
||||
return 1
|
||||
|
||||
if(.)
|
||||
update_uis()
|
||||
|
||||
// Function used to obtain data for header. All relevant entries begin with "PC_"
|
||||
/obj/item/modular_computer/proc/get_header_data(data)
|
||||
if(!data)
|
||||
data = list()
|
||||
|
||||
if(battery_module)
|
||||
switch(battery_module.battery.percent())
|
||||
if(80 to 200) // 100 should be maximal but just in case..
|
||||
VUEUI_SET_CHECK(data["batteryicon"], "batt_100.gif", ., data)
|
||||
if(60 to 80)
|
||||
VUEUI_SET_CHECK(data["batteryicon"], "batt_80.gif", ., data)
|
||||
if(40 to 60)
|
||||
VUEUI_SET_CHECK(data["batteryicon"], "batt_60.gif", ., data)
|
||||
if(20 to 40)
|
||||
VUEUI_SET_CHECK(data["batteryicon"], "batt_40.gif", ., data)
|
||||
if(5 to 20)
|
||||
VUEUI_SET_CHECK(data["batteryicon"], "batt_20.gif", ., data)
|
||||
else
|
||||
VUEUI_SET_CHECK(data["batteryicon"], "batt_5.gif", ., data)
|
||||
VUEUI_SET_CHECK(data["batterypercent"], "[round(battery_module.battery.percent())] %", ., data)
|
||||
data["showbatteryicon"] = 1
|
||||
else
|
||||
data["batteryicon"] = "batt_5.gif"
|
||||
VUEUI_SET_CHECK(data["batterypercent"], "N/C", ., data)
|
||||
data["showbatteryicon"] = battery_module ? 1 : 0
|
||||
|
||||
if(tesla_link && tesla_link.enabled && apc_powered)
|
||||
VUEUI_SET_CHECK(data["apclinkicon"], "charging.gif", ., data)
|
||||
else
|
||||
VUEUI_SET_CHECK(data["apclinkicon"], "", ., data)
|
||||
|
||||
data["flashlight"] = flashlight?.enabled ? TRUE : FALSE
|
||||
|
||||
switch(get_ntnet_status())
|
||||
if(0)
|
||||
VUEUI_SET_CHECK(data["ntneticon"], "sig_none.gif", ., data)
|
||||
if(1)
|
||||
VUEUI_SET_CHECK(data["ntneticon"], "sig_low.gif", ., data)
|
||||
if(2)
|
||||
VUEUI_SET_CHECK(data["ntneticon"], "sig_high.gif", ., data)
|
||||
if(3)
|
||||
VUEUI_SET_CHECK(data["ntneticon"], "sig_lan.gif", ., data)
|
||||
else
|
||||
VUEUI_SET_CHECK(data["ntneticon"], "", ., data)
|
||||
|
||||
LAZYINITLIST(data["programheaders"])
|
||||
if(idle_threads.len)
|
||||
for(var/datum/computer_file/program/P in idle_threads)
|
||||
if(!P.ui_header)
|
||||
if(data["programheaders"][P.filename])
|
||||
data["programheaders"][P.filename] = null
|
||||
. = data
|
||||
continue
|
||||
VUEUI_SET_CHECK(data["programheaders"][P.filename], P.ui_header, ., data)
|
||||
|
||||
VUEUI_SET_CHECK(data["showexitprogram"], !!active_program, ., data) // Hides "Exit Program" button on mainscreen
|
||||
update_icon()
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
var/apc_powered = FALSE // Set automatically. Whether the computer used APC power last tick.
|
||||
var/base_active_power_usage = 50 // Power usage when the computer is open (screen is active) and can be interacted with. Remember hardware can use power too.
|
||||
var/base_idle_power_usage = 5 // Power usage when the computer is idle and screen is off (currently only applies to laptops)
|
||||
var/enrolled = 0 // Weather the computer is enrolled in the company device management or not. 0 - unconfigured 1 - enrolled (work device) 2 - unenrolled (private device)
|
||||
var/enrolled = DEVICE_UNSET // Weather the computer is enrolled in the company device management or not. 0 - unconfigured 1 - enrolled (work device) 2 - unenrolled (private device)
|
||||
var/_app_preset_type // Used for specifying the software preset of the console
|
||||
var/ambience_last_played_time // Last time sound was played
|
||||
var/pAI_lock = FALSE // Toggles whether pAI can interact with the modular computer while installed in it
|
||||
|
||||
Reference in New Issue
Block a user