From c045d79fcabda92ea29214eda93863e746fd404c Mon Sep 17 00:00:00 2001 From: SyncIt21 <110812394+SyncIt21@users.noreply.github.com> Date: Thu, 11 Apr 2024 00:33:13 +0530 Subject: [PATCH] General maintenance for chem master (#82002) ## About The Pull Request **1. Qol** - Adds screen tips & examines for screwdriver, wrench, crowbar & beaker insertion, removal & replacing actions - Analyzing reagents is now a client side feature & not a back end mode, meaning one person can see details of a reagent while the other can print stuff and do other operations so it's a non blocking operation. This also means 2 players can see information of 2 different reagents in their own screens, With that the overlay for analysis mode has been removed - You cannot do any tool acts on machines while printing. Balloon alerts will be displayed warning you of that. - The preferred container for the master reagent in the beaker is now showed in both condiment & chem master. It can be enabled/disabled via a CheckBox **2. Code Improvements** - Removed defines like `TARGET_BEAKER` , `TARGET_BEAKER` etc. ther functionality is implemented as params in the `transfer_reagent()` proc directly - Removed all variables relating to analyzing reagents like `reagent_analysis_mode`, `has_container_suggestion` etc. all memory savings - `printable_containers` now stores static values that can be shared across many chem masters - Updates only overlays and not the whole icon during operations for efficiency **3. Fixes** - You can hit the chem master with the screwdriver, wrench, crowbar & beaker when in combat mode - You cannot insert hologram items into the chem master - Deconstructing a condiment master will give you the circuit board already pre-programmed with that option selected so you don't need to use a screwdriver to re program it - `printing_amount` is now the maximum number of containers that can be printed at a time. Presently this number with upgraded parts would print out empty containers especially for patches. This is because `volume_per_item` does not take into consideration this var. Also this var would not give control to the player on exactly how many containers to print as whatever amount the player entered would be multiplied with this value producing a lot of waste & worse empty containers. Now this var determines exactly how many containers you can print and is imposed on the client side UI as well **4. Refactors (UI performance)** - Beaker data is compressed into a single entity & sent to the UI. This is set to null if no beaker is loaded thus saving data sent - Reuses Beaker props from chem synthesizer to reduce code - reagent REF replaced with direct type converted to text and later converted with `text2path()` cause its much faster ## Changelog :cl: qol: Adds screen tips & examines for screwdriver, wrench, crowbar & beaker insertion, removal & replacing actions qol: Analyzing reagents no longer blocks other players from doing other operations. Multiple players can analyze different reagents on the same machine qol: You cannot do any tool acts on the machine while printing to prevent any side effects. qol: The preferred container for the master reagent in the beaker is now showed in both condiment & chem master. The feature can be enabled/disabled via a check box code: removed defines for reagent transfer, vars for reagent analyzis to save memory. Autodoc for other vars & procs fix: You can hit the chem master with tools like screwdriver, crowbar, wrench & beaker in combat mode fix: You cannot insert hologram items into the chem master fix: Deconstructing a condiment master will give you the circuit board already pre-programmed with that option fix: You now print the exact amount of containers requested even with upgraded parts without creating empty containers. Max printable containers is 13 with tier 4 parts able to print 50 containers. refactor: Optimized client side UI code & chem master as a whole. /:cl: --------- Co-authored-by: MrMelbert <51863163+MrMelbert@users.noreply.github.com> --- .../chemistry/machinery/chem_master.dm | 727 ++++++++++-------- icons/obj/medical/chemical.dmi | Bin 71028 -> 70968 bytes tgui/packages/tgui/interfaces/ChemMaster.tsx | 317 ++++---- .../tgui/interfaces/common/BeakerDisplay.tsx | 2 +- 4 files changed, 571 insertions(+), 475 deletions(-) diff --git a/code/modules/reagents/chemistry/machinery/chem_master.dm b/code/modules/reagents/chemistry/machinery/chem_master.dm index afd81d7fb95..4b9cb532d4a 100644 --- a/code/modules/reagents/chemistry/machinery/chem_master.dm +++ b/code/modules/reagents/chemistry/machinery/chem_master.dm @@ -1,84 +1,94 @@ -#define TRANSFER_MODE_DESTROY 0 -#define TRANSFER_MODE_MOVE 1 -#define TARGET_BEAKER "beaker" -#define TARGET_BUFFER "buffer" - /obj/machinery/chem_master name = "ChemMaster 3000" desc = "Used to separate chemicals and distribute them in a variety of forms." - density = TRUE - layer = BELOW_OBJ_LAYER icon = 'icons/obj/medical/chemical.dmi' icon_state = "chemmaster" base_icon_state = "chemmaster" + density = TRUE idle_power_usage = BASE_MACHINE_IDLE_CONSUMPTION * 0.2 + active_power_usage = BASE_MACHINE_ACTIVE_CONSUMPTION * 0.2 resistance_flags = FIRE_PROOF | ACID_PROOF circuit = /obj/item/circuitboard/machine/chem_master - /// Icons for different percentages of buffer reagents - var/fill_icon = 'icons/obj/medical/reagent_fillings.dmi' - var/fill_icon_state = "chemmaster" - var/static/list/fill_icon_thresholds = list(10, 20, 30, 40, 50, 60, 70, 80, 90, 100) + /// Inserted reagent container var/obj/item/reagent_containers/beaker /// Whether separated reagents should be moved back to container or destroyed. - var/transfer_mode = TRANSFER_MODE_MOVE - /// Whether reagent analysis screen is active - var/reagent_analysis_mode = FALSE - /// Reagent being analyzed - var/datum/reagent/analyzed_reagent + var/is_transfering = TRUE /// List of printable container types - var/list/printable_containers = list() - /// Container used by default to reset to (REF) - var/default_container - /// Selected printable container type (REF) - var/selected_container - /// Whether the machine has an option to suggest container - var/has_container_suggestion = FALSE - /// Whether to suggest container or not - var/do_suggest_container = FALSE - /// The container suggested by main reagent in the buffer - var/suggested_container + var/list/printable_containers + /// Container used by default to reset to + var/obj/item/reagent_containers/default_container + /// Selected printable container type + var/obj/item/reagent_containers/selected_container /// Whether the machine is busy with printing containers var/is_printing = FALSE - /// Number of printed containers in the current printing cycle for UI progress bar + /// Number of containers printed so far var/printing_progress + /// Number of containers to be printed var/printing_total - /// Default duration of printing cycle - var/printing_speed = 0.75 SECONDS // Duration of animation - /// The amount of containers printed in one cycle + /// The amount of containers that can be printed in 1 cycle var/printing_amount = 1 /obj/machinery/chem_master/Initialize(mapload) create_reagents(100) - load_printable_containers() - default_container = REF(printable_containers[printable_containers[1]][1]) + + printable_containers = load_printable_containers() + default_container = printable_containers[printable_containers[1]][1] selected_container = default_container - return ..() + + register_context() + + . = ..() + + var/obj/item/circuitboard/machine/chem_master/board = circuit + board.build_path = type + board.name = name /obj/machinery/chem_master/Destroy() QDEL_NULL(beaker) return ..() -/obj/machinery/chem_master/on_deconstruction(disassembled) - replace_beaker() - return ..() +/obj/machinery/chem_master/add_context(atom/source, list/context, obj/item/held_item, mob/user) + . = NONE + if(isnull(held_item) || (held_item.item_flags & ABSTRACT) || (held_item.flags_1 & HOLOGRAM_1)) + if(isnull(held_item)) + context[SCREENTIP_CONTEXT_RMB] = "Remove beaker" + . = CONTEXTUAL_SCREENTIP_SET + return . -/obj/machinery/chem_master/Exited(atom/movable/gone, direction) + if(is_reagent_container(held_item) && held_item.is_open_container()) + if(!QDELETED(beaker)) + context[SCREENTIP_CONTEXT_LMB] = "Replace beaker" + else + context[SCREENTIP_CONTEXT_LMB] = "Insert beaker" + return CONTEXTUAL_SCREENTIP_SET + + if(held_item.tool_behaviour == TOOL_SCREWDRIVER) + context[SCREENTIP_CONTEXT_LMB] = "[panel_open ? "Close" : "Open"] panel" + return CONTEXTUAL_SCREENTIP_SET + else if(held_item.tool_behaviour == TOOL_WRENCH) + context[SCREENTIP_CONTEXT_LMB] = "[anchored ? "Un" : ""] anchor" + return CONTEXTUAL_SCREENTIP_SET + else if(panel_open && held_item.tool_behaviour == TOOL_CROWBAR) + context[SCREENTIP_CONTEXT_LMB] = "Deconstruct" + return CONTEXTUAL_SCREENTIP_SET + +/obj/machinery/chem_master/examine(mob/user) . = ..() - if(gone == beaker) - beaker = null - update_appearance(UPDATE_ICON) + if(in_range(user, src) || isobserver(user)) + . += span_notice("The status display reads:
Reagent buffer capacity: [reagents.maximum_volume] units.
Number of containers printed per cycle [printing_amount].") + if(!QDELETED(beaker)) + . += span_notice("[beaker] of [beaker.reagents.maximum_volume]u capacity inserted") + . += span_notice("Right click with empty hand to remove beaker") + else + . += span_warning("Missing input beaker") -/obj/machinery/chem_master/RefreshParts() - . = ..() - reagents.maximum_volume = 0 - for(var/obj/item/reagent_containers/cup/beaker/beaker in component_parts) - reagents.maximum_volume += beaker.reagents.maximum_volume - printing_amount = 0 - for(var/datum/stock_part/servo/servo in component_parts) - printing_amount += servo.tier + . += span_notice("It can be [EXAMINE_HINT("wrenched")] [anchored ? "loose" : "in place"]") + . += span_notice("Its maintainence panel can be [EXAMINE_HINT("screwed")] [panel_open ? "close" : "open"]") + if(panel_open) + . += span_notice("The machine can be [EXAMINE_HINT("pried")] apart.") -/obj/machinery/chem_master/update_appearance(updates=ALL) +/obj/machinery/chem_master/update_appearance(updates) . = ..() if(panel_open || (machine_stat & (NOPOWER|BROKEN))) set_light(0) @@ -102,9 +112,7 @@ // Screen overlay if(!panel_open && !(machine_stat & (NOPOWER | BROKEN))) var/screen_overlay = base_icon_state + "_overlay_screen" - if(reagent_analysis_mode) - screen_overlay += "_analysis" - else if(is_printing) + if(is_printing) screen_overlay += "_active" else if(reagents.total_volume > 0) screen_overlay += "_main" @@ -114,45 +122,124 @@ // Buffer reagents overlay if(reagents.total_volume) var/threshold = null + var/static/list/fill_icon_thresholds = list(10, 20, 30, 40, 50, 60, 70, 80, 90, 100) for(var/i in 1 to fill_icon_thresholds.len) - if(ROUND_UP(100 * reagents.total_volume / reagents.maximum_volume) >= fill_icon_thresholds[i]) + if(ROUND_UP(100 * (reagents.total_volume / reagents.maximum_volume)) >= fill_icon_thresholds[i]) threshold = i if(threshold) - var/fill_name = "[fill_icon_state][fill_icon_thresholds[threshold]]" - var/mutable_appearance/filling = mutable_appearance(fill_icon, fill_name) + var/fill_name = "chemmaster[fill_icon_thresholds[threshold]]" + var/mutable_appearance/filling = mutable_appearance('icons/obj/medical/reagent_fillings.dmi', fill_name) filling.color = mix_color_from_reagents(reagents.reagent_list) . += filling -/obj/machinery/chem_master/wrench_act(mob/living/user, obj/item/tool) - if(default_unfasten_wrench(user, tool) == SUCCESSFUL_UNFASTEN) - return ITEM_INTERACT_SUCCESS - return ITEM_INTERACT_BLOCKING +/obj/machinery/chem_master/Exited(atom/movable/gone, direction) + . = ..() + if(gone == beaker) + beaker = null + update_appearance(UPDATE_OVERLAYS) -/obj/machinery/chem_master/screwdriver_act(mob/living/user, obj/item/tool) - if(default_deconstruction_screwdriver(user, icon_state, icon_state, tool)) - update_appearance(UPDATE_ICON) - return ITEM_INTERACT_SUCCESS - return ITEM_INTERACT_BLOCKING +/obj/machinery/chem_master/on_set_is_operational(old_value) + if(!is_operational) + is_printing = FALSE + update_appearance(UPDATE_OVERLAYS) -/obj/machinery/chem_master/crowbar_act(mob/living/user, obj/item/tool) - if(default_deconstruction_crowbar(tool)) - return ITEM_INTERACT_SUCCESS - return ITEM_INTERACT_BLOCKING +/obj/machinery/chem_master/RefreshParts() + . = ..() + reagents.maximum_volume = 0 + for(var/obj/item/reagent_containers/cup/beaker/beaker in component_parts) + reagents.maximum_volume += beaker.reagents.maximum_volume + + printing_amount = 0 + for(var/datum/stock_part/servo/servo in component_parts) + printing_amount += servo.tier * 12.5 + printing_amount = min(50, ROUND_UP(printing_amount)) + +///Return a map of category->list of containers this machine can print +/obj/machinery/chem_master/proc/load_printable_containers() + PROTECTED_PROC(TRUE) + SHOULD_BE_PURE(TRUE) + + var/static/list/containers + if(!length(containers)) + containers = list( + CAT_TUBES = GLOB.reagent_containers[CAT_TUBES], + CAT_PILLS = GLOB.reagent_containers[CAT_PILLS], + CAT_PATCHES = GLOB.reagent_containers[CAT_PATCHES], + ) + return containers /obj/machinery/chem_master/item_interaction(mob/living/user, obj/item/tool, list/modifiers, is_right_clicking) - if(is_reagent_container(tool) && !(tool.item_flags & ABSTRACT) && tool.is_open_container()) + if(user.combat_mode || (tool.item_flags & ABSTRACT) || (tool.flags_1 & HOLOGRAM_1) || !can_interact(user) || !user.can_perform_action(src, ALLOW_SILICON_REACH | FORBID_TELEKINESIS_REACH)) + return ..() + + if(is_reagent_container(tool) && tool.is_open_container()) replace_beaker(user, tool) if(!panel_open) ui_interact(user) - return ITEM_INTERACT_SUCCESS + return ITEM_INTERACT_SUCCESS + else + return ITEM_INTERACT_BLOCKING return ..() +/obj/machinery/chem_master/wrench_act(mob/living/user, obj/item/tool) + if(user.combat_mode) + return NONE + + . = ITEM_INTERACT_BLOCKING + if(is_printing) + balloon_alert(user, "still printing!") + return . + + if(default_unfasten_wrench(user, tool) == SUCCESSFUL_UNFASTEN) + return ITEM_INTERACT_SUCCESS + +/obj/machinery/chem_master/screwdriver_act(mob/living/user, obj/item/tool) + if(user.combat_mode) + return NONE + + . = ITEM_INTERACT_BLOCKING + if(is_printing) + balloon_alert(user, "still printing!") + return . + + if(default_deconstruction_screwdriver(user, icon_state, icon_state, tool)) + update_appearance(UPDATE_OVERLAYS) + return ITEM_INTERACT_SUCCESS + +/obj/machinery/chem_master/crowbar_act(mob/living/user, obj/item/tool) + if(user.combat_mode) + return NONE + + . = ITEM_INTERACT_BLOCKING + if(is_printing) + balloon_alert(user, "still printing!") + return . + + if(default_deconstruction_crowbar(tool)) + return ITEM_INTERACT_SUCCESS + +/** + * Insert, remove, replace the existig beaker + * Arguments + * + * * mob/living/user - the player trying to replace the beaker + * * obj/item/reagent_containers/new_beaker - the beaker we are trying to insert, swap with existing or remove if null + */ +/obj/machinery/chem_master/proc/replace_beaker(mob/living/user, obj/item/reagent_containers/new_beaker) + PRIVATE_PROC(TRUE) + + if(!QDELETED(beaker)) + try_put_in_hand(beaker, user) + if(!QDELETED(new_beaker) && user.transferItemToLoc(new_beaker, src)) + beaker = new_beaker + update_appearance(UPDATE_OVERLAYS) + /obj/machinery/chem_master/attack_hand_secondary(mob/user, list/modifiers) . = ..() if(. == SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN) return . - if(!can_interact(user) || !user.can_perform_action(src, ALLOW_SILICON_REACH|FORBID_TELEKINESIS_REACH)) + if(!can_interact(user) || !user.can_perform_action(src, ALLOW_SILICON_REACH | FORBID_TELEKINESIS_REACH)) return . replace_beaker(user) return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN @@ -163,25 +250,6 @@ /obj/machinery/chem_master/attack_ai_secondary(mob/user, list/modifiers) return attack_hand_secondary(user, modifiers) -/// Insert new beaker and/or eject the inserted one -/obj/machinery/chem_master/proc/replace_beaker(mob/living/user, obj/item/reagent_containers/new_beaker) - if(new_beaker && user && !user.transferItemToLoc(new_beaker, src)) - return FALSE - if(beaker) - try_put_in_hand(beaker, user) - beaker = null - if(new_beaker) - beaker = new_beaker - update_appearance(UPDATE_ICON) - return TRUE - -/obj/machinery/chem_master/proc/load_printable_containers() - printable_containers = list( - CAT_TUBES = GLOB.reagent_containers[CAT_TUBES], - CAT_PILLS = GLOB.reagent_containers[CAT_PILLS], - CAT_PATCHES = GLOB.reagent_containers[CAT_PATCHES], - ) - /obj/machinery/chem_master/ui_assets(mob/user) return list( get_asset_datum(/datum/asset/spritesheet/chemmaster) @@ -195,269 +263,282 @@ /obj/machinery/chem_master/ui_static_data(mob/user) var/list/data = list() + data["categories"] = list() for(var/category in printable_containers) - var/container_data = list() + //make the category + var/list/category_list = list( + "name" = category, + "containers" = list(), + ) + + //add containers to this category for(var/obj/item/reagent_containers/container as anything in printable_containers[category]) - container_data += list(list( + category_list["containers"] += list(list( "icon" = sanitize_css_class_name("[container]"), "ref" = REF(container), "name" = initial(container.name), "volume" = initial(container.volume), )) - data["categories"]+= list(list( - "name" = category, - "containers" = container_data, - )) + + //add the category + data["categories"] += list(category_list) return data /obj/machinery/chem_master/ui_data(mob/user) - var/list/data = list() + . = list() - data["reagentAnalysisMode"] = reagent_analysis_mode - if(reagent_analysis_mode && analyzed_reagent) - var/state - switch(analyzed_reagent.reagent_state) - if(SOLID) - state = "Solid" - if(LIQUID) - state = "Liquid" - if(GAS) - state = "Gas" - else - state = "Unknown" - data["analysisData"] = list( - "name" = analyzed_reagent.name, - "state" = state, - "pH" = analyzed_reagent.ph, - "color" = analyzed_reagent.color, - "description" = analyzed_reagent.description, - "purity" = analyzed_reagent.purity, - "metaRate" = analyzed_reagent.metabolization_rate, - "overdose" = analyzed_reagent.overdose_threshold, - "addictionTypes" = reagents.parse_addictions(analyzed_reagent), - ) - else - data["isPrinting"] = is_printing - data["printingProgress"] = printing_progress - data["printingTotal"] = printing_total - data["hasBeaker"] = beaker ? TRUE : FALSE - data["beakerCurrentVolume"] = beaker ? round(beaker.reagents.total_volume, 0.01) : null - data["beakerMaxVolume"] = beaker ? beaker.volume : null - var/list/beaker_contents = list() - if(beaker) - for(var/datum/reagent/reagent in beaker.reagents.reagent_list) - beaker_contents.Add(list(list("name" = reagent.name, "ref" = REF(reagent), "volume" = round(reagent.volume, 0.01)))) - data["beakerContents"] = beaker_contents + //printing statictics + .["isPrinting"] = is_printing + .["printingProgress"] = printing_progress + .["printingTotal"] = printing_total + .["maxPrintable"] = printing_amount - var/list/buffer_contents = list() - if(reagents.total_volume) - for(var/datum/reagent/reagent in reagents.reagent_list) - buffer_contents.Add(list(list("name" = reagent.name, "ref" = REF(reagent), "volume" = round(reagent.volume, 0.01)))) - data["bufferContents"] = buffer_contents - data["bufferCurrentVolume"] = round(reagents.total_volume, 0.01) - data["bufferMaxVolume"] = reagents.maximum_volume + //contents of source beaker + var/list/beaker_data = null + if(!QDELETED(beaker)) + beaker_data = list() + beaker_data["maxVolume"] = beaker.volume + beaker_data["currentVolume"] = round(beaker.reagents.total_volume, CHEMICAL_VOLUME_ROUNDING) + var/list/beakerContents = list() + if(length(beaker.reagents.reagent_list)) + for(var/datum/reagent/reagent as anything in beaker.reagents.reagent_list) + beakerContents += list(list( + "ref" = "[reagent.type]", + "name" = reagent.name, + "volume" = round(reagent.volume, CHEMICAL_VOLUME_ROUNDING), + "pH" = reagent.ph, + "color" = reagent.color, + "description" = reagent.description, + "purity" = reagent.purity, + "metaRate" = reagent.metabolization_rate, + "overdose" = reagent.overdose_threshold, + "addictionTypes" = reagents.parse_addictions(reagent), + )) + beaker_data["contents"] = beakerContents + .["beaker"] = beaker_data - data["transferMode"] = transfer_mode + //contents of buffer + beaker_data = list() + beaker_data["maxVolume"] = reagents.maximum_volume + beaker_data["currentVolume"] = round(reagents.total_volume, CHEMICAL_VOLUME_ROUNDING) + var/list/beakerContents = list() + if(length(reagents.reagent_list)) + for(var/datum/reagent/reagent as anything in reagents.reagent_list) + beakerContents += list(list( + "ref" = "[reagent.type]", + "name" = reagent.name, + "volume" = round(reagent.volume, CHEMICAL_VOLUME_ROUNDING), + "pH" = reagent.ph, + "color" = reagent.color, + "description" = reagent.description, + "purity" = reagent.purity, + "metaRate" = reagent.metabolization_rate, + "overdose" = reagent.overdose_threshold, + "addictionTypes" = reagents.parse_addictions(reagent), + )) + beaker_data["contents"] = beakerContents + .["buffer"] = beaker_data - data["hasContainerSuggestion"] = !!has_container_suggestion - if(has_container_suggestion) - data["doSuggestContainer"] = !!do_suggest_container - if(do_suggest_container) - if(reagents.total_volume > 0) - var/master_reagent = reagents.get_master_reagent() - suggested_container = get_suggested_container(master_reagent) - else - suggested_container = default_container - data["suggestedContainer"] = suggested_container - selected_container = suggested_container - else if (isnull(selected_container)) - selected_container = default_container + //is transfering or destroying reagents. applied only for buffer + .["isTransfering"] = is_transfering - data["selectedContainerRef"] = selected_container - var/obj/item/reagent_containers/container = locate(selected_container) - data["selectedContainerVolume"] = initial(container.volume) + //container along with the suggested type + var/obj/item/reagent_containers/suggested_container = default_container + if(reagents.total_volume > 0) + var/datum/reagent/master_reagent = reagents.get_master_reagent() + var/container_found = FALSE + suggested_container = master_reagent.default_container + for(var/category in printable_containers) + for(var/obj/item/reagent_containers/container as anything in printable_containers[category]) + if(container == suggested_container) + suggested_container = REF(container) + container_found = TRUE + break + if(!container_found) + suggested_container = REF(default_container) + .["suggestedContainerRef"] = suggested_container - return data + //selected container + .["selectedContainerRef"] = REF(selected_container) + .["selectedContainerVolume"] = initial(selected_container.volume) -/obj/machinery/chem_master/ui_act(action, params) +/** + * Transfers a single reagent between buffer & beaker + * Arguments + * + * * mob/user - the player who is attempting the transfer + * * datum/reagents/source - the holder we are transferring from + * * datum/reagents/target - the holder we are transferring to + * * datum/reagent/path - the reagent typepath we are transfering + * * amount - volume to transfer -1 means custom amount + * * do_transfer - transfer the reagents else destroy them + */ +/obj/machinery/chem_master/proc/transfer_reagent(mob/user, datum/reagents/source, datum/reagents/target, datum/reagent/path, amount, do_transfer) + PRIVATE_PROC(TRUE) + + //sanity checks for transfer amount + if(isnull(amount)) + return FALSE + amount = text2num(amount) + if(isnull(amount)) + return FALSE + if(amount == -1) + var/target_amount = tgui_input_number(user, "Enter amount to transfer", "Transfer amount") + if(!target_amount) + return FALSE + amount = text2num(target_amount) + if(isnull(amount)) + return FALSE + if(amount <= 0) + return FALSE + + //sanity checks for reagent path + var/datum/reagent/reagent = text2path(path) + if (!reagent) + return FALSE + + //use energy + if(!use_energy(active_power_usage)) + return FALSE + + //do the operation + . = FALSE + if(do_transfer) + if(target.is_reacting) + return FALSE + if(source.trans_to(target, amount, target_id = reagent)) + . = TRUE + else if(source.remove_reagent(reagent, amount)) + . = TRUE + if(. && !QDELETED(src)) //transferring volatile reagents can cause a explosion & destory us + update_appearance(UPDATE_OVERLAYS) + return . + +/obj/machinery/chem_master/ui_act(action, params, datum/tgui/ui, datum/ui_state/state) . = ..() if(.) return - if(action == "eject") - replace_beaker(usr) - return TRUE - - if(action == "transfer") - var/reagent_ref = params["reagentRef"] - var/amount = text2num(params["amount"]) - var/target = params["target"] - return transfer_reagent(reagent_ref, amount, target) - - if(action == "toggleTransferMode") - transfer_mode = !transfer_mode - return TRUE - - if(action == "analyze") - analyzed_reagent = locate(params["reagentRef"]) - if(analyzed_reagent) - reagent_analysis_mode = TRUE - update_appearance(UPDATE_ICON) + switch(action) + if("eject") + replace_beaker(ui.user) return TRUE - if(action == "stopAnalysis") - reagent_analysis_mode = FALSE - analyzed_reagent = null - update_appearance(UPDATE_ICON) - return TRUE + if("transfer") + if(is_printing) + say("buffer locked while printing!") + return - if(action == "stopPrinting") + var/reagent_ref = params["reagentRef"] + var/amount = params["amount"] + var/target = params["target"] + + if(target == "buffer") + return transfer_reagent(ui.user, beaker.reagents, reagents, reagent_ref, amount, TRUE) + else if(target == "beaker") + return transfer_reagent(ui.user, reagents, beaker.reagents, reagent_ref, amount, is_transfering) + return FALSE + + if("toggleTransferMode") + is_transfering = !is_transfering + return TRUE + + if("stopPrinting") + is_printing = FALSE + update_appearance(UPDATE_OVERLAYS) + return TRUE + + if("selectContainer") + var/obj/item/reagent_containers/target = locate(params["ref"]) + if(!ispath(target)) + return FALSE + + selected_container = target + return TRUE + + if("create") + if(!reagents.total_volume || is_printing) + return FALSE + + //validate print count + var/item_count = params["itemCount"] + if(isnull(item_count)) + return FALSE + item_count = text2num(item_count) + if(isnull(item_count) || item_count <= 0) + return FALSE + item_count = min(item_count, printing_amount) + var/volume_in_each = round(reagents.total_volume / item_count, CHEMICAL_VOLUME_ROUNDING) + + // Generate item name + var/item_name_default = initial(selected_container.name) + var/datum/reagent/master_reagent = reagents.get_master_reagent() + if(selected_container == default_container) // Tubes and bottles gain reagent name + item_name_default = "[master_reagent.name] [item_name_default]" + if(!(initial(selected_container.reagent_flags) & OPENCONTAINER)) // Closed containers get both reagent name and units in the name + item_name_default = "[master_reagent.name] [item_name_default] ([volume_in_each]u)" + var/item_name = tgui_input_text(usr, + "Container name", + "Name", + item_name_default, + MAX_NAME_LEN) + if(!item_name) + return FALSE + + //start printing + is_printing = TRUE + printing_progress = 0 + printing_total = item_count + update_appearance(UPDATE_OVERLAYS) + create_containers(ui.user, item_count, item_name, volume_in_each) + return TRUE + +/** + * Create N selected containers with reagents from buffer split between them + * Arguments + * + * * mob/user - the player printing these containers + * * item_count - number of containers to print + * * item_name - the name for each container printed + * * volume_in_each - volume in each container created + */ +/obj/machinery/chem_master/proc/create_containers(mob/user, item_count, item_name, volume_in_each) + PRIVATE_PROC(TRUE) + + //lost power or manually stopped + if(!is_printing) + return + + //use power + if(!use_energy(active_power_usage)) + return + + //print the stuff + var/obj/item/reagent_containers/item = new selected_container(drop_location()) + adjust_item_drop_location(item) + item.name = item_name + item.reagents.clear_reagents() + reagents.trans_to(item, volume_in_each, transferred_by = user) + printing_progress++ + update_appearance(UPDATE_OVERLAYS) + + //print more items + item_count -- + if(item_count > 0) + addtimer(CALLBACK(src, PROC_REF(create_containers), user, item_count, item_name, volume_in_each), 0.75 SECONDS) + else is_printing = FALSE - return TRUE - - if(action == "toggleContainerSuggestion") - do_suggest_container = !do_suggest_container - return TRUE - - if(action == "selectContainer") - selected_container = params["ref"] - return TRUE - - if(action == "create") - if(reagents.total_volume == 0) - return FALSE - var/item_count = text2num(params["itemCount"]) - if(item_count <= 0) - return FALSE - create_containers(item_count) - return TRUE - -/// Create N selected containers with reagents from buffer split between them -/obj/machinery/chem_master/proc/create_containers(item_count = 1) - var/obj/item/reagent_containers/container_style = locate(selected_container) - var/is_pill_subtype = ispath(container_style, /obj/item/reagent_containers/pill) - var/volume_in_each = reagents.total_volume / item_count - var/printing_amount_current = is_pill_subtype ? printing_amount * 2 : printing_amount - - // Generate item name - var/item_name_default = initial(container_style.name) - var/datum/reagent/master_reagent = reagents.get_master_reagent() - if(selected_container == default_container) // Tubes and bottles gain reagent name - item_name_default = "[master_reagent.name] [item_name_default]" - if(!(initial(container_style.reagent_flags) & OPENCONTAINER)) // Closed containers get both reagent name and units in the name - item_name_default = "[master_reagent.name] [item_name_default] ([volume_in_each]u)" - var/item_name = tgui_input_text(usr, - "Container name", - "Name", - item_name_default, - MAX_NAME_LEN) - - if(!item_name || !reagents.total_volume || QDELETED(src) || !usr.can_perform_action(src, ALLOW_SILICON_REACH)) - return FALSE - - // Print and fill containers - is_printing = TRUE - update_appearance(UPDATE_ICON) - printing_progress = 0 - printing_total = item_count - while(item_count > 0) - if(!is_printing) - break - use_energy(active_power_usage) - stoplag(printing_speed) - for(var/i in 1 to printing_amount_current) - if(!item_count) - continue - var/obj/item/reagent_containers/item = new container_style(drop_location()) - adjust_item_drop_location(item) - item.name = item_name - item.reagents.clear_reagents() - reagents.trans_to(item, volume_in_each, transferred_by = src) - printing_progress++ - item_count-- - update_appearance(UPDATE_ICON) - is_printing = FALSE - update_appearance(UPDATE_ICON) - return TRUE - -/// Transfer reagents to specified target from the opposite source -/obj/machinery/chem_master/proc/transfer_reagent(reagent_ref, amount, target) - if (amount == -1) - amount = text2num(input("Enter the amount you want to transfer:", name, "")) - if (amount == null || amount <= 0) - return FALSE - if (!beaker && target == TARGET_BEAKER && transfer_mode == TRANSFER_MODE_MOVE) - return FALSE - var/datum/reagent/reagent = locate(reagent_ref) - if (!reagent) - return FALSE - - use_energy(active_power_usage) - - if (target == TARGET_BUFFER) - if(!check_reactions(reagent, beaker.reagents)) - return FALSE - beaker.reagents.trans_to(src, amount, target_id = reagent.type) - update_appearance(UPDATE_ICON) - return TRUE - - if (target == TARGET_BEAKER && transfer_mode == TRANSFER_MODE_DESTROY) - reagents.remove_reagent(reagent.type, amount) - update_appearance(UPDATE_ICON) - return TRUE - if (target == TARGET_BEAKER && transfer_mode == TRANSFER_MODE_MOVE) - if(!check_reactions(reagent, reagents)) - return FALSE - reagents.trans_to(beaker, amount, target_id = reagent.type) - update_appearance(UPDATE_ICON) - return TRUE - - return FALSE - -/// Checks to see if the target reagent is being created (reacting) and if so prevents transfer -/// Only prevents reactant from being moved so that people can still manlipulate input reagents -/obj/machinery/chem_master/proc/check_reactions(datum/reagent/reagent, datum/reagents/holder) - if(!reagent) - return FALSE - var/canMove = TRUE - for(var/datum/equilibrium/equilibrium as anything in holder.reaction_list) - if(equilibrium.reaction.reaction_flags & REACTION_COMPETITIVE) - continue - for(var/datum/reagent/result as anything in equilibrium.reaction.required_reagents) - if(result == reagent.type) - canMove = FALSE - if(!canMove) - say("Cannot move reagent during reaction!") - return canMove - -/// Retrieve REF to the best container for provided reagent -/obj/machinery/chem_master/proc/get_suggested_container(datum/reagent/reagent) - var/preferred_container = reagent.default_container - for(var/category in printable_containers) - for(var/container in printable_containers[category]) - if(container == preferred_container) - return REF(container) - return default_container - -/obj/machinery/chem_master/examine(mob/user) - . = ..() - if(in_range(user, src) || isobserver(user)) - . += span_notice("The status display reads:
Reagent buffer capacity: [reagents.maximum_volume] units.
Number of containers printed at once increased by [100 * (printing_amount / initial(printing_amount)) - 100]%.") + update_appearance(UPDATE_OVERLAYS) /obj/machinery/chem_master/condimaster name = "CondiMaster 3000" desc = "Used to create condiments and other cooking supplies." icon_state = "condimaster" - has_container_suggestion = TRUE /obj/machinery/chem_master/condimaster/load_printable_containers() - printable_containers = list( - CAT_CONDIMENTS = GLOB.reagent_containers[CAT_CONDIMENTS], - ) - -#undef TRANSFER_MODE_DESTROY -#undef TRANSFER_MODE_MOVE -#undef TARGET_BEAKER -#undef TARGET_BUFFER + var/static/list/containers + if(!length(containers)) + containers = list(CAT_CONDIMENTS = GLOB.reagent_containers[CAT_CONDIMENTS]) + return containers diff --git a/icons/obj/medical/chemical.dmi b/icons/obj/medical/chemical.dmi index b4b26e4f848c62158486744538a343f2397e17ba..84dfd01d2d455103f75a8026227d79c70922cc64 100644 GIT binary patch delta 10637 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%0D69XdT;l}qL4}jgU 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 { - const { data } = useBackend(); - const { reagentAnalysisMode } = data; + const [analyzedReagent, setAnalyzedReagent] = useState(); + return ( - + - {reagentAnalysisMode ? : } + {analyzedReagent ? ( + setAnalyzedReagent(undefined)} + /> + ) : ( + + setAnalyzedReagent(chemical) + } + /> + )} ); }; -const ChemMasterContent = (props) => { +const ChemMasterContent = (props: { + analyze: (chemical: AnalyzableReagent) => void; +}) => { const { act, data } = useBackend(); const { isPrinting, printingProgress, printingTotal, - transferMode, - hasBeaker, - beakerCurrentVolume, - beakerMaxVolume, - beakerContents, - bufferContents, - bufferCurrentVolume, - bufferMaxVolume, + maxPrintable, + isTransfering, + beaker, + buffer, categories, selectedContainerVolume, - hasContainerSuggestion, - doSuggestContainer, - suggestedContainer, } = data; - const [itemCount, setItemCount] = useState(1); + const [itemCount, setItemCount] = useState(1); + const [showPreferredContainer, setShowPreferredContainer] = + useState(false); + const buffer_contents = buffer.contents; return (
- - {` / ${beakerMaxVolume} units`} + + {` / ${beaker.maxVolume} units`} - ) } > - {!hasBeaker && ( + {!beaker ? ( No beaker loaded. - )} - {!!hasBeaker && beakerCurrentVolume === 0 && ( + ) : beaker.currentVolume === 0 ? ( Beaker is empty. + ) : ( + + {beaker.contents.map((chemical) => ( + + ))} +
)} - - {beakerContents.map((chemical) => ( - - ))} -
- - {` / ${bufferMaxVolume} units`} + + {` / ${buffer.maxVolume} units`} } > - {bufferContents.length === 0 && ( + {buffer_contents.length === 0 ? ( Buffer is empty. + ) : ( + + {buffer_contents.map((chemical) => ( + + ))} +
)} - - {bufferContents.map((chemical) => ( - - ))} -
{!isPrinting && (
+ + setShowPreferredContainer((currentValue) => !currentValue) + } + > + Suggest + { setItemCount(value); }} @@ -200,51 +208,36 @@ const ChemMasterContent = (props) => { Math.round( Math.min( selectedContainerVolume, - bufferCurrentVolume / itemCount, + buffer.currentVolume / itemCount, ) * 100, ) / 100 } u. each`} - ) : ( -
@@ -256,9 +249,10 @@ const ChemMasterContent = (props) => { } > { ); }; -const ReagentEntry = (props) => { +type ReagentProps = { + chemical: AnalyzableReagent; + transferTo: string; + analyze: (chemical: AnalyzableReagent) => void; +}; + +const ReagentEntry = (props: ReagentProps) => { const { data, act } = useBackend(); - const { chemical, transferTo } = props; + const { chemical, transferTo, analyze } = props; const { isPrinting } = data; return ( @@ -295,7 +295,6 @@ const ReagentEntry = (props) => { } > @@ -438,7 +454,6 @@ const AnalysisResults = (props) => {
{pH} - {state} {color} diff --git a/tgui/packages/tgui/interfaces/common/BeakerDisplay.tsx b/tgui/packages/tgui/interfaces/common/BeakerDisplay.tsx index 6c2d3649325..3483c3ceede 100644 --- a/tgui/packages/tgui/interfaces/common/BeakerDisplay.tsx +++ b/tgui/packages/tgui/interfaces/common/BeakerDisplay.tsx @@ -8,7 +8,7 @@ import { Section, } from '../../components'; -type BeakerReagent = { +export type BeakerReagent = { name: string; volume: number; };