/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 += "
| [C] | " line_length++ l += "