From a6d0e503cbed54662afd4d90cff1ff98732371b8 Mon Sep 17 00:00:00 2001 From: NovaBot <154629622+NovaBot13@users.noreply.github.com> Date: Wed, 10 Apr 2024 23:26:47 -0400 Subject: [PATCH] [MIRROR] General maintenance for chem master (#1904) * General maintenance for chem master * Update chem_master.dm * Update chem_master.dm * Update chem_master.dm * Update chem_master.dm --------- Co-authored-by: SyncIt21 <110812394+SyncIt21@users.noreply.github.com> Co-authored-by: Mal <13398309+vinylspiders@users.noreply.github.com> --- .../chemistry/machinery/chem_master.dm | 725 ++++++++++-------- 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, 569 insertions(+), 475 deletions(-) diff --git a/code/modules/reagents/chemistry/machinery/chem_master.dm b/code/modules/reagents/chemistry/machinery/chem_master.dm index dfad101c273..cc6b09c72aa 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(gone == beaker) - beaker = null - update_appearance(UPDATE_ICON) + 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/RefreshParts() +/obj/machinery/chem_master/examine(mob/user) . = ..() - 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 - -/obj/machinery/chem_master/update_appearance(updates=ALL) + 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") + + . += 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) . = ..() 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,46 +122,126 @@ // 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/Exited(atom/movable/gone, direction) + . = ..() + if(gone == beaker) + beaker = null + update_appearance(UPDATE_OVERLAYS) + +/obj/machinery/chem_master/on_set_is_operational(old_value) + if(!is_operational) + is_printing = FALSE + update_appearance(UPDATE_OVERLAYS) + +/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], + CAT_HYPOS = GLOB.reagent_containers[CAT_HYPOS], // NOVA EDIT ADDITION + CAT_DARTS = GLOB.reagent_containers[CAT_DARTS], // NOVA EDIT ADDITION + ) + return containers + +/obj/machinery/chem_master/item_interaction(mob/living/user, obj/item/tool, list/modifiers, is_right_clicking) + 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 + 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 - return ITEM_INTERACT_BLOCKING /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_ICON) + update_appearance(UPDATE_OVERLAYS) return ITEM_INTERACT_SUCCESS - return ITEM_INTERACT_BLOCKING /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 - return ITEM_INTERACT_BLOCKING -/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()) - replace_beaker(user, tool) - if(!panel_open) - ui_interact(user) - 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) - return ..() + 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 @@ -164,27 +252,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], - CAT_HYPOS = GLOB.reagent_containers[CAT_HYPOS], // NOVA EDIT ADDITION - CAT_DARTS = GLOB.reagent_containers[CAT_DARTS], // NOVA EDIT ADDITION - ) - /obj/machinery/chem_master/ui_assets(mob/user) return list( get_asset_datum(/datum/asset/spritesheet/chemmaster) @@ -198,274 +265,286 @@ /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() + + //printing statictics + .["isPrinting"] = is_printing + .["printingProgress"] = printing_progress + .["printingTotal"] = printing_total + .["maxPrintable"] = printing_amount + + //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 + + //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 + + //is transfering or destroying reagents. applied only for buffer + .["isTransfering"] = is_transfering + + //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 + + //selected container + .["selectedContainerRef"] = REF(selected_container) + .["selectedContainerVolume"] = initial(selected_container.volume) + +/** + * 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 - 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 - - 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 - - data["transferMode"] = transfer_mode - - 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 - - data["selectedContainerRef"] = selected_container - var/obj/item/reagent_containers/container = locate(selected_container) - data["selectedContainerVolume"] = initial(container.volume) + //sanity checks for reagent path + var/datum/reagent/reagent = text2path(path) + if (!reagent) + return FALSE - return data + //use energy + if(!use_energy(active_power_usage)) + return FALSE -/obj/machinery/chem_master/ui_act(action, params) + //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") - is_printing = FALSE - return TRUE + var/reagent_ref = params["reagentRef"] + var/amount = params["amount"] + var/target = params["target"] - if(action == "toggleContainerSuggestion") - do_suggest_container = !do_suggest_container - return TRUE + 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(action == "selectContainer") - selected_container = params["ref"] - return TRUE + if("toggleTransferMode") + is_transfering = !is_transfering + 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)" - // NOVA EDIT ADDITION START - Autonamed hyposprays/smartdarts - if(ispath(container_style, /obj/item/reagent_containers/cup/vial) || ispath(container_style, /obj/item/reagent_containers/syringe/smartdart)) - item_name_default = "[master_reagent.name] [item_name_default]" - // NOVA EDIT ADDITION END - - 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 + if("stopPrinting") + is_printing = FALSE + update_appearance(UPDATE_OVERLAYS) + return TRUE - // 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 + if("selectContainer") + var/obj/item/reagent_containers/target = locate(params["ref"]) + if(!ispath(target)) + return FALSE - use_energy(active_power_usage) + selected_container = target + return TRUE - 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 + 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)" + // NOVA EDIT ADDITION START - Autonamed hyposprays/smartdarts + if(ispath(selected_container, /obj/item/reagent_containers/cup/vial) || ispath(selected_container, /obj/item/reagent_containers/syringe/smartdart)) + item_name_default = "[master_reagent.name] [item_name_default]" + // NOVA EDIT ADDITION END + 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 - return FALSE +/** + * 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 -/// 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 + //use power + if(!use_energy(active_power_usage)) + return -/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]%.") + //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 + 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; };