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 b4b26e4f848..84dfd01d2d4 100644
Binary files a/icons/obj/medical/chemical.dmi and b/icons/obj/medical/chemical.dmi differ
diff --git a/tgui/packages/tgui/interfaces/ChemMaster.tsx b/tgui/packages/tgui/interfaces/ChemMaster.tsx
index 37b5db33f90..c23ca29f8d9 100644
--- a/tgui/packages/tgui/interfaces/ChemMaster.tsx
+++ b/tgui/packages/tgui/interfaces/ChemMaster.tsx
@@ -18,32 +18,22 @@ import {
Tooltip,
} from '../components';
import { Window } from '../layouts';
+import { Beaker, BeakerReagent } from './common/BeakerDisplay';
-type Data = {
- reagentAnalysisMode: BooleanLike;
- analysisData: Analysis;
- isPrinting: BooleanLike;
- printingProgress: number;
- printingTotal: number;
- transferMode: BooleanLike;
- hasBeaker: BooleanLike;
- beakerCurrentVolume: number;
- beakerMaxVolume: number;
- beakerContents: Reagent[];
- bufferContents: Reagent[];
- bufferCurrentVolume: number;
- bufferMaxVolume: number;
- categories: Category[];
- selectedContainerRef: string;
- selectedContainerVolume: number;
- hasContainerSuggestion: BooleanLike;
- doSuggestContainer: BooleanLike;
- suggestedContainer: string;
+type Container = {
+ icon: string;
+ ref: string;
+ name: string;
+ volume: number;
};
-type Analysis = {
+type Category = {
name: string;
- state: string;
+ containers: Container[];
+};
+
+type AnalyzableReagent = BeakerReagent & {
+ ref: string;
pH: number;
color: string;
description: string;
@@ -53,144 +43,162 @@ type Analysis = {
addictionTypes: string[];
};
-type Category = {
- name: string;
- containers: Container[];
-};
-
-type Reagent = {
- ref: string;
- name: string;
- volume: number;
-};
+type AnalyzableBeaker = {
+ contents: AnalyzableReagent[];
+} & Beaker;
-type Container = {
- icon: string;
- ref: string;
- name: string;
- volume: number;
+type Data = {
+ categories: Category[];
+ isPrinting: BooleanLike;
+ printingProgress: number;
+ printingTotal: number;
+ maxPrintable: number;
+ beaker: AnalyzableBeaker;
+ buffer: AnalyzableBeaker;
+ isTransfering: BooleanLike;
+ suggestedContainerRef: string;
+ selectedContainerRef: string;
+ selectedContainerVolume: number;
};
export const ChemMaster = (props) => {
- 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;
};