tgui: Autolathe (#60800)

Moves autolathe over to TGUI

Features include:

- Dropdown Materials list, with colorcoded materials(Bananium has a yellow bar!)
- Categories are now easier to switch through as the autolathe all fits on one scrollable page
- You can now see the most you can make with your materials of any design, rather than just stacks
- Can now print a specific amount

Co-authored-by: Aleksej Komarov <stylemistake@gmail.com>
This commit is contained in:
Couls
2021-08-27 10:28:41 +03:00
committed by GitHub
co-authored by Aleksej Komarov
parent 1960ff8b24
commit 4aad465e5f
2 changed files with 365 additions and 204 deletions
+162 -204
View File
@@ -31,8 +31,7 @@
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
@@ -61,27 +60,175 @@
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
if(shocked && !(machine_stat & NOPOWER))
shock(user,50)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "Autolathe")
ui.open()
var/dat
/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)
data["materialtotal"] = materials.total_amount
data["materialsmax"] = materials.max_amount
data["categories"] = categories
data["designs"] = list()
data["active"] = busy
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)
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.greyscale_colors,
)
data["materials"] += list(material_data)
if(selected_category != "None" && !length(matching_designs))
data["designs"] = handle_designs(stored_research.researched_designs, TRUE)
else
data["designs"] = handle_designs(matching_designs, FALSE)
return data
var/datum/browser/popup = new(user, "autolathe", name, 400, 500)
popup.set_content(dat)
popup.open()
/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)))
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/ui_act(action, params)
. = ..()
if(.)
return
if(action == "menu")
selected_category = null
matching_designs.Cut()
. = TRUE
if(action == "category")
selected_category = params["selectedCategory"]
matching_designs.Cut()
. = TRUE
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(params["id"])
if(!being_built)
return
var/multiplier = text2num(params["multiplier"])
if(!multiplier)
to_chat(usr, "<span class=\"alert\">[src] only accepts a numerical multiplier!</span>")
return
var/is_stack = ispath(being_built.build_path, /obj/item/stack)
multiplier = clamp(round(multiplier),1,50)
/////////////////
var/coeff = (is_stack ? 1 : creation_efficiency) //stacks are unaffected by production coefficient
var/total_amount = 0
for(var/MAT in being_built.materials)
total_amount += being_built.materials[MAT]
var/power = max(2000, (total_amount)*multiplier/5) //Change this to use all materials
var/datum/component/material_container/materials = GetComponent(/datum/component/material_container)
var/list/materials_used = list()
var/list/custom_materials = list() //These will apply their material effect, This should usually only be one.
for(var/MAT in being_built.materials)
var/datum/material/used_material = MAT
var/amount_needed = being_built.materials[MAT] * coeff * multiplier
if(istext(used_material)) //This means its a category
var/list/list_to_show = list()
for(var/i in SSmaterials.materials_by_category[used_material])
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)
return //Didn't pick any material, so you can't build shit either.
custom_materials[used_material] += amount_needed
materials_used[used_material] = amount_needed
if(materials.has_materials(materials_used))
busy = TRUE
to_chat(usr, "<span class=\"notice\">You print [multiplier] item(s) from the [src]</span>")
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, "<span class=\"alert\">Not enough materials for this operation.</span>")
else
to_chat(usr, "<span class=\"alert\">The autolathe is busy. Please wait for completion of previous operation.</span>")
/obj/machinery/autolathe/on_deconstruction()
var/datum/component/material_container/materials = GetComponent(/datum/component/material_container)
@@ -93,7 +240,6 @@
return TRUE
if(default_deconstruction_screwdriver(user, "autolathe_t", "autolathe", O))
updateUsrDialog()
return TRUE
if(default_deconstruction_crowbar(O))
@@ -134,86 +280,6 @@
flick("autolathe_o", src)//plays metal insertion animation
use_power(min(1000, amount_inserted / 100))
updateUsrDialog()
/obj/machinery/autolathe/Topic(href, href_list)
if(..())
return
if (!busy)
if(href_list["menu"])
screen = text2num(href_list["menu"])
updateUsrDialog()
if(href_list["category"])
selected_category = href_list["category"]
updateUsrDialog()
if(href_list["make"])
/////////////////
//href protection
being_built = stored_research.isDesignResearchedID(href_list["make"])
if(!being_built)
return
var/multiplier = text2num(href_list["multiplier"])
var/is_stack = ispath(being_built.build_path, /obj/item/stack)
multiplier = clamp(multiplier,1,50)
/////////////////
var/coeff = (is_stack ? 1 : creation_efficiency) //stacks are unaffected by production coefficient
var/total_amount = 0
for(var/MAT in being_built.materials)
total_amount += being_built.materials[MAT]
var/power = max(2000, (total_amount)*multiplier/5) //Change this to use all materials
var/datum/component/material_container/materials = GetComponent(/datum/component/material_container)
var/list/materials_used = list()
var/list/custom_materials = list() //These will apply their material effect, This should usually only be one.
for(var/MAT in being_built.materials)
var/datum/material/used_material = MAT
var/amount_needed = being_built.materials[MAT] * coeff * multiplier
if(istext(used_material)) //This means its a category
var/list/list_to_show = list()
for(var/i in SSmaterials.materials_by_category[used_material])
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)
return //Didn't pick any material, so you can't build shit either.
custom_materials[used_material] += amount_needed
materials_used[used_material] = amount_needed
if(materials.has_materials(materials_used))
busy = TRUE
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)
else
to_chat(usr, "<span class=\"alert\">Not enough materials for this operation.</span>")
if(href_list["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,href_list["to_search"]))
matching_designs.Add(D)
updateUsrDialog()
else
to_chat(usr, "<span class=\"alert\">The autolathe is busy. Please wait for completion of previous operation.</span>")
updateUsrDialog()
return
/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)
@@ -241,7 +307,6 @@
icon_state = "autolathe"
busy = FALSE
updateDialog()
/obj/machinery/autolathe/RefreshParts()
var/mat_capacity = 0
@@ -261,113 +326,6 @@
if(in_range(user, src) || isobserver(user))
. += span_notice("The status display reads: Storing up to <b>[materials.max_amount]</b> material units.<br>Material consumption at <b>[creation_efficiency*100]%</b>.")
/obj/machinery/autolathe/proc/main_win(mob/user)
var/dat = "<div class='statusDisplay'><h3>Autolathe Menu:</h3><br>"
dat += materials_printout()
dat += "<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='menu' value='[AUTOLATHE_SEARCH_MENU]'>\
<input type='text' name='to_search'>\
<input type='submit' value='Search'>\
</form><hr>"
var/line_length = 1
dat += "<table style='width:100%' align='center'><tr>"
for(var/C in categories)
if(line_length > 2)
dat += "</tr><tr>"
line_length = 1
dat += "<td><A href='?src=[REF(src)];category=[C];menu=[AUTOLATHE_CATEGORY_MENU]'>[C]</A></td>"
line_length++
dat += "</tr></table></div>"
return dat
/obj/machinery/autolathe/proc/category_win(mob/user,selected_category)
var/dat = "<A href='?src=[REF(src)];menu=[AUTOLATHE_MAIN_MENU]'>Return to main menu</A>"
dat += "<div class='statusDisplay'><h3>Browsing [selected_category]:</h3><br>"
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 += "<span class='linkOff'>[D.name]</span>"
else
dat += "<a href='?src=[REF(src)];make=[D.id];multiplier=1'>[D.name]</a>"
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 += " <a href='?src=[REF(src)];make=[D.id];multiplier=10'>x10</a>"
if (max_multiplier>25 && !disabled)
dat += " <a href='?src=[REF(src)];make=[D.id];multiplier=25'>x25</a>"
if(max_multiplier > 0 && !disabled)
dat += " <a href='?src=[REF(src)];make=[D.id];multiplier=[max_multiplier]'>x[max_multiplier]</a>"
else
if(!disabled && can_build(D, 5))
dat += " <a href='?src=[REF(src)];make=[D.id];multiplier=5'>x5</a>"
if(!disabled && can_build(D, 10))
dat += " <a href='?src=[REF(src)];make=[D.id];multiplier=10'>x10</a>"
dat += "[get_design_cost(D)]<br>"
dat += "</div>"
return dat
/obj/machinery/autolathe/proc/search_win(mob/user)
var/dat = "<A href='?src=[REF(src)];menu=[AUTOLATHE_MAIN_MENU]'>Return to main menu</A>"
dat += "<div class='statusDisplay'><h3>Search results:</h3><br>"
dat += materials_printout()
for(var/v in matching_designs)
var/datum/design/D = v
if(disabled || !can_build(D))
dat += "<span class='linkOff'>[D.name]</span>"
else
dat += "<a href='?src=[REF(src)];make=[D.id];multiplier=1'>[D.name]</a>"
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 += " <a href='?src=[REF(src)];make=[D.id];multiplier=10'>x10</a>"
if (max_multiplier>25 && !disabled)
dat += " <a href='?src=[REF(src)];make=[D.id];multiplier=25'>x25</a>"
if(max_multiplier > 0 && !disabled)
dat += " <a href='?src=[REF(src)];make=[D.id];multiplier=[max_multiplier]'>x[max_multiplier]</a>"
else
if(!disabled && can_build(D, 5))
dat += " <a href='?src=[REF(src)];make=[D.id];multiplier=5'>x5</a>"
if(!disabled && can_build(D, 10))
dat += " <a href='?src=[REF(src)];make=[D.id];multiplier=10'>x10</a>"
dat += "[get_design_cost(D)]<br>"
dat += "</div>"
return dat
/obj/machinery/autolathe/proc/materials_printout()
var/datum/component/material_container/materials = GetComponent(/datum/component/material_container)
var/dat = "<b>Total amount:</b> [materials.total_amount] / [materials.max_amount] cm<sup>3</sup><br>"
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 += "<b>[M.name] amount:</b> [mineral_amount] cm<sup>3</sup><br>"
return dat
/obj/machinery/autolathe/proc/can_build(datum/design/D, amount = 1)
if(D.make_reagents.len)
return FALSE
+203
View File
@@ -0,0 +1,203 @@
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 (
<Window
title="Autolathe"
width={600}
height={600}>
<Window.Content scrollable>
<Section title="Total Materials">
<LabeledList>
<LabeledList.Item
label="Total Materials">
<ProgressBar
value={materialtotal}
minValue={0}
maxValue={materialsmax}
ranges={{
"good": [materialsmax * 0.85, materialsmax],
"average": [materialsmax * 0.25, materialsmax * 0.85],
"bad": [0, materialsmax * 0.25],
}}>
{materialtotal + '/' + materialsmax + ' cm³'}
</ProgressBar>
</LabeledList.Item>
<LabeledList.Item>
{filteredmaterials.length > 0 && (
<Collapsible title="Materials">
<LabeledList>
{filteredmaterials.map(filteredmaterial => (
<LabeledList.Item
key={filteredmaterial.id}
label={capitalize(filteredmaterial.name)}>
<ProgressBar
style={{
transform: 'scaleX(-1) scaleY(1)',
}}
value={materialsmax - filteredmaterial.mineral_amount}
maxValue={materialsmax}
color="black"
backgroundColor={filteredmaterial.matcolour}>
<div style={{ transform: 'scaleX(-1)' }}>{filteredmaterial.mineral_amount + ' cm³'}</div>
</ProgressBar>
</LabeledList.Item>
))}
</LabeledList>
</Collapsible>)}
</LabeledList.Item>
</LabeledList>
</Section>
<Section
title="Search">
<Input fluid
placeholder="Search Recipes..."
selfClear
onChange={(e, value) => {
if (value.length) {
act('search', {
to_search: value,
});
setCategory('results for "' + value + '"');
}
}} />
</Section>
<Section title="Categories">
<Box>
{categories.map(category => (
// eslint-disable-next-line react/jsx-key
<Button
selected={current_category === category}
content={category}
onClick={() => {
act('category', {
selectedCategory: category,
});
setCategory(category);
}} />
))}
</Box>
</Section>
{current_category.toString() !== "None" && (
<Section
title={'Displaying ' + current_category.toString()}
buttons={(
<Button
icon="times"
content="Close Category"
onClick={() => {
act('menu');
setCategory("None");
}} />
)}>
{active === 1 && (
<Dimmer fontSize="32px">
<Icon name="cog" spin />
{'Building items...'}
</Dimmer>
)}
<Flex direction="row" wrap="nowrap">
<Table>
{designs.length
&& (designs.map(design => (
<Table.Row
key={design.id}>
<Flex.Item>
<Button
content={design.name}
disabled={design.buildable}
onClick={() => act('make', {
id: design.id,
multiplier: '1',
})} />
</Flex.Item>
{design.sheet ? (
<Table.Cell>
<Flex.Item grow={1}>
<Button
icon="hammer"
content="10"
disabled={!design.mult10}
onClick={() => act('make', {
id: design.id,
multiplier: '10',
})} />
<Button
icon="hammer"
content="25"
disabled={!design.mult25}
onClick={() => act('make', {
id: design.id,
multiplier: '25',
})} />
</Flex.Item>
</Table.Cell>
) : (
<Table.Cell>
<Flex.Item grow={3}>
<Button
icon="hammer"
content="5"
disabled={!design.mult5}
onClick={() => act('make', {
id: design.id,
multiplier: '5',
})} />
<Button
icon="hammer"
content="10"
disabled={!design.mult10}
onClick={() => act('make', {
id: design.id,
multiplier: '10',
})} />
</Flex.Item>
</Table.Cell>
)}
<Table.Cell>
<Button.Input
content={"[Max:" + design.maxmult + ']'}
maxValue={design.maxmult}
disabled={design.buildable}
backgroundColor={design.buildable ? '#999999' : 'default'}
onCommit={(e, value) => act('make', {
id: design.id,
multiplier: value,
})} />
</Table.Cell>
{design.cost}
</Table.Row>
))) || (
<Table.Row>
<Table.Cell>
{"No designs found."}
</Table.Cell>
</Table.Row>
)}
</Table>
</Flex>
</Section>
)}
</Window.Content>
</Window>
);
};