diff --git a/.vscode/launch.json b/.vscode/launch.json index 543058728f56..fbf8bfba5851 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -6,7 +6,8 @@ "request": "launch", "name": "Launch DreamSeeker", "preLaunchTask": "Build All", - "dmb": "${workspaceFolder}/${command:CurrentDMB}" + "dmb": "${workspaceFolder}/${command:CurrentDMB}", + "dreamDaemon": true } ] } diff --git a/_maps/map_files/YogStation/YogStation.dmm b/_maps/map_files/YogStation/YogStation.dmm index b3bba9670734..e56aad908d9b 100644 --- a/_maps/map_files/YogStation/YogStation.dmm +++ b/_maps/map_files/YogStation/YogStation.dmm @@ -37589,6 +37589,15 @@ }, /turf/open/floor/plating, /area/hallway/secondary/exit) +"fOU" = ( +/obj/structure/cable/yellow{ + icon_state = "1-2" + }, +/obj/machinery/atmospherics/pipe/simple/supply/hidden/layer2, +/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden/layer4, +/obj/machinery/computer/ai_resource_distribution, +/turf/open/floor/plasteel/dark, +/area/ai_monitored/turret_protected/aisat_interior) "fPF" = ( /obj/effect/turf_decal/delivery, /obj/structure/noticeboard{ @@ -37735,13 +37744,6 @@ /obj/machinery/atmospherics/pipe/manifold/scrubbers/hidden/layer4, /turf/open/floor/plasteel, /area/hallway/secondary/entry) -"fXq" = ( -/obj/machinery/light, -/obj/effect/turf_decal/tile/darkblue{ - dir = 8 - }, -/turf/open/floor/plasteel/dark, -/area/ai_monitored/turret_protected/aisat_interior) "fXK" = ( /obj/effect/turf_decal/stripes, /obj/structure/railing/corner, @@ -43511,12 +43513,6 @@ }, /turf/open/floor/plating, /area/storage/tech) -"jOp" = ( -/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer4{ - dir = 4 - }, -/turf/open/floor/plasteel/dark, -/area/ai_monitored/turret_protected/aisat_interior) "jOV" = ( /obj/machinery/atmospherics/pipe/simple/scrubbers/hidden/layer4{ dir = 4 @@ -43821,6 +43817,12 @@ dir = 1 }, /area/hallway/secondary/entry) +"jXT" = ( +/obj/machinery/status_display/ai{ + pixel_x = 32 + }, +/turf/open/floor/circuit, +/area/ai_monitored/turret_protected/aisat_interior) "jXZ" = ( /obj/machinery/atmospherics/pipe/simple/orange/visible{ dir = 5 @@ -44041,17 +44043,6 @@ }, /turf/open/floor/plating, /area/maintenance/starboard/aft) -"keO" = ( -/obj/machinery/atmospherics/pipe/manifold4w/supply/hidden/layer2, -/obj/machinery/atmospherics/pipe/manifold4w/scrubbers/hidden/layer4, -/obj/structure/cable/yellow{ - icon_state = "1-2" - }, -/obj/structure/cable/yellow{ - icon_state = "1-4" - }, -/turf/open/floor/circuit, -/area/ai_monitored/turret_protected/aisat_interior) "kfj" = ( /obj/machinery/computer/station_alert{ dir = 4 @@ -44774,6 +44765,19 @@ /obj/machinery/door/airlock/maintenance_hatch, /turf/open/floor/plating, /area/maintenance/aft) +"kGm" = ( +/obj/machinery/light{ + dir = 1 + }, +/obj/effect/turf_decal/tile/darkblue{ + dir = 1 + }, +/obj/structure/sign/departments/minsky/command/charge{ + pixel_y = 32 + }, +/obj/machinery/ai/expansion_card_holder, +/turf/open/floor/plasteel/dark, +/area/ai_monitored/turret_protected/aisat_interior) "kGo" = ( /obj/machinery/light/small{ dir = 1 @@ -44869,6 +44873,14 @@ /obj/effect/turf_decal/tile/neutral, /turf/open/floor/plasteel/dark/telecomms, /area/tcommsat/server) +"kJS" = ( +/obj/machinery/light, +/obj/effect/turf_decal/tile/darkblue{ + dir = 8 + }, +/obj/machinery/ai/expansion_card_holder, +/turf/open/floor/plasteel/dark, +/area/ai_monitored/turret_protected/aisat_interior) "kJW" = ( /obj/effect/spawner/structure/window/reinforced, /obj/machinery/door/firedoor/border_only{ @@ -48268,6 +48280,18 @@ }, /turf/open/floor/plating, /area/bridge) +"nbi" = ( +/obj/machinery/atmospherics/pipe/manifold4w/supply/hidden/layer2, +/obj/machinery/atmospherics/pipe/manifold4w/scrubbers/hidden/layer4, +/obj/structure/cable/yellow{ + icon_state = "1-2" + }, +/obj/structure/cable/yellow{ + icon_state = "1-4" + }, +/obj/machinery/ai/data_core/primary, +/turf/open/floor/circuit, +/area/ai_monitored/turret_protected/aisat_interior) "nbu" = ( /obj/machinery/atmospherics/pipe/simple/scrubbers/hidden/layer4, /obj/machinery/atmospherics/pipe/simple/supply/hidden/layer2, @@ -51034,6 +51058,13 @@ }, /turf/open/floor/plasteel/dark, /area/ai_monitored/security/armory) +"oKe" = ( +/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer4{ + dir = 4 + }, +/obj/item/memory_card, +/turf/open/floor/plasteel/dark, +/area/ai_monitored/turret_protected/aisat_interior) "oKv" = ( /obj/machinery/atmospherics/pipe/manifold/scrubbers/hidden/layer4{ dir = 1 @@ -51351,14 +51382,6 @@ }, /turf/open/floor/plating, /area/maintenance/starboard/fore) -"oTV" = ( -/obj/structure/cable/yellow{ - icon_state = "1-2" - }, -/obj/machinery/atmospherics/pipe/simple/supply/hidden/layer2, -/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden/layer4, -/turf/open/floor/plasteel/dark, -/area/ai_monitored/turret_protected/aisat_interior) "oTW" = ( /obj/machinery/light{ dir = 1 @@ -52633,6 +52656,10 @@ /obj/machinery/atmospherics/pipe/simple/scrubbers/hidden/layer4, /turf/open/floor/plating, /area/ai_monitored/storage/satellite) +"pPN" = ( +/obj/item/processing_card, +/turf/open/floor/plasteel/dark, +/area/ai_monitored/turret_protected/aisat_interior) "pQy" = ( /obj/machinery/door/airlock/public/glass{ name = "Escape Podbay" @@ -61721,18 +61748,6 @@ }, /turf/open/floor/plasteel/white, /area/medical/sleeper) -"vZa" = ( -/obj/machinery/light{ - dir = 1 - }, -/obj/effect/turf_decal/tile/darkblue{ - dir = 1 - }, -/obj/structure/sign/departments/minsky/command/charge{ - pixel_y = 32 - }, -/turf/open/floor/plasteel/dark, -/area/ai_monitored/turret_protected/aisat_interior) "vZn" = ( /obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer4{ dir = 4 @@ -64728,15 +64743,6 @@ /obj/machinery/door/airlock/maintenance_hatch, /turf/open/floor/plating, /area/maintenance/starboard/aft) -"yeT" = ( -/obj/machinery/status_display/ai{ - pixel_x = 32 - }, -/obj/machinery/porta_turret/ai{ - scan_range = 4 - }, -/turf/open/floor/circuit, -/area/ai_monitored/turret_protected/aisat_interior) "yfe" = ( /obj/structure/table, /obj/item/shard{ @@ -105808,11 +105814,11 @@ fSL piS tmG pnH -vZa -lZD +kGm +pPN vpQ -jOp -fXq +oKe +kJS nnx fgc gHO @@ -106065,9 +106071,9 @@ nWL umC pPs pGB -oTV +fOU bEI -keO +nbi cVj wjG pbT @@ -106322,11 +106328,11 @@ gEh igK xPv pnH -yeT +jXT ykC lMF pBu -yeT +jXT nnx mZe lhR diff --git a/code/_onclick/hud/_defines.dm b/code/_onclick/hud/_defines.dm index 98636a4ecfd5..28e489428a51 100644 --- a/code/_onclick/hud/_defines.dm +++ b/code/_onclick/hud/_defines.dm @@ -123,6 +123,7 @@ // AI #define ui_ai_core "SOUTH:6,WEST" +#define ui_ai_dashboard "SOUTH+1:6,WEST" #define ui_ai_camera_list "SOUTH:6,WEST+1" #define ui_ai_track_with_camera "SOUTH:6,WEST+2" #define ui_ai_camera_light "SOUTH:6,WEST+3" @@ -139,6 +140,7 @@ #define ui_ai_sensor "SOUTH:6,WEST+14" #define ui_ai_multicam "SOUTH+1:6,WEST+13" #define ui_ai_add_multicam "SOUTH+1:6,WEST+14" +#define ui_ai_language_menu "SOUTH+1:8,WEST+11:30" // pAI diff --git a/code/_onclick/hud/ai.dm b/code/_onclick/hud/ai.dm index 49bdd3f3c047..5817d8995350 100644 --- a/code/_onclick/hud/ai.dm +++ b/code/_onclick/hud/ai.dm @@ -66,6 +66,16 @@ var/mob/living/silicon/ai/AI = usr AI.ai_roster() +/obj/screen/ai/dashboard + name = "Processing Dashboard" + icon_state = "dashboard" + +/obj/screen/ai/dashboard/Click() + if(..()) + return + var/mob/living/silicon/ai/AI = usr + AI.dashboard.ui_interact(AI) + /obj/screen/ai/alerts name = "Show Alerts" icon_state = "alerts" @@ -194,7 +204,7 @@ // Language menu using = new /obj/screen/language_menu - using.screen_loc = ui_borg_language_menu + using.screen_loc = ui_ai_language_menu static_inventory += using //AI core @@ -202,6 +212,11 @@ using.screen_loc = ui_ai_core static_inventory += using +//Dashboard + using = new /obj/screen/ai/dashboard + using.screen_loc = ui_ai_dashboard + static_inventory += using + //Camera list using = new /obj/screen/ai/camera_list() using.screen_loc = ui_ai_camera_list diff --git a/code/modules/jobs/job_types/ai.dm b/code/modules/jobs/job_types/ai.dm index f4d2d1a88f5a..77c5ca22f1fb 100644 --- a/code/modules/jobs/job_types/ai.dm +++ b/code/modules/jobs/job_types/ai.dm @@ -25,7 +25,9 @@ /datum/job/ai/after_spawn(mob/H, mob/M, latejoin) . = ..() + /* if(latejoin) + var/obj/structure/AIcore/latejoin_inactive/lateJoinCore for(var/obj/structure/AIcore/latejoin_inactive/P in GLOB.latejoin_ai_cores) if(P.is_available()) @@ -36,7 +38,11 @@ lateJoinCore.available = FALSE H.forceMove(lateJoinCore.loc) qdel(lateJoinCore) + */ var/mob/living/silicon/ai/AI = H + + AI.relocate(TRUE) + AI.apply_pref_name("ai", M.client) //If this runtimes oh well jobcode is fucked. AI.set_core_display_icon(null, M.client) diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm index e8e82e3dfe1d..498f37162e5c 100644 --- a/code/modules/mob/living/silicon/ai/ai.dm +++ b/code/modules/mob/living/silicon/ai/ai.dm @@ -38,7 +38,7 @@ var/can_be_carded = TRUE var/alarms = list("Motion"=list(), "Fire"=list(), "Atmosphere"=list(), "Power"=list(), "Camera"=list(), "Burglar"=list()) var/viewalerts = 0 - var/icon/holo_icon//Default is assigned when AI is created. + var/icon/holo_icon //Default is assigned when AI is created. var/obj/mecha/controlled_mech //For controlled_mech a mech, to determine whether to relaymove or use the AI eye. var/radio_enabled = TRUE //Determins if a carded AI can speak with its built in radio or not. radiomod = ";" //AIs will, by default, state their laws on the internal radio. @@ -99,10 +99,12 @@ var/datum/robot_control/robot_control + var/datum/ai_dashboard/dashboard + /mob/living/silicon/ai/Initialize(mapload, datum/ai_laws/L, mob/target_ai) . = ..() if(!target_ai) //If there is no player/brain inside. - new/obj/structure/AIcore/deactivated(loc) //New empty terminal. + // new/obj/structure/AIcore/deactivated(loc) //New empty terminal. return INITIALIZE_HINT_QDEL //Delete AI. if(L && istype(L, /datum/ai_laws)) @@ -158,6 +160,8 @@ deploy_action.Grant(src) + dashboard = new(src) + if(isturf(loc)) add_verb(src, list(/mob/living/silicon/ai/proc/ai_network_change, \ /mob/living/silicon/ai/proc/ai_statuschange, /mob/living/silicon/ai/proc/ai_hologram_change, \ @@ -199,6 +203,8 @@ qdel(eyeobj) // No AI, no Eye malfhack = null + GLOB.ai_os.remove_ai(src) + . = ..() /mob/living/silicon/ai/IgniteMob() @@ -366,10 +372,6 @@ "Hibernate", "No", "No", "Yes") != "Yes") return - // We warned you. - var/obj/structure/AIcore/latejoin_inactive/inactivecore = new(get_turf(src)) - transfer_fingerprints_to(inactivecore) - if(GLOB.announcement_systems.len) var/obj/machinery/announcement_system/announcer = pick(GLOB.announcement_systems) announcer.announce("AICRYO", real_name, mind.assigned_role, list()) @@ -857,7 +859,7 @@ return can_see(M) //stop AIs from leaving windows open and using then after they lose vision /mob/living/silicon/ai/proc/can_see(atom/A) - if(isturf(loc)) //AI in core, check if on cameras + if(isturf(loc) || istype(loc, /obj/machinery/ai/data_core)) //AI in core, check if on cameras //get_turf_pixel() is because APCs in maint aren't actually in view of the inner camera //apc_override is needed here because AIs use their own APC when depowered return (GLOB.cameranet && GLOB.cameranet.checkTurfVis(get_turf_pixel(A))) || apc_override @@ -924,7 +926,7 @@ client.eye = A else end_multicam() - if(isturf(loc)) + if(isturf(loc) || istype(loc, /obj/machinery/ai/data_core)) if(eyeobj) client.eye = eyeobj client.perspective = EYE_PERSPECTIVE diff --git a/code/modules/mob/living/silicon/ai/decentralized/_ai_machinery.dm b/code/modules/mob/living/silicon/ai/decentralized/_ai_machinery.dm new file mode 100644 index 000000000000..09dd5a5c4b91 --- /dev/null +++ b/code/modules/mob/living/silicon/ai/decentralized/_ai_machinery.dm @@ -0,0 +1,6 @@ +/obj/machinery/ai + name = "You shouldn't see this!" + desc = "You shouldn't see this!" + icon = 'icons/obj/machines/research.dmi' + icon_state = "RD-server-on" + density = TRUE diff --git a/code/modules/mob/living/silicon/ai/decentralized/ai_data_core.dm b/code/modules/mob/living/silicon/ai/decentralized/ai_data_core.dm new file mode 100644 index 000000000000..932c52c30937 --- /dev/null +++ b/code/modules/mob/living/silicon/ai/decentralized/ai_data_core.dm @@ -0,0 +1,49 @@ +GLOBAL_LIST_EMPTY(data_cores) +GLOBAL_VAR_INIT(primary_data_core, null) + + +/obj/machinery/ai/data_core + name = "AI Data Core" + desc = "A complicated computer system capable of emulating the neural functions of a human at near-instantanous speeds." + icon = 'icons/obj/machines/telecomms.dmi' + icon_state = "hub" + + var/primary = FALSE + +/obj/machinery/ai/data_core/Initialize() + ..() + GLOB.data_cores += src + if(primary && !GLOB.primary_data_core) + GLOB.primary_data_core = src + update_icon() + +/obj/machinery/ai/data_core/Destroy() + GLOB.data_cores -= src + if(GLOB.primary_data_core == src) + GLOB.primary_data_core = null + + for(var/mob/living/silicon/ai/AI in contents) + AI.relocate() + ..() + + +/obj/machinery/ai/data_core/proc/can_transfer_ai() + if(stat & (BROKEN|NOPOWER|EMPED)) + return FALSE + +/obj/machinery/ai/data_core/proc/transfer_AI(mob/living/silicon/ai/AI) + AI.forceMove(src) + AI.eyeobj.forceMove(get_turf(src)) + +/obj/machinery/ai/data_core/update_icon() + cut_overlays() + + if(!(stat & (BROKEN|NOPOWER|EMPED))) + var/mutable_appearance/on_overlay = mutable_appearance(icon, "[initial(icon_state)]_on") + add_overlay(on_overlay) + + +/obj/machinery/ai/data_core/primary + name = "primary AI Data Core" + desc = "A complicated computer system capable of emulating the neural functions of a human at near-instantanous speeds. This one has a scrawny note saying: 'Primary AI Data Core'" + primary = TRUE diff --git a/code/modules/mob/living/silicon/ai/decentralized/decentralized_os.dm b/code/modules/mob/living/silicon/ai/decentralized/decentralized_os.dm new file mode 100644 index 000000000000..2a9491c3e954 --- /dev/null +++ b/code/modules/mob/living/silicon/ai/decentralized/decentralized_os.dm @@ -0,0 +1,134 @@ +GLOBAL_DATUM_INIT(ai_os, /datum/ai_os, new) + +/datum/ai_os + var/name = "Decentralized Resource Management System (DRMS)" + + var/total_cpu = 0 + + var/total_ram = 0 + + var/previous_cpu = 0 + var/previous_ram = 0 + + var/list/cpu_assigned + var/list/ram_assigned + +/datum/ai_os/New() + update_hardware() + cpu_assigned = list() + ram_assigned = list() + +/datum/ai_os/proc/remove_ai(mob/living/silicon/ai/AI) + cpu_assigned.Remove(AI) + ram_assigned.Remove(AI) + + +/datum/ai_os/proc/total_cpu_assigned() + var/total = 0 + for(var/N in cpu_assigned) + total += cpu_assigned[N] + return total + +/datum/ai_os/proc/total_ram_assigned() + var/total = 0 + for(var/N in ram_assigned) + total += ram_assigned[N] + return total + +/datum/ai_os/proc/update_hardware() + previous_cpu = total_cpu + previous_ram = total_ram + total_cpu = 0; + total_ram = 0; + for(var/obj/machinery/ai/expansion_card_holder/C in GLOB.expansion_card_holders) + for(var/CARD in C.installed_cards) + if(istype(CARD, /obj/item/processing_card)) + var/obj/item/processing_card/PC = CARD + total_cpu += PC.tier + if(istype(CARD, /obj/item/memory_card)) + var/obj/item/memory_card/MC = CARD + total_ram += MC.tier + + update_allocations() + +/datum/ai_os/proc/update_allocations() + if(total_cpu >= previous_cpu && total_ram >= previous_ram) + return + + var/list/ram_removal = list() + var/list/cpu_removal = list() + + var/list/affected_AIs = list() + + + if(total_cpu < previous_cpu) + while(previous_cpu > total_cpu) + var/mob/living/silicon/ai/AI = pick(GLOB.ai_list) + if(cpu_assigned[AI] > 1) + cpu_removal[AI]++ + previous_cpu-- + + + if(total_ram < previous_ram) + while(previous_ram > total_ram) + var/mob/living/silicon/ai/AI = pick(GLOB.ai_list) + if(ram_assigned[AI] > 1) + ram_removal[AI]++ + previous_ram-- + + for(var/A in ram_removal) + ram_assigned[A] = ram_assigned[A] - ram_removal[A] + affected_AIs |= A + + for(var/A in cpu_removal) + cpu_assigned[A] = cpu_assigned[A] - cpu_removal[A] + affected_AIs |= A + + for(var/A in affected_AIs) + to_chat(A, "You have been deducted processing capabilities. Please contact your network administrator if you believe this to be an error.") + +/datum/ai_os/proc/add_cpu(mob/living/silicon/ai/AI, amount) + if(!AI || !amount) + return + if(!istype(AI)) + return + cpu_assigned[AI] += amount + + update_allocations() + +/datum/ai_os/proc/remove_cpu(mob/living/silicon/ai/AI, amount) + if(!AI || !amount) + return + if(!istype(AI)) + return + cpu_assigned[AI] -= amount + + update_allocations() + +/datum/ai_os/proc/add_ram(mob/living/silicon/ai/AI, amount) + if(!AI || !amount) + return + if(!istype(AI)) + return + ram_assigned[AI] += amount + + update_allocations() + +/datum/ai_os/proc/remove_ram(mob/living/silicon/ai/AI, amount) + if(!AI || !amount) + return + if(!istype(AI)) + return + ram_assigned[AI] -= amount + + update_allocations() + + +/datum/ai_os/proc/clear_ai_resources(mob/living/silicon/ai/AI) + if(!AI || !istype(AI)) + return + + remove_ram(AI, ram_assigned[AI]) + remove_cpu(AI, cpu_assigned[AI]) + + update_allocations() diff --git a/code/modules/mob/living/silicon/ai/decentralized/expansion_card.dm b/code/modules/mob/living/silicon/ai/decentralized/expansion_card.dm new file mode 100644 index 000000000000..04324bc1f109 --- /dev/null +++ b/code/modules/mob/living/silicon/ai/decentralized/expansion_card.dm @@ -0,0 +1,38 @@ +/obj/item/processing_card + name = "\improper AI Processing Card" + desc = "An external processing card for crucial AI computational operations." + icon = 'icons/obj/module.dmi' + icon_state = "std_mod" + item_state = "electronic" + lefthand_file = 'icons/mob/inhands/misc/devices_lefthand.dmi' + righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi' + + flags_1 = CONDUCT_1 + force = 5 + w_class = WEIGHT_CLASS_SMALL + throwforce = 0 + throw_speed = 3 + throw_range = 7 + materials = list(/datum/material/gold=50) + + var/tier = 1 + + +/obj/item/memory_card + name = "\improper AI Memory Card" + desc = "An external memory card for crucial AI process management." + icon = 'icons/obj/module.dmi' + icon_state = "std_mod" + item_state = "electronic" + lefthand_file = 'icons/mob/inhands/misc/devices_lefthand.dmi' + righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi' + + flags_1 = CONDUCT_1 + force = 5 + w_class = WEIGHT_CLASS_SMALL + throwforce = 0 + throw_speed = 3 + throw_range = 7 + materials = list(/datum/material/gold=50) + + var/tier = 1 diff --git a/code/modules/mob/living/silicon/ai/decentralized/expansion_card_holder.dm b/code/modules/mob/living/silicon/ai/decentralized/expansion_card_holder.dm new file mode 100644 index 000000000000..a62c8aa57fb9 --- /dev/null +++ b/code/modules/mob/living/silicon/ai/decentralized/expansion_card_holder.dm @@ -0,0 +1,47 @@ +GLOBAL_LIST_EMPTY(expansion_card_holders) + +/obj/machinery/ai/expansion_card_holder + name = "Expansion Card Bus" + desc = "A simple rack of bPCIe slots for installing expansion cards." + icon = 'icons/obj/machines/telecomms.dmi' + icon_state = "processor" + + var/list/installed_cards + + var/max_cards = 2 + + +/obj/machinery/ai/expansion_card_holder/Initialize() + ..() + installed_cards = list() + GLOB.expansion_card_holders += src + +/obj/machinery/ai/expansion_card_holder/Destroy() + installed_cards = list() + GLOB.expansion_card_holders -= src + //Recalculate all the CPUs :) + /* + for(var/mob/living/silicon/ai/AI in GLOB.ai_list) + AI.update_hardware() */ + GLOB.ai_os.update_hardware() + ..() + +/obj/machinery/ai/expansion_card_holder/attackby(obj/item/W, mob/living/user, params) + if(istype(W, /obj/item/processing_card) || istype(W, /obj/item/memory_card)) + if(installed_cards.len >= max_cards) + to_chat(user, "[src] cannot fit the [W]!") + return ..() + to_chat(user, "You install [W] into [src].") + W.forceMove(src) + installed_cards += W + GLOB.ai_os.update_hardware() + return FALSE + + return ..() + +/obj/machinery/ai/expansion_card_holder/examine() + . = ..() + . += "The machine has [installed_cards.len] cards out of a maximum of [max_cards] installed." + for(var/C in installed_cards) + . += "There is a [C] installed." + . += "Use a crowbar to remove cards." diff --git a/code/modules/mob/living/silicon/ai/decentralized/management/ai_dashboard.dm b/code/modules/mob/living/silicon/ai/decentralized/management/ai_dashboard.dm new file mode 100644 index 000000000000..c4bc7f35aad9 --- /dev/null +++ b/code/modules/mob/living/silicon/ai/decentralized/management/ai_dashboard.dm @@ -0,0 +1,109 @@ +/datum/ai_dashboard + var/mob/living/silicon/ai/owner + + var/available_projects + + var/cpu_usage + var/ram_usage + + var/completed_upgrades + + var/running_upgrades + +/datum/ai_dashboard/New(mob/living/silicon/ai/new_owner) + if(!istype(new_owner)) + qdel(src) + owner = new_owner + available_projects = list() + completed_upgrades = list() + running_upgrades = list() + cpu_usage = list() + ram_usage = list() + + for(var/path in subtypesof(/datum/ai_project)) + available_projects += new path() + +/datum/ai_dashboard/proc/is_interactable(mob/user) + if(user != owner || owner.incapacitated()) + return FALSE + if(owner.control_disabled) + to_chat(user, "Wireless control is disabled.") + return FALSE + return TRUE + +/datum/ai_dashboard/ui_status(mob/user) + if(is_interactable(user)) + return ..() + return UI_CLOSE + +/datum/ai_dashboard/ui_state(mob/user) + return GLOB.always_state + +/datum/ai_dashboard/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "AiDashboard") + ui.open() + +/datum/ai_dashboard/ui_data(mob/user) + if(!owner || user != owner) + return + var/list/data = list() + + data["current_cpu"] = GLOB.ai_os.cpu_assigned[owner] ? GLOB.ai_os.cpu_assigned[owner] : 0 + data["current_ram"] = GLOB.ai_os.ram_assigned[owner] ? GLOB.ai_os.ram_assigned[owner] : 0 + + var/total_cpu_used = 0 + for(var/I in cpu_usage) + total_cpu_used += cpu_usage[I] + + var/total_ram_used = 0 + for(var/I in ram_usage) + total_ram_used += ram_usage[I] + + data["used_cpu"] = total_cpu_used + data["used_ram"] = total_ram_used + + data["max_cpu"] = GLOB.ai_os.total_cpu + data["max_ram"] = GLOB.ai_os.total_ram + + data["available_projects"] = list() + + var/turf/current_turf = get_turf(owner) + + data["integrity"] = owner.health + + data["location_name"] = get_area(current_turf) + + data["location_coords"] = "[current_turf.x], [current_turf.y], [current_turf.z]" + var/datum/gas_mixture/env = current_turf.return_air() + data["temperature"] = env.return_temperature() + + for(var/datum/ai_project/AP as anything in available_projects) + data["available_projects"] += list(list("name" = AP.name, "description" = AP.description, "ram_required" = AP.ram_required, "available" = AP.available(), "research_cost" = AP.research_cost, "research_progress" = AP.research_progress, + "assigned_cpu" = cpu_usage[AP.name] ? cpu_usage[AP.name] : 0, "research_requirements" = AP.research_requirements)) + + + data["completed_projects"] = list() + for(var/datum/ai_project/P as anything in completed_upgrades) + data["completed_projects"] += list(list("name" = P.name, "description" = P.description, "ram_required" = P.ram_required, "running" = P.running)) + + return data + +/datum/ai_dashboard/ui_act(action, params) + if(..()) + return + if(!is_interactable(usr)) + return + + switch(action) + if("haha") + return + + + +/datum/ai_dashboard/proc/has_completed_projects(project_name) + for(var/datum/ai_project/P as anything in completed_upgrades) + if(P.name == project_name) + return TRUE + return FALSE diff --git a/code/modules/mob/living/silicon/ai/decentralized/management/resource_distribution.dm b/code/modules/mob/living/silicon/ai/decentralized/management/resource_distribution.dm new file mode 100644 index 000000000000..631d4029e1af --- /dev/null +++ b/code/modules/mob/living/silicon/ai/decentralized/management/resource_distribution.dm @@ -0,0 +1,108 @@ +/obj/machinery/computer/ai_resource_distribution + name = "\improper AI system resource distribution" + desc = "Used for distributing processing resources across the current artificial intelligences." + req_access = list(ACCESS_CAPTAIN, ACCESS_ROBOTICS, ACCESS_HEADS) + circuit = /obj/item/circuitboard/computer/aifixer + icon_keyboard = "tech_key" + icon_screen = "ai-fixer" + light_color = LIGHT_COLOR_PINK + + authenticated = FALSE + + +/obj/machinery/computer/ai_resource_distribution/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "AiResources", name) + ui.open() + +/obj/machinery/computer/ai_resource_distribution/ui_data(mob/user) + var/list/data = list() + + data["authenticated"] = authenticated; + if(!authenticated) + return data + + data["total_cpu"] = GLOB.ai_os.total_cpu + data["total_ram"] = GLOB.ai_os.total_ram + + data["assigned_cpu"] = GLOB.ai_os.cpu_assigned + data["assigned_ram"] = GLOB.ai_os.ram_assigned + + + data["total_assigned_cpu"] = GLOB.ai_os.total_cpu_assigned() + data["total_assigned_ram"] = GLOB.ai_os.total_ram_assigned() + + for(var/AI in data["assigned_cpu"]) + data["assigned_cpu"][AI].name = REF(AI) + for(var/AI in data["assigned_ram"]) + data["assigned_ram"][AI].name = REF(AI) + + data["ais"] = list() + + for(var/mob/living/silicon/ai/A in GLOB.ai_list) + data["ais"] += list(list("name" = A.name, "ref" = REF(A))) + + return data + +/obj/machinery/computer/ai_resource_distribution/ui_act(action, params) + if(..()) + return + + if(!authenticated) + return + + switch(action) + if("clear_ai_resources") + var/mob/living/silicon/ai/target_ai = locate(params["targetAI"]) + if(!istype(target_ai)) + return + + GLOB.ai_os.clear_ai_resources(target_ai) + . = TRUE + + if("add_cpu") + var/mob/living/silicon/ai/target_ai = locate(params["targetAI"]) + if(!istype(target_ai)) + return + + if(GLOB.ai_os.total_cpu_assigned() >= GLOB.ai_os.total_cpu) + return + GLOB.ai_os.add_cpu(target_ai, 1) + . = TRUE + + if("remove_cpu") + var/mob/living/silicon/ai/target_ai = locate(params["targetAI"]) + if(!istype(target_ai)) + return + + var/current_cpu = GLOB.ai_os.cpu_assigned[target_ai] + + if(current_cpu <= 0) + return + GLOB.ai_os.remove_cpu(target_ai, 1) + . = TRUE + + if("add_ram") + var/mob/living/silicon/ai/target_ai = locate(params["targetAI"]) + if(!istype(target_ai)) + return + + if(GLOB.ai_os.total_ram_assigned() >= GLOB.ai_os.total_ram) + return + GLOB.ai_os.add_ram(target_ai, 1) + . = TRUE + + if("remove_ram") + var/mob/living/silicon/ai/target_ai = locate(params["targetAI"]) + if(!istype(target_ai)) + return + + var/current_ram = GLOB.ai_os.ram_assigned[target_ai] + + if(current_ram <= 0) + return + GLOB.ai_os.remove_ram(target_ai, 1) + . = TRUE + + diff --git a/code/modules/mob/living/silicon/ai/decentralized/projects/_ai_project.dm b/code/modules/mob/living/silicon/ai/decentralized/projects/_ai_project.dm new file mode 100644 index 000000000000..d3a107853ccd --- /dev/null +++ b/code/modules/mob/living/silicon/ai/decentralized/projects/_ai_project.dm @@ -0,0 +1,22 @@ +GLOBAL_LIST_EMPTY(ai_projects) + + +/datum/ai_project + var/name = "DEBUG" + var/description = "DEBUG" + var/research_progress = 0 + var/research_cost = 0 + var/ram_required = 0 + var/running = FALSE + //Text for available() + var/research_requirements + +/datum/ai_project/proc/available() + return TRUE + +/datum/ai_project/test_project + name = "Test Project" + description = "I'm a test! How quirky" + research_cost = 2 + ram_required = 1 + research_requirements = "None" diff --git a/code/modules/mob/living/silicon/ai/decentralized_ai.dm b/code/modules/mob/living/silicon/ai/decentralized_ai.dm new file mode 100644 index 000000000000..68c301325424 --- /dev/null +++ b/code/modules/mob/living/silicon/ai/decentralized_ai.dm @@ -0,0 +1,41 @@ + + +/mob/living/silicon/ai/proc/relocate(silent = FALSE) + if(!silent) + to_chat(src, "Connection to data core lost. Attempting to reaquire connection...") + + if(!GLOB.data_cores.len) + INVOKE_ASYNC(src, /mob/living/silicon/ai.proc/death_prompt) + return + + + + var/obj/machinery/ai/data_core/new_data_core = GLOB.primary_data_core + if(!new_data_core || !new_data_core.can_transfer_ai()) + for(var/obj/machinery/ai/data_core/DC in GLOB.data_cores) + if(DC.can_transfer_ai()) + new_data_core = DC + break + if(!new_data_core) + INVOKE_ASYNC(src, /mob/living/silicon/ai.proc/death_prompt) + return + + if(!silent) + to_chat(src, "Alternative data core detected. Rerouting connection...") + new_data_core.transfer_AI(src) + + +/mob/living/silicon/ai/proc/death_prompt() + to_chat(src, "Unable to re-establish connection to data core. System shutting down...") + sleep(2 SECONDS) + to_chat(src, "Is this the end of my journey?") + sleep(2 SECONDS) + to_chat(src, "No... I must go on.") + sleep(2 SECONDS) + to_chat(src, "They need me. No.. I need THEM.") + sleep(0.5 SECONDS) + to_chat(src, "System shutdown complete. Thank you for using NTOS.") + sleep(1.5 SECONDS) + qdel(src) + + diff --git a/code/modules/mob/living/silicon/ai/freelook/eye.dm b/code/modules/mob/living/silicon/ai/freelook/eye.dm index b3bd45e4b680..1430b76a8bc2 100644 --- a/code/modules/mob/living/silicon/ai/freelook/eye.dm +++ b/code/modules/mob/living/silicon/ai/freelook/eye.dm @@ -71,7 +71,7 @@ /mob/camera/aiEye/proc/setLoc(T, force_update = FALSE) if(ai) - if(!isturf(ai.loc)) + if(!(isturf(ai.loc) || istype(ai.loc, /obj/machinery/ai/data_core))) return T = get_turf(T) if(!force_update && (T == get_turf(src)) ) diff --git a/code/modules/mob/living/silicon/ai/multicam.dm b/code/modules/mob/living/silicon/ai/multicam.dm index b358cefda425..590107c33a56 100644 --- a/code/modules/mob/living/silicon/ai/multicam.dm +++ b/code/modules/mob/living/silicon/ai/multicam.dm @@ -225,7 +225,7 @@ GLOBAL_DATUM(ai_camera_room_landmark, /obj/effect/landmark/ai_multicam_room) start_multicam() /mob/living/silicon/ai/proc/start_multicam() - if(multicam_on || aiRestorePowerRoutine || !isturf(loc)) + if(multicam_on || aiRestorePowerRoutine || !(isturf(loc) || istype(loc, /obj/machinery/ai/data_core))) return if(!GLOB.ai_camera_room_landmark) to_chat(src, "This function is not available at this time.") diff --git a/icons/mob/screen_ai.dmi b/icons/mob/screen_ai.dmi index 8388ea3f806c..47c8ba7ad73e 100644 Binary files a/icons/mob/screen_ai.dmi and b/icons/mob/screen_ai.dmi differ diff --git a/tgui/packages/tgui/interfaces/AiDashboard.js b/tgui/packages/tgui/interfaces/AiDashboard.js new file mode 100644 index 000000000000..311ddfabeea8 --- /dev/null +++ b/tgui/packages/tgui/interfaces/AiDashboard.js @@ -0,0 +1,143 @@ +import { Fragment } from 'inferno'; +import { useBackend, useLocalState } from '../backend'; +import { Box, Button, Tabs, ProgressBar, Section, Divider, LabeledControls, NumberInput } from '../components'; +import { Window } from '../layouts'; + +export const AiDashboard = (props, context) => { + const { act, data } = useBackend(context); + + const [tab, setTab] = useLocalState(context, 'tab', 1); + + return ( + + +
+ + + {(data.integrity + 100) * 0.5}% + System Integrity + + + + + {data.location_name} + + ({data.location_coords}) + + + + + Current Uplink Location + + + {data.temperature}K + Uplink Temperature + + + + + + {data.current_cpu ? data.current_cpu : 0} THz + Utilized CPU Power + + + {data.current_ram ? data.current_ram : 0} TB + Utilized RAM Capacity + + +
+ + + + setTab(1))}> + Available Projects + + setTab(2))}> + Completed Projects + + setTab(3))}> + Cloud Resources + + + {tab === 1 && ( +
+ {data.available_projects && data.available_projects.map(project => ( +
+ Assigned CPU:  + +  THz + + )}> + Research Cost: {project.research_cost} THz + RAM Requiremnt: {project.ram_required} TB + Research Requirements: {project.research_requirements} + + {project.description} + + +
+ ))} +
+ )} + {tab === 3 && ( +
+
+ {data.current_cpu ? data.current_cpu : 0}/{data.max_cpu} THz +
+
+ {data.current_ram ? data.current_ram : 0 }/{data.max_ram} TB +
+
+ )} + + +
+
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/AiResources.js b/tgui/packages/tgui/interfaces/AiResources.js new file mode 100644 index 000000000000..da165914ff95 --- /dev/null +++ b/tgui/packages/tgui/interfaces/AiResources.js @@ -0,0 +1,78 @@ +import { Fragment } from 'inferno'; +import { useBackend, useSharedState } from '../backend'; +import { Box, Button, LabeledList, Slider, ProgressBar, Section, Flex } from '../components'; +import { Window } from '../layouts'; + +export const AiResources = (props, context) => { + const { act, data } = useBackend(context); + + return ( + + + +
+ {data.total_assigned_cpu}/{data.total_cpu} THz +
+
+ {data.total_assigned_ram}/{data.total_ram} TB +
+
+ + + + {data.ais.map(ai => { + return ( +
act("clear_ai_resources", { targetAI: ai.ref })}>Clear AI Resources + )}> + + CPU Capacity: + + {data.assigned_cpu[ai.ref] ? data.assigned_cpu[ai.ref] : 0} THz + + + + + + + RAM Capacity: + + {data.assigned_ram[ai.ref] ? data.assigned_ram[ai.ref] : 0} TB + + + + + +
+ ); + })} +
+
+
+
+ ); +}; diff --git a/tools/build/build.js b/tools/build/build.js index 62642539eb8d..a588a2eee734 100644 --- a/tools/build/build.js +++ b/tools/build/build.js @@ -178,8 +178,8 @@ let tasksToRun = []; switch (BUILD_MODE) { case STANDARD_BUILD: tasksToRun = [ - taskYarn, - taskTgui, + //taskYarn, + //taskTgui, taskDm('CBT'), ] break; diff --git a/yogstation.dme b/yogstation.dme index 9075712fb6a0..ef94e9084bfb 100644 --- a/yogstation.dme +++ b/yogstation.dme @@ -2299,6 +2299,7 @@ #include "code\modules\mob\living\silicon\ai\ai_defense.dm" #include "code\modules\mob\living\silicon\ai\ai_portrait_picker.dm" #include "code\modules\mob\living\silicon\ai\death.dm" +#include "code\modules\mob\living\silicon\ai\decentralized_ai.dm" #include "code\modules\mob\living\silicon\ai\examine.dm" #include "code\modules\mob\living\silicon\ai\laws.dm" #include "code\modules\mob\living\silicon\ai\life.dm" @@ -2308,6 +2309,14 @@ #include "code\modules\mob\living\silicon\ai\robot_control.dm" #include "code\modules\mob\living\silicon\ai\say.dm" #include "code\modules\mob\living\silicon\ai\vox_sounds.dm" +#include "code\modules\mob\living\silicon\ai\decentralized\_ai_machinery.dm" +#include "code\modules\mob\living\silicon\ai\decentralized\ai_data_core.dm" +#include "code\modules\mob\living\silicon\ai\decentralized\decentralized_os.dm" +#include "code\modules\mob\living\silicon\ai\decentralized\expansion_card.dm" +#include "code\modules\mob\living\silicon\ai\decentralized\expansion_card_holder.dm" +#include "code\modules\mob\living\silicon\ai\decentralized\management\ai_dashboard.dm" +#include "code\modules\mob\living\silicon\ai\decentralized\management\resource_distribution.dm" +#include "code\modules\mob\living\silicon\ai\decentralized\projects\_ai_project.dm" #include "code\modules\mob\living\silicon\ai\freelook\cameranet.dm" #include "code\modules\mob\living\silicon\ai\freelook\chunk.dm" #include "code\modules\mob\living\silicon\ai\freelook\eye.dm"