diff --git a/code/modules/asset_cache/assets/chemmaster.dm b/code/modules/asset_cache/assets/chemmaster.dm new file mode 100644 index 00000000000..d37e6de91a5 --- /dev/null +++ b/code/modules/asset_cache/assets/chemmaster.dm @@ -0,0 +1,15 @@ +///Icons for containers printed in ChemMaster +/datum/asset/spritesheet/chemmaster + name = "chemmaster" + +/datum/asset/spritesheet/chemmaster/create_spritesheets() + var/list/ids = list() + for(var/category in GLOB.chem_master_containers) + for(var/obj/item/reagent_containers/container as anything in GLOB.chem_master_containers[category]) + var/icon_file = initial(container.icon) + var/icon_state = initial(container.icon_state) + var/id = sanitize_css_class_name("[container]") + if(id in ids) // exclude duplicate containers + continue + ids += id + Insert(id, icon_file, icon_state) diff --git a/code/modules/reagents/chemistry/machinery/chem_master.dm b/code/modules/reagents/chemistry/machinery/chem_master.dm index 3d7570894db..941f46cec12 100644 --- a/code/modules/reagents/chemistry/machinery/chem_master.dm +++ b/code/modules/reagents/chemistry/machinery/chem_master.dm @@ -1,143 +1,188 @@ -/** - * Machine that allows to identify and separate reagents in fitting container - * as well as to create new containers with separated reagents in it. - * - * Contains logic for both ChemMaster and CondiMaster, switched by "condi". - */ +#define TRANSFER_MODE_DESTROY 0 +#define TRANSFER_MODE_MOVE 1 +#define TARGET_BEAKER "beaker" +#define TARGET_BUFFER "buffer" +#define CAT_CONDIMENTS "condiments" +#define CAT_TUBES "tubes" +#define CAT_PILLS "pills" +#define CAT_PATCHES "patches" + +/// List of containers the Chem Master machine can print +GLOBAL_LIST_INIT(chem_master_containers, list( + CAT_CONDIMENTS = list( + /obj/item/reagent_containers/cup/bottle, + /obj/item/reagent_containers/condiment/flour, + /obj/item/reagent_containers/condiment/sugar, + /obj/item/reagent_containers/condiment/rice, + /obj/item/reagent_containers/condiment/cornmeal, + /obj/item/reagent_containers/condiment/milk, + /obj/item/reagent_containers/condiment/soymilk, + /obj/item/reagent_containers/condiment/yoghurt, + /obj/item/reagent_containers/condiment/saltshaker, + /obj/item/reagent_containers/condiment/peppermill, + /obj/item/reagent_containers/condiment/soysauce, + /obj/item/reagent_containers/condiment/bbqsauce, + /obj/item/reagent_containers/condiment/enzyme, + /obj/item/reagent_containers/condiment/hotsauce, + /obj/item/reagent_containers/condiment/coldsauce, + /obj/item/reagent_containers/condiment/mayonnaise, + /obj/item/reagent_containers/condiment/ketchup, + /obj/item/reagent_containers/condiment/quality_oil, + /obj/item/reagent_containers/condiment/cooking_oil, + /obj/item/reagent_containers/condiment/peanut_butter, + /obj/item/reagent_containers/condiment/cherryjelly, + /obj/item/reagent_containers/condiment/honey, + /obj/item/reagent_containers/condiment/pack, + ), + CAT_TUBES = list( + /obj/item/reagent_containers/cup/tube + ), + CAT_PILLS = typecacheof(list( + /obj/item/reagent_containers/pill/style + )), + CAT_PATCHES = typecacheof(list( + /obj/item/reagent_containers/pill/patch/style + )) +)) + /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 = "mixer0" - base_icon_state = "mixer" + icon_state = "chemmaster" + base_icon_state = "chemmaster" idle_power_usage = BASE_MACHINE_IDLE_CONSUMPTION * 0.2 resistance_flags = FIRE_PROOF | ACID_PROOF circuit = /obj/item/circuitboard/machine/chem_master - - /// Input reagents container + /// Icons for different percentages of buffer reagents + var/fill_icon = 'icons/obj/reagentfillings.dmi' + var/fill_icon_state = "chemmaster" + var/list/fill_icon_thresholds = list(10,20,30,40,50,60,70,80,90,100) + /// Inserted reagent container var/obj/item/reagent_containers/beaker - /// Pill bottle for newly created pills - var/obj/item/storage/pill_bottle/bottle - /// Whether separated reagents should be moved back to container or destroyed. 1 - move, 0 - destroy - var/mode = 1 - /// Decides what UI to show. If TRUE shows UI of CondiMaster, if FALSE - ChemMaster - var/condi = FALSE - /// Currently selected pill style - var/chosen_pill_style = 1 - /// Currently selected condiment bottle style - var/chosen_condi_style = CONDIMASTER_STYLE_AUTO - /// Current UI screen. On the moment of writing this comment there were two: 'home' - main screen, and 'analyze' - info about specific reagent - var/screen = "home" - /// Info to display on 'analyze' screen - var/analyze_vars[0] - /// List of available pill styles for UI - var/list/pill_styles - /// List of available condibottle styles for UI - var/list/condi_styles - /// Currently selected patch style - var/patch_style = DEFAULT_PATCH_STYLE - /// List of available patch styles for UI - var/list/patch_styles + /// 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 + /// 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 + /// 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 + var/printing_progress + 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 + 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]) + selected_container = default_container + return ..() /obj/machinery/chem_master/Destroy() QDEL_NULL(beaker) - QDEL_NULL(bottle) return ..() +/obj/machinery/chem_master/on_deconstruction() + replace_beaker() + return ..() + +/obj/machinery/chem_master/handle_atom_del(atom/deleted_atom) + ..() + if(deleted_atom == beaker) + beaker = null + update_appearance(UPDATE_ICON) + /obj/machinery/chem_master/RefreshParts() . = ..() reagents.maximum_volume = 0 - for(var/obj/item/reagent_containers/cup/beaker/B in component_parts) - reagents.maximum_volume += B.reagents.maximum_volume + 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/ex_act(severity, target) - if(severity <= EXPLODE_LIGHT) - return FALSE - return ..() - -/obj/machinery/chem_master/contents_explosion(severity, target) +/obj/machinery/chem_master/update_appearance(updates=ALL) . = ..() - switch(severity) - if(EXPLODE_DEVASTATE) - if(beaker) - SSexplosions.high_mov_atom += beaker - if(bottle) - SSexplosions.high_mov_atom += bottle - if(EXPLODE_HEAVY) - if(beaker) - SSexplosions.med_mov_atom += beaker - if(bottle) - SSexplosions.med_mov_atom += bottle - if(EXPLODE_LIGHT) - if(beaker) - SSexplosions.low_mov_atom += beaker - if(bottle) - SSexplosions.low_mov_atom += bottle - -/obj/machinery/chem_master/handle_atom_del(atom/A) - ..() - if(A == beaker) - beaker = null - reagents.clear_reagents() - update_appearance() - else if(A == bottle) - bottle = null - -/obj/machinery/chem_master/update_icon_state() - icon_state = "[base_icon_state][beaker ? 1 : 0][(machine_stat & BROKEN) ? "_b" : (powered() ? null : "_nopower")]" - return ..() + if(panel_open || (machine_stat & (NOPOWER|BROKEN))) + set_light(0) + else + set_light(1, 1, "#fffb00") /obj/machinery/chem_master/update_overlays() . = ..() + if(!isnull(beaker)) + . += mutable_appearance(icon, base_icon_state + "_overlay_container") if(machine_stat & BROKEN) - . += "waitlight" + . += mutable_appearance(icon, base_icon_state + "_overlay_broken") + if(panel_open) + . += mutable_appearance(icon, base_icon_state + "_overlay_panel") -/obj/machinery/chem_master/blob_act(obj/structure/blob/B) - if (prob(50)) - qdel(src) + if(is_printing) + . += mutable_appearance(icon, base_icon_state + "_overlay_extruder_active") + else + . += mutable_appearance(icon, base_icon_state + "_overlay_extruder") + + // 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) + screen_overlay += "_active" + else if(reagents.total_volume > 0) + screen_overlay += "_main" + . += mutable_appearance(icon, screen_overlay) + . += emissive_appearance(icon, base_icon_state + "_overlay_lightmask", src, alpha = src.alpha) + + // Buffer reagents overlay + if(reagents.total_volume) + var/threshold = null + for(var/i in 1 to fill_icon_thresholds.len) + 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) + filling.color = mix_color_from_reagents(reagents.reagent_list) + . += filling /obj/machinery/chem_master/wrench_act(mob/living/user, obj/item/tool) . = ..() default_unfasten_wrench(user, tool) return TOOL_ACT_TOOLTYPE_SUCCESS -/obj/machinery/chem_master/attackby(obj/item/I, mob/user, params) - if(default_deconstruction_screwdriver(user, "mixer0_nopower", "mixer0", I)) +/obj/machinery/chem_master/attackby(obj/item/item, mob/user, params) + if(default_deconstruction_screwdriver(user, icon_state, icon_state, item)) + update_appearance(UPDATE_ICON) return - - else if(default_deconstruction_crowbar(I)) + if(default_deconstruction_crowbar(item)) return - - if(is_reagent_container(I) && !(I.item_flags & ABSTRACT) && I.is_open_container()) - . = TRUE // no afterattack - if(panel_open) - to_chat(user, span_warning("You can't use the [src.name] while its panel is opened!")) - return - var/obj/item/reagent_containers/B = I - . = TRUE // no afterattack - if(!user.transferItemToLoc(B, src)) - return - replace_beaker(user, B) - to_chat(user, span_notice("You add [B] to [src].")) - ui_interact(user) - update_appearance() - else if(!condi && istype(I, /obj/item/storage/pill_bottle)) - if(bottle) - to_chat(user, span_warning("A pill bottle is already loaded into [src]!")) - return - if(!user.transferItemToLoc(I, src)) - return - bottle = I - to_chat(user, span_notice("You add [I] into the dispenser slot.")) - ui_interact(user) - else - return ..() + if(is_reagent_container(item) && !(item.item_flags & ABSTRACT) && item.is_open_container()) + . = TRUE // No afterattack + var/obj/item/reagent_containers/beaker = item + replace_beaker(user, beaker) + if(!panel_open) + ui_interact(user) + return ..() /obj/machinery/chem_master/attack_hand_secondary(mob/user, list/modifiers) . = ..() @@ -154,62 +199,28 @@ /obj/machinery/chem_master/attack_ai_secondary(mob/user, list/modifiers) return attack_hand_secondary(user, modifiers) -/** - * Handles process of moving input reagents containers in/from machine - * - * When called checks for previously inserted beaker and gives it to user. - * Then, if new_beaker provided, places it into src.beaker. - * Returns `boolean`. TRUE if user provided (ignoring whether threre was any beaker change) and FALSE if not. - * - * Arguments: - * * user - Mob that initialized replacement, gets previously inserted beaker if there's any - * * new_beaker - New beaker to insert. Optional - */ +/// 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(!user) + if(!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_appearance(UPDATE_ICON) return TRUE -/obj/machinery/chem_master/on_deconstruction() - replace_beaker() - if(bottle) - bottle.forceMove(drop_location()) - adjust_item_drop_location(bottle) - bottle = null - return ..() - -/obj/machinery/chem_master/proc/load_styles() - //Calculate the span tags and ids fo all the available pill icons - var/datum/asset/spritesheet/simple/assets = get_asset_datum(/datum/asset/spritesheet/simple/pills) - pill_styles = list() - for (var/x in 1 to PILL_STYLE_COUNT) - var/list/SL = list() - SL["id"] = x - SL["className"] = assets.icon_class_name("pill[x]") - pill_styles += list(SL) - - var/datum/asset/spritesheet/simple/patches_assets = get_asset_datum(/datum/asset/spritesheet/simple/patches) - patch_styles = list() - for (var/raw_patch_style in PATCH_STYLE_LIST) - //adding class_name for use in UI - var/list/patch_style = list() - patch_style["style"] = raw_patch_style - patch_style["class_name"] = patches_assets.icon_class_name(raw_patch_style) - patch_styles += list(patch_style) - - condi_styles = strip_condi_styles_to_icons(get_condi_styles()) +/obj/machinery/chem_master/proc/load_printable_containers() + printable_containers = list( + CAT_TUBES = GLOB.chem_master_containers[CAT_TUBES], + CAT_PILLS = GLOB.chem_master_containers[CAT_PILLS], + CAT_PATCHES = GLOB.chem_master_containers[CAT_PATCHES], + ) /obj/machinery/chem_master/ui_assets(mob/user) return list( - get_asset_datum(/datum/asset/spritesheet/simple/pills), - get_asset_datum(/datum/asset/spritesheet/simple/condiments), - get_asset_datum(/datum/asset/spritesheet/simple/patches), + get_asset_datum(/datum/asset/spritesheet/chemmaster) ) /obj/machinery/chem_master/ui_interact(mob/user, datum/tgui/ui) @@ -218,42 +229,92 @@ ui = new(user, src, "ChemMaster", name) ui.open() +/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() + for(var/obj/item/reagent_containers/container as anything in printable_containers[category]) + container_data += 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, + )) + + return data + /obj/machinery/chem_master/ui_data(mob/user) var/list/data = list() - data["isBeakerLoaded"] = beaker ? 1 : 0 - data["beakerCurrentVolume"] = beaker ? round(beaker.reagents.total_volume, 0.01) : null - data["beakerMaxVolume"] = beaker ? beaker.volume : null - data["mode"] = mode - data["condi"] = condi - data["screen"] = screen - data["analyzeVars"] = analyze_vars - data["chosenPillStyle"] = chosen_pill_style - data["chosenCondiStyle"] = chosen_condi_style - data["autoCondiStyle"] = CONDIMASTER_STYLE_AUTO - data["isPillBottleLoaded"] = bottle ? 1 : 0 - if(bottle) - data["pillBottleCurrentAmount"] = bottle.contents.len - data["pillBottleMaxAmount"] = bottle.atom_storage.max_slots - var/beaker_contents[0] - if(beaker) - for(var/datum/reagent/R in beaker.reagents.reagent_list) - beaker_contents.Add(list(list("name" = R.name, "id" = ckey(R.name), "volume" = round(R.volume, 0.01)))) // list in a list because Byond merges the first list... - data["beakerContents"] = beaker_contents + 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/buffer_contents[0] - if(reagents.total_volume) - for(var/datum/reagent/N in reagents.reagent_list) - buffer_contents.Add(list(list("name" = N.name, "id" = ckey(N.name), "volume" = round(N.volume, 0.01)))) // ^ - data["bufferContents"] = buffer_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) - //Calculated once since it'll never change - if(!pill_styles || !condi_styles || !patch_style || !patch_styles) - load_styles() - data["pillStyles"] = pill_styles - data["condiStyles"] = condi_styles - data["patch_style"] = patch_style - data["patch_styles"] = patch_styles return data /obj/machinery/chem_master/ui_act(action, params) @@ -265,379 +326,175 @@ replace_beaker(usr) return TRUE - if(action == "ejectPillBottle") - if(!bottle) - return FALSE - bottle.forceMove(drop_location()) - adjust_item_drop_location(bottle) - bottle = null - return TRUE - if(action == "transfer") - var/reagent = GLOB.name2reagent[params["id"]] + var/reagent_ref = params["reagentRef"] var/amount = text2num(params["amount"]) - var/to_container = params["to"] - // Custom amount - if (amount == -1) - amount = text2num(input( - "Enter the amount you want to transfer:", - name, "")) - if (amount == null || amount <= 0) - return FALSE - use_power(active_power_usage) - if (to_container == "beaker" && !mode) - reagents.remove_reagent(reagent, amount) - return TRUE - if (!beaker) - return FALSE - if (to_container == "buffer") - var/datum/reagent/R = beaker.reagents.get_reagent(reagent) - if(!check_reactions(R, beaker.reagents)) - return FALSE - beaker.reagents.trans_id_to(src, reagent, amount) - return TRUE - if (to_container == "beaker" && mode) - var/datum/reagent/R = reagents.get_reagent(reagent) - if(!check_reactions(R, reagents)) - return FALSE - reagents.trans_id_to(beaker, reagent, amount) - return TRUE - return FALSE + var/target = params["target"] + return transfer_reagent(reagent_ref, amount, target) - if(action == "toggleMode") - mode = !mode + if(action == "toggleTransferMode") + transfer_mode = !transfer_mode return TRUE - if(action == "pillStyle") - var/id = text2num(params["id"]) - chosen_pill_style = id + if(action == "analyze") + analyzed_reagent = locate(params["reagentRef"]) + if(analyzed_reagent) + reagent_analysis_mode = TRUE + update_appearance(UPDATE_ICON) + return TRUE + + if(action == "stopAnalysis") + reagent_analysis_mode = FALSE + analyzed_reagent = null + update_appearance(UPDATE_ICON) return TRUE - if(action == "condiStyle") - chosen_condi_style = params["id"] + if(action == "stopPrinting") + 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_type = params["type"] - // Get amount of items - var/amount = text2num(params["amount"]) - if(amount == null) - amount = text2num(input(usr, - "Max 10. Buffer content will be split evenly.", - "How many to make?", 1)) - amount = clamp(round(amount), 0, 10) - if (amount <= 0) + var/item_count = text2num(params["itemCount"]) + if(item_count <= 0) return FALSE - // Get units per item - var/vol_each = text2num(params["volume"]) - var/vol_each_text = params["volume"] - var/vol_each_max = reagents.total_volume / amount - var/list/style - use_power(active_power_usage) - if (item_type == "pill") - vol_each_max = min(50, vol_each_max) - else if (item_type == "patch") - vol_each_max = min(40, vol_each_max) - else if (item_type == "bottle") - vol_each_max = min(30, vol_each_max) - else if (item_type == "condimentPack") - vol_each_max = min(10, vol_each_max) - else if (item_type == "condimentBottle") - var/list/styles = get_condi_styles() - if (chosen_condi_style == CONDIMASTER_STYLE_AUTO || !(chosen_condi_style in styles)) - style = guess_condi_style(reagents) - else - style = styles[chosen_condi_style] - vol_each_max = min(50, vol_each_max) - else - return FALSE - if(vol_each_text == "auto") - vol_each = vol_each_max - if(vol_each == null) - vol_each = text2num(input(usr, - "Maximum [vol_each_max] units per item.", - "How many units to fill?", - vol_each_max)) - vol_each = round(clamp(vol_each, 0, vol_each_max), 0.01) - if(vol_each <= 0) - return FALSE - // Get item name - var/name = strip_html(params["name"], limit = 100) - var/name_has_units = item_type == "pill" || item_type == "patch" - if(!name) - var/name_default - if (style && style["name"] && !style["generate_name"]) - name_default = style["name"] - else - name_default = reagents.get_master_reagent_name() - if (name_has_units) - name_default += " ([vol_each]u)" - name = tgui_input_text(usr, - "Give it a name!", - "Name", - name_default, - MAX_NAME_LEN) - if(!name || !reagents.total_volume || !src || QDELETED(src) || !usr.can_perform_action(src, ALLOW_SILICON_REACH)) - return FALSE - // Start filling - if(item_type == "pill") - var/obj/item/reagent_containers/pill/P - var/target_loc = drop_location() - var/drop_threshold = INFINITY - if(bottle) - if(bottle.atom_storage) - drop_threshold = bottle.atom_storage.max_slots - bottle.contents.len - target_loc = bottle - for(var/i in 1 to amount) - if(i-1 < drop_threshold) - P = new/obj/item/reagent_containers/pill(target_loc) - else - P = new/obj/item/reagent_containers/pill(drop_location()) - P.name = trim("[name] pill") - if(chosen_pill_style == RANDOM_PILL_STYLE) - P.icon_state ="pill[rand(1,21)]" - else - P.icon_state = "pill[chosen_pill_style]" - if(P.icon_state == "pill4") - P.desc = "A tablet or capsule, but not just any, a red one, one taken by the ones not scared of knowledge, freedom, uncertainty and the brutal truths of reality." - adjust_item_drop_location(P) - reagents.trans_to(P, vol_each, transfered_by = usr) - return TRUE - if(item_type == "patch") - var/obj/item/reagent_containers/pill/patch/P - for(var/i in 1 to amount) - P = new/obj/item/reagent_containers/pill/patch(drop_location()) - P.name = trim("[name] patch") - P.icon_state = patch_style - adjust_item_drop_location(P) - reagents.trans_to(P, vol_each, transfered_by = usr) - return TRUE - if(item_type == "bottle") - var/obj/item/reagent_containers/cup/tube/P - for(var/i in 1 to amount) - P = new/obj/item/reagent_containers/cup/tube(drop_location()) - P.name = trim("[name] test tube") - adjust_item_drop_location(P) - reagents.trans_to(P, vol_each, transfered_by = usr) - return TRUE - if(item_type == "condimentPack") - var/obj/item/reagent_containers/condiment/pack/P - for(var/i in 1 to amount) - P = new/obj/item/reagent_containers/condiment/pack(drop_location()) - P.originalname = name - P.name = trim("[name] pack") - P.desc = "A small condiment pack. The label says it contains [name]." - reagents.trans_to(P, vol_each, transfered_by = usr) - return TRUE - if(item_type == "condimentBottle") - var/obj/item/reagent_containers/condiment/P - for(var/i in 1 to amount) - P = new/obj/item/reagent_containers/condiment(drop_location()) - if (style) - apply_condi_style(P, style) - P.renamedByPlayer = TRUE - P.name = name - reagents.trans_to(P, vol_each, transfered_by = usr) - return TRUE - return FALSE - - if(action == "analyze") - var/datum/reagent/analyzed_reagent = GLOB.name2reagent[params["id"]] - if(analyzed_reagent) - var/state = "Unknown" - if(initial(analyzed_reagent.reagent_state) == SOLID) - state = "Solid" - else if(initial(analyzed_reagent.reagent_state) == LIQUID) - state = "Liquid" - else if(initial(analyzed_reagent.reagent_state) == GAS) - state = "Gas" - var/metabolization_rate = initial(analyzed_reagent.metabolization_rate) * (60 / SSMOBS_DT) - analyze_vars = list("name" = initial(analyzed_reagent.name), "state" = state, "color" = initial(analyzed_reagent.color), "description" = initial(analyzed_reagent.description), "metaRate" = metabolization_rate, "overD" = initial(analyzed_reagent.overdose_threshold), "pH" = initial(analyzed_reagent.ph)) - screen = "analyze" - return TRUE - - if(action == "goScreen") - screen = params["screen"] + create_containers(item_count) return TRUE - if(action == "change_patch_style") - patch_style = params["patch_style"] +/// 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) + if(!(initial(container_style.reagent_flags) & OPENCONTAINER)) // Closed containers get reagent name and units in the name + item_name_default = "[reagents.get_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_power(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, transfered_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_power(active_power_usage) + + if (target == TARGET_BUFFER) + if(!check_reactions(reagent, beaker.reagents)) + return FALSE + beaker.reagents.trans_id_to(src, reagent.type, amount) + 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_id_to(beaker, reagent.type, amount) + update_appearance(UPDATE_ICON) return TRUE return FALSE -/obj/machinery/chem_master/adjust_item_drop_location(atom/movable/AM) // Special version for chemmasters and condimasters - if (AM == beaker) - AM.pixel_x = AM.base_pixel_x - 8 - AM.pixel_y = AM.base_pixel_y + 8 - return null - else if (AM == bottle) - if (length(bottle.contents)) - AM.pixel_x = AM.base_pixel_x - 13 - else - AM.pixel_x = AM.base_pixel_x - 7 - AM.pixel_y = AM.base_pixel_y - 8 - return null - else - var/md5 = md5(AM.name) - for (var/i in 1 to 32) - . += hex2num(md5[i]) - . = . % 9 - AM.pixel_x = AM.base_pixel_x + ((.%3)*6) - AM.pixel_y = AM.base_pixel_y - 8 + (round( . / 3)*8) - -/** - * Translates styles data into UI compatible format - * - * Expects to receive list of availables condiment styles in its complete format, and transforms them in simplified form with enough data to get UI going. - * Returns list(list("id" = , "className" = , "title" = ),..). - * - * Arguments: - * * styles - List of styles for condiment bottles in internal format: [/obj/machinery/chem_master/proc/get_condi_styles] - */ -/obj/machinery/chem_master/proc/strip_condi_styles_to_icons(list/styles) - var/list/icons = list() - for (var/s in styles) - if (styles[s] && styles[s]["class_name"]) - var/list/icon = list() - var/list/style = styles[s] - icon["id"] = s - icon["className"] = style["class_name"] - icon["title"] = "[style["name"]]\n[style["desc"]]" - icons += list(icon) - - return icons - -/** - * Defines and provides list of available condiment bottle styles - * - * Uses typelist() for styles storage after initialization. - * For fallback style must provide style with key (const) CONDIMASTER_STYLE_FALLBACK - * Returns list( - * = list( - * "icon_state" = , - * "name" = , - * "desc" = , - * ?"generate_name" = , - * ?"icon_empty" = , - * ?"fill_icon_thresholds" = , - * ?"inhand_icon_state" = , - * ?"lefthand_file" = , - * ?"righthand_file" = , - * ), - * .. - * ) - * - */ -/obj/machinery/chem_master/proc/get_condi_styles() - var/list/styles = typelist("condi_styles") - if (!styles.len) - //Possible_states has the reagent type as key and a list of, in order, the icon_state, the name and the desc as values. Was used in the condiment/on_reagent_change(changetype) to change names, descs and sprites. - styles += list( - CONDIMASTER_STYLE_FALLBACK = list("icon_state" = "bottle", "icon_empty" = "", "name" = "bottle", "desc" = "Just your average condiment bottle.", "fill_icon_thresholds" = list(1, 20, 40, 60, 80, 100), "generate_name" = TRUE), - - "flour" = list("icon_state" = "flour", "icon_empty" = "", "name" = "flour sack", "desc" = "A big bag of flour. Good for baking!"), - "rice" = list("icon_state" = "rice", "icon_empty" = "", "name" = "rice sack", "desc" = "A big bag of rice. Good for cooking!"), - "sugar" = list("icon_state" = "sugar", "icon_empty" = "", "name" = "sugar sack", "desc" = "Tasty spacey sugar!"), - "enzyme" = list("icon_state" = "enzyme", "icon_empty" = "", "name" = "universal enzyme bottle", "desc" = "Used in cooking various dishes."), - "capsaicin" = list("icon_state" = "hotsauce", "icon_empty" = "", "name" = "hotsauce bottle", "desc" = "You can almost TASTE the stomach ulcers!"), - "frostoil" = list("icon_state" = "coldsauce", "icon_empty" = "", "name" = "coldsauce bottle", "desc" = "Leaves the tongue numb from its passage."), - "mayonnaise" = list("icon_state" = "mayonnaise", "icon_empty" = "", "name" = "mayonnaise bottle", "desc" = "An oily condiment made from egg yolks."), - "ketchup" = list("icon_state" = "ketchup", "icon_empty" = "", "name" = "ketchup bottle", "desc" = "A tomato slurry in a tall plastic bottle. Somehow still vaguely American."), - "blackpepper" = list("icon_state" = "peppermillsmall", "inhand_icon_state" = "", "icon_empty" = "emptyshaker", "name" = "pepper mill", "desc" = "Often used to flavor food or make people sneeze."), - "sodiumchloride" = list("icon_state" = "saltshakersmall", "inhand_icon_state" = "", "icon_empty" = "emptyshaker", "name" = "salt shaker", "desc" = "Salt. From dead crew, presumably."), - "milk" = list("icon_state" = "milk", "icon_empty" = "", "name" = "space milk", "desc" = "It's milk. White and nutritious goodness!"), - "soymilk" = list("icon_state" = "soymilk", "icon_empty" = "", "name" = "soy milk", "desc" = "It's soy milk. White and nutritious goodness!"), - "soysauce" = list("icon_state" = "soysauce", "inhand_icon_state" = "", "icon_empty" = "", "name" = "soy sauce bottle", "desc" = "A salty soy-based flavoring."), - "bbqsauce" = list("icon_state" = "bbqsauce", "icon_empty" = "", "name" = "bbq sauce bottle", "desc" = "Hand wipes not included."), - "oliveoil" = list("icon_state" = "oliveoil", "icon_empty" = "", "name" = "olive oil bottle", "desc" = "A delicious oil made from olives."), - "cooking_oil" = list("icon_state" = "cooking_oil", "icon_empty" = "", "name" = "cooking oil bottle", "desc" = "A cooking oil for deep frying."), - "peanut_butter" = list("icon_state" = "peanutbutter", "icon_empty" = "", "name" = "peanut butter jar", "desc" = "A creamy paste made from ground peanuts."), - "cherryjelly" = list("icon_state" = "cherryjelly", "icon_empty" = "", "name" = "cherry jelly jar", "desc" = "A jar of super-sweet cherry jelly."), - "honey" = list("icon_state" = "honey", "icon_empty" = "", "name" = "honey bottle", "desc" = "A cheerful bear-shaped bottle of tasty honey."), - ) - var/list/carton_in_hand = list( - "inhand_icon_state" = "carton", - "lefthand_file" = 'icons/mob/inhands/equipment/kitchen_lefthand.dmi', - "righthand_file" = 'icons/mob/inhands/equipment/kitchen_righthand.dmi' - ) - for (var/style_reagent in list("flour", "milk", "rice", "soymilk", "sugar")) - if (style_reagent in styles) - styles[style_reagent] += carton_in_hand - var/datum/asset/spritesheet/simple/assets = get_asset_datum(/datum/asset/spritesheet/simple/condiments) - for (var/reagent in styles) - styles[reagent]["class_name"] = assets.icon_class_name(reagent) - return styles - -/** - * Provides condiment bottle style based on reagents. - * - * Gets style from available by key, using last part of main reagent type (eg. "rice" for /datum/reagent/consumable/rice) as key. - * If not available returns fallback style, or null if no such thing. - * Returns list that is one of condibottle styles from [/obj/machinery/chem_master/proc/get_condi_styles] - */ -/obj/machinery/chem_master/proc/guess_condi_style(datum/reagents/reagents) - var/list/styles = get_condi_styles() - if (reagents.reagent_list.len > 0) - var/main_reagent = reagents.get_master_reagent_id() - if (main_reagent) - var/list/path = splittext("[main_reagent]", "/") - main_reagent = path[path.len] - if(main_reagent in styles) - return styles[main_reagent] - return styles[CONDIMASTER_STYLE_FALLBACK] - -/** - * Applies style to condiment bottle. - * - * Applies props provided in "style" assuming that "container" is freshly created with no styles applied before. - * User specified name for bottle applied after this method during bottle creation, - * so container.name overwritten here for consistency rather than with some purpose in mind. - * - * Arguments: - * * container - condiment bottle that gets style applied to it - * * style - assoc list, must probably one from [/obj/machinery/chem_master/proc/get_condi_styles] - */ -/obj/machinery/chem_master/proc/apply_condi_style(obj/item/reagent_containers/condiment/container, list/style) - container.name = style["name"] - container.desc = style["desc"] - container.icon_state = style["icon_state"] - container.icon_empty = style["icon_empty"] - container.fill_icon_thresholds = style["fill_icon_thresholds"] - if ("inhand_icon_state" in style) - container.inhand_icon_state = style["inhand_icon_state"] - if (style["lefthand_file"] || style["righthand_file"]) - container.lefthand_file = style["lefthand_file"] - container.righthand_file = style["righthand_file"] - - -//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 +/// 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/e in holder.reaction_list) - var/datum/equilibrium/E = e - if(E.reaction.reaction_flags & REACTION_COMPETITIVE) + for(var/datum/equilibrium/equilibrium as anything in holder.reaction_list) + if(equilibrium.reaction.reaction_flags & REACTION_COMPETITIVE) continue - for(var/result in E.reaction.required_reagents) - var/datum/reagent/R = result - if(R == reagent.type) + for(var/datum/reagent/result as anything in equilibrium.reaction.required_reagents) + if(result == reagent.type) canMove = FALSE if(!canMove) - say("Cannot move arrested chemical reaction reagents!") + say("Cannot move reagent during reaction!") return canMove -/** - * Machine that allows to identify and separate reagents in fitting container - * as well as to create new containers with separated reagents in it. - * - * All logic related to this is in [/obj/machinery/chem_master] and condimaster specific UI enabled by "condi = TRUE" - */ +/// 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]%.") + /obj/machinery/chem_master/condimaster name = "CondiMaster 3000" desc = "Used to create condiments and other cooking supplies." - condi = TRUE + icon_state = "condimaster" + has_container_suggestion = TRUE + +/obj/machinery/chem_master/condimaster/load_printable_containers() + printable_containers = list( + CAT_CONDIMENTS = GLOB.chem_master_containers[CAT_CONDIMENTS], + ) + +#undef TRANSFER_MODE_DESTROY +#undef TRANSFER_MODE_MOVE +#undef TARGET_BEAKER +#undef TARGET_BUFFER +#undef CAT_CONDIMENTS +#undef CAT_TUBES +#undef CAT_PILLS +#undef CAT_PATCHES diff --git a/code/modules/reagents/reagent_containers/condiment.dm b/code/modules/reagents/reagent_containers/condiment.dm index 170246d45d8..47d408053e2 100644 --- a/code/modules/reagents/reagent_containers/condiment.dm +++ b/code/modules/reagents/reagent_containers/condiment.dm @@ -340,6 +340,19 @@ icon_state = "condi_chocolate" list_reagents = list(/datum/reagent/consumable/choccyshake = 10) + +/obj/item/reagent_containers/condiment/hotsauce + name = "hotsauce bottle" + desc= "You can almost TASTE the stomach ulcers!" + icon_state = "hotsauce" + list_reagents = list(/datum/reagent/consumable/capsaicin = 50) + +/obj/item/reagent_containers/condiment/coldsauce + name = "coldsauce bottle" + desc= "Leaves the tongue numb from its passage." + icon_state = "coldsauce" + list_reagents = list(/datum/reagent/consumable/frostoil = 50) + //Food packs. To easily apply deadly toxi... delicious sauces to your food! /obj/item/reagent_containers/condiment/pack diff --git a/code/modules/reagents/reagent_containers/patch.dm b/code/modules/reagents/reagent_containers/patch.dm index 5e166b36ce6..1682395ddb0 100644 --- a/code/modules/reagents/reagent_containers/patch.dm +++ b/code/modules/reagents/reagent_containers/patch.dm @@ -44,3 +44,46 @@ desc = "Helps with brute and burn injuries. Slightly toxic." list_reagents = list(/datum/reagent/medicine/c2/synthflesh = 20) icon_state = "bandaid_both" + +// Patch styles for chem master + +/obj/item/reagent_containers/pill/patch/style + icon_state = "bandaid_blank" +/obj/item/reagent_containers/pill/patch/style/brute + icon_state = "bandaid_brute_2" +/obj/item/reagent_containers/pill/patch/style/burn + icon_state = "bandaid_burn_2" +/obj/item/reagent_containers/pill/patch/style/bruteburn + icon_state = "bandaid_both" +/obj/item/reagent_containers/pill/patch/style/toxin + icon_state = "bandaid_toxin_2" +/obj/item/reagent_containers/pill/patch/style/oxygen + icon_state = "bandaid_suffocation_2" +/obj/item/reagent_containers/pill/patch/style/omni + icon_state = "bandaid_mix" +/obj/item/reagent_containers/pill/patch/style/bruteplus + icon_state = "bandaid_brute" +/obj/item/reagent_containers/pill/patch/style/burnplus + icon_state = "bandaid_burn" +/obj/item/reagent_containers/pill/patch/style/toxinplus + icon_state = "bandaid_toxin" +/obj/item/reagent_containers/pill/patch/style/oxygenplus + icon_state = "bandaid_suffocation" +/obj/item/reagent_containers/pill/patch/style/monkey + icon_state = "bandaid_monke" +/obj/item/reagent_containers/pill/patch/style/clown + icon_state = "bandaid_clown" +/obj/item/reagent_containers/pill/patch/style/one + icon_state = "bandaid_1" +/obj/item/reagent_containers/pill/patch/style/two + icon_state = "bandaid_2" +/obj/item/reagent_containers/pill/patch/style/three + icon_state = "bandaid_3" +/obj/item/reagent_containers/pill/patch/style/four + icon_state = "bandaid_4" +/obj/item/reagent_containers/pill/patch/style/exclamation + icon_state = "bandaid_exclaimationpoint" +/obj/item/reagent_containers/pill/patch/style/question + icon_state = "bandaid_questionmark" +/obj/item/reagent_containers/pill/patch/style/colonthree + icon_state = "bandaid_colonthree" diff --git a/code/modules/reagents/reagent_containers/pill.dm b/code/modules/reagents/reagent_containers/pill.dm index e01d0540717..72915b66420 100644 --- a/code/modules/reagents/reagent_containers/pill.dm +++ b/code/modules/reagents/reagent_containers/pill.dm @@ -302,3 +302,54 @@ icon_state = "pill8" list_reagents = list(/datum/reagent/iron = 30) rename_with_volume = TRUE + +// Pill styles for chem master + +/obj/item/reagent_containers/pill/style + icon_state = "pill0" +/obj/item/reagent_containers/pill/style/purplered + icon_state = "pill1" +/obj/item/reagent_containers/pill/style/greenwhite + icon_state = "pill2" +/obj/item/reagent_containers/pill/style/teal + icon_state = "pill3" +/obj/item/reagent_containers/pill/style/red + icon_state = "pill4" +/obj/item/reagent_containers/pill/style/redwhite + icon_state = "pill5" +/obj/item/reagent_containers/pill/style/tealbrown + icon_state = "pill6" +/obj/item/reagent_containers/pill/style/yellowflat + icon_state = "pill7" +/obj/item/reagent_containers/pill/style/tealflat + icon_state = "pill8" +/obj/item/reagent_containers/pill/style/whiteflat + icon_state = "pill9" +/obj/item/reagent_containers/pill/style/purpleflat + icon_state = "pill10" +/obj/item/reagent_containers/pill/style/limelat + icon_state = "pill11" +/obj/item/reagent_containers/pill/style/redflat + icon_state = "pill12" +/obj/item/reagent_containers/pill/style/greenpurpleflat + icon_state = "pill13" +/obj/item/reagent_containers/pill/style/yellowpurpleflat + icon_state = "pill14" +/obj/item/reagent_containers/pill/style/redyellowflat + icon_state = "pill15" +/obj/item/reagent_containers/pill/style/bluetealflat + icon_state = "pill16" +/obj/item/reagent_containers/pill/style/greenlimeflat + icon_state = "pill17" +/obj/item/reagent_containers/pill/style/white + icon_state = "pill18" +/obj/item/reagent_containers/pill/style/whitered + icon_state = "pill19" +/obj/item/reagent_containers/pill/style/purpleyellow + icon_state = "pill20" +/obj/item/reagent_containers/pill/style/blackwhite + icon_state = "pill21" +/obj/item/reagent_containers/pill/style/limewhite + icon_state = "pill22" +/obj/item/reagent_containers/pill/style/happy + icon_state = "pill_happy" diff --git a/icons/obj/medical/chemical.dmi b/icons/obj/medical/chemical.dmi index a8b360d4f15..aaedc090f2d 100644 Binary files a/icons/obj/medical/chemical.dmi and b/icons/obj/medical/chemical.dmi differ diff --git a/icons/obj/reagentfillings.dmi b/icons/obj/reagentfillings.dmi index 11f23391089..b00c622c315 100644 Binary files a/icons/obj/reagentfillings.dmi and b/icons/obj/reagentfillings.dmi differ diff --git a/tgstation.dme b/tgstation.dme index a1a3f61a7a4..fa13b25dc4a 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -2778,6 +2778,7 @@ #include "code\modules\asset_cache\assets\bibles.dm" #include "code\modules\asset_cache\assets\body_zones.dm" #include "code\modules\asset_cache\assets\chat.dm" +#include "code\modules\asset_cache\assets\chemmaster.dm" #include "code\modules\asset_cache\assets\circuits.dm" #include "code\modules\asset_cache\assets\common.dm" #include "code\modules\asset_cache\assets\condiments.dm" diff --git a/tgui/packages/tgui/interfaces/ChemMaster.js b/tgui/packages/tgui/interfaces/ChemMaster.js deleted file mode 100644 index 3e19a45bc1d..00000000000 --- a/tgui/packages/tgui/interfaces/ChemMaster.js +++ /dev/null @@ -1,438 +0,0 @@ -import { useBackend, useSharedState } from '../backend'; -import { AnimatedNumber, Box, Button, ColorBox, LabeledList, NumberInput, Section, Table } from '../components'; -import { Window } from '../layouts'; - -export const ChemMaster = (props, context) => { - const { data } = useBackend(context); - const { screen } = data; - return ( - - - {(screen === 'analyze' && ) || } - - - ); -}; - -const ChemMasterContent = (props, context) => { - const { act, data } = useBackend(context); - const { - screen, - beakerContents = [], - bufferContents = [], - beakerCurrentVolume, - beakerMaxVolume, - isBeakerLoaded, - isPillBottleLoaded, - pillBottleCurrentAmount, - pillBottleMaxAmount, - } = data; - if (screen === 'analyze') { - return ; - } - return ( - <> -
- - - {` / ${beakerMaxVolume} units`} - -
-
- - Mode: - -
-
- -
- {!!isPillBottleLoaded && ( -
- - {pillBottleCurrentAmount} / {pillBottleMaxAmount} pills - - - ))} - - )} - {!condi && ( - setPillAmount(value)} - onCreate={() => - act('create', { - type: 'pill', - amount: pillAmount, - volume: 'auto', - }) - } - /> - )} - {!condi && ( - - {patch_styles.map((patch) => ( - - ))} - - )} - {!condi && ( - setPatchAmount(value)} - onCreate={() => - act('create', { - type: 'patch', - amount: patchAmount, - volume: 'auto', - }) - } - /> - )} - {!condi && ( - setBottleAmount(value)} - onCreate={() => - act('create', { - type: 'bottle', - amount: bottleAmount, - volume: 'auto', - }) - } - /> - )} - {!!condi && ( - - - act('condiStyle', { - id: autoCondiStyleChosen ? condiStyles[0].id : autoCondiStyle, - }) - } - checked={autoCondiStyleChosen} - disabled={!condiStyles.length}> - Guess from contents - - - )} - {!!condi && !autoCondiStyleChosen && ( - - {condiStyles.map((style) => ( - - ))} - - )} - {!!condi && ( - setBottleAmount(value)} - onCreate={() => - act('create', { - type: 'condimentBottle', - amount: bottleAmount, - volume: 'auto', - }) - } - /> - )} - {!!condi && ( - setPackAmount(value)} - onCreate={() => - act('create', { - type: 'condimentPack', - amount: packAmount, - volume: 'auto', - }) - } - /> - )} - - ); -}; - -const AnalysisResults = (props, context) => { - const { act, data } = useBackend(context); - const { analyzeVars } = data; - return ( -
- act('goScreen', { - screen: 'home', - }) - } - /> - }> - - {analyzeVars.name} - {analyzeVars.state} - {analyzeVars.ph} - - - {analyzeVars.color} - - - {analyzeVars.description} - - - {analyzeVars.metaRate} u/minute - - - {analyzeVars.overD} - - - {analyzeVars.addicD} - - -
- ); -}; diff --git a/tgui/packages/tgui/interfaces/ChemMaster.tsx b/tgui/packages/tgui/interfaces/ChemMaster.tsx new file mode 100644 index 00000000000..d5de45c4d93 --- /dev/null +++ b/tgui/packages/tgui/interfaces/ChemMaster.tsx @@ -0,0 +1,453 @@ +import { BooleanLike, classes } from 'common/react'; +import { capitalize } from 'common/string'; +import { useBackend, useLocalState } from '../backend'; +import { AnimatedNumber, Box, Button, Section, Table, NumberInput, Tooltip, LabeledList, ColorBox, ProgressBar, Stack, Divider } from '../components'; +import { Window } from '../layouts'; + +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 Analysis = { + name: string; + state: string; + pH: number; + color: string; + description: string; + purity: number; + metaRate: number; + overdose: number; + addictionTypes: string[]; +}; + +type Category = { + name: string; + containers: Container[]; +}; + +type Reagent = { + ref: string; + name: string; + volume: number; +}; + +type Container = { + icon: string; + ref: string; + name: string; + volume: number; +}; + +export const ChemMaster = (props, context) => { + const { data } = useBackend(context); + const { reagentAnalysisMode } = data; + return ( + + + {reagentAnalysisMode ? : } + + + ); +}; + +const ChemMasterContent = (props, context) => { + const { act, data } = useBackend(context); + const { + isPrinting, + printingProgress, + printingTotal, + transferMode, + hasBeaker, + beakerCurrentVolume, + beakerMaxVolume, + beakerContents, + bufferContents, + bufferCurrentVolume, + bufferMaxVolume, + categories, + selectedContainerVolume, + hasContainerSuggestion, + doSuggestContainer, + suggestedContainer, + } = data; + + const [itemCount, setItemCount] = useLocalState(context, 'itemCount', 1); + + return ( + +
+ + + {` / ${beakerMaxVolume} units`} + +
+
+ + + {` / ${bufferMaxVolume} units`} + +
+ {!isPrinting && ( +
+ { + setItemCount(value); + }} + /> + + {`${ + Math.round( + Math.min( + selectedContainerVolume, + bufferCurrentVolume / itemCount + ) * 100 + ) / 100 + } u. each`} + +
+ )} + {!!isPrinting && ( +
act('stopPrinting')} + /> + }> + + + {`Printing ${printingProgress} out of ${printingTotal}`} + + +
+ )} +
+ ); +}; + +const ReagentEntry = (props, context) => { + const { data, act } = useBackend(context); + const { chemical, transferTo } = props; + const { isPrinting } = data; + return ( + + + {`${chemical.name} `} + + {`u`} + + + + + ) as any; +}; + +const AnalysisResults = (props, context) => { + const { act, data } = useBackend(context); + const { + name, + state, + pH, + color, + description, + purity, + metaRate, + overdose, + addictionTypes, + } = data.analysisData; + const purityLevel = + purity <= 0.5 ? 'bad' : purity <= 0.75 ? 'average' : 'good'; // Color names + return ( +
act('stopAnalysis')} + /> + }> + + {name} + + + {purityLevel} + + + {pH} + {state} + + + {color} + + {description} + + {metaRate} units/second + + + {overdose > 0 ? `${overdose} units` : 'N/A'} + + + {addictionTypes.length ? addictionTypes.toString() : 'N/A'} + + +
+ ); +}; + +const GroupTitle = ({ title }) => { + return ( + + + + + + {title} + + + + + + ) as any; +};