mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-07-21 04:57:57 +01:00
TGUI for techfabs (#69353)
This commit is contained in:
@@ -1,12 +1,5 @@
|
||||
#define RDSCREEN_NOBREAK "<NO_HTML_BREAK>"
|
||||
|
||||
//! Defines for the Protolathe screens, see: [/obj/machinery/rnd/production/protolathe]
|
||||
#define RESEARCH_FABRICATOR_SCREEN_MAIN 1
|
||||
#define RESEARCH_FABRICATOR_SCREEN_CHEMICALS 2
|
||||
#define RESEARCH_FABRICATOR_SCREEN_MATERIALS 3
|
||||
#define RESEARCH_FABRICATOR_SCREEN_SEARCH 4
|
||||
#define RESEARCH_FABRICATOR_SCREEN_CATEGORYVIEW 5
|
||||
|
||||
/// For instances where we don't want a design showing up due to it being for debug/sanity purposes
|
||||
#define DESIGN_ID_IGNORE "IGNORE_THIS_DESIGN"
|
||||
|
||||
|
||||
@@ -163,3 +163,10 @@ handles linking back and forth.
|
||||
matlist[material_ref] = eject_amount
|
||||
silo_log(parent, "ejected", -count, "sheets", matlist)
|
||||
return count
|
||||
|
||||
/// Returns `TRUE` if and only if the given material ref can be inserted/removed from this component
|
||||
/datum/component/remote_materials/proc/can_hold_material(datum/material/material_ref)
|
||||
if(!mat_container)
|
||||
return FALSE
|
||||
|
||||
return mat_container.can_hold_material(material_ref)
|
||||
|
||||
@@ -1,449 +1,343 @@
|
||||
|
||||
/obj/machinery/rnd/production
|
||||
name = "technology fabricator"
|
||||
desc = "Makes researched and prototype items with materials and energy."
|
||||
layer = BELOW_OBJ_LAYER
|
||||
/// Materials needed / coeff = actual.
|
||||
|
||||
/// The efficiency coefficient. Material costs and print times are multiplied by this number;
|
||||
/// better parts result in a higher efficiency (and lower value).
|
||||
var/efficiency_coeff = 1
|
||||
var/list/categories = list()
|
||||
|
||||
/// The material storage used by this fabricator.
|
||||
var/datum/component/remote_materials/materials
|
||||
|
||||
/// Which departments forego the lathe tax when using this lathe.
|
||||
var/allowed_department_flags = ALL
|
||||
|
||||
/// What's flick()'d on print.
|
||||
var/production_animation
|
||||
var/allowed_buildtypes = NONE
|
||||
var/list/datum/design/cached_designs
|
||||
var/list/datum/design/matching_designs
|
||||
/// Used for material distribution among other things.
|
||||
var/department_tag = "Unidentified"
|
||||
|
||||
var/screen = RESEARCH_FABRICATOR_SCREEN_MAIN
|
||||
var/selected_category
|
||||
/// The types of designs this fabricator can print.
|
||||
var/allowed_buildtypes = NONE
|
||||
|
||||
/// All designs in the techweb that can be fabricated by this machine, since the last update.
|
||||
var/list/datum/design/cached_designs
|
||||
|
||||
/// The department this fabricator is assigned to.
|
||||
var/department_tag = "Unassigned"
|
||||
|
||||
/// What color is this machine's stripe? Leave null to not have a stripe.
|
||||
var/stripe_color = null
|
||||
|
||||
/// Does this charge the user's ID on fabrication?
|
||||
var/charges_tax = TRUE
|
||||
|
||||
/obj/machinery/rnd/production/Initialize(mapload)
|
||||
. = ..()
|
||||
create_reagents(0, OPENCONTAINER)
|
||||
matching_designs = list()
|
||||
|
||||
cached_designs = list()
|
||||
update_designs()
|
||||
materials = AddComponent(/datum/component/remote_materials, "lathe", mapload, mat_container_flags=BREAKDOWN_FLAGS_LATHE)
|
||||
AddComponent(/datum/component/payment, 0, SSeconomy.get_dep_account(payment_department), PAYMENT_CLINICAL, TRUE)
|
||||
|
||||
create_reagents(0, OPENCONTAINER)
|
||||
update_designs()
|
||||
RefreshParts()
|
||||
update_icon(UPDATE_OVERLAYS)
|
||||
|
||||
/obj/machinery/rnd/production/Destroy()
|
||||
materials = null
|
||||
cached_designs = null
|
||||
matching_designs = null
|
||||
return ..()
|
||||
|
||||
/// Updates the list of designs this fabricator can print.
|
||||
/obj/machinery/rnd/production/proc/update_designs()
|
||||
cached_designs.Cut()
|
||||
for(var/i in stored_research.researched_designs)
|
||||
var/datum/design/d = SSresearch.techweb_design_by_id(i)
|
||||
if((isnull(allowed_department_flags) || (d.departmental_flags & allowed_department_flags)) && (d.build_type & allowed_buildtypes))
|
||||
cached_designs |= d
|
||||
|
||||
for(var/design_id in stored_research.researched_designs)
|
||||
var/datum/design/design = SSresearch.techweb_design_by_id(design_id)
|
||||
|
||||
if((isnull(allowed_department_flags) || (design.departmental_flags & allowed_department_flags)) && (design.build_type & allowed_buildtypes))
|
||||
cached_designs |= design
|
||||
|
||||
update_static_data(usr)
|
||||
|
||||
say("Successfully synchronized with R&D server.")
|
||||
|
||||
/obj/machinery/rnd/production/RefreshParts()
|
||||
. = ..()
|
||||
|
||||
calculate_efficiency()
|
||||
update_static_data(usr)
|
||||
|
||||
/obj/machinery/rnd/production/ui_interact(mob/user)
|
||||
/obj/machinery/rnd/production/ui_assets(mob/user)
|
||||
return list(
|
||||
get_asset_datum(/datum/asset/spritesheet/sheetmaterials),
|
||||
)
|
||||
|
||||
/obj/machinery/rnd/production/ui_interact(mob/user, datum/tgui/ui)
|
||||
user.set_machine(src)
|
||||
var/datum/browser/popup = new(user, "rndconsole", name, 460, 550)
|
||||
popup.set_content(generate_ui())
|
||||
popup.open()
|
||||
|
||||
ui = SStgui.try_update_ui(user, src, ui)
|
||||
|
||||
if(!ui)
|
||||
ui = new(user, src, "Fabricator")
|
||||
ui.open()
|
||||
|
||||
/obj/machinery/rnd/production/ui_static_data(mob/user)
|
||||
var/list/data = list()
|
||||
var/list/designs = list()
|
||||
|
||||
for(var/datum/design/design in cached_designs)
|
||||
var/cost = list()
|
||||
|
||||
for(var/datum/material/material in design.materials)
|
||||
var/coefficient = efficient_with(design.build_path) ? efficiency_coeff : 1
|
||||
cost[material.name] = design.materials[material] * coefficient
|
||||
|
||||
designs[design.id] = list(
|
||||
"name" = design.name,
|
||||
"desc" = design.get_description(),
|
||||
"cost" = cost,
|
||||
"id" = design.id,
|
||||
"categories" = design.category
|
||||
)
|
||||
|
||||
data["designs"] = designs
|
||||
data["fab_name"] = name
|
||||
|
||||
return data
|
||||
|
||||
/obj/machinery/rnd/production/ui_data(mob/user)
|
||||
var/list/data = list()
|
||||
|
||||
data["materials"] = materials.mat_container?.ui_data()
|
||||
data["on_hold"] = materials.on_hold()
|
||||
data["busy"] = busy
|
||||
|
||||
return data
|
||||
|
||||
/obj/machinery/rnd/production/ui_act(action, list/params)
|
||||
. = ..()
|
||||
|
||||
if(.)
|
||||
return
|
||||
|
||||
. = TRUE
|
||||
|
||||
switch (action)
|
||||
if("remove_mat")
|
||||
var/datum/material/material = locate(params["ref"])
|
||||
|
||||
if(!materials.can_hold_material(material))
|
||||
// I don't know who you are or what you want, but whatever it is,
|
||||
// we don't have it.
|
||||
return
|
||||
|
||||
eject_sheets(material, params["amount"])
|
||||
|
||||
if("sync_rnd")
|
||||
update_designs()
|
||||
|
||||
if("build")
|
||||
user_try_print_id(params["ref"], params["amount"])
|
||||
|
||||
/// Updates the fabricator's efficiency coefficient based on the installed parts.
|
||||
/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.
|
||||
|
||||
if(reagents)
|
||||
reagents.maximum_volume = 0
|
||||
for(var/obj/item/reagent_containers/cup/G in component_parts)
|
||||
reagents.maximum_volume += G.volume
|
||||
G.reagents.trans_to(src, G.reagents.total_volume)
|
||||
|
||||
for(var/obj/item/reagent_containers/cup/beaker in component_parts)
|
||||
reagents.maximum_volume += beaker.volume
|
||||
beaker.reagents.trans_to(src, beaker.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 = 1.2
|
||||
for(var/obj/item/stock_parts/manipulator/M in component_parts)
|
||||
total_rating = clamp(total_rating - (M.rating * 0.1), 0, 1)
|
||||
if(total_rating == 0)
|
||||
efficiency_coeff = INFINITY
|
||||
else
|
||||
efficiency_coeff = 1/total_rating
|
||||
|
||||
//we eject the materials upon deconstruction.
|
||||
for(var/obj/item/stock_parts/matter_bin/bin in component_parts)
|
||||
total_storage += bin.rating * 75000
|
||||
|
||||
materials.set_local_size(total_storage)
|
||||
|
||||
var/total_rating = 1.2
|
||||
|
||||
for(var/obj/item/stock_parts/manipulator/manipulator in component_parts)
|
||||
total_rating -= manipulator.rating * 0.1
|
||||
|
||||
efficiency_coeff = max(total_rating, 0)
|
||||
|
||||
/obj/machinery/rnd/production/on_deconstruction()
|
||||
for(var/obj/item/reagent_containers/cup/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)
|
||||
if(notify_admins)
|
||||
investigate_log("[key_name(usr)] built [amount] of [path] at [src]([type]).", INVESTIGATE_RESEARCH)
|
||||
message_admins("[ADMIN_LOOKUPFLW(usr)] has built [amount] of [path] at \a [src]([type]).")
|
||||
|
||||
for(var/i in 1 to amount)
|
||||
new path(get_turf(src))
|
||||
|
||||
SSblackbox.record_feedback("nested tally", "item_printed", amount, list("[type]", "[path]"))
|
||||
|
||||
/**
|
||||
* Returns how many times over the given material requirement for the given design is satisfied.
|
||||
*
|
||||
* Arguments:
|
||||
* - [being_built][/datum/design]: The design being referenced.
|
||||
* - material: The material being checked.
|
||||
*/
|
||||
/obj/machinery/rnd/production/proc/check_material_req(datum/design/being_built, material)
|
||||
if(!materials.mat_container) // no connected silo
|
||||
return 0
|
||||
|
||||
var/mat_amt = materials.mat_container.get_material_amount(material)
|
||||
if(!mat_amt)
|
||||
return 0
|
||||
|
||||
// these types don't have their .materials set in do_print, so don't allow
|
||||
// them to be constructed efficiently
|
||||
var/efficiency = efficient_with(being_built.build_path) ? efficiency_coeff : 1
|
||||
return round(mat_amt / max(1, being_built.materials[material] / efficiency))
|
||||
|
||||
/**
|
||||
* Returns how many times over the given reagent requirement for the given design is satisfied.
|
||||
*
|
||||
* Arguments:
|
||||
* - [being_built][/datum/design]: The design being referenced.
|
||||
* - reagent: The reagent being checked.
|
||||
*/
|
||||
/obj/machinery/rnd/production/proc/check_reagent_req(datum/design/being_built, reagent)
|
||||
if(!reagents) // no reagent storage
|
||||
return 0
|
||||
|
||||
var/chem_amt = reagents.get_reagent_amount(reagent)
|
||||
if(!chem_amt)
|
||||
return 0
|
||||
|
||||
// these types don't have their .materials set in do_print, so don't allow
|
||||
// them to be constructed efficiently
|
||||
var/efficiency = efficient_with(being_built.build_path) ? efficiency_coeff : 1
|
||||
return round(chem_amt / max(1, being_built.reagents_list[reagent] / efficiency))
|
||||
|
||||
/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, print_quantity)
|
||||
if(!id)
|
||||
/obj/machinery/rnd/production/proc/user_try_print_id(design_id, print_quantity)
|
||||
if(!design_id)
|
||||
return FALSE
|
||||
|
||||
if(istext(print_quantity))
|
||||
print_quantity = text2num(print_quantity)
|
||||
|
||||
if(isnull(print_quantity))
|
||||
print_quantity = 1
|
||||
var/datum/design/D = stored_research.researched_designs[id] ? SSresearch.techweb_design_by_id(id) : null
|
||||
if(!istype(D))
|
||||
|
||||
var/datum/design/design = stored_research.researched_designs[design_id] ? SSresearch.techweb_design_by_id(design_id) : null
|
||||
|
||||
if(!istype(design))
|
||||
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!")
|
||||
|
||||
if(busy)
|
||||
say("Warning: fabricator is busy!")
|
||||
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!")
|
||||
|
||||
if(!(isnull(allowed_department_flags) || (design.departmental_flags & allowed_department_flags)))
|
||||
say("This fabricator does not have the necessary keys to decrypt this design.")
|
||||
return FALSE
|
||||
|
||||
if(design.build_type && !(design.build_type & allowed_buildtypes))
|
||||
say("This fabricator does not have the necessary manipulation systems for this design.")
|
||||
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 = active_power_usage
|
||||
|
||||
print_quantity = clamp(print_quantity, 1, 50)
|
||||
for(var/M in D.materials)
|
||||
power += round(D.materials[M] * print_quantity / 35)
|
||||
|
||||
for(var/material in design.materials)
|
||||
power += round(design.materials[material] * print_quantity / 35)
|
||||
|
||||
power = min(active_power_usage, power)
|
||||
use_power(power)
|
||||
var/coeff = efficient_with(D.build_path) ? efficiency_coeff : 1
|
||||
|
||||
var/coefficient = efficient_with(design.build_path) ? efficiency_coeff : 1
|
||||
var/list/efficient_mats = list()
|
||||
for(var/MAT in D.materials)
|
||||
efficient_mats[MAT] = D.materials[MAT]/coeff
|
||||
|
||||
for(var/material in design.materials)
|
||||
efficient_mats[material] = design.materials[material] * coefficient
|
||||
|
||||
if(!materials.mat_container.has_materials(efficient_mats, print_quantity))
|
||||
say("Not enough materials to complete prototype[print_quantity > 1? "s" : ""].")
|
||||
return FALSE
|
||||
for(var/R in D.reagents_list)
|
||||
if(!reagents.has_reagent(R, D.reagents_list[R]*print_quantity/coeff))
|
||||
|
||||
for(var/reagent in design.reagents_list)
|
||||
if(!reagents.has_reagent(reagent, design.reagents_list[reagent] * print_quantity * coefficient))
|
||||
say("Not enough reagents to complete prototype[print_quantity > 1? "s" : ""].")
|
||||
return FALSE
|
||||
var/total_cost = LATHE_TAX
|
||||
|
||||
// Charge the lathe tax at least once per ten items.
|
||||
var/total_cost = LATHE_TAX * max(round(print_quantity / 10), 1)
|
||||
|
||||
if(!charges_tax)
|
||||
total_cost = 0
|
||||
|
||||
if(isliving(usr))
|
||||
var/mob/living/user = usr
|
||||
var/obj/item/card/id/card = user.get_idcard(TRUE)
|
||||
|
||||
if(!card && istype(user.pulling, /obj/item/card/id))
|
||||
card = user.pulling
|
||||
|
||||
if(card && card.registered_account)
|
||||
var/datum/bank_account/our_acc = card.registered_account
|
||||
if(our_acc.account_job.departments_bitflags & allowed_department_flags)
|
||||
total_cost = 0 //We are not charging crew for printing their own supplies and equipment.
|
||||
total_cost = 0 // We are not charging crew for printing their own supplies and equipment.
|
||||
|
||||
if(attempt_charge(src, usr, total_cost) & COMPONENT_OBJ_CANCEL_CHARGE)
|
||||
say("Insufficient funds to complete prototype. Please present a holochip or valid ID card.")
|
||||
return FALSE
|
||||
|
||||
if(iscyborg(usr))
|
||||
var/mob/living/silicon/robot/borg = usr
|
||||
|
||||
if(!borg.cell)
|
||||
return
|
||||
return FALSE
|
||||
|
||||
borg.cell.use(SILICON_LATHE_TAX)
|
||||
|
||||
materials.mat_container.use_materials(efficient_mats, print_quantity)
|
||||
materials.silo_log(src, "built", -print_quantity, "[D.name]", efficient_mats)
|
||||
for(var/R in D.reagents_list)
|
||||
reagents.remove_reagent(R, D.reagents_list[R]*print_quantity/coeff)
|
||||
materials.silo_log(src, "built", -print_quantity, "[design.name]", efficient_mats)
|
||||
|
||||
for(var/reagent in design.reagents_list)
|
||||
reagents.remove_reagent(reagent, design.reagents_list[reagent] * print_quantity * coefficient)
|
||||
|
||||
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 * print_quantity) ** 0.5)
|
||||
addtimer(CALLBACK(src, .proc/do_print, D.build_path, print_quantity, efficient_mats, D.dangerous_construction), (32 * timecoeff * print_quantity) ** 0.8)
|
||||
|
||||
var/time_coefficient = design.lathe_time_factor * efficiency_coeff
|
||||
|
||||
addtimer(CALLBACK(src, .proc/reset_busy), (30 * time_coefficient * print_quantity) ** 0.5)
|
||||
addtimer(CALLBACK(src, .proc/do_print, design.build_path, print_quantity, efficient_mats, design.dangerous_construction), (32 * time_coefficient * print_quantity) ** 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 = SSresearch.techweb_design_by_id(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] += "<br>"
|
||||
ui[i] = replacetextEx(ui[i], RDSCREEN_NOBREAK, "")
|
||||
return ui.Join("")
|
||||
|
||||
/obj/machinery/rnd/production/proc/ui_header()
|
||||
var/list/l = list()
|
||||
l += "<div class='statusDisplay'><b>[stored_research.organization] [department_tag] Department Lathe</b>"
|
||||
l += "Security protocols: [(obj_flags & EMAGGED)? "<font color='red'>Disabled</font>" : "<font color='green'>Enabled</font>"]"
|
||||
if (materials.mat_container)
|
||||
l += "<A href='?src=[REF(src)];switch_screen=[RESEARCH_FABRICATOR_SCREEN_MATERIALS]'><B>Material Amount:</B> [materials.format_amount()]</A>"
|
||||
else
|
||||
l += "<font color='red'>No material storage connected, please contact the quartermaster.</font>"
|
||||
l += "<A href='?src=[REF(src)];switch_screen=[RESEARCH_FABRICATOR_SCREEN_CHEMICALS]'><B>Chemical volume:</B> [reagents.total_volume] / [reagents.maximum_volume]</A>"
|
||||
l += "<a href='?src=[REF(src)];sync_research=1'>Synchronize Research</a>"
|
||||
l += "<a href='?src=[REF(src)];switch_screen=[RESEARCH_FABRICATOR_SCREEN_MAIN]'>Main Screen</a></div>[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 += "<div class='statusDisplay'><h3>Material Storage:</h3>"
|
||||
for(var/mat_id in materials.mat_container.materials)
|
||||
var/datum/material/M = mat_id
|
||||
var/amount = materials.mat_container.materials[mat_id]
|
||||
var/ref = REF(M)
|
||||
l += "* [amount] of [M.name]: "
|
||||
if(amount >= MINERAL_MATERIAL_AMOUNT) l += "<A href='?src=[REF(src)];ejectsheet=[ref];eject_amt=1'>Eject</A> [RDSCREEN_NOBREAK]"
|
||||
if(amount >= MINERAL_MATERIAL_AMOUNT*5) l += "<A href='?src=[REF(src)];ejectsheet=[ref];eject_amt=5'>5x</A> [RDSCREEN_NOBREAK]"
|
||||
if(amount >= MINERAL_MATERIAL_AMOUNT) l += "<A href='?src=[REF(src)];ejectsheet=[ref];eject_amt=50'>All</A>[RDSCREEN_NOBREAK]"
|
||||
l += ""
|
||||
l += "</div>[RDSCREEN_NOBREAK]"
|
||||
return l
|
||||
|
||||
/obj/machinery/rnd/production/proc/ui_screen_chemicals()
|
||||
var/list/l = list()
|
||||
l += "<div class='statusDisplay'><A href='?src=[REF(src)];disposeall=1'>Disposal All Chemicals in Storage</A>"
|
||||
l += "<h3>Chemical Storage:</h3>"
|
||||
for(var/datum/reagent/R in reagents.reagent_list)
|
||||
l += "[R.name]: [R.volume]"
|
||||
l += "<A href='?src=[REF(src)];dispose=[R.type]'>Purge</A>"
|
||||
l += "</div>"
|
||||
return l
|
||||
|
||||
/obj/machinery/rnd/production/proc/ui_screen_search()
|
||||
var/list/l = list()
|
||||
var/coeff = efficiency_coeff
|
||||
l += "<h2>Search Results:</h2>"
|
||||
l += "<form name='search' action='?src=[REF(src)]'>\
|
||||
<input type='hidden' name='src' value='[REF(src)]'>\
|
||||
<input type='hidden' name='search' value='to_search'>\
|
||||
<input type='text' name='to_search'>\
|
||||
<input type='submit' value='Search'>\
|
||||
</form><HR>"
|
||||
for(var/datum/design/D in matching_designs)
|
||||
l += design_menu_entry(D, coeff)
|
||||
l += "</div>"
|
||||
return l
|
||||
|
||||
/obj/machinery/rnd/production/proc/design_menu_entry(datum/design/D, coeff)
|
||||
if(!istype(D))
|
||||
return
|
||||
if(!efficient_with(D.build_path))
|
||||
coeff = 1
|
||||
else if(!coeff)
|
||||
coeff = efficiency_coeff
|
||||
|
||||
var/list/entry_text = list()
|
||||
var/temp_material
|
||||
var/max_production = 50
|
||||
var/list/cached_mats = D.materials
|
||||
for(var/material in cached_mats)
|
||||
var/enough_mats = check_material_req(D, material)
|
||||
max_production = min(max_production, enough_mats)
|
||||
|
||||
temp_material += " | "
|
||||
if (enough_mats < 1)
|
||||
temp_material += "<span class='bad'>[cached_mats[material]/coeff] [CallMaterialName(material)]</span>"
|
||||
else
|
||||
temp_material += " [cached_mats[material]/coeff] [CallMaterialName(material)]"
|
||||
|
||||
var/list/cached_reagents = D.reagents_list
|
||||
for(var/reagent in cached_reagents)
|
||||
var/enough_chems = check_reagent_req(D, reagent)
|
||||
max_production = min(max_production, enough_chems)
|
||||
|
||||
temp_material += " | "
|
||||
if (enough_chems < 1)
|
||||
temp_material += "<span class='bad'>[cached_reagents[reagent]/coeff] [CallMaterialName(reagent)]</span>"
|
||||
else
|
||||
temp_material += " [cached_reagents[reagent]/coeff] [CallMaterialName(reagent)]"
|
||||
|
||||
if (max_production >= 1)
|
||||
entry_text += "<A href='?src=[REF(src)];build=[D.id];amount=1'>[D.name]</A>[RDSCREEN_NOBREAK]"
|
||||
if(max_production >= 5)
|
||||
entry_text += "<A href='?src=[REF(src)];build=[D.id];amount=5'>x5</A>[RDSCREEN_NOBREAK]"
|
||||
if(max_production >= 10)
|
||||
entry_text += "<A href='?src=[REF(src)];build=[D.id];amount=10'>x10</A>[RDSCREEN_NOBREAK]"
|
||||
entry_text += "[temp_material][RDSCREEN_NOBREAK]"
|
||||
else
|
||||
entry_text += "<span class='linkOff'>[D.name]</span>[temp_material][RDSCREEN_NOBREAK]"
|
||||
entry_text += ""
|
||||
return entry_text
|
||||
|
||||
/obj/machinery/rnd/production/Topic(raw, ls)
|
||||
if(..())
|
||||
return
|
||||
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_designs()
|
||||
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)
|
||||
var/reagent_path = text2path(ls["dispose"])
|
||||
if(!ispath(reagent_path, /datum/reagent))
|
||||
stack_trace("Invalid reagent typepath - [ls["dispose"]] - returned in reagent disposal topic call")
|
||||
else
|
||||
reagents.del_reagent(reagent_path)
|
||||
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
|
||||
var/datum/material/M = locate(ls["ejectsheet"])
|
||||
eject_sheets(M, 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)
|
||||
|
||||
if(!mat_container)
|
||||
say("No access to material storage, please contact the quartermaster.")
|
||||
return 0
|
||||
if (materials.on_hold())
|
||||
|
||||
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 += "<form name='search' action='?src=[REF(src)]'>\
|
||||
<input type='hidden' name='src' value='[REF(src)]'>\
|
||||
<input type='hidden' name='search' value='to_search'>\
|
||||
<input type='hidden' name='type' value='proto'>\
|
||||
<input type='text' name='to_search'>\
|
||||
<input type='submit' value='Search'>\
|
||||
</form><HR>"
|
||||
|
||||
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 += "<div class='statusDisplay'><h3>Browsing [selected_category]:</h3>"
|
||||
var/coeff = efficiency_coeff
|
||||
for(var/v in stored_research.researched_designs)
|
||||
var/datum/design/D = SSresearch.techweb_design_by_id(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 += "</div>"
|
||||
return l
|
||||
|
||||
/obj/machinery/rnd/production/proc/list_categories(list/categories, menu_num)
|
||||
if(!categories)
|
||||
return
|
||||
|
||||
var/line_length = 1
|
||||
var/list/l = "<table style='width:100%' align='center'><tr>"
|
||||
|
||||
for(var/C in categories)
|
||||
if(line_length > 2)
|
||||
l += "</tr><tr>"
|
||||
line_length = 1
|
||||
|
||||
l += "<td><A href='?src=[REF(src)];category=[C];switch_screen=[menu_num]'>[C]</A></td>"
|
||||
line_length++
|
||||
|
||||
l += "</tr></table></div>"
|
||||
return l
|
||||
|
||||
// Stuff for the stripe on the department machines
|
||||
/obj/machinery/rnd/production/default_deconstruction_screwdriver(mob/user, icon_state_open, icon_state_closed, obj/item/screwdriver)
|
||||
. = ..()
|
||||
|
||||
update_icon(UPDATE_OVERLAYS)
|
||||
|
||||
/obj/machinery/rnd/production/update_overlays()
|
||||
. = ..()
|
||||
|
||||
if(!stripe_color)
|
||||
return
|
||||
|
||||
var/mutable_appearance/stripe = mutable_appearance('icons/obj/machines/research.dmi', "protolate_stripe")
|
||||
|
||||
if(!panel_open)
|
||||
stripe.icon_state = "protolathe_stripe"
|
||||
else
|
||||
stripe.icon_state = "protolathe_stripe_t"
|
||||
|
||||
stripe.color = stripe_color
|
||||
|
||||
. += stripe
|
||||
|
||||
/obj/machinery/rnd/production/examine(mob/user)
|
||||
. = ..()
|
||||
|
||||
if(in_range(user, src) || isobserver(user))
|
||||
. += span_notice("The status display reads: Storing up to <b>[materials.local_size]</b> material units.<br>Material consumption at <b>[efficiency_coeff * 100]%</b>.<br>Build time reduced by <b>[100 - efficiency_coeff * 100]%</b>.")
|
||||
|
||||
@@ -3,30 +3,18 @@
|
||||
desc = "Manufactures circuit boards for the construction of machines."
|
||||
icon_state = "circuit_imprinter"
|
||||
circuit = /obj/item/circuitboard/machine/circuit_imprinter
|
||||
categories = list(
|
||||
RND_CATEGORY_AI_MODULES,
|
||||
RND_CATEGORY_COMPUTER_BOARDS,
|
||||
RND_CATEGORY_TELEPORTATION_MACHINERY,
|
||||
RND_CATEGORY_MEDICAL_MACHINERY,
|
||||
RND_CATEGORY_ENGINEERING_MACHINERY,
|
||||
RND_CATEGORY_EXOSUIT_MODULES,
|
||||
RND_CATEGORY_HYDROPONICS_MACHINERY,
|
||||
RND_CATEGORY_SUBSPACE_TELECOMMS,
|
||||
RND_CATEGORY_RESEARCH_MACHINERY,
|
||||
RND_CATEGORY_MISC_MACHINERY,
|
||||
RND_CATEGORY_COMPUTER_PARTS,
|
||||
RND_CATEGORY_CIRCUITRY
|
||||
)
|
||||
production_animation = "circuit_imprinter_ani"
|
||||
allowed_buildtypes = IMPRINTER
|
||||
|
||||
/obj/machinery/rnd/production/circuit_imprinter/calculate_efficiency()
|
||||
. = ..()
|
||||
var/total_rating = 0
|
||||
for(var/obj/item/stock_parts/manipulator/M in component_parts)
|
||||
total_rating += M.rating * 2 //There is only one.
|
||||
total_rating = max(1, total_rating)
|
||||
efficiency_coeff = total_rating
|
||||
|
||||
var/rating = 0
|
||||
|
||||
for(var/obj/item/stock_parts/manipulator/manipulator in component_parts)
|
||||
rating += manipulator.rating // There is only one.
|
||||
|
||||
efficiency_coeff = 0.5 ** max(rating - 1, 0) // One sheet, half sheet, quarter sheet, eighth sheet.
|
||||
|
||||
/obj/machinery/rnd/production/circuit_imprinter/offstation
|
||||
name = "ancient circuit imprinter"
|
||||
|
||||
@@ -3,21 +3,6 @@
|
||||
desc = "Converts raw materials into useful objects."
|
||||
icon_state = "protolathe"
|
||||
circuit = /obj/item/circuitboard/machine/protolathe
|
||||
categories = list(
|
||||
RND_CATEGORY_POWER_DESIGNS,
|
||||
RND_CATEGORY_MEDICAL_DESIGNS,
|
||||
RND_CATEGORY_BLUESPACE_DESIGNS,
|
||||
RND_CATEGORY_STOCK_PARTS,
|
||||
RND_CATEGORY_EQUIPMENT,
|
||||
RND_CATEGORY_TOOL_DESIGNS,
|
||||
RND_CATEGORY_MINING_DESIGNS,
|
||||
RND_CATEGORY_ELECTRONICS,
|
||||
RND_CATEGORY_WEAPONS,
|
||||
RND_CATEGORY_AMMO,
|
||||
RND_CATEGORY_FIRING_PINS,
|
||||
RND_CATEGORY_COMPUTER_PARTS,
|
||||
RND_CATEGORY_CIRCUITRY
|
||||
)
|
||||
production_animation = "protolathe_n"
|
||||
allowed_buildtypes = PROTOLATHE
|
||||
|
||||
|
||||
@@ -3,32 +3,6 @@
|
||||
desc = "Produces researched prototypes with raw materials and energy."
|
||||
icon_state = "protolathe"
|
||||
circuit = /obj/item/circuitboard/machine/techfab
|
||||
categories = list(
|
||||
RND_CATEGORY_POWER_DESIGNS,
|
||||
RND_CATEGORY_MEDICAL_DESIGNS,
|
||||
RND_CATEGORY_BLUESPACE_DESIGNS,
|
||||
RND_CATEGORY_STOCK_PARTS,
|
||||
RND_CATEGORY_EQUIPMENT,
|
||||
RND_CATEGORY_TOOL_DESIGNS,
|
||||
RND_CATEGORY_MINING_DESIGNS,
|
||||
RND_CATEGORY_ELECTRONICS,
|
||||
RND_CATEGORY_WEAPONS,
|
||||
RND_CATEGORY_AMMO,
|
||||
RND_CATEGORY_FIRING_PINS,
|
||||
RND_CATEGORY_COMPUTER_PARTS,
|
||||
RND_CATEGORY_AI_MODULES,
|
||||
RND_CATEGORY_COMPUTER_BOARDS,
|
||||
RND_CATEGORY_TELEPORTATION_MACHINERY,
|
||||
RND_CATEGORY_MEDICAL_MACHINERY,
|
||||
RND_CATEGORY_ENGINEERING_MACHINERY,
|
||||
RND_CATEGORY_EXOSUIT_MODULES,
|
||||
RND_CATEGORY_HYDROPONICS_MACHINERY,
|
||||
RND_CATEGORY_SUBSPACE_TELECOMMS,
|
||||
RND_CATEGORY_RESEARCH_MACHINERY,
|
||||
RND_CATEGORY_MISC_MACHINERY,
|
||||
RND_CATEGORY_COMPUTER_PARTS,
|
||||
RND_CATEGORY_CIRCUITRY
|
||||
)
|
||||
console_link = FALSE
|
||||
production_animation = "protolathe_n"
|
||||
allowed_buildtypes = PROTOLATHE | IMPRINTER
|
||||
|
||||
@@ -0,0 +1,408 @@
|
||||
import { useBackend, useSharedState } from '../backend';
|
||||
import { Stack, Section, Button, Input, Icon, Tabs, Dimmer } from '../components';
|
||||
import { Window } from '../layouts';
|
||||
import { Material, MaterialAmount, MaterialFormatting, Materials, MATERIAL_KEYS } from './common/Materials';
|
||||
import { Fragment } from 'inferno';
|
||||
import { sortBy } from 'common/collections';
|
||||
|
||||
type MaterialMap = Partial<Record<keyof typeof MATERIAL_KEYS, number>>;
|
||||
|
||||
/**
|
||||
* A single design that the fabricator can print.
|
||||
*/
|
||||
type Design = {
|
||||
/**
|
||||
* The name of the design.
|
||||
*/
|
||||
name: string;
|
||||
|
||||
/**
|
||||
* A human-readable description of the design.
|
||||
*/
|
||||
desc: string;
|
||||
|
||||
/**
|
||||
* The individual material cost to print the design, adjusted for the
|
||||
* fabricator's part efficiency.
|
||||
*/
|
||||
cost: MaterialMap;
|
||||
|
||||
/**
|
||||
* A reference to the design's design datum.
|
||||
*/
|
||||
id: string;
|
||||
|
||||
/**
|
||||
* The categories the design should be present in.
|
||||
*/
|
||||
categories?: string[];
|
||||
};
|
||||
|
||||
type FabricatorData = {
|
||||
/**
|
||||
* The materials available to the fabricator, via ore silo or local storage.
|
||||
*/
|
||||
materials: Material[];
|
||||
|
||||
/**
|
||||
* The name of the fabricator, as displayed on the title bar.
|
||||
*/
|
||||
fab_name: string;
|
||||
|
||||
/**
|
||||
* Whether mineral access is disabled from the ore silo (contact the
|
||||
* quartermaster).
|
||||
*/
|
||||
on_hold: boolean;
|
||||
|
||||
/**
|
||||
* The set of designs that this fabricator can print, ordered by their ID.
|
||||
*/
|
||||
designs: Record<string, Design>;
|
||||
|
||||
/**
|
||||
* Whether the fabricator is currently printing an item.
|
||||
*/
|
||||
busy: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* Categories present in this object are not rendered to the final fabricator
|
||||
* UI.
|
||||
*/
|
||||
const BLACKLISTED_CATEGORIES: Record<string, boolean> = {
|
||||
'initial': true,
|
||||
'core': true,
|
||||
'hacked': true,
|
||||
};
|
||||
|
||||
/**
|
||||
* A dummy category that, when selected, renders ALL recipes to the UI.
|
||||
*/
|
||||
const ALL_CATEGORY = '__ALL';
|
||||
|
||||
export const Fabricator = (props, context) => {
|
||||
const { act, data } = useBackend<FabricatorData>(context);
|
||||
const { materials, fab_name, on_hold, designs, busy } = data;
|
||||
|
||||
const [selectedCategory, setSelectedCategory] = useSharedState(
|
||||
context,
|
||||
'selected_category',
|
||||
ALL_CATEGORY
|
||||
);
|
||||
const [searchText, setSearchText] = useSharedState(
|
||||
context,
|
||||
'search_text',
|
||||
''
|
||||
);
|
||||
const [displayMatCost, setDisplayMatCost] = useSharedState(
|
||||
context,
|
||||
'display_material_cost',
|
||||
true
|
||||
);
|
||||
|
||||
// Sort the designs by name.
|
||||
const sortedDesigns = sortBy((design: Design) => design.name)(
|
||||
Object.values(designs)
|
||||
);
|
||||
|
||||
// Find the number of items in each unique category, and the sum total of all
|
||||
// printable items.
|
||||
const categoryCounts: Record<string, number> = {};
|
||||
let totalRecipes = 0;
|
||||
|
||||
for (const design of sortedDesigns) {
|
||||
totalRecipes += 1;
|
||||
|
||||
for (const category of design.categories ?? []) {
|
||||
categoryCounts[category] = (categoryCounts[category] ?? 0) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Strip blacklisted categories from the output.
|
||||
for (const blacklistedCategory in BLACKLISTED_CATEGORIES) {
|
||||
delete categoryCounts[blacklistedCategory];
|
||||
}
|
||||
|
||||
// Reduce the material count array to a map of actually available materials.
|
||||
const availableMaterials: MaterialMap = {};
|
||||
|
||||
for (const material of data.materials) {
|
||||
availableMaterials[material.name] = material.amount;
|
||||
}
|
||||
|
||||
// Render all categories with items, sorted by name.
|
||||
const namedCategories = Object.keys(categoryCounts).sort();
|
||||
|
||||
return (
|
||||
<Window title={data.fab_name} width={670} height={768}>
|
||||
<Window.Content>
|
||||
<Stack vertical fill>
|
||||
<Stack.Item grow>
|
||||
<Stack fill>
|
||||
<Stack.Item>
|
||||
<Section fill title="Categories">
|
||||
<Tabs vertical>
|
||||
<Tabs.Tab
|
||||
fluid
|
||||
selected={selectedCategory === ALL_CATEGORY}
|
||||
onClick={() => {
|
||||
setSelectedCategory(ALL_CATEGORY);
|
||||
setSearchText('');
|
||||
}}
|
||||
color="transparent">
|
||||
All Designs ({totalRecipes})
|
||||
</Tabs.Tab>
|
||||
|
||||
{namedCategories.map((categoryName) => (
|
||||
<Tabs.Tab
|
||||
key={categoryName}
|
||||
selected={selectedCategory === categoryName}
|
||||
onClick={() => {
|
||||
setSelectedCategory(categoryName);
|
||||
setSearchText('');
|
||||
}}
|
||||
fluid
|
||||
color="transparent">
|
||||
{categoryName} ({categoryCounts[categoryName]})
|
||||
</Tabs.Tab>
|
||||
))}
|
||||
</Tabs>
|
||||
</Section>
|
||||
</Stack.Item>
|
||||
<Stack.Item grow>
|
||||
<Section
|
||||
title="Designs"
|
||||
fill
|
||||
buttons={
|
||||
<Fragment>
|
||||
<Button.Checkbox
|
||||
onClick={() => setDisplayMatCost(!displayMatCost)}
|
||||
checked={displayMatCost}>
|
||||
Display Material Costs
|
||||
</Button.Checkbox>
|
||||
<Button
|
||||
content="R&D Sync"
|
||||
onClick={() => act('sync_rnd')}
|
||||
/>
|
||||
</Fragment>
|
||||
}>
|
||||
<Stack vertical fill>
|
||||
<Stack.Item>
|
||||
<Section>
|
||||
<SearchBar
|
||||
searchText={searchText}
|
||||
setSearchText={setSearchText}
|
||||
/>
|
||||
</Section>
|
||||
</Stack.Item>
|
||||
<Stack.Item grow>
|
||||
<Section fill scrollable>
|
||||
{sortedDesigns
|
||||
.filter(
|
||||
(design) =>
|
||||
selectedCategory === ALL_CATEGORY ||
|
||||
design.categories?.indexOf(selectedCategory) !==
|
||||
-1
|
||||
)
|
||||
.filter((design) =>
|
||||
design.name.toLowerCase().includes(searchText)
|
||||
)
|
||||
.map((design) => (
|
||||
<Recipe
|
||||
key={design.name}
|
||||
design={design}
|
||||
available={availableMaterials}
|
||||
/>
|
||||
))}
|
||||
</Section>
|
||||
{!!busy && (
|
||||
<Dimmer
|
||||
style={{
|
||||
'font-size': '2em',
|
||||
'text-align': 'center',
|
||||
}}>
|
||||
<Icon name="cog" spin />
|
||||
{' Building items...'}
|
||||
</Dimmer>
|
||||
)}
|
||||
</Stack.Item>
|
||||
</Stack>
|
||||
</Section>
|
||||
{!!on_hold && (
|
||||
<Dimmer
|
||||
style={{ 'font-size': '2em', 'text-align': 'center' }}>
|
||||
Mineral access is on hold, please contact the quartermaster.
|
||||
</Dimmer>
|
||||
)}
|
||||
</Stack.Item>
|
||||
</Stack>
|
||||
</Stack.Item>
|
||||
<Stack.Item>
|
||||
<Section>
|
||||
<Materials
|
||||
materials={sortBy((a: Material) => a.name)(
|
||||
data.materials ?? []
|
||||
)}
|
||||
onEject={(ref, amount) => act('remove_mat', { ref, amount })}
|
||||
/>
|
||||
</Section>
|
||||
</Stack.Item>
|
||||
</Stack>
|
||||
</Window.Content>
|
||||
</Window>
|
||||
);
|
||||
};
|
||||
|
||||
type SearchBarProps = {
|
||||
searchText: string;
|
||||
setSearchText: (text: string) => void;
|
||||
};
|
||||
|
||||
const SearchBar = (props: SearchBarProps, context) => {
|
||||
const { searchText, setSearchText } = props;
|
||||
|
||||
return (
|
||||
<Stack align="baseline">
|
||||
<Stack.Item>
|
||||
<Icon name="search" />
|
||||
</Stack.Item>
|
||||
<Stack.Item grow>
|
||||
<Input
|
||||
fluid
|
||||
placeholder="Search for..."
|
||||
onInput={(_e: unknown, v: string) => setSearchText(v.toLowerCase())}
|
||||
value={searchText}
|
||||
/>
|
||||
</Stack.Item>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
type MaterialCostProps = {
|
||||
design: Design;
|
||||
amount: number;
|
||||
available: MaterialMap;
|
||||
};
|
||||
|
||||
const MaterialCost = (props: MaterialCostProps, context) => {
|
||||
const { design, amount, available } = props;
|
||||
|
||||
return (
|
||||
<Stack wrap justify="space-around">
|
||||
{Object.entries(design.cost).map(([material, cost]) => (
|
||||
<Stack.Item key={material}>
|
||||
<MaterialAmount
|
||||
name={material as keyof typeof MATERIAL_KEYS}
|
||||
amount={cost * amount}
|
||||
formatting={MaterialFormatting.SIUnits}
|
||||
color={
|
||||
cost * amount > available[material]
|
||||
? 'bad'
|
||||
: cost * amount * 2 > available[material]
|
||||
? 'average'
|
||||
: 'normal'
|
||||
}
|
||||
/>
|
||||
</Stack.Item>
|
||||
))}
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
type PrintButtonProps = {
|
||||
design: Design;
|
||||
quantity: number;
|
||||
available: MaterialMap;
|
||||
};
|
||||
|
||||
const PrintButton = (props: PrintButtonProps, context) => {
|
||||
const { act, data } = useBackend<FabricatorData>(context);
|
||||
const { design, quantity, available } = props;
|
||||
|
||||
const [displayMatCost] = useSharedState(
|
||||
context,
|
||||
'display_material_cost',
|
||||
true
|
||||
);
|
||||
const canPrint = !Object.entries(design.cost).some(
|
||||
([material, amount]) =>
|
||||
!available[material] || amount * quantity > (available[material] ?? 0)
|
||||
);
|
||||
|
||||
return (
|
||||
<Button
|
||||
className={`Fabricator__PrintAmount ${
|
||||
!canPrint ? 'Fabricator__PrintAmount--disabled' : ''
|
||||
}`}
|
||||
tooltip={
|
||||
displayMatCost && (
|
||||
<MaterialCost
|
||||
design={design}
|
||||
amount={quantity}
|
||||
available={available}
|
||||
/>
|
||||
)
|
||||
}
|
||||
color={'transparent'}
|
||||
onClick={() => act('build', { ref: design.id, amount: quantity })}>
|
||||
x{quantity}
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
|
||||
const Recipe = (props: { design: Design; available: MaterialMap }, context) => {
|
||||
const { act, data } = useBackend<FabricatorData>(context);
|
||||
const { design, available } = props;
|
||||
|
||||
const [displayMatCost] = useSharedState(
|
||||
context,
|
||||
'display_material_cost',
|
||||
true
|
||||
);
|
||||
const canPrint = !Object.entries(design.cost).some(
|
||||
([material, amount]) =>
|
||||
!available[material] || amount > (available[material] ?? 0)
|
||||
);
|
||||
|
||||
return (
|
||||
<div class="Fabricator__Recipe">
|
||||
<Stack justify="space-between" align="stretch">
|
||||
<Stack.Item>
|
||||
<Button
|
||||
icon="question-circle"
|
||||
color="transparent"
|
||||
tooltip={design.desc}
|
||||
tooltipPosition="left"
|
||||
/>
|
||||
</Stack.Item>
|
||||
<Stack.Item grow>
|
||||
<Button
|
||||
color={'transparent'}
|
||||
className={`Fabricator__Button ${
|
||||
!canPrint ? 'Fabricator__Button--disabled' : ''
|
||||
}`}
|
||||
fluid
|
||||
tooltip={
|
||||
displayMatCost && (
|
||||
<MaterialCost
|
||||
design={design}
|
||||
amount={1}
|
||||
available={available}
|
||||
/>
|
||||
)
|
||||
}
|
||||
onClick={() => act('build', { ref: design.id, amount: 1 })}>
|
||||
{design.name}
|
||||
</Button>
|
||||
</Stack.Item>
|
||||
<Stack.Item>
|
||||
<PrintButton design={design} quantity={5} available={available} />
|
||||
</Stack.Item>
|
||||
<Stack.Item>
|
||||
<PrintButton design={design} quantity={10} available={available} />
|
||||
</Stack.Item>
|
||||
</Stack>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,22 @@
|
||||
.Fabricator__Recipe {
|
||||
padding: 0.25em 0;
|
||||
border-bottom: 1px solid #000;
|
||||
}
|
||||
|
||||
.Fabricator__Button {
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
Fabricator__Icon {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.Fabricator__Button--disabled,
|
||||
.Fabricator__PrintAmount--disabled {
|
||||
opacity: 0.5;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.Fabricator__PrintAmount--disabled {
|
||||
text-decoration: line-through;
|
||||
}
|
||||
@@ -69,6 +69,7 @@
|
||||
@include meta.load-css('./interfaces/Techweb.scss');
|
||||
@include meta.load-css('./interfaces/RequestManager.scss');
|
||||
@include meta.load-css('./interfaces/UtilityModulesPane.scss');
|
||||
@include meta.load-css('./interfaces/Fabricator.scss');
|
||||
|
||||
// Layouts
|
||||
@include meta.load-css('./layouts/Layout.scss');
|
||||
|
||||
Reference in New Issue
Block a user