/* Research and Development (R&D) Console This is the main work horse of the R&D system. It contains the menus/controls for the Destructive Analyzer, Protolathe, and Circuit imprinter. It also contains the /datum/research holder with all the known/possible technology paths and device designs. Basic use: When it first is created, it will attempt to link up to related devices within 3 squares. It'll only link up if they aren't already linked to another console. Any consoles it cannot link up with (either because all of a certain type are already linked or there aren't any in range), you'll just not have access to that menu. In the settings menu, there are menu options that allow a player to attempt to re-sync with nearby consoles. You can also force it to disconnect from a specific console. The imprinting and construction menus do NOT require toxins access to access but all the other menus do. However, if you leave it on a menu, nothing is to stop the person from using the options on that menu (although they won't be able to change to a different one). You can also lock the console on the settings menu if you're feeling paranoid and you don't want anyone messing with it who doesn't have toxins access. When a R&D console is destroyed or even partially disassembled, you lose all research data on it. However, there are two ways around this dire fate: - The easiest way is to go to the settings menu and select "Sync Database with Network." That causes it to upload (but not download) it's data to every other device in the game. Each console has a "disconnect from network" option that'll will cause data base sync operations to skip that console. This is useful if you want to make a "public" R&D console or, for example, give the engineers a circuit imprinter with certain designs on it and don't want it accidentally updating. The downside of this method is that you have to have physical access to the other console to send data back. Note: An R&D console is on CentCom so if a random griffan happens to cause a ton of data to be lost, an admin can go send it back. - The second method is with Technology Disks and Design Disks. Each of these disks can hold a single technology or design datum in it's entirety. You can then take the disk to any R&D console and upload it's data to it. This method is a lot more secure (since it won't update every console in existence) but it's more of a hassle to do. Also, the disks can be stolen. */ /obj/machinery/computer/rdconsole name = "R&D control console" icon_state = "rdcomp" circuit = /obj/item/weapon/circuitboard/rdconsole var/datum/research/files //Stores all the collected research data. var/obj/item/weapon/disk/tech_disk/t_disk = null //Stores the technology disk. var/obj/item/weapon/disk/design_disk/d_disk = null //Stores the design disk. var/obj/machinery/r_n_d/destructive_analyzer/linked_destroy = null //Linked Destructive Analyzer var/obj/machinery/r_n_d/protolathe/linked_lathe = null //Linked Protolathe var/obj/machinery/r_n_d/circuit_imprinter/linked_imprinter = null //Linked Circuit Imprinter var/screen = 1.0 //Which screen is currently showing. var/id = 0 //ID of the computer (for server restrictions). var/sync = 1 //If sync = 0, it doesn't show up on Server Control Console var/errored = 0 //Errored during item construction. req_access = list(access_research) //Data and setting manipulation requires scientist access. /obj/machinery/computer/rdconsole/proc/CallTechName(var/ID) //A simple helper proc to find the name of a tech with a given ID. var/datum/tech/check_tech var/return_name = null for(var/T in typesof(/datum/tech) - /datum/tech) check_tech = null check_tech = new T() if(check_tech.id == ID) return_name = check_tech.name del(check_tech) check_tech = null break return return_name /obj/machinery/computer/rdconsole/proc/CallMaterialName(var/ID) var/datum/reagent/temp_reagent var/return_name = null if (copytext(ID, 1, 2) == "$") return_name = copytext(ID, 2) switch(return_name) if("metal") return_name = "Metal" if("glass") return_name = "Glass" if("gold") return_name = "Gold" if("silver") return_name = "Silver" if("phoron") return_name = "Solid Phoron" if("uranium") return_name = "Uranium" if("diamond") return_name = "Diamond" else for(var/R in typesof(/datum/reagent) - /datum/reagent) temp_reagent = null temp_reagent = new R() if(temp_reagent.id == ID) return_name = temp_reagent.name del(temp_reagent) temp_reagent = null break return return_name /obj/machinery/computer/rdconsole/proc/SyncRDevices() //Makes sure it is properly sync'ed up with the devices attached to it (if any). for(var/obj/machinery/r_n_d/D in oview(3,src)) if(D.linked_console != null || D.disabled || D.panel_open) continue if(istype(D, /obj/machinery/r_n_d/destructive_analyzer)) if(linked_destroy == null) linked_destroy = D D.linked_console = src else if(istype(D, /obj/machinery/r_n_d/protolathe)) if(linked_lathe == null) linked_lathe = D D.linked_console = src else if(istype(D, /obj/machinery/r_n_d/circuit_imprinter)) if(linked_imprinter == null) linked_imprinter = D D.linked_console = src return //Have it automatically push research to the centcomm server so wild griffins can't fuck up R&D's work --NEO /obj/machinery/computer/rdconsole/proc/griefProtection() for(var/obj/machinery/r_n_d/server/centcom/C in machines) for(var/datum/tech/T in files.known_tech) C.files.AddTech2Known(T) for(var/datum/design/D in files.known_designs) C.files.AddDesign2Known(D) C.files.RefreshResearch() /obj/machinery/computer/rdconsole/New() ..() files = new /datum/research(src) //Setup the research data holder. if(!id) for(var/obj/machinery/r_n_d/server/centcom/S in machines) S.initialize() break /obj/machinery/computer/rdconsole/initialize() SyncRDevices() /* Instead of calling this every tick, it is only being called when needed /obj/machinery/computer/rdconsole/process() griefProtection() */ /obj/machinery/computer/rdconsole/attackby(var/obj/item/weapon/D as obj, var/mob/user as mob) //Loading a disk into it. if(istype(D, /obj/item/weapon/disk)) if(t_disk || d_disk) user << "A disk is already loaded into the machine." return if(istype(D, /obj/item/weapon/disk/tech_disk)) t_disk = D else if (istype(D, /obj/item/weapon/disk/design_disk)) d_disk = D else user << "\red Machine cannot accept disks in that format." return user.drop_item() D.loc = src user << "\blue You add the disk to the machine!" else if(istype(D, /obj/item/weapon/card/emag) && !emagged) playsound(src.loc, 'sound/effects/sparks4.ogg', 75, 1) emagged = 1 user << "\blue You you disable the security protocols" else //The construction/deconstruction of the console code. ..() src.updateUsrDialog() return /obj/machinery/computer/rdconsole/Topic(href, href_list) if(..()) return 1 add_fingerprint(usr) usr.set_machine(src) if(href_list["menu"]) //Switches menu screens. Converts a sent text string into a number. Saves a LOT of code. var/temp_screen = text2num(href_list["menu"]) if(temp_screen <= 1.1 || (3 <= temp_screen && 4.9 >= temp_screen) || src.allowed(usr) || emagged) //Unless you are making something, you need access. screen = temp_screen else usr << "Unauthorized Access." else if(href_list["reset"]) warning("RnD console has errored during protolathe operation. Resetting.") errored = 0 screen = 1.0 updateUsrDialog() else if(href_list["updt_tech"]) //Update the research holder with information from the technology disk. screen = 0.0 spawn(50) screen = 1.2 files.AddTech2Known(t_disk.stored) updateUsrDialog() griefProtection() //Update centcomm too else if(href_list["clear_tech"]) //Erase data on the technology disk. t_disk.stored = null else if(href_list["eject_tech"]) //Eject the technology disk. t_disk:loc = src.loc t_disk = null screen = 1.0 else if(href_list["copy_tech"]) //Copys some technology data from the research holder to the disk. for(var/datum/tech/T in files.known_tech) if(href_list["copy_tech_ID"] == T.id) t_disk.stored = T break screen = 1.2 else if(href_list["updt_design"]) //Updates the research holder with design data from the design disk. screen = 0.0 spawn(50) screen = 1.4 files.AddDesign2Known(d_disk.blueprint) updateUsrDialog() griefProtection() //Update centcomm too else if(href_list["clear_design"]) //Erases data on the design disk. d_disk.blueprint = null else if(href_list["eject_design"]) //Eject the design disk. d_disk:loc = src.loc d_disk = null screen = 1.0 else if(href_list["copy_design"]) //Copy design data from the research holder to the design disk. for(var/datum/design/D in files.known_designs) if(href_list["copy_design_ID"] == D.id) d_disk.blueprint = D break screen = 1.4 else if(href_list["eject_item"]) //Eject the item inside the destructive analyzer. if(linked_destroy) if(linked_destroy.busy) usr << "\red The destructive analyzer is busy at the moment." else if(linked_destroy.loaded_item) linked_destroy.loaded_item.loc = linked_destroy.loc linked_destroy.loaded_item = null linked_destroy.icon_state = "d_analyzer" screen = 2.1 else if(href_list["deconstruct"]) //Deconstruct the item in the destructive analyzer and update the research holder. if(linked_destroy) if(linked_destroy.busy) usr << "\red The destructive analyzer is busy at the moment." else if(alert("Proceeding will destroy loaded item. Continue?", "Destructive analyzer confirmation", "Yes", "No") == "No" || !linked_destroy) return linked_destroy.busy = 1 screen = 0.1 updateUsrDialog() flick("d_analyzer_process", linked_destroy) spawn(24) if(linked_destroy) linked_destroy.busy = 0 if(!linked_destroy.hacked) if(!linked_destroy.loaded_item) usr <<"\red The destructive analyzer appears to be empty." screen = 1.0 return if(linked_destroy.loaded_item.reliability >= linked_destroy.min_reliability) var/list/temp_tech = linked_destroy.ConvertReqString2List(linked_destroy.loaded_item.origin_tech) for(var/T in temp_tech) files.UpdateTech(T, temp_tech[T]) if(linked_destroy.loaded_item.reliability < 100 && linked_destroy.loaded_item.crit_fail) files.UpdateDesign(linked_destroy.loaded_item.type) if(linked_lathe && linked_destroy.loaded_item.matter) //Also sends salvaged materials to a linked protolathe, if any. linked_lathe.m_amount += min((linked_lathe.max_material_storage - linked_lathe.TotalMaterials()), (linked_destroy.loaded_item.matter["metal"]*linked_destroy.decon_mod)) linked_lathe.g_amount += min((linked_lathe.max_material_storage - linked_lathe.TotalMaterials()), (linked_destroy.loaded_item.matter["glass"]*linked_destroy.decon_mod)) linked_destroy.loaded_item = null for(var/obj/I in linked_destroy.contents) for(var/mob/M in I.contents) M.death() if(istype(I,/obj/item/stack/sheet))//Only deconsturcts one sheet at a time instead of the entire stack var/obj/item/stack/sheet/S = I if(S.get_amount() > 1) S.use(1) linked_destroy.loaded_item = S else del(S) linked_destroy.icon_state = "d_analyzer" else if(!(I in linked_destroy.component_parts)) del(I) linked_destroy.icon_state = "d_analyzer" use_power(linked_destroy.active_power_usage) screen = 1.0 updateUsrDialog() else if(href_list["lock"]) //Lock the console from use by anyone without tox access. if(src.allowed(usr)) screen = text2num(href_list["lock"]) else usr << "Unauthorized Access." else if(href_list["sync"]) //Sync the research holder with all the R&D consoles in the game that aren't sync protected. screen = 0.0 if(!sync) usr << "\red You must connect to the network first!" else griefProtection() //Putting this here because I dont trust the sync process spawn(30) if(src) for(var/obj/machinery/r_n_d/server/S in machines) var/server_processed = 0 if(S.disabled) continue if((id in S.id_with_upload) || istype(S, /obj/machinery/r_n_d/server/centcom)) for(var/datum/tech/T in files.known_tech) S.files.AddTech2Known(T) for(var/datum/design/D in files.known_designs) S.files.AddDesign2Known(D) S.files.RefreshResearch() server_processed = 1 if(((id in S.id_with_download) && !istype(S, /obj/machinery/r_n_d/server/centcom)) || S.hacked) for(var/datum/tech/T in S.files.known_tech) files.AddTech2Known(T) for(var/datum/design/D in S.files.known_designs) files.AddDesign2Known(D) files.RefreshResearch() server_processed = 1 if(!istype(S, /obj/machinery/r_n_d/server/centcom) && server_processed) S.produce_heat() screen = 1.6 updateUsrDialog() else if(href_list["togglesync"]) //Prevents the console from being synced by other consoles. Can still send data. sync = !sync else if(href_list["build"]) //Causes the Protolathe to build something. if(linked_lathe) var/datum/design/being_built = null for(var/datum/design/D in files.known_designs) if(D.id == href_list["build"]) being_built = D break if(being_built) var/power = linked_lathe.active_power_usage for(var/M in being_built.materials) power += round(being_built.materials[M] / 5) power = max(linked_lathe.active_power_usage, power) screen = 0.3 linked_lathe.busy = 1 flick("protolathe_n",linked_lathe) var/key = usr.key //so we don't lose the info during the spawn delay spawn(16) use_power(power) spawn(16) errored = 1 for(var/M in being_built.materials) switch(M) if("$metal") linked_lathe.m_amount = max(0, (linked_lathe.m_amount-being_built.materials[M]*linked_lathe.mat_efficiency)) if("$glass") linked_lathe.g_amount = max(0, (linked_lathe.g_amount-being_built.materials[M]*linked_lathe.mat_efficiency)) if("$gold") linked_lathe.gold_amount = max(0, (linked_lathe.gold_amount-being_built.materials[M]*linked_lathe.mat_efficiency)) if("$silver") linked_lathe.silver_amount = max(0, (linked_lathe.silver_amount-being_built.materials[M]*linked_lathe.mat_efficiency)) if("$phoron") linked_lathe.phoron_amount = max(0, (linked_lathe.phoron_amount-being_built.materials[M]*linked_lathe.mat_efficiency)) if("$uranium") linked_lathe.uranium_amount = max(0, (linked_lathe.uranium_amount-being_built.materials[M]*linked_lathe.mat_efficiency)) if("$diamond") linked_lathe.diamond_amount = max(0, (linked_lathe.diamond_amount-being_built.materials[M]*linked_lathe.mat_efficiency)) else linked_lathe.reagents.remove_reagent(M, being_built.materials[M]*linked_lathe.mat_efficiency) if(being_built.build_path) var/obj/new_item = new being_built.build_path(src) if( new_item.type == /obj/item/weapon/storage/backpack/holding ) new_item.investigate_log("built by [key]","singulo") new_item.reliability = being_built.reliability if(linked_lathe.hacked) being_built.reliability = max((reliability / 2), 0) /*if(being_built.locked) var/obj/item/weapon/storage/lockbox/L = new/obj/item/weapon/storage/lockbox(linked_lathe.loc) new_item.loc = L L.name += " ([new_item.name])"*/ else new_item.loc = linked_lathe.loc if(linked_lathe.mat_efficiency != 1) // No matter out of nowhere if(new_item.matter && new_item.matter.len > 0) for(var/i in new_item.matter) new_item.matter[i] = new_item.matter[i] * linked_lathe.mat_efficiency linked_lathe.busy = 0 screen = 3.1 errored = 0 updateUsrDialog() else if(href_list["imprint"]) //Causes the Circuit Imprinter to build something. if(linked_imprinter) var/datum/design/being_built = null for(var/datum/design/D in files.known_designs) if(D.id == href_list["imprint"]) being_built = D break if(being_built) var/power = linked_imprinter.active_power_usage for(var/M in being_built.materials) power += round(being_built.materials[M] / 5) power = max(linked_imprinter.active_power_usage, power) screen = 0.4 linked_imprinter.busy = 1 flick("circuit_imprinter_ani",linked_imprinter) spawn(16) errored = 1 use_power(power) for(var/M in being_built.materials) switch(M) if("$glass") linked_imprinter.g_amount = max(0, (linked_imprinter.g_amount-being_built.materials[M]*linked_imprinter.mat_efficiency)) if("$gold") linked_imprinter.gold_amount = max(0, (linked_imprinter.gold_amount-being_built.materials[M]*linked_imprinter.mat_efficiency)) if("$diamond") linked_imprinter.diamond_amount = max(0, (linked_imprinter.diamond_amount-being_built.materials[M]*linked_imprinter.mat_efficiency)) if("$uranium") linked_imprinter.uranium_amount = max(0, (linked_imprinter.uranium_amount-being_built.materials[M]*linked_imprinter.mat_efficiency)) else linked_imprinter.reagents.remove_reagent(M, being_built.materials[M]*linked_imprinter.mat_efficiency) var/obj/new_item = new being_built.build_path(src) new_item.reliability = being_built.reliability if(linked_imprinter.hacked) being_built.reliability = max((reliability / 2), 0) new_item.loc = linked_imprinter.loc linked_imprinter.busy = 0 screen = 4.1 errored = 0 updateUsrDialog() else if(href_list["disposeI"] && linked_imprinter) //Causes the circuit imprinter to dispose of a single reagent (all of it) linked_imprinter.reagents.del_reagent(href_list["dispose"]) else if(href_list["disposeallI"] && linked_imprinter) //Causes the circuit imprinter to dispose of all it's reagents. linked_imprinter.reagents.clear_reagents() else if(href_list["disposeP"] && linked_lathe) //Causes the protolathe to dispose of a single reagent (all of it) linked_lathe.reagents.del_reagent(href_list["dispose"]) else if(href_list["disposeallP"] && linked_lathe) //Causes the protolathe to dispose of all it's reagents. linked_lathe.reagents.clear_reagents() else if(href_list["lathe_ejectsheet"] && linked_lathe) //Causes the protolathe to eject a sheet of material var/desired_num_sheets = text2num(href_list["amount"]) var/res_amount, type switch(href_list["lathe_ejectsheet"]) if("metal") type = /obj/item/stack/sheet/metal res_amount = "m_amount" if("glass") type = /obj/item/stack/sheet/glass res_amount = "g_amount" if("gold") type = /obj/item/stack/sheet/mineral/gold res_amount = "gold_amount" if("silver") type = /obj/item/stack/sheet/mineral/silver res_amount = "silver_amount" if("phoron") type = /obj/item/stack/sheet/mineral/phoron res_amount = "phoron_amount" if("uranium") type = /obj/item/stack/sheet/mineral/uranium res_amount = "uranium_amount" if("diamond") type = /obj/item/stack/sheet/mineral/diamond res_amount = "diamond_amount" if(ispath(type) && hasvar(linked_lathe, res_amount)) var/obj/item/stack/sheet/sheet = new type(linked_lathe.loc) var/available_num_sheets = round(linked_lathe.vars[res_amount]/sheet.perunit) if(available_num_sheets>0) sheet.amount = min(available_num_sheets, desired_num_sheets) linked_lathe.vars[res_amount] = max(0, (linked_lathe.vars[res_amount]-sheet.amount * sheet.perunit)) else del sheet else if(href_list["imprinter_ejectsheet"] && linked_imprinter) //Causes the protolathe to eject a sheet of material var/desired_num_sheets = text2num(href_list["amount"]) var/res_amount, type switch(href_list["imprinter_ejectsheet"]) if("glass") type = /obj/item/stack/sheet/glass res_amount = "g_amount" if("gold") type = /obj/item/stack/sheet/mineral/gold res_amount = "gold_amount" if("diamond") type = /obj/item/stack/sheet/mineral/diamond res_amount = "diamond_amount" if("uranium") type = /obj/item/stack/sheet/mineral/uranium res_amount = "uranium_amount" if(ispath(type) && hasvar(linked_imprinter, res_amount)) var/obj/item/stack/sheet/sheet = new type(linked_imprinter.loc) var/available_num_sheets = round(linked_imprinter.vars[res_amount]/sheet.perunit) if(available_num_sheets>0) sheet.amount = min(available_num_sheets, desired_num_sheets) linked_imprinter.vars[res_amount] = max(0, (linked_imprinter.vars[res_amount]-sheet.amount * sheet.perunit)) else del sheet else if(href_list["find_device"]) //The R&D console looks for devices nearby to link up with. screen = 0.0 spawn(20) SyncRDevices() screen = 1.7 updateUsrDialog() else if(href_list["disconnect"]) //The R&D console disconnects with a specific device. switch(href_list["disconnect"]) if("destroy") linked_destroy.linked_console = null linked_destroy = null if("lathe") linked_lathe.linked_console = null linked_lathe = null if("imprinter") linked_imprinter.linked_console = null linked_imprinter = null else if(href_list["reset"]) //Reset the R&D console's database. griefProtection() var/choice = alert("R&D Console Database Reset", "Are you sure you want to reset the R&D console's database? Data lost cannot be recovered.", "Continue", "Cancel") if(choice == "Continue") screen = 0.0 del(files) files = new /datum/research(src) spawn(20) screen = 1.6 updateUsrDialog() else if (href_list["print"]) //Print research information screen = 0.5 spawn(20) var/obj/item/weapon/paper/PR = new/obj/item/weapon/paper PR.name = "list of researched technologies" PR.info = "