diff --git a/code/game/machinery/autolathe.dm b/code/game/machinery/autolathe.dm index 0e7b4aa0fa..4db565aec1 100644 --- a/code/game/machinery/autolathe.dm +++ b/code/game/machinery/autolathe.dm @@ -1,7 +1,3 @@ -#define AUTOLATHE_MAIN_MENU 1 -#define AUTOLATHE_CATEGORY_MENU 2 -#define AUTOLATHE_SEARCH_MENU 3 - /obj/machinery/autolathe name = "autolathe" desc = "It produces items using metal and glass." @@ -24,13 +20,14 @@ var/shock_wire var/busy = FALSE - var/prod_coeff = 1 + + ///the multiplier for how much materials the created object takes from this machines stored materials + var/creation_efficiency = 1.6 var/datum/design/being_built var/datum/techweb/stored_research var/list/datum/design/matching_designs - var/selected_category - var/screen = 1 + var/selected_category = "None" var/base_price = 25 var/hacked_price = 50 @@ -60,7 +57,7 @@ QDEL_NULL(wires) return ..() -/obj/machinery/autolathe/ui_interact(mob/user) +/obj/machinery/autolathe/ui_interact(mob/user, datum/tgui/ui) . = ..() if(!is_operational()) return @@ -68,101 +65,129 @@ if(shocked && !(stat & NOPOWER)) shock(user,50) - var/dat + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "Autolathe", capitalize(src.name)) + ui.open() - switch(screen) - if(AUTOLATHE_MAIN_MENU) - dat = main_win(user) - if(AUTOLATHE_CATEGORY_MENU) - dat = category_win(user,selected_category) - if(AUTOLATHE_SEARCH_MENU) - dat = search_win(user) - - var/datum/browser/popup = new(user, "autolathe", name, 400, 500) - popup.set_content(dat) - popup.open() - -/obj/machinery/autolathe/on_deconstruction() +/obj/machinery/autolathe/ui_data(mob/user) + var/list/data = list() + data["materials"] = list() var/datum/component/material_container/materials = GetComponent(/datum/component/material_container) - materials.retrieve_all() + data["materialtotal"] = materials.total_amount + data["materialsmax"] = materials.max_amount + data["categories"] = categories + data["designs"] = list() + data["active"] = busy -/obj/machinery/autolathe/attackby(obj/item/O, mob/user, params) - if (busy) - to_chat(user, "The autolathe is busy. Please wait for completion of previous operation.") - return TRUE - - if(default_deconstruction_screwdriver(user, "autolathe_t", "autolathe", O)) - updateUsrDialog() - return TRUE - - if(default_deconstruction_crowbar(O)) - return TRUE - - if(panel_open && is_wire_tool(O)) - wires.interact(user) - return TRUE - - if(user.a_intent == INTENT_HARM) //so we can hit the machine - return ..() - - if(stat) - return TRUE - - if(istype(O, /obj/item/disk/design_disk)) - user.visible_message("[user] begins to load \the [O] in \the [src]...", - "You begin to load a design from \the [O]...", - "You hear the chatter of a floppy drive.") - busy = TRUE - var/obj/item/disk/design_disk/D = O - if(do_after(user, 14.4, target = src)) - for(var/B in D.blueprints) - if(B) - stored_research.add_design(B) - busy = FALSE - return TRUE - - return ..() - - -/obj/machinery/autolathe/proc/AfterMaterialInsert(item_inserted, id_inserted, amount_inserted) - if(istype(item_inserted, /obj/item/stack/ore/bluespace_crystal)) - use_power(MINERAL_MATERIAL_AMOUNT / 10) - else if(custom_materials && custom_materials.len && custom_materials[SSmaterials.GetMaterialRef(/datum/material/glass)]) - flick("autolathe_r",src)//plays glass insertion animation by default otherwise + for(var/mat_id in materials.materials) + var/datum/material/M = mat_id + var/mineral_count = materials.materials[mat_id] + var/list/material_data = list( + name = M.name, + mineral_amount = mineral_count, + matcolour = M.color, + ) + data["materials"] += list(material_data) + if(selected_category != "None" && !length(matching_designs)) + data["designs"] = handle_designs(stored_research.researched_designs, TRUE) else - flick("autolathe_o",src)//plays metal insertion animation + data["designs"] = handle_designs(matching_designs, FALSE) + return data +/obj/machinery/autolathe/proc/handle_designs(list/designs, categorycheck) + var/list/output = list() + for(var/v in designs) + var/datum/design/D = categorycheck ? SSresearch.techweb_design_by_id(v) : v + if(categorycheck) + if(!(selected_category in D.category)) + continue + var/unbuildable = FALSE // we can't build the design currently + var/m10 = FALSE // 10x mult + var/m25 = FALSE // 25x mult + var/m50 = FALSE // 50x mult + var/m5 = FALSE // 5x mult + var/sheets = FALSE // sheets or no? + if(disabled || !can_build(D)) + unbuildable = TRUE + var/max_multiplier = unbuildable ? 0 : 1 + if(ispath(D.build_path, /obj/item/stack)) + sheets = TRUE + if(!unbuildable) + var/datum/component/material_container/mats = GetComponent(/datum/component/material_container) + for(var/datum/material/mat in D.materials) + max_multiplier = min(D.maxstack, round(mats.get_material_amount(mat)/D.materials[mat])) + if (max_multiplier>10 && !disabled) + m10 = TRUE + if (max_multiplier>25 && !disabled) + m25 = TRUE + else + if(!unbuildable) + if(!disabled && can_build(D, 5)) + m5 = TRUE + if(!disabled && can_build(D, 10)) + m10 = TRUE + var/datum/component/material_container/mats = GetComponent(/datum/component/material_container) + for(var/datum/material/mat in D.materials) + max_multiplier = min(50, round(mats.get_material_amount(mat)/(D.materials[mat] * creation_efficiency))) - use_power(min(1000, amount_inserted / 100)) - updateUsrDialog() + var/list/design = list( + name = D.name, + id = D.id, + ref = REF(src), + cost = get_design_cost(D), + buildable = unbuildable, + mult5 = m5, + mult10 = m10, + mult25 = m25, + mult50 = m50, + sheet = sheets, + maxmult = max_multiplier, + ) + output += list(design) + return output -/obj/machinery/autolathe/Topic(href, href_list) - if(..()) +/obj/machinery/autolathe/ui_act(action, params) + . = ..() + if(.) return - if (!busy) - if(href_list["menu"]) - screen = text2num(href_list["menu"]) - updateUsrDialog() + if(action == "menu") + selected_category = null + matching_designs.Cut() + . = TRUE - if(href_list["category"]) - selected_category = href_list["category"] - updateUsrDialog() + if(action == "category") + selected_category = params["selectedCategory"] + matching_designs.Cut() + . = TRUE - if(href_list["make"]) + if(action == "search") + matching_designs.Cut() + for(var/v in stored_research.researched_designs) + var/datum/design/D = SSresearch.techweb_design_by_id(v) + if(findtext(D.name,params["to_search"])) + matching_designs.Add(D) + . = TRUE + + if(action == "make") + if (!busy) ///////////////// //href protection - being_built = stored_research.isDesignResearchedID(href_list["make"]) + being_built = stored_research.isDesignResearchedID(params["id"]) if(!being_built) return - var/multiplier = text2num(href_list["multiplier"]) + var/multiplier = text2num(params["multiplier"]) + if(!multiplier) + to_chat(usr, span_alert("[src] only accepts a numerical multiplier!")) + return var/is_stack = ispath(being_built.build_path, /obj/item/stack) - multiplier = clamp(multiplier,1,50) + multiplier = clamp(round(multiplier),1,50) ///////////////// - var/coeff = (is_stack ? 1 : prod_coeff) //stacks are unaffected by production coefficient + var/coeff = (is_stack ? 1 : creation_efficiency) //stacks are unaffected by production coefficient var/total_amount = 0 for(var/MAT in being_built.materials) @@ -184,8 +209,8 @@ if(materials.materials[i] > 0) list_to_show += i - used_material = input("Choose [used_material]", "Custom Material") as null|anything in sortList(list_to_show, /proc/cmp_typepaths_asc) - if(!used_material) + used_material = tgui_input_list(usr, "Choose [used_material]", "Custom Material", sortList(list_to_show, /proc/cmp_typepaths_asc)) + if(isnull(used_material)) return //Didn't pick any material, so you can't build shit either. custom_materials[used_material] += amount_needed @@ -193,27 +218,95 @@ if(materials.has_materials(materials_used)) busy = TRUE + to_chat(usr, span_notice("You print [multiplier] item(s) from the [src]")) use_power(power) icon_state = "autolathe_n" var/time = is_stack ? 32 : (32 * coeff * multiplier) ** 0.8 addtimer(CALLBACK(src, .proc/make_item, power, materials_used, custom_materials, multiplier, coeff, is_stack, usr), time) + . = TRUE else - to_chat(usr, "Not enough materials for this operation.") + to_chat(usr, span_alert("Not enough materials for this operation.")) + else + to_chat(usr, span_alert("The autolathe is busy. Please wait for completion of previous operation.")) - if(href_list["search"]) - matching_designs.Cut() +/obj/machinery/autolathe/on_deconstruction() + var/datum/component/material_container/materials = GetComponent(/datum/component/material_container) + materials.retrieve_all() - for(var/v in stored_research.researched_designs) - var/datum/design/D = SSresearch.techweb_design_by_id(v) - if(findtext(D.name,href_list["to_search"])) - matching_designs.Add(D) - updateUsrDialog() +/obj/machinery/autolathe/attackby(obj/item/O, mob/living/user, params) + if(busy) + balloon_alert(user, "it's busy!") + return TRUE + + if(user.a_intent == INTENT_HARM) //so we can hit the machine + return ..() + + if(stat) + return TRUE + + if(istype(O, /obj/item/disk/design_disk)) + user.visible_message(span_notice("[user] begins to load \the [O] in \the [src]..."), + balloon_alert(user, "uploading design..."), + span_hear("You hear the chatter of a floppy drive.")) + busy = TRUE + if(do_after(user, 14.4, target = src)) + var/obj/item/disk/design_disk/disky = O + var/list/not_imported + for(var/datum/design/blueprint as anything in disky.blueprints) + if(!blueprint) + continue + if(blueprint.build_type & AUTOLATHE) + stored_research.add_design(blueprint) + else + LAZYADD(not_imported, blueprint.name) + if(not_imported) + to_chat(user, span_warning("The following design[length(not_imported) > 1 ? "s" : ""] couldn't be imported: [english_list(not_imported)]")) + busy = FALSE + return TRUE + + if(panel_open) + balloon_alert(user, "close the panel first!") + return FALSE + + return ..() + +/obj/machinery/autolathe/screwdriver_act(mob/living/user, obj/item/I) + . = ..() + if(busy) + balloon_alert(user, "it's busy!") + return STOP_ATTACK_PROC_CHAIN + + if(default_deconstruction_screwdriver(user, "autolathe_t", "autolathe", I)) + return STOP_ATTACK_PROC_CHAIN + +/obj/machinery/autolathe/crowbar_act(mob/living/user, obj/item/I) + . = ..() + if(busy) + balloon_alert(user, "it's busy!") + return STOP_ATTACK_PROC_CHAIN + + if(default_deconstruction_crowbar(I)) + return STOP_ATTACK_PROC_CHAIN + +/obj/machinery/autolathe/multitool_act(mob/living/user, obj/item/I) + . = ..() + if(busy) + balloon_alert(user, "it's busy!") + return STOP_ATTACK_PROC_CHAIN + + if(panel_open) + wires.interact(user) + return STOP_ATTACK_PROC_CHAIN + +/obj/machinery/autolathe/proc/AfterMaterialInsert(obj/item/item_inserted, id_inserted, amount_inserted) + if(istype(item_inserted, /obj/item/stack/ore/bluespace_crystal)) + use_power(MINERAL_MATERIAL_AMOUNT / 10) + else if(custom_materials && custom_materials.len && custom_materials[SSmaterials.GetMaterialRef(/datum/material/glass)]) + flick("autolathe_r", src)//plays glass insertion animation by default otherwise else - to_chat(usr, "The autolathe is busy. Please wait for completion of previous operation.") + flick("autolathe_o", src)//plays metal insertion animation - updateUsrDialog() - - return + use_power(min(1000, amount_inserted / 100)) /obj/machinery/autolathe/proc/make_item(power, list/materials_used, list/picked_materials, multiplier, coeff, is_stack, mob/user) var/datum/component/material_container/materials = GetComponent(/datum/component/material_container) @@ -223,11 +316,11 @@ materials.use_materials(materials_used) if(is_stack) - var/obj/item/stack/N = new being_built.build_path(A, multiplier) - N.update_icon() + var/obj/item/stack/N = new being_built.build_path(A, multiplier, FALSE) + N.update_appearance() N.autolathe_crafted(src) else - for(var/i=1, i<=multiplier, i++) + for(var/i in 1 to multiplier) var/obj/item/new_item = new being_built.build_path(A) new_item.autolathe_crafted(src) @@ -241,132 +334,30 @@ icon_state = "autolathe" busy = FALSE - updateDialog() /obj/machinery/autolathe/RefreshParts() - var/T = 0 - for(var/obj/item/stock_parts/matter_bin/MB in component_parts) - T += MB.rating*75000 + var/mat_capacity = 0 + for(var/obj/item/stock_parts/matter_bin/new_matter_bin in component_parts) + mat_capacity += new_matter_bin.rating*75000 var/datum/component/material_container/materials = GetComponent(/datum/component/material_container) - materials.max_amount = T - T=1.2 - for(var/obj/item/stock_parts/manipulator/M in component_parts) - T -= M.rating*0.2 - prod_coeff = min(1,max(0,T)) // Coeff going 1 -> 0,8 -> 0,6 -> 0,4 + materials.max_amount = mat_capacity + + var/efficiency=1.2 + for(var/obj/item/stock_parts/manipulator/new_manipulator in component_parts) + efficiency -= new_manipulator.rating*0.2 + creation_efficiency = max(1,efficiency) // creation_efficiency goes 1 -> 0,8 -> 0,6 -> 0,4 per level of manipulator efficiency /obj/machinery/autolathe/examine(mob/user) . += ..() var/datum/component/material_container/materials = GetComponent(/datum/component/material_container) if(in_range(user, src) || isobserver(user)) - . += "The status display reads: Storing up to [materials.max_amount] material units.
Material consumption at [prod_coeff*100]%.
" - -/obj/machinery/autolathe/proc/main_win(mob/user) - var/dat = "

