mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-29 08:08:49 +01:00
tgui: Biogenerator (#51022)
* First version * Pre-release version * Cleanup * Review fixes and finishing touches
This commit is contained in:
@@ -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, "<span class='notice'>You add the container to the machine.</span>")
|
||||
update_icon()
|
||||
updateUsrDialog()
|
||||
else
|
||||
to_chat(user, "<span class='warning'>Close the maintenance panel first.</span>")
|
||||
return
|
||||
@@ -157,101 +157,51 @@
|
||||
else
|
||||
to_chat(user, "<span class='warning'>You cannot put this in [src.name]!</span>")
|
||||
|
||||
/obj/machinery/biogenerator/ui_interact(mob/user)
|
||||
if(machine_stat & BROKEN || panel_open)
|
||||
return
|
||||
. = ..()
|
||||
var/dat
|
||||
if(processing)
|
||||
dat += "<div class='statusDisplay'>Biogenerator is processing! Please wait...</div><BR>"
|
||||
else
|
||||
switch(menustat)
|
||||
if("nopoints")
|
||||
dat += "<div class='statusDisplay'>You do not have enough biomass to create products.<BR>Please, put growns into reactor and activate it.</div>"
|
||||
menustat = "menu"
|
||||
if("complete")
|
||||
dat += "<div class='statusDisplay'>Operation complete.</div>"
|
||||
menustat = "menu"
|
||||
if("void")
|
||||
dat += "<div class='statusDisplay'>Error: No growns inside.<BR>Please, put growns into reactor.</div>"
|
||||
menustat = "menu"
|
||||
if("nobeakerspace")
|
||||
dat += "<div class='statusDisplay'>Not enough space left in container. Unable to create product.</div>"
|
||||
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 += "<div class='statusDisplay'>Biomass: [points] units.</div><BR>"
|
||||
dat += "<A href='?src=[REF(src)];activate=1'>Activate</A><A href='?src=[REF(src)];detach=1'>Detach Container</A>"
|
||||
for(var/cat in categories)
|
||||
dat += "<h3>[cat]:</h3>"
|
||||
dat += "<div class='statusDisplay'>"
|
||||
for(var/V in categories[cat])
|
||||
var/datum/design/D = V
|
||||
dat += "[D.name]: <A href='?src=[REF(src)];create=[D.id];amount=1'>Make</A>"
|
||||
if(cat in timesFiveCategories)
|
||||
dat += "<A href='?src=[REF(src)];create=[D.id];amount=5'>x5</A>"
|
||||
if(ispath(D.build_path, /obj/item/stack))
|
||||
dat += "<A href='?src=[REF(src)];create=[D.id];amount=10'>x10</A>"
|
||||
dat += "([D.materials[SSmaterials.GetMaterialRef(/datum/material/biomass)]/efficiency])<br>"
|
||||
dat += "</div>"
|
||||
else
|
||||
dat += "<div class='statusDisplay'>No container inside, please insert container.</div>"
|
||||
|
||||
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, "<span class='warning'>The biogenerator is in the process of working.</span>")
|
||||
to_chat(user, "<span class='warning'>The biogenerator is in the process of working.</span>")
|
||||
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, "<!--customheadhtml-->", 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
|
||||
|
||||
@@ -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 (
|
||||
<Window resizable>
|
||||
{!!processing && (
|
||||
<Dimmer fontSize="32px">
|
||||
<Icon name="cog" spin={1} />
|
||||
{' Processing...'}
|
||||
</Dimmer>
|
||||
)}
|
||||
<Window.Content scrollable>
|
||||
{!beaker && (
|
||||
<NoticeBox>No Container</NoticeBox>
|
||||
)}
|
||||
{!!beaker && (
|
||||
<BiogeneratorContent />
|
||||
)}
|
||||
</Window.Content>
|
||||
</Window>
|
||||
);
|
||||
};
|
||||
|
||||
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 (
|
||||
<Section
|
||||
title={(
|
||||
<Box
|
||||
inline
|
||||
color={biomass > 0 ? 'good' : 'bad'}>
|
||||
{formatMoney(biomass)} Biomass
|
||||
</Box>
|
||||
)}
|
||||
buttons={(
|
||||
<Fragment>
|
||||
Search
|
||||
<Input
|
||||
value={searchText}
|
||||
onInput={(e, value) => setSearchText(value)}
|
||||
mx={1} />
|
||||
<Button
|
||||
icon="eject"
|
||||
content="Eject"
|
||||
onClick={() => act('detach')} />
|
||||
<Button
|
||||
icon="cog"
|
||||
content="Activate"
|
||||
disabled={!can_process}
|
||||
onClick={() => act('activate')} />
|
||||
</Fragment>
|
||||
)}>
|
||||
<Flex>
|
||||
{searchText.length === 0 && (
|
||||
<Flex.Item>
|
||||
<Tabs vertical>
|
||||
{categories.map(category => (
|
||||
<Tabs.Tab
|
||||
key={category.name}
|
||||
selected={category.name === selectedCategory}
|
||||
onClick={() => setSelectedCategory(category.name)}>
|
||||
{category.name} ({category.items?.length || 0})
|
||||
</Tabs.Tab>
|
||||
))}
|
||||
</Tabs>
|
||||
</Flex.Item>
|
||||
)}
|
||||
<Flex.Item grow={1} basis={0}>
|
||||
{items.length === 0 && (
|
||||
<NoticeBox>
|
||||
{searchText.length === 0
|
||||
? 'No items in this category.'
|
||||
: 'No results found.'}
|
||||
</NoticeBox>
|
||||
)}
|
||||
<Table>
|
||||
<ItemList
|
||||
biomass={biomass}
|
||||
items={items} />
|
||||
</Table>
|
||||
</Flex.Item>
|
||||
</Flex>
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
|
||||
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 => (
|
||||
<Table.Row key={item.id}>
|
||||
<Table.Cell>
|
||||
<span
|
||||
className={classes(['design32x32', item.id])}
|
||||
style={{
|
||||
'vertical-align': 'middle',
|
||||
}} />
|
||||
{' '}<b>{item.name}</b>
|
||||
</Table.Cell>
|
||||
<Table.Cell collapsing>
|
||||
<NumberInput
|
||||
value={Math.round(item.amount)}
|
||||
width="35px"
|
||||
minValue={1}
|
||||
maxValue={10}
|
||||
onChange={(e, value) => item.setAmount(value)} />
|
||||
</Table.Cell>
|
||||
<Table.Cell collapsing>
|
||||
<Button
|
||||
style={{
|
||||
'text-align': 'right',
|
||||
}}
|
||||
fluid
|
||||
content={item.cost * item.amount + ' ' + "BIO"}
|
||||
disabled={item.disabled}
|
||||
onmouseover={() => setHoveredItem(item)}
|
||||
onmouseout={() => setHoveredItem({})}
|
||||
onClick={() => act('create', {
|
||||
id: item.id,
|
||||
amount: item.amount,
|
||||
})} />
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
));
|
||||
};
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user