From d010f09e343a7ac5217f5543d5b95b8a0ac381b2 Mon Sep 17 00:00:00 2001 From: Arkatos1 <43862960+Arkatos1@users.noreply.github.com> Date: Mon, 11 May 2020 01:32:52 +0200 Subject: [PATCH] tgui: Biogenerator (#51022) * First version * Pre-release version * Cleanup * Review fixes and finishing touches --- code/modules/hydroponics/biogenerator.dm | 214 +++++++++--------- tgui/packages/tgui/interfaces/Biogenerator.js | 188 +++++++++++++++ tgui/packages/tgui/public/tgui.bundle.js | 2 +- 3 files changed, 295 insertions(+), 109 deletions(-) create mode 100644 tgui/packages/tgui/interfaces/Biogenerator.js diff --git a/code/modules/hydroponics/biogenerator.dm b/code/modules/hydroponics/biogenerator.dm index 356d8560eb3..d0715982687 100644 --- a/code/modules/hydroponics/biogenerator.dm +++ b/code/modules/hydroponics/biogenerator.dm @@ -7,16 +7,18 @@ use_power = IDLE_POWER_USE idle_power_usage = 40 circuit = /obj/item/circuitboard/machine/biogenerator + ui_x = 550 + ui_y = 380 var/processing = FALSE var/obj/item/reagent_containers/glass/beaker = null var/points = 0 - var/menustat = "menu" var/efficiency = 0 var/productivity = 0 var/max_items = 40 var/datum/techweb/stored_research var/list/show_categories = list("Food", "Botany Chemicals", "Organic Materials") - var/list/timesFiveCategories = list("Food", "Botany Chemicals") + /// Currently selected category in the UI + var/selected_cat /obj/machinery/biogenerator/Initialize() . = ..() @@ -43,7 +45,6 @@ if(A == beaker) beaker = null update_icon() - updateUsrDialog() /obj/machinery/biogenerator/RefreshParts() var/E = 0 @@ -106,7 +107,6 @@ beaker = O to_chat(user, "You add the container to the machine.") update_icon() - updateUsrDialog() else to_chat(user, "Close the maintenance panel first.") return @@ -157,101 +157,51 @@ else to_chat(user, "You cannot put this in [src.name]!") -/obj/machinery/biogenerator/ui_interact(mob/user) - if(machine_stat & BROKEN || panel_open) - return - . = ..() - var/dat - if(processing) - dat += "
Biogenerator is processing! Please wait...

" - else - switch(menustat) - if("nopoints") - dat += "
You do not have enough biomass to create products.
Please, put growns into reactor and activate it.
" - menustat = "menu" - if("complete") - dat += "
Operation complete.
" - menustat = "menu" - if("void") - dat += "
Error: No growns inside.
Please, put growns into reactor.
" - menustat = "menu" - if("nobeakerspace") - dat += "
Not enough space left in container. Unable to create product.
" - menustat = "menu" - if(beaker) - var/categories = show_categories.Copy() - for(var/V in categories) - categories[V] = list() - for(var/V in stored_research.researched_designs) - var/datum/design/D = SSresearch.techweb_design_by_id(V) - for(var/C in categories) - if(C in D.category) - categories[C] += D - - dat += "
Biomass: [points] units.

" - dat += "ActivateDetach Container" - for(var/cat in categories) - dat += "

[cat]:

" - dat += "
" - for(var/V in categories[cat]) - var/datum/design/D = V - dat += "[D.name]: Make" - if(cat in timesFiveCategories) - dat += "x5" - if(ispath(D.build_path, /obj/item/stack)) - dat += "x10" - dat += "([D.materials[SSmaterials.GetMaterialRef(/datum/material/biomass)]/efficiency])
" - dat += "
" - else - dat += "
No container inside, please insert container.
" - - var/datum/browser/popup = new(user, "biogen", name, 350, 520) - popup.set_content(dat) - popup.open() - /obj/machinery/biogenerator/AltClick(mob/living/user) . = ..() if(user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK) && can_interact(user)) detach(user) -/obj/machinery/biogenerator/proc/activate() - if (usr.stat != CONSCIOUS) +/** + * activate: Activates biomass processing and converts all inserted grown products into biomass + * + * Arguments: + * * user The mob starting the biomass processing + */ +/obj/machinery/biogenerator/proc/activate(mob/user) + if(user.stat != CONSCIOUS) return - if (src.machine_stat != NONE) //NOPOWER etc + if(machine_stat != NONE) return if(processing) - to_chat(usr, "The biogenerator is in the process of working.") + to_chat(user, "The biogenerator is in the process of working.") return var/S = 0 for(var/obj/item/reagent_containers/food/snacks/grown/I in contents) S += 5 if(I.reagents.get_reagent_amount(/datum/reagent/consumable/nutriment) < 0.1) - points += 1*productivity - else points += I.reagents.get_reagent_amount(/datum/reagent/consumable/nutriment)*10*productivity + points += 1 * productivity + else + points += I.reagents.get_reagent_amount(/datum/reagent/consumable/nutriment) * 10 * productivity qdel(I) if(S) processing = TRUE update_icon() - updateUsrDialog() - playsound(src.loc, 'sound/machines/blender.ogg', 50, TRUE) - use_power(S*30) - sleep(S+15/productivity) + playsound(loc, 'sound/machines/blender.ogg', 50, TRUE) + use_power(S * 30) + sleep(S + 15 / productivity) processing = FALSE update_icon() - else - menustat = "void" /obj/machinery/biogenerator/proc/check_cost(list/materials, multiplier = 1, remove_points = TRUE) if(materials.len != 1 || materials[1] != SSmaterials.GetMaterialRef(/datum/material/biomass)) return FALSE if (materials[SSmaterials.GetMaterialRef(/datum/material/biomass)]*multiplier/efficiency > points) - menustat = "nopoints" return FALSE else if(remove_points) points -= materials[SSmaterials.GetMaterialRef(/datum/material/biomass)]*multiplier/efficiency update_icon() - updateUsrDialog() return TRUE /obj/machinery/biogenerator/proc/check_container_volume(list/reagents, multiplier = 1) @@ -261,7 +211,6 @@ sum_reagents *= multiplier if(beaker.reagents.total_volume + sum_reagents > beaker.reagents.maximum_volume) - menustat = "nobeakerspace" return FALSE return TRUE @@ -283,6 +232,7 @@ var/i = amount while(i > 0) if(!check_container_volume(D.make_reagents)) + say("Warning: Attached container does not have enough free capacity!") return . if(!check_cost(D.materials)) return . @@ -292,8 +242,6 @@ beaker.reagents.add_reagent(R, D.make_reagents[R]) . = 1 --i - - menustat = "complete" update_icon() return . @@ -306,40 +254,90 @@ beaker = null update_icon() -/obj/machinery/biogenerator/Topic(href, href_list) - if(..() || panel_open) +/obj/machinery/biogenerator/ui_status(mob/user) + if(machine_stat & BROKEN || panel_open) + return UI_CLOSE + return ..() + +/obj/machinery/biogenerator/ui_base_html(html) + var/datum/asset/spritesheet/simple/assets = get_asset_datum(/datum/asset/spritesheet/research_designs) + . = replacetext(html, "", assets.css_tag()) + +/obj/machinery/biogenerator/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ + datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) + ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + var/datum/asset/assets = get_asset_datum(/datum/asset/spritesheet/research_designs) + assets.send(user) + ui = new(user, src, ui_key, "Biogenerator", name, ui_x, ui_y, master_ui, state) + ui.open() + +/obj/machinery/biogenerator/ui_data(mob/user) + var/list/data = list() + data["beaker"] = beaker ? TRUE : FALSE + data["biomass"] = points + data["processing"] = processing + if(locate(/obj/item/reagent_containers/food/snacks/grown) in contents) + data["can_process"] = TRUE + else + data["can_process"] = FALSE + return data + +/obj/machinery/biogenerator/ui_static_data(mob/user) + var/list/data = list() + data["categories"] = list() + + var/categories = show_categories.Copy() + for(var/V in categories) + categories[V] = list() + for(var/V in stored_research.researched_designs) + var/datum/design/D = SSresearch.techweb_design_by_id(V) + for(var/C in categories) + if(C in D.category) + categories[C] += D + + for(var/category in categories) + var/list/cat = list( + "name" = category, + "items" = (category == selected_cat ? list() : null)) + for(var/item in categories[category]) + var/datum/design/D = item + cat["items"] += list(list( + "id" = D.id, + "name" = D.name, + "cost" = D.materials[SSmaterials.GetMaterialRef(/datum/material/biomass)]/efficiency, + )) + data["categories"] += list(cat) + + return data + +/obj/machinery/biogenerator/ui_act(action, list/params) + if(..()) return - usr.set_machine(src) - - if(href_list["activate"]) - activate() - updateUsrDialog() - - else if(href_list["detach"]) - detach(usr) - updateUsrDialog() - - else if(href_list["create"]) - var/amount = (text2num(href_list["amount"])) - //Can't be outside these (if you change this keep a sane limit) - amount = clamp(amount, 1, 10) - var/id = href_list["create"] - if(!stored_research.researched_designs.Find(id)) - //naughty naughty - stack_trace("ID did not map to a researched datum [id]") - return - - //Get design by id (or may return error design) - var/datum/design/D = SSresearch.techweb_design_by_id(id) - //Valid design datum, amount and the datum is not the error design, lets proceed - if(D && amount && !istype(D, /datum/design/error_design)) - create_product(D, amount) - //This shouldnt happen normally but href forgery is real - else - stack_trace("ID could not be turned into a valid techweb design datum [id]") - updateUsrDialog() - - else if(href_list["menu"]) - menustat = "menu" - updateUsrDialog() + switch(action) + if("activate") + activate(usr) + return TRUE + if("detach") + detach(usr) + return TRUE + if("create") + var/amount = text2num(params["amount"]) + amount = clamp(amount, 1, 10) + if(!amount) + return + var/id = params["id"] + if(!stored_research.researched_designs.Find(id)) + stack_trace("ID did not map to a researched datum [id]") + return + var/datum/design/D = SSresearch.techweb_design_by_id(id) + if(D && !istype(D, /datum/design/error_design)) + create_product(D, amount) + else + stack_trace("ID could not be turned into a valid techweb design datum [id]") + return + return TRUE + if("select") + selected_cat = params["category"] + return TRUE diff --git a/tgui/packages/tgui/interfaces/Biogenerator.js b/tgui/packages/tgui/interfaces/Biogenerator.js new file mode 100644 index 00000000000..7142cb045ba --- /dev/null +++ b/tgui/packages/tgui/interfaces/Biogenerator.js @@ -0,0 +1,188 @@ +import { classes } from 'common/react'; +import { createSearch } from 'common/string'; +import { Fragment } from 'inferno'; +import { useBackend, useLocalState } from '../backend'; +import { Box, Button, Dimmer, Flex, Icon, Input, Section, Table, Tabs, NoticeBox, NumberInput } from '../components'; +import { formatMoney } from '../format'; +import { Window } from '../layouts'; + +const MAX_SEARCH_RESULTS = 25; + +export const Biogenerator = (props, context) => { + const { data } = useBackend(context); + const { + beaker, + processing, + } = data; + return ( + + {!!processing && ( + + + {' Processing...'} + + )} + + {!beaker && ( + No Container + )} + {!!beaker && ( + + )} + + + ); +}; + +export const BiogeneratorContent = (props, context) => { + const { act, data } = useBackend(context); + const { + biomass, + can_process, + categories = [], + } = data; + const [ + searchText, + setSearchText, + ] = useLocalState(context, 'searchText', ''); + const [ + selectedCategory, + setSelectedCategory, + ] = useLocalState(context, 'category', categories[0]?.name); + const testSearch = createSearch(searchText, item => { + return item.name; + }); + const items = searchText.length > 0 + // Flatten all categories and apply search to it + && categories + .flatMap(category => category.items || []) + .filter(testSearch) + .filter((item, i) => i < MAX_SEARCH_RESULTS) + // Select a category and show all items in it + || categories + .find(category => category.name === selectedCategory) + ?.items + // If none of that results in a list, return an empty list + || []; + return ( +
0 ? 'good' : 'bad'}> + {formatMoney(biomass)} Biomass + + )} + buttons={( + + Search + setSearchText(value)} + mx={1} /> +
+ ); +}; + +const ItemList = (props, context) => { + const { act } = useBackend(context); + const [ + hoveredItem, + setHoveredItem, + ] = useLocalState(context, 'hoveredItem', {}); + const hoveredCost = hoveredItem && hoveredItem.cost || 0; + // Append extra hover data to items + const items = props.items.map(item => { + const [ + amount, + setAmount, + ] = useLocalState(context, "amount" + item.name, 1); + const notSameItem = hoveredItem && hoveredItem.name !== item.name; + const notEnoughHovered = props.biomass - hoveredCost + * hoveredItem.amount < item.cost * amount; + const disabledDueToHovered = notSameItem && notEnoughHovered; + const disabled = props.biomass < item.cost * amount || disabledDueToHovered; + return { + ...item, + disabled, + amount, + setAmount, + }; + }); + return items.map(item => ( + + + + {' '}{item.name} + + + item.setAmount(value)} /> + + +