Autolathe Menu:


" - dat += materials_printout() - - dat += "
\ - \ - \ - \ - \ - \ -

" - - var/line_length = 1 - dat += "" - - for(var/C in categories) - if(line_length > 2) - dat += "" - line_length = 1 - - dat += "" - line_length++ - - dat += "
[C]
" - return dat - -/obj/machinery/autolathe/proc/category_win(mob/user,selected_category) - var/dat = "Return to main menu" - dat += "

Browsing [selected_category]:


" - dat += materials_printout() - - for(var/v in stored_research.researched_designs) - var/datum/design/D = SSresearch.techweb_design_by_id(v) - if(!(selected_category in D.category)) - continue - - if(disabled || !can_build(D)) - dat += "[D.name]" - else - dat += "[D.name]" - - if(ispath(D.build_path, /obj/item/stack)) - var/datum/component/material_container/materials = GetComponent(/datum/component/material_container) - var/max_multiplier - for(var/datum/material/mat in D.materials) - max_multiplier = min(D.maxstack, round(materials.get_material_amount(mat)/D.materials[mat])) - if (max_multiplier>10 && !disabled) - dat += " x10" - if (max_multiplier>25 && !disabled) - dat += " x25" - if(max_multiplier > 0 && !disabled) - dat += " x[max_multiplier]" - else - if(!disabled && can_build(D, 5)) - dat += " x5" - if(!disabled && can_build(D, 10)) - dat += " x10" - - dat += "[get_design_cost(D)]
" - - dat += "
" - return dat - -/obj/machinery/autolathe/proc/search_win(mob/user) - var/dat = "Return to main menu" - dat += "

