diff --git a/monkestation/code/modules/hydroponics/machines/composter.dm b/monkestation/code/modules/hydroponics/machines/composter.dm index 2ee13764934c..ee6310ed26c4 100644 --- a/monkestation/code/modules/hydroponics/machines/composter.dm +++ b/monkestation/code/modules/hydroponics/machines/composter.dm @@ -10,21 +10,30 @@ var/biomatter = 0 /// The amount of biomatter needed to make 1 biocube. var/biocube_cost = 40 + /// Multiplier for how much biomass is created from input. + var/input_multiplier = 1 /obj/machinery/composters/Initialize(mapload) . = ..() if(mapload) - biomatter = 80 + biomatter = biocube_cost * 2 -/obj/machinery/composters/attacked_by(obj/item/attacking_item, mob/living/user) +/obj/machinery/composters/RefreshParts() . = ..() - if(istype(attacking_item, /obj/item/seeds)) - compost(attacking_item) + input_multiplier = src::input_multiplier + for(var/datum/stock_part/matter_bin/bin in component_parts) + input_multiplier += max((bin.tier - 1) / 10, 0) - if(istype(attacking_item, /obj/item/food)) - compost(attacking_item) + biocube_cost = src::biocube_cost + for(var/datum/stock_part/manipulator/manipulator in component_parts) + biocube_cost /= 1 + ((manipulator.tier - 1) / 8) + biocube_cost = FLOOR(biocube_cost, 5) - if(istype(attacking_item, /obj/item/storage/bag)) // covers any kind of bag that has a compostible item +/obj/machinery/composters/attacked_by(obj/item/attacking_item, mob/living/user) + . = ..() + if(istype(attacking_item, /obj/item/seeds) || istype(attacking_item, /obj/item/food)) + compost(attacking_item) + else if(istype(attacking_item, /obj/item/storage/bag)) // covers any kind of bag that has a compostible item var/obj/item/storage/bag/bag = attacking_item var/list/to_compost for(var/item in bag.contents) @@ -37,7 +46,7 @@ compost(to_compost) to_chat(user, span_info("You empty \the [bag] into \the [src].")) -/obj/machinery/gibber/attack_paw(mob/user, list/modifiers) +/obj/machinery/composters/attack_paw(mob/user, list/modifiers) return attack_hand(user, modifiers) /obj/machinery/composters/attack_hand(mob/living/user, list/modifiers) @@ -68,23 +77,39 @@ if(C && user.pulling == C && !C.buckled && !C.has_buckled_mobs() && !occupant) user.visible_message(span_danger("[user] stuffs [C] into [src]!")) compost(C, allow_carbons = TRUE) + return if(biomatter < biocube_cost) - to_chat(user, span_notice("Not enough biomatter to produce Bio-Cube")) + to_chat(user, span_warning("You need at least [biocube_cost] units of biomatter to produce a biocube.")) + return + + var/biocubes_to_produce = tgui_input_number( + user, + message = "How many biocubes to produce?", + title = name, + default = 1, + max_value = min(floor(biomatter / biocube_cost), /obj/item/stack/biocube::max_amount), + min_value = 1, + ) + if(!biocubes_to_produce || QDELETED(user) || QDELETED(src) || !user.can_perform_action(src, FORBID_TELEKINESIS_REACH | ALLOW_RESTING)) return - new /obj/item/stack/biocube(drop_location(), 1) - biomatter -= biocube_cost - update_desc() + var/biomatter_needed = biocubes_to_produce * biocube_cost + if(biomatter < biomatter_needed) + to_chat(user, span_warning("You need at least [biomatter_needed] units of biomatter to produce [biocubes_to_produce] biocube\s.")) + return + new /obj/item/stack/biocube(drop_location(), biocubes_to_produce) + biomatter -= biomatter_needed update_appearance() -/obj/machinery/composters/update_desc() +/obj/machinery/composters/examine(mob/user) . = ..() - desc = "Just insert your bio degradable materials and it will produce compost." - desc += "\nBiomatter: [biomatter]" + . += "It currently has [biomatter] units of biomatter." + . += "It costs [biocube_cost] units of biomatter to produce a biocube." + . += "It composts input into biomatter with [input_multiplier]x efficiency." /obj/machinery/composters/update_overlays() . = ..() - if(biomatter < 40) + if(biomatter < biocube_cost) . += mutable_appearance('monkestation/icons/obj/machines/composter.dmi', "light_off", layer = OBJ_LAYER + 0.01) else . += mutable_appearance('monkestation/icons/obj/machines/composter.dmi', "light_on", layer = OBJ_LAYER + 0.01) @@ -120,11 +145,10 @@ CHECK_TICK if(!biomatter_added) return - biomatter += biomatter_added + biomatter += round(biomatter_added * input_multiplier) if(yucky) playsound(loc, 'sound/machines/juicer.ogg', vol = 50, vary = TRUE) audible_message(span_hear("You hear a loud squelchy grinding sound.")) - update_desc() update_appearance() flick("composter_animate", src) diff --git a/tgui/packages/tgui/interfaces/Biogenerator.tsx b/tgui/packages/tgui/interfaces/Biogenerator.tsx index 964f78396da9..d87ce7b3d11a 100644 --- a/tgui/packages/tgui/interfaces/Biogenerator.tsx +++ b/tgui/packages/tgui/interfaces/Biogenerator.tsx @@ -1,176 +1,91 @@ -import { BooleanLike } from 'common/react'; -import { classes } from 'common/react'; -import { useBackend, useLocalState } from '../backend'; -import { Window } from '../layouts'; import { Box, - Section, - NumberInput, - Table, - Tabs, + Button, + Icon, LabeledList, NoticeBox, - Button, + NumberInput, ProgressBar, + Section, Stack, + Table, + Tabs, } from '../components'; +import { BooleanLike } from 'common/react'; +import { classes } from 'common/react'; +import { useBackend, useLocalState } from '../backend'; +import { Window } from '../layouts'; -type BiogeneratorData = { - processing: BooleanLike; +type Data = { beaker: BooleanLike; - reagent_color: string; - biomass: number; - max_visual_biomass: number; - can_process: BooleanLike; beakerCurrentVolume: number; beakerMaxVolume: number; - max_output: number; - efficiency: number; + biomass: number; + can_process: BooleanLike; categories: Category[]; + efficiency: number; + max_output: number; + max_visual_biomass: number; + processing: BooleanLike; + reagent_color: string; }; type Category = { - name: string; items: Design[]; + name: string; }; type Design = { - id: number; - name: string; - is_reagent: BooleanLike; - disable: BooleanLike; - cost: number; amount: number; + cost: number; + disable: BooleanLike; + id: string; + is_reagent: BooleanLike; + name: string; }; -export const Biogenerator = (props) => { - const { act, data } = useBackend(); - const { - processing, - beaker, - reagent_color, - biomass, - max_visual_biomass, - can_process, - beakerCurrentVolume, - beakerMaxVolume, - max_output, - efficiency, - categories, - } = data; - const [selectedCategory, setSelectedCategory] = useLocalState( +export const Biogenerator = () => { + const { data } = useBackend(); + const { beaker, beakerCurrentVolume, beakerMaxVolume, categories } = data; + + const [selectedCategory, setSelectedCategory] = useLocalState( 'category', data.categories[0]?.name, ); + const items = categories.find((category) => category.name === selectedCategory)?.items || []; + + const space = beaker ? beakerMaxVolume - beakerCurrentVolume : 1; + return ( - + -
- - act('activate')} - /> - } - > - - - {`${parseFloat(biomass.toFixed(2))} units`} - - - - {!!beaker && ( - act('eject')} - /> - } - > - - - {`${beakerCurrentVolume} of ${beakerMaxVolume} units`} - - - - )} - {!beaker && ( - - - No liquid container - - - )} - -
+
- {categories.map((category) => ( + {categories.map(({ name }) => ( setSelectedCategory(category.name)} + key={name} + selected={name === selectedCategory} + onClick={() => setSelectedCategory(name)} > - {category.name} + {name} ))} - +
- + {items.map((item) => ( + + ))}
@@ -180,65 +95,164 @@ export const Biogenerator = (props) => { ); }; -const ItemList = (props) => { - const { act } = useBackend(); - const items = props.items.map((item) => { - const [amount, setAmount] = useLocalState( - 'amount' + item.name, - item.is_reagent ? Math.min(Math.max(props.space, 1), 10) : 1, - ); - const disabled = - props.processing || - (item.is_reagent && !props.beaker) || - (item.is_reagent && props.space < amount) || - props.biomass < Math.ceil((item.cost * amount) / props.efficiency); - const max_possible = Math.floor( - (props.efficiency * props.biomass) / item.cost, - ); - const max_capacity = item.is_reagent ? props.space : props.max_output; - const max_amount = Math.max(1, Math.min(max_capacity, max_possible)); - return { - ...item, - disabled, - max_amount, - amount, - setAmount, - }; - }); - return items.map((item) => ( - +const Controls = () => { + const { act, data } = useBackend(); + const { + beaker, + beakerCurrentVolume, + beakerMaxVolume, + biomass, + can_process, + max_visual_biomass, + processing, + reagent_color, + } = data; + + return ( +
+ + act('activate')} + > + Generate + + } + > + + + {`${parseFloat(biomass.toFixed(2))} units`} + + + + {!!beaker && ( + act('eject')} + > + Eject + + } + > + + + {`${beakerCurrentVolume} of ${beakerMaxVolume} units`} + + + + )} + {!beaker && ( + + + No liquid container + + + )} + +
+ ); +}; + +type Props = { + item: Design; + space: number; +}; + +const Item = (props: Props) => { + const { item, space } = props; + const { cost, id, is_reagent, name } = item; + + const { act, data } = useBackend(); + const { biomass, beaker, efficiency, max_output, processing } = data; + + const minAmount = is_reagent ? Math.min(Math.max(space, 1), 10) : 1; + + const [amount, setAmount] = useLocalState('amount-' + id, minAmount); + + const disabled = + processing || + (is_reagent && !beaker) || + (is_reagent && space < amount) || + biomass < Math.ceil(cost * amount); + + const maxPossible = Math.floor(biomass / cost); + + const maxCapacity = is_reagent ? space : max_output; + const maxAmount = Math.max(1, Math.min(maxCapacity, maxPossible)); + + return ( + {' '} - {item.name} + {name} item.setAmount(value)} + maxValue={maxAmount} + onChange={(_, value) => setAmount(value)} /> - )); + ); };