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`}
act('toggleTransferMode')}
- />
+ >
+ {isTransfering ? 'Moving reagents' : 'Destroying reagents'}
+
>
}
>
- {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`}
act('create', {
itemCount: itemCount,
})
}
- />
+ >
+ Print
+
- ) : (
-
- ))
+ )
}
>
- {!!hasContainerSuggestion && (
- act('toggleContainerSuggestion')}
- checked={doSuggestContainer}
- mb={1}
- >
- Guess container by main reagent in the buffer
-
- )}
{categories.map((category) => (
- {category.containers.map(
- (container) =>
- (!hasContainerSuggestion || // Doesn't have suggestion
- (!!hasContainerSuggestion && !doSuggestContainer) || // Has sugestion and it's disabled
- (!!doSuggestContainer &&
- container.ref === suggestedContainer)) && ( // Suggestion enabled and container matches
-
- ),
- )}
+ {category.containers.map((container) => (
+
+ ))}
))}
@@ -256,9 +249,10 @@ const ChemMasterContent = (props) => {
act('stopPrinting')}
- />
+ >
+ Stop
+
}
>
{
);
};
-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) => {
{
act('transfer', {
@@ -304,9 +303,10 @@ const ReagentEntry = (props) => {
target: transferTo,
});
}}
- />
+ >
+ 1
+
act('transfer', {
@@ -315,9 +315,10 @@ const ReagentEntry = (props) => {
target: transferTo,
})
}
- />
+ >
+ 5
+
act('transfer', {
@@ -326,9 +327,10 @@ const ReagentEntry = (props) => {
target: transferTo,
})
}
- />
+ >
+ 10
+
act('transfer', {
@@ -337,7 +339,9 @@ const ReagentEntry = (props) => {
target: transferTo,
})
}
- />
+ >
+ All
+
{
- act('analyze', {
- reagentRef: chemical.ref,
- })
- }
+ onClick={() => analyze(chemical)}
/>
);
};
-const ContainerButton = ({ container, category }) => {
+type CategoryButtonProps = {
+ category: Category;
+ container: Container;
+ showPreferredContainer: BooleanLike;
+};
+
+const ContainerButton = (props: CategoryButtonProps) => {
const { act, data } = useBackend();
- const { isPrinting, selectedContainerRef } = data;
+ const { isPrinting, selectedContainerRef, suggestedContainerRef } = data;
+ const { category, container, showPreferredContainer } = props;
const isPillPatch = ['pills', 'patches'].includes(category.name);
+
return (
{
>
{
) as any;
};
-const AnalysisResults = (props) => {
- const { act, data } = useBackend();
+const AnalysisResults = (props: {
+ analysisData: AnalyzableReagent;
+ onExit: () => void;
+}) => {
const {
name,
- state,
pH,
color,
description,
@@ -411,18 +427,18 @@ const AnalysisResults = (props) => {
metaRate,
overdose,
addictionTypes,
- } = data.analysisData;
+ } = props.analysisData;
+
const purityLevel =
purity <= 0.5 ? 'bad' : purity <= 0.75 ? 'average' : 'good'; // Color names
+
return (
act('stopAnalysis')}
- />
+ props.onExit()}>
+ Back
+
}
>
@@ -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;
};