Search results:


" - dat += materials_printout() - - for(var/v in matching_designs) - var/datum/design/D = v - if(disabled || !can_build(D)) - dat += "[D.name]" - else - dat += "[D.name]" - - if(ispath(D.build_path, /obj/item/stack)) - var/datum/component/material_container/materials = GetComponent(/datum/component/material_container) - var/max_multiplier - for(var/datum/material/mat in D.materials) - max_multiplier = min(D.maxstack, round(materials.get_material_amount(mat)/D.materials[mat])) - if (max_multiplier>10 && !disabled) - dat += " x10" - if (max_multiplier>25 && !disabled) - dat += " x25" - if(max_multiplier > 0 && !disabled) - dat += " x[max_multiplier]" - - dat += "[get_design_cost(D)]
" - - dat += "
" - return dat - -/obj/machinery/autolathe/proc/materials_printout() - var/datum/component/material_container/materials = GetComponent(/datum/component/material_container) - var/dat = "Total amount: [materials.total_amount] / [materials.max_amount] cm3
" - for(var/mat_id in materials.materials) - var/datum/material/M = mat_id - var/mineral_amount = materials.materials[mat_id] - if(mineral_amount > 0) - dat += "[M.name] amount: [mineral_amount] cm3
" - return dat + . += span_notice("The status display reads: Storing up to [materials.max_amount] material units.
Material consumption at [creation_efficiency*100]%.") /obj/machinery/autolathe/proc/can_build(datum/design/D, amount = 1) - if(D.make_reagents.len) + if(length(D.make_reagents)) return FALSE - var/coeff = (ispath(D.build_path, /obj/item/stack) ? 1 : prod_coeff) + var/coeff = (ispath(D.build_path, /obj/item/stack) ? 1 : creation_efficiency) var/list/required_materials = list() @@ -379,7 +370,7 @@ /obj/machinery/autolathe/proc/get_design_cost(datum/design/D) - var/coeff = (ispath(D.build_path, /obj/item/stack) ? 1 : prod_coeff) + var/coeff = (ispath(D.build_path, /obj/item/stack) ? 1 : creation_efficiency) var/dat for(var/i in D.materials) if(istext(i)) //Category handling @@ -402,7 +393,7 @@ disabled = FALSE /obj/machinery/autolathe/proc/shock(mob/user, prb) - if(stat & (BROKEN|NOPOWER)) // unpowered, no shock + if(stat & (BROKEN|NOPOWER)) // unpowered, no shock return FALSE if(!prob(prb)) return FALSE @@ -424,7 +415,7 @@ else stored_research.remove_design(D) -/obj/machinery/autolathe/hacked/Initialize() +/obj/machinery/autolathe/hacked/Initialize(mapload) . = ..() adjust_hacked(TRUE) diff --git a/tgui/packages/tgui/interfaces/Autolathe.js b/tgui/packages/tgui/interfaces/Autolathe.js new file mode 100644 index 0000000000..ede46b5526 --- /dev/null +++ b/tgui/packages/tgui/interfaces/Autolathe.js @@ -0,0 +1,202 @@ +import { useBackend, useLocalState } from '../backend'; +import { Button, LabeledList, Section, ProgressBar, Flex, Box, Table, Collapsible, Input, Dimmer, Icon } from '../components'; +import { Window } from '../layouts'; +import { capitalize } from "common/string"; + +export const Autolathe = (props, context) => { + const { act, data } = useBackend(context); + // Extract `health` and `color` variables from the `data` object. + const { + materialtotal, + materialsmax, + materials = [], + categories = [], + designs = [], + active, + } = data; + const [ + current_category, + setCategory, + ] = useLocalState(context, 'current_category', "None"); + const filteredmaterials = materials.filter(material => + material.mineral_amount > 0); + return ( + + +
+ + + + {materialtotal + '/' + materialsmax + ' cm³'} + + + + {filteredmaterials.length > 0 && ( + + + {filteredmaterials.map(filteredmaterial => ( + + +
{filteredmaterial.mineral_amount + ' cm³'}
+
+
+ ))} +
+
)} +
+
+
+
+ { + if (value.length) { + act('search', { + to_search: value, + }); + setCategory('results for "' + value + '"'); + } + }} /> +
+
+ + {categories.map(category => ( + // eslint-disable-next-line react/jsx-key +
+ {current_category.toString() !== "None" && ( +
{ + act('menu'); + setCategory("None"); + }} /> + )}> + {active === 1 && ( + + + {'Building items...'} + + )} + + + {designs.length + && (designs.map(design => ( + + +
+
+
+ )} +
+
+ ); +};