/obj/machinery/rnd/production name = "technology fabricator" desc = "Makes researched and prototype items with materials and energy." container_type = OPENCONTAINER layer = BELOW_OBJ_LAYER var/consoleless_interface = FALSE //Whether it can be used without a console. var/efficiency_coeff = 1 //Materials needed / coeff = actual. var/list/categories = list() var/datum/component/remote_materials/materials var/allowed_department_flags = ALL var/production_animation //What's flick()'d on print. var/allowed_buildtypes = NONE var/list/datum/design/cached_designs var/list/datum/design/matching_designs var/department_tag = "Unidentified" //used for material distribution among other things. var/datum/techweb/stored_research var/datum/techweb/host_research var/screen = RESEARCH_FABRICATOR_SCREEN_MAIN var/selected_category /obj/machinery/rnd/production/Initialize(mapload) . = ..() create_reagents(0) matching_designs = list() cached_designs = list() stored_research = new host_research = SSresearch.science_tech update_research() materials = AddComponent(/datum/component/remote_materials, "lathe", mapload) RefreshParts() /obj/machinery/rnd/production/proc/update_research() host_research.copy_research_to(stored_research, TRUE) update_designs() /obj/machinery/rnd/production/proc/update_designs() cached_designs.Cut() for(var/i in stored_research.researched_designs) var/datum/design/d = stored_research.researched_designs[i] if((isnull(allowed_department_flags) || (d.departmental_flags & allowed_department_flags)) && (d.build_type & allowed_buildtypes)) cached_designs |= d /obj/machinery/rnd/production/RefreshParts() calculate_efficiency() /obj/machinery/rnd/production/ui_interact(mob/user) if(!consoleless_interface) return ..() user.set_machine(src) var/datum/browser/popup = new(user, "rndconsole", name, 460, 550) popup.set_content(generate_ui()) popup.open() /obj/machinery/rnd/production/Destroy() QDEL_NULL(stored_research) return ..() /obj/machinery/rnd/production/proc/calculate_efficiency() efficiency_coeff = 1 if(reagents) //If reagents/materials aren't initialized, don't bother, we'll be doing this again after reagents init anyways. reagents.maximum_volume = 0 for(var/obj/item/reagent_containers/glass/G in component_parts) reagents.maximum_volume += G.volume G.reagents.trans_to(src, G.reagents.total_volume) if(materials) var/total_storage = 0 for(var/obj/item/stock_parts/matter_bin/M in component_parts) total_storage += M.rating * 75000 materials.set_local_size(total_storage) var/total_rating = 0 for(var/obj/item/stock_parts/manipulator/M in component_parts) total_rating += M.rating total_rating = max(1, total_rating) efficiency_coeff = total_rating //we eject the materials upon deconstruction. /obj/machinery/rnd/production/on_deconstruction() for(var/obj/item/reagent_containers/glass/G in component_parts) reagents.trans_to(G, G.reagents.maximum_volume) return ..() /obj/machinery/rnd/production/proc/do_print(path, amount, list/matlist, notify_admins, mob/user) if(notify_admins) investigate_log("[key_name(user)] built [amount] of [path] at [src]([type]).", INVESTIGATE_RESEARCH) message_admins("[ADMIN_LOOKUPFLW(user)] has built [amount] of [path] at a [src]([type]).") for(var/i in 1 to amount) var/obj/item/I = new path(get_turf(src)) if(efficient_with(I.type)) I.materials = matlist.Copy() SSblackbox.record_feedback("nested tally", "item_printed", amount, list("[type]", "[path]")) /obj/machinery/rnd/production/proc/check_mat(datum/design/being_built, M) // now returns how many times the item can be built with the material if (!materials.mat_container) // no connected silo return 0 var/list/all_materials = being_built.reagents_list + being_built.materials var/A = materials.mat_container.amount(M) if(!A) A = reagents.get_reagent_amount(M) // these types don't have their .materials set in do_print, so don't allow // them to be constructed efficiently var/ef = efficient_with(being_built.build_path) ? efficiency_coeff : 1 return round(A / max(1, all_materials[M] / ef)) /obj/machinery/rnd/production/proc/efficient_with(path) return !ispath(path, /obj/item/stack/sheet) && !ispath(path, /obj/item/stack/ore/bluespace_crystal) /obj/machinery/rnd/production/proc/user_try_print_id(id, amount) if((!istype(linked_console) && requires_console) || !id) return FALSE if(istext(amount)) amount = text2num(amount) if(isnull(amount)) amount = 1 var/datum/design/D = (linked_console || requires_console)? linked_console.stored_research.researched_designs[id] : get_techweb_design_by_id(id) if(!istype(D)) return FALSE if(!(isnull(allowed_department_flags) || (D.departmental_flags & allowed_department_flags))) say("Warning: Printing failed: This fabricator does not have the necessary keys to decrypt design schematics. Please update the research data with the on-screen button and contact Nanotrasen Support!") return FALSE if(D.build_type && !(D.build_type & allowed_buildtypes)) say("This machine does not have the necessary manipulation systems for this design. Please contact Nanotrasen Support!") return FALSE if(!materials.mat_container) say("No connection to material storage, please contact the quartermaster.") return FALSE if(materials.on_hold()) say("Mineral access is on hold, please contact the quartermaster.") return FALSE var/power = 1000 amount = CLAMP(amount, 1, 50) for(var/M in D.materials) power += round(D.materials[M] * amount / 35) power = min(3000, power) use_power(power) var/coeff = efficient_with(D.build_path) ? efficiency_coeff : 1 var/list/efficient_mats = list() for(var/MAT in D.materials) efficient_mats[MAT] = D.materials[MAT]/coeff if(!materials.mat_container.has_materials(efficient_mats, amount)) say("Not enough materials to complete prototype[amount > 1? "s" : ""].") return FALSE for(var/R in D.reagents_list) if(!reagents.has_reagent(R, D.reagents_list[R]*amount/coeff)) say("Not enough reagents to complete prototype[amount > 1? "s" : ""].") return FALSE materials.mat_container.use_amount(efficient_mats, amount) materials.silo_log(src, "built", -amount, "[D.name]", efficient_mats) for(var/R in D.reagents_list) reagents.remove_reagent(R, D.reagents_list[R]*amount/coeff) busy = TRUE if(production_animation) flick(production_animation, src) var/timecoeff = D.lathe_time_factor / efficiency_coeff addtimer(CALLBACK(src, .proc/reset_busy), (30 * timecoeff * amount) ** 0.5) addtimer(CALLBACK(src, .proc/do_print, D.build_path, amount, efficient_mats, D.dangerous_construction, usr), (32 * timecoeff * amount) ** 0.8) return TRUE /obj/machinery/rnd/production/proc/search(string) matching_designs.Cut() for(var/v in stored_research.researched_designs) var/datum/design/D = stored_research.researched_designs[v] if(!(D.build_type & allowed_buildtypes) || !(isnull(allowed_department_flags) || (D.departmental_flags & allowed_department_flags))) continue if(findtext(D.name,string)) matching_designs.Add(D) /obj/machinery/rnd/production/proc/generate_ui() var/list/ui = list() ui += ui_header() switch(screen) if(RESEARCH_FABRICATOR_SCREEN_MATERIALS) ui += ui_screen_materials() if(RESEARCH_FABRICATOR_SCREEN_CHEMICALS) ui += ui_screen_chemicals() if(RESEARCH_FABRICATOR_SCREEN_SEARCH) ui += ui_screen_search() if(RESEARCH_FABRICATOR_SCREEN_CATEGORYVIEW) ui += ui_screen_category_view() else ui += ui_screen_main() for(var/i in 1 to length(ui)) if(!findtextEx(ui[i], RDSCREEN_NOBREAK)) ui[i] += "
" ui[i] = replacetextEx(ui[i], RDSCREEN_NOBREAK, "") return ui.Join("") /obj/machinery/rnd/production/proc/ui_header() var/list/l = list() l += "
[host_research.organization] [department_tag] Department Lathe" l += "Security protocols: [(obj_flags & EMAGGED)? "Disabled" : "Enabled"]" if (materials.mat_container) l += "Material Amount: [materials.format_amount()]" else l += "No material storage connected, please contact the quartermaster." l += "Chemical volume: [reagents.total_volume] / [reagents.maximum_volume]" l += "Synchronize Research" l += "Main Screen
[RDSCREEN_NOBREAK]" return l /obj/machinery/rnd/production/proc/ui_screen_materials() if (!materials.mat_container) screen = RESEARCH_FABRICATOR_SCREEN_MAIN return ui_screen_main() var/list/l = list() l += "

Material Storage:

" for(var/mat_id in materials.mat_container.materials) var/datum/material/M = materials.mat_container.materials[mat_id] l += "* [M.amount] of [M.name]: " if(M.amount >= MINERAL_MATERIAL_AMOUNT) l += "Eject [RDSCREEN_NOBREAK]" if(M.amount >= MINERAL_MATERIAL_AMOUNT*5) l += "5x [RDSCREEN_NOBREAK]" if(M.amount >= MINERAL_MATERIAL_AMOUNT) l += "All[RDSCREEN_NOBREAK]" l += "" l += "
[RDSCREEN_NOBREAK]" return l /obj/machinery/rnd/production/proc/ui_screen_chemicals() var/list/l = list() l += "
Disposal All Chemicals in Storage" l += "

Chemical Storage:

" for(var/datum/reagent/R in reagents.reagent_list) l += "[R.name]: [R.volume]" l += "Purge" l += "
" return l /obj/machinery/rnd/production/proc/ui_screen_search() var/list/l = list() var/coeff = efficiency_coeff l += "

Search Results:

" l += "
\ \ \ \ \

" for(var/datum/design/D in matching_designs) l += design_menu_entry(D, coeff) l += "" return l /obj/machinery/rnd/production/proc/design_menu_entry(datum/design/D, coeff) if(!istype(D)) return if(!coeff) coeff = efficiency_coeff if(!efficient_with(D.build_path)) coeff = 1 var/list/l = list() var/temp_material var/c = 50 var/t var/all_materials = D.materials + D.reagents_list for(var/M in all_materials) t = check_mat(D, M) temp_material += " | " if (t < 1) temp_material += "[all_materials[M]/coeff] [CallMaterialName(M)]" else temp_material += " [all_materials[M]/coeff] [CallMaterialName(M)]" c = min(c,t) if (c >= 1) l += "[D.name][RDSCREEN_NOBREAK]" if(c >= 5) l += "x5[RDSCREEN_NOBREAK]" if(c >= 10) l += "x10[RDSCREEN_NOBREAK]" l += "[temp_material][RDSCREEN_NOBREAK]" else l += "[D.name][temp_material][RDSCREEN_NOBREAK]" l += "" return l /obj/machinery/rnd/production/Topic(raw, ls) if(..()) return add_fingerprint(usr) usr.set_machine(src) if(ls["switch_screen"]) screen = text2num(ls["switch_screen"]) if(ls["build"]) //Causes the Protolathe to build something. if(busy) say("Warning: Fabricators busy!") else user_try_print_id(ls["build"], ls["amount"]) if(ls["search"]) //Search for designs with name matching pattern search(ls["to_search"]) screen = RESEARCH_FABRICATOR_SCREEN_SEARCH if(ls["sync_research"]) update_research() say("Synchronizing research with host technology database.") if(ls["category"]) selected_category = ls["category"] if(ls["dispose"]) //Causes the protolathe to dispose of a single reagent (all of it) reagents.del_reagent(ls["dispose"]) if(ls["disposeall"]) //Causes the protolathe to dispose of all it's reagents. reagents.clear_reagents() if(ls["ejectsheet"]) //Causes the protolathe to eject a sheet of material eject_sheets(ls["ejectsheet"], ls["eject_amt"]) updateUsrDialog() /obj/machinery/rnd/production/proc/eject_sheets(eject_sheet, eject_amt) var/datum/component/material_container/mat_container = materials.mat_container if (!mat_container) say("No access to material storage, please contact the quartermaster.") return 0 if (materials.on_hold()) say("Mineral access is on hold, please contact the quartermaster.") return 0 var/count = mat_container.retrieve_sheets(text2num(eject_amt), eject_sheet, drop_location()) var/list/matlist = list() matlist[eject_sheet] = MINERAL_MATERIAL_AMOUNT materials.silo_log(src, "ejected", -count, "sheets", matlist) return count /obj/machinery/rnd/production/proc/ui_screen_main() var/list/l = list() l += "
\ \ \ \ \ \

" l += list_categories(categories, RESEARCH_FABRICATOR_SCREEN_CATEGORYVIEW) return l /obj/machinery/rnd/production/proc/ui_screen_category_view() if(!selected_category) return ui_screen_main() var/list/l = list() l += "

Browsing [selected_category]:

" var/coeff = efficiency_coeff for(var/v in stored_research.researched_designs) var/datum/design/D = stored_research.researched_designs[v] if(!(selected_category in D.category)|| !(D.build_type & allowed_buildtypes)) continue if(!(isnull(allowed_department_flags) || (D.departmental_flags & allowed_department_flags))) continue l += design_menu_entry(D, coeff) l += "
" return l /obj/machinery/rnd/production/proc/list_categories(list/categories, menu_num) if(!categories) return var/line_length = 1 var/list/l = "" for(var/C in categories) if(line_length > 2) l += "" line_length = 1 l += "" line_length++ l += "
[C]
" return l