diff --git a/aurorastation.dme b/aurorastation.dme index d697e460811..6e5bb3de469 100644 --- a/aurorastation.dme +++ b/aurorastation.dme @@ -1495,7 +1495,6 @@ #include "code\game\objects\structures\machinery\lightswitch.dm" #include "code\game\objects\structures\machinery\mass_driver.dm" #include "code\game\objects\structures\machinery\mech_recharger.dm" -#include "code\game\objects\structures\machinery\mecha_fabricator.dm" #include "code\game\objects\structures\machinery\navbeacon.dm" #include "code\game\objects\structures\machinery\newscaster.dm" #include "code\game\objects\structures\machinery\nuclear_bomb.dm" @@ -3735,6 +3734,8 @@ #include "code\modules\rendering\render_plate.dm" #include "code\modules\research\circuitprinter.dm" #include "code\modules\research\destructive_analyzer.dm" +#include "code\modules\research\material_silo.dm" +#include "code\modules\research\mecha_fabricator.dm" #include "code\modules\research\protolathe.dm" #include "code\modules\research\rd-readme.dm" #include "code\modules\research\rdconsole.dm" diff --git a/code/game/objects/items/weapons/circuitboards/machinery/research.dm b/code/game/objects/items/weapons/circuitboards/machinery/research.dm index 2cd056192d3..f0910824d23 100644 --- a/code/game/objects/items/weapons/circuitboards/machinery/research.dm +++ b/code/game/objects/items/weapons/circuitboards/machinery/research.dm @@ -51,7 +51,7 @@ /obj/item/circuitboard/protolathe name = T_BOARD("protolathe") - build_path = /obj/structure/machinery/r_n_d/protolathe + build_path = /obj/structure/machinery/r_n_d/fabricator/protolathe board_type = BOARD_MACHINE origin_tech = list(TECH_ENGINEERING = 2, TECH_DATA = 2) req_components = list( @@ -62,7 +62,7 @@ /obj/item/circuitboard/circuit_imprinter name = T_BOARD("circuit imprinter") - build_path = /obj/structure/machinery/r_n_d/circuit_imprinter + build_path = /obj/structure/machinery/r_n_d/fabricator/circuit_imprinter board_type = BOARD_MACHINE origin_tech = list(TECH_ENGINEERING = 2, TECH_DATA = 2) req_components = list( @@ -72,7 +72,7 @@ /obj/item/circuitboard/mechfab name = T_BOARD("mechatronic fabricator") - build_path = /obj/structure/machinery/mecha_part_fabricator + build_path = /obj/structure/machinery/r_n_d/fabricator/mecha_part_fabricator board_type = BOARD_MACHINE origin_tech = list(TECH_DATA = 3, TECH_ENGINEERING = 3) req_components = list( diff --git a/code/game/objects/structures/crates_lockers/crates.dm b/code/game/objects/structures/crates_lockers/crates.dm index 36527a0b07b..098a0d52a35 100644 --- a/code/game/objects/structures/crates_lockers/crates.dm +++ b/code/game/objects/structures/crates_lockers/crates.dm @@ -797,3 +797,8 @@ new /obj/item/storage/box/unique/tea/messa(src) new /obj/item/reagent_containers/toothpaste(src) new /obj/item/reagent_containers/food/drinks/flask/vacuumflask/mouthwash(src) + +/obj/structure/closet/crate/tool/machinist/fill() + new /obj/item/reagent_containers/weldpack(src) + new /obj/item/portable_whiteboard(src) + new /obj/item/paint_sprayer(src) diff --git a/code/game/objects/structures/machinery/mecha_fabricator.dm b/code/game/objects/structures/machinery/mecha_fabricator.dm deleted file mode 100644 index 86de54055a4..00000000000 --- a/code/game/objects/structures/machinery/mecha_fabricator.dm +++ /dev/null @@ -1,506 +0,0 @@ -/obj/structure/machinery/mecha_part_fabricator - name = "synthetic fabricator" - desc = "A general purpose fabricator that can be used to fabricate equipment for synthetics or exosuits." - icon = 'icons/obj/machinery/robotics_fabricator.dmi' - icon_state = "fab" - density = TRUE - anchored = TRUE - idle_power_usage = 20 - active_power_usage = 5000 - req_access = list(ACCESS_ROBOTICS) - component_types = list( - /obj/item/circuitboard/mechfab, - /obj/item/stock_parts/matter_bin = 2, - /obj/item/stock_parts/manipulator, - /obj/item/stock_parts/micro_laser, - /obj/item/stock_parts/console_screen - ) - manufacturer = "hephaestus" - - /** - * A `/list` of enqueued `/datum/design` to be printed, processed in the queue - */ - var/list/datum/design/queue = list() - - /** - * How much efficient (or inefficient) the protolathe is at manufacturing things - */ - var/mat_efficiency = 1 - - /** - * The production speed of this specific protolathe, a factor - */ - var/production_speed = 1 - - var/list/materials = list(MATERIAL_STEEL = 0, MATERIAL_GLASS = 0, MATERIAL_GOLD = 0, MATERIAL_SILVER = 0, MATERIAL_DIAMOND = 0, MATERIAL_PHORON = 0, MATERIAL_URANIUM = 0, MATERIAL_PLASTEEL = 0, MATERIAL_ALUMINIUM = 0, MATERIAL_LEAD = 0) - var/res_max_amount = 200000 - - var/datum/research/files - - /// The looping sound for production. - var/datum/looping_sound/synth_fab/fab_loop - - var/list/categories = list() - var/category = null - var/sync_message = "" - var/limb_manufacturer - - ///The timer id for the build callback, if we're building something - var/build_callback_timer - -/obj/structure/machinery/mecha_part_fabricator/upgrade_hints(mob/user, distance, is_adjacent) - . += ..() - . += "- Upgraded matter bins will increase material storage capacity." - . += SPAN_NOTICE(" - The current storage capacity is [res_max_amount / 2000] sheets") - . += "- Upgraded micro-lasers will increase fabrication speed." - . += SPAN_NOTICE(" - The current speed increase is [round((1 - (1 / production_speed)) * 100)]%") - . += "- Upgraded manipulators will improve material use efficiency." - . += SPAN_NOTICE(" - The current cost reduction is [round((1 - mat_efficiency) * 100)]%") - -/obj/structure/machinery/mecha_part_fabricator/antagonist_hints(mob/user, distance, is_adjacent) - . += ..() - . += "Click-drag someone with long hair onto this machine to start feeding it their hair! This will hurt and piss them off!" - -/obj/structure/machinery/mecha_part_fabricator/Initialize() - . = ..() - - files = new /datum/research(src) //Setup the research data holder. - fab_loop = new(src) - limb_manufacturer = GLOB.basic_robolimb.company - update_categories() - update_icon() - -/// Make sure the machine starts the round properly synced. -/obj/structure/machinery/mecha_part_fabricator/LateInitialize() - . = ..() - sync() - -/obj/structure/machinery/mecha_part_fabricator/update_icon() - ClearOverlays() - update_fab_audio() - if(panel_open) - AddOverlays("[icon_state]_panel") - if(!(stat & (NOPOWER|BROKEN))) - AddOverlays(emissive_appearance(icon, "[icon_state]_lights")) - AddOverlays("[icon_state]_lights") - if(build_callback_timer) - AddOverlays("[icon_state]_working") - AddOverlays(emissive_appearance(icon, "[icon_state]_lights_working")) - AddOverlays("[icon_state]_lights_working") - -/// Starts and stops the necessary audio. -/obj/structure/machinery/mecha_part_fabricator/proc/update_fab_audio() - if(!fab_loop) - return - - if(build_callback_timer) - fab_loop.start() - else - fab_loop.stop() - -/obj/structure/machinery/mecha_part_fabricator/dismantle() - SSmaterials.normalize_material_amounts(materials) - for(var/f in materials.Copy()) - eject_materials(f, materials[f]) - - //Stop the queue building if you're dismantling - deltimer(build_callback_timer) - - ..() - -/obj/structure/machinery/mecha_part_fabricator/RefreshParts() - ..() - res_max_amount = 0 - - for(var/obj/item/stock_parts/matter_bin/M in component_parts) - res_max_amount += M.rating * 100000 // 200k -> 600k - var/T = 0 - - for(var/obj/item/stock_parts/manipulator/M in component_parts) - T += M.rating - mat_efficiency = 1 - (T - 1) / 4 // 1 -> 0.5 - - for(var/obj/item/stock_parts/micro_laser/M in component_parts) // Not resetting T is intended; speed is affected by both - T += M.rating - production_speed = T / 2 // 1 -> 3 - -/obj/structure/machinery/mecha_part_fabricator/attack_hand(var/mob/user) - if(..()) - return - if(!allowed(user)) - to_chat(user, SPAN_WARNING("Access denied.")) - return - do_hair_pull(user) - - // Yes I'm doing this after do_hair_pull() intentionally, you can totally fail the skill check and still get your hair stuck in a blender. - var/cancelled = FALSE - SEND_SIGNAL(user, COMSIG_USE_MECH_FAB, &cancelled) - if (cancelled) - return - - ui_interact(user) - -/obj/structure/machinery/mecha_part_fabricator/ui_interact(mob/user, datum/tgui/ui) - . = ..() - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "SyntheticFabricator", "Synthetic Fabricator", 1200, 800) - ui.open() - -/obj/structure/machinery/mecha_part_fabricator/ui_data(mob/user) - var/list/data = list() - data["manufacturer"] = manufacturer - var/datum/design/current = queue.len ? queue[1] : null - if(current) - data["current"] = current.name - data["queue"] = get_queue_names() - data["buildable"] = get_build_options() - data["category"] = category - data["available_categories"] = list() - for(var/possible_category in categories) - data["available_categories"] += list(list("name" = possible_category)) - - if(GLOB.fabricator_robolimbs) - var/list/T = list() - for(var/A in GLOB.fabricator_robolimbs) - var/datum/robolimb/R = GLOB.fabricator_robolimbs[A] - T += list(list("name" = R.company, "type" = A)) - data["manufacturers"] = T - data["selected_manufacturer"] = limb_manufacturer - - data["materials"] = get_materials() - data["maximum_resource_amount"] = res_max_amount - data["sync"] = sync_message - - //If we have something in the queue that we're trying to build, show the time left if the build is undergoing, otherwise null - //the logic of displaying it is handled client-side - if(current) - if(build_callback_timer) - data["timeleft"] = round(timeleft(build_callback_timer)) - else - data["timeleft"] = null - return data - - -/obj/structure/machinery/mecha_part_fabricator/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) - . = ..() - if(.) - return - - switch(action) - if("build") - var/path = text2path(params["build"]) - add_to_queue(path) - . = TRUE - - if("remove") - remove_from_queue(text2num(params["remove"])) - . = TRUE - - if("category") - if(params["category"] in categories) - category = params["category"] - . = TRUE - - if("manufacturer") - if(params["manufacturer"] in GLOB.fabricator_robolimbs) - limb_manufacturer = params["manufacturer"] - . = TRUE - - if("eject") - eject_materials(params["eject"], text2num(params["amount"])) - . = TRUE - - if("sync") - sync() - . = TRUE - else - sync_message = "" - - if(.) - playsound(src, 'sound/machines/synthfab/synthfab_button.ogg', 50) - -/obj/structure/machinery/mecha_part_fabricator/attackby(obj/item/attacking_item, mob/user) - if(build_callback_timer) - to_chat(user, SPAN_NOTICE("\The [src] is busy. Please wait for completion of previous operation.")) - return TRUE - if(default_deconstruction_screwdriver(user, attacking_item)) - return TRUE - if(default_deconstruction_crowbar(user, attacking_item)) - return TRUE - if(default_part_replacement(user, attacking_item)) - return TRUE - - if(!istype(attacking_item, /obj/item/stack/material)) - return ..() - - var/obj/item/stack/material/M = attacking_item - if(!M.material) - return ..() - var/material = SSmaterials.material_to_path(M.material, FALSE) - if(!(material in list(MATERIAL_STEEL, MATERIAL_GLASS, MATERIAL_GOLD, MATERIAL_SILVER, MATERIAL_DIAMOND, MATERIAL_PHORON, MATERIAL_URANIUM, MATERIAL_PLASTEEL, MATERIAL_ALUMINIUM, MATERIAL_LEAD))) - to_chat(user, SPAN_WARNING("\The [src] cannot hold [M.material.name].")) - return TRUE - - var/sname = "[M.name]" - SSmaterials.normalize_material_amounts(materials) - if((materials[material] || 0) + SHEET_MATERIAL_AMOUNT <= res_max_amount) - if(M.amount >= 1) - var/count = 0 - var/mutable_appearance/MA = mutable_appearance(icon, "material_insertion") - MA.color = M.material.icon_colour - //first play the insertion animation - flick_overlay_view(MA, 1 SECONDS) - - //now play the progress bar animation - flick_overlay_view(mutable_appearance(icon, "fab_progress"), 1 SECONDS) - - while((materials[material] || 0) + SHEET_MATERIAL_AMOUNT <= res_max_amount && M.amount >= 1) - SSmaterials.add_material_amount(materials, material, M.perunit) - M.use(1) - count++ - to_chat(user, SPAN_NOTICE("You insert [count] [sname] into \the [src].")) - - //Wake up, we have things to do (maybe) - handle_queue() - - else - to_chat(user, SPAN_NOTICE("\The [src] cannot hold more [sname].")) - return TRUE - -/obj/structure/machinery/mecha_part_fabricator/mouse_drop_receive(atom/dropped, mob/user, params) - var/mob/living/carbon/human/target = dropped - if (!istype(target) || target.buckled_to || get_dist(user, src) > 1 || get_dist(user, target) > 1 || user.stat || istype(user, /mob/living/silicon/ai)) - return - if(target == user) - return - src.add_fingerprint(user) - var/target_loc = target.loc - - if(target != user && !user.restrained() && !user.stat && !user.weakened && !user.stunned && !user.paralysis) - if(isskrell(target) || isunathi(target)) - return - - for(var/obj/item/protection in list(target.head)) - if(protection && (protection.flags_inv & BLOCKHAIR)) - return - - var/datum/sprite_accessory/hair/hair_style = GLOB.hair_styles_list[target.h_style] - if(hair_style.length < 4) - return - - user.visible_message(SPAN_WARNING("[user] starts feeding [target]'s hair into \the [src]!"), SPAN_WARNING("You start feeding [target]'s hair into \the [src]!")) - if(!do_after(user, 5 SECONDS, target, DO_UNIQUE)) - return - if(target_loc != target.loc) - return - if(target != user && !user.restrained() && !user.stat && !user.weakened && !user.stunned && !user.paralysis) - user.visible_message(SPAN_WARNING("[user] feeds the [target]'s hair into the [src] and flicks it on!"), SPAN_ALERT("You turn the [src] on!")) - do_hair_pull(target) - user.attack_log += "\[[time_stamp()]\] Has fed [target.name]'s ([target.ckey]) hair into a [src]." - target.attack_log += "\[[time_stamp()]\] Has had their hair fed into [src] by [user.name] ([user.ckey])" - msg_admin_attack("[key_name_admin(user)] fed [key_name_admin(target)] in a [src]. (JMP)",ckey=key_name(user),ckey_target=key_name(target)) - else - return - if(!do_after(user, 3.5 SECONDS, target, DO_UNIQUE)) - return - if(target_loc != target.loc) - return - if(target != user && !user.restrained() && !user.stat && !user.weakened && !user.stunned && !user.paralysis) - user.visible_message(SPAN_ALERT("[user] starts tugging on [target]'s head as the [src] keeps running!"), SPAN_ALERT("You start tugging on [target]'s head!")) - do_hair_pull(target) - spawn(10) - user.visible_message(SPAN_ALERT("[user] stops the [src] and leaves [target] resting as they are."), SPAN_ALERT("You turn the [src] off and let go of [target].")) - -/obj/structure/machinery/mecha_part_fabricator/emag_act(var/remaining_charges, var/mob/user) - switch(emagged) - if(0) - emagged = 0.5 - visible_message("[icon2html(src, viewers(get_turf(src)))] [src] beeps: \"DB error \[Code 0x00F1\]\"") - sleep(10) - visible_message("[icon2html(src, viewers(get_turf(src)))] [src] beeps: \"Attempting auto-repair\"") - sleep(15) - visible_message("[icon2html(src, viewers(get_turf(src)))] [src] beeps: \"User DB corrupted \[Code 0x00FA\]. Truncating data structure...\"") - sleep(30) - visible_message("[icon2html(src, viewers(get_turf(src)))] [src] beeps: \"User DB truncated. Please contact your [SSatlas.current_map.company_name] system operator for future assistance.\"") - req_access = null - emagged = 1 - return 1 - if(0.5) - visible_message("[icon2html(src, viewers(get_turf(src)))] [src] beeps: \"DB not responding \[Code 0x0003\]...\"") - if(1) - visible_message("[icon2html(src, viewers(get_turf(src)))] [src] beeps: \"No records in User DB\"") - -/** - * Adds a design to the queue - * - * * path: The path of the design to add - */ -/obj/structure/machinery/mecha_part_fabricator/proc/add_to_queue(path) - var/datum/design/D = files.known_designs[path] - if(!D) - return - queue += D - - //Wake up, we have things to do - handle_queue() - -/** - * Removes a design from the queue - * - * * index: The index of the design to remove - */ -/obj/structure/machinery/mecha_part_fabricator/proc/remove_from_queue(index) - queue.Cut(index, index + 1) - - //If we're removing the first thing in the queue, stop the build timer - if(index == 1) - deltimer(build_callback_timer) - build_callback_timer = null - - //Wake up, we have things to do - handle_queue() - -/** - * Handle the construction queue - */ -/obj/structure/machinery/mecha_part_fabricator/proc/handle_queue() - - //No work to do or already busy, stop - if(!length(queue) || build_callback_timer) - //If we don't have a build in progress, revert to idle power usage - if(!build_callback_timer) - update_use_power(POWER_USE_IDLE) - - update_icon() - return - - //If there's no power, there's no building - if(stat & NOPOWER) - queue = list() - return - - //Get the first design in the queue - var/datum/design/D = queue[1] - - //If we can build it, process the request - if(can_build(D)) - update_use_power(POWER_USE_ACTIVE) - - //Add the callback timer for printing, and remove the design from the queue - build_callback_timer = addtimer(CALLBACK(src, PROC_REF(build), D), (D.time / production_speed), TIMER_UNIQUE|TIMER_STOPPABLE) - //No queue removal here because we show how long it takes to build in the UI - - else - visible_message(SPAN_NOTICE("[icon2html(src, viewers(get_turf(src)))] \The [src] flashes: Insufficient materials to complete build: [D.name].")) - remove_from_queue(1) - - update_icon() - -/obj/structure/machinery/mecha_part_fabricator/proc/can_build(var/datum/design/D) - SSmaterials.normalize_material_amounts(materials) - for(var/M in D.materials) - var/material = SSmaterials.material_to_path(M, FALSE) - if(!material) - material = M - if((materials[material] || 0) < D.materials[M] * mat_efficiency) - return 0 - return 1 - -/** - * Builds the given design, assuming all the necessary conditions are met - * - * * design_to_build: The design to build - */ -/obj/structure/machinery/mecha_part_fabricator/proc/build(datum/design/design_to_build) - //Consume the materials - SSmaterials.normalize_material_amounts(materials) - for(var/M in design_to_build.materials) - SSmaterials.remove_material_amount(materials, M, design_to_build.materials[M] * mat_efficiency) - - intent_message(MACHINE_SOUND) - - if(design_to_build.build_path) - var/loc_offset = get_step(src, dir) - var/obj/new_item = design_to_build.Fabricate(loc_offset, src) - visible_message("\The [src] pings, indicating that \the [new_item] is complete.", "You hear a ping.", intent_message = PING_SOUND) - if(mat_efficiency != 1) - if(new_item.matter && new_item.matter.len > 0) - for(var/i in new_item.matter) - new_item.matter[i] = new_item.matter[i] * mat_efficiency - - //We finished building, clear the timer and remove the thing from the queue - build_callback_timer = null - remove_from_queue(1) - - //Do the queue handling for the next item, or to stop - handle_queue() - -/obj/structure/machinery/mecha_part_fabricator/proc/get_queue_names() - . = list() - for(var/i = 1 to queue.len) - var/datum/design/D = queue[i] - . += list(list("name" = D.name, "time" = get_design_time(D), "index" = i)) - -/obj/structure/machinery/mecha_part_fabricator/proc/get_build_options() - . = list() - for(var/path in files.known_designs) - var/datum/design/D = files.known_designs[path] - if(!D.build_path || !(D.build_type & MECHFAB) || !D.category || (D.category != category)) - continue - . += list(list("name" = D.name, "desc" = D.desc, "type" = D.type, "category" = D.category, "resources" = get_design_resourses(D), "time" = get_design_time(D))) - -/obj/structure/machinery/mecha_part_fabricator/proc/get_design_resourses(var/datum/design/D) - var/list/F = list() - for(var/T in D.materials) - F += "[SSmaterials.material_display_name(T)]: [D.materials[T] * mat_efficiency]" - return english_list(F, and_text = ", ") - -/obj/structure/machinery/mecha_part_fabricator/proc/get_design_time(var/datum/design/D) - return time2text(round(10 * D.time / production_speed), "mm:ss") - -/obj/structure/machinery/mecha_part_fabricator/proc/update_categories() - categories = list() - for(var/path in files.known_designs) - var/datum/design/D = files.known_designs[path] - if(!D.build_path || !(D.build_type & MECHFAB) || !D.category) - continue - categories |= D.category - if(!category || !(category in categories)) - category = categories[1] - -/obj/structure/machinery/mecha_part_fabricator/proc/get_materials() - . = list() - SSmaterials.normalize_material_amounts(materials) - for(var/T in materials) - . += list(list("name" = SSmaterials.material_display_name(T), "amount" = materials[T])) - -/obj/structure/machinery/mecha_part_fabricator/proc/eject_materials(var/material, var/amount) - if(!amount) - return - SSmaterials.normalize_material_amounts(materials) - material = SSmaterials.material_to_path(material, FALSE) - if(!material) - return - var/stack_type = SSmaterials.material_stack_type(material) - if(!stack_type) - return - - var/real_amount = min(round(amount / SHEET_MATERIAL_AMOUNT), round((materials[material] || 0) / SHEET_MATERIAL_AMOUNT)) - if(real_amount < 1) - return - var/obj/item/stack/material/S = new stack_type(loc, real_amount) - S.update_icon() - SSmaterials.remove_material_amount(materials, material, real_amount * SHEET_MATERIAL_AMOUNT) - -/obj/structure/machinery/mecha_part_fabricator/proc/sync() - sync_message = "Error: no console found." - for(var/obj/structure/machinery/computer/rdconsole/RDC in get_area(src)) - if(!RDC.sync) - continue - for(var/id in RDC.files.known_tech) - var/datum/tech/T = RDC.files.known_tech[id] - files.AddTech2Known(T) - files.RefreshResearch() - sync_message = "Sync complete." - update_categories() diff --git a/code/modules/clothing/spacesuits/rig/modules/computer.dm b/code/modules/clothing/spacesuits/rig/modules/computer.dm index 0289b7136ef..1eabc0fed0b 100644 --- a/code/modules/clothing/spacesuits/rig/modules/computer.dm +++ b/code/modules/clothing/spacesuits/rig/modules/computer.dm @@ -315,9 +315,9 @@ else if(istype(input_device,/obj/structure/machinery/r_n_d/server)) var/obj/structure/machinery/r_n_d/server/input_machine = input_device incoming_files = input_machine.files - else if(istype(input_device,/obj/structure/machinery/mecha_part_fabricator)) - var/obj/structure/machinery/mecha_part_fabricator/input_machine = input_device - incoming_files = input_machine.files + else if(istype(input_device, /obj/structure/machinery/r_n_d/fabricator/mecha_part_fabricator)) + var/obj/structure/machinery/r_n_d/fabricator/mecha_part_fabricator/input_mechfab = input_device + incoming_files = input_mechfab.linked_console?.files if(!incoming_files || !incoming_files.known_tech || !incoming_files.known_tech.len) to_chat(user, SPAN_WARNING("Memory failure. There is nothing accessible stored on this terminal.")) diff --git a/code/modules/item_worth/worths_list.dm b/code/modules/item_worth/worths_list.dm index 610b34744fe..c5d4b22bb0c 100644 --- a/code/modules/item_worth/worths_list.dm +++ b/code/modules/item_worth/worths_list.dm @@ -752,7 +752,7 @@ var/list/worths = list( /obj/structure/machinery/conveyor_switch = 10, /obj/structure/machinery/disposal = 50, /obj/structure/machinery/blackbox_recorder = -950, - /obj/structure/machinery/r_n_d/protolathe = -1500, + /obj/structure/machinery/r_n_d/fabricator/protolathe = -1500, /obj/structure/machinery/r_n_d/server = -2000, /obj/structure/machinery/r_n_d = -100, /obj/structure/machinery/auto_cloner = -1300, diff --git a/code/modules/research/circuitprinter.dm b/code/modules/research/circuitprinter.dm index 40b1ee17a78..698969471a1 100644 --- a/code/modules/research/circuitprinter.dm +++ b/code/modules/research/circuitprinter.dm @@ -1,82 +1,47 @@ /*///////////////Circuit Imprinter (By Darem)//////////////////////// - Used to print new circuit boards (for computers and similar systems) and AI modules. Each circuit board pattern are stored in -a /datum/desgin on the linked R&D console. You can then print them out in a fasion similar to a regular lathe. However, instead of -using metal and glass, it uses glass and reagents (usually sulphuric acid). + Used to print new circuit boards (for computers and similar systems) and AI modules. */ -/obj/structure/machinery/r_n_d/circuit_imprinter +/obj/structure/machinery/r_n_d/fabricator/circuit_imprinter name = "circuit imprinter" desc = "An advanced device that can only be operated via a nearby RnD console, it can print any circuitboard the user requests, provided it has the correct materials to do so." icon_state = "circuit_imprinter" atom_flags = ATOM_FLAG_OPEN_CONTAINER - idle_power_usage = 30 active_power_usage = 2500 - + build_type = IMPRINTER + uses_reagents = TRUE + fabrication_loop_type = /datum/looping_sound/synth_fab component_types = list( /obj/item/circuitboard/circuit_imprinter, - /obj/item/stock_parts/matter_bin, /obj/item/stock_parts/manipulator, /obj/item/reagent_containers/glass/beaker = 2 ) - var/max_material_storage = 75000 - var/list/materials = list(MATERIAL_STEEL = 0, MATERIAL_GLASS = 0, MATERIAL_GOLD = 0, MATERIAL_SILVER = 0, MATERIAL_PHORON = 0, MATERIAL_URANIUM = 0, MATERIAL_DIAMOND = 0) - - /** - * A `/list` of enqueued `/datum/design` to be printed, processed in the queue - */ - var/list/datum/design/queue = list() - - /** - * How much efficient (or inefficient) the circuit imprinter is at manufacturing circuit boards - */ - var/mat_efficiency = 1 - - /** - * The production speed of this specific circuit imprinter, a factor - */ - var/production_speed = 1 - - ///Boolean, if to make the printer spawn its product in a neighboring turf dictated by dir - var/product_offset = FALSE - - ///The timer id for the build callback, if we're building something - var/build_callback_timer - - -/obj/structure/machinery/r_n_d/circuit_imprinter/upgrade_hints(mob/user, distance, is_adjacent) +/obj/structure/machinery/r_n_d/fabricator/circuit_imprinter/upgrade_hints(mob/user, distance, is_adjacent) . += ..() - . += "- Upgraded matter bins will increase material storage capacity." - . += SPAN_NOTICE(" - The current storage capacity is [max_material_storage / 2000] sheets") + . += "- Materials are drawn from the research material silo linked to the same R&D console." . += "- Upgraded manipulators will improve material use efficiency and increase fabrication speed." . += SPAN_NOTICE(" - The current speed increase is [round((1 - (1 / production_speed)) * 100)]%") . += SPAN_NOTICE(" - The current cost reduction is [round((1 - mat_efficiency) * 100)]%") -/obj/structure/machinery/r_n_d/circuit_imprinter/RefreshParts() +/obj/structure/machinery/r_n_d/fabricator/circuit_imprinter/RefreshParts() ..() - // Adjust reagent container volume to match combined volume of the inserted beakers - var/T = 0 - for(var/obj/item/reagent_containers/glass/G in component_parts) - T += G.reagents.maximum_volume - create_reagents(T) - // Transfer all reagents from the beakers to internal reagent container - for(var/obj/item/reagent_containers/glass/G in component_parts) - G.reagents.trans_to_obj(src, G.reagents.total_volume) + var/total_volume = 0 + for(var/obj/item/reagent_containers/glass/beaker in component_parts) + total_volume += beaker.reagents.maximum_volume + create_reagents(total_volume) + for(var/obj/item/reagent_containers/glass/beaker in component_parts) + beaker.reagents.trans_to_obj(src, beaker.reagents.total_volume) - // Adjust material storage capacity to scale with matter bin rating - max_material_storage = 0 - for(var/obj/item/stock_parts/matter_bin/M in component_parts) - max_material_storage += M.rating * 75000 + var/manipulator_rating = 0 + for(var/obj/item/stock_parts/manipulator/manipulator in component_parts) + manipulator_rating += manipulator.rating + mat_efficiency = 1 - (manipulator_rating - 1) / 8 + production_speed = manipulator_rating + update_icon() - // Adjust production speed to increase with manipulator rating - T = 0 - for(var/obj/item/stock_parts/manipulator/M in component_parts) - T += M.rating - mat_efficiency = 1 - (T - 1) / 4 - production_speed = T - -/obj/structure/machinery/r_n_d/circuit_imprinter/update_icon() +/obj/structure/machinery/r_n_d/fabricator/circuit_imprinter/update_icon() if(panel_open) icon_state = "circuit_imprinter_t" else if(build_callback_timer) @@ -84,240 +49,11 @@ using metal and glass, it uses glass and reagents (usually sulphuric acid). else icon_state = "circuit_imprinter" -/obj/structure/machinery/r_n_d/circuit_imprinter/proc/TotalMaterials() - SSmaterials.normalize_material_amounts(materials) - var/t = 0 - for(var/f in materials) - t += materials[f] - return t - -/obj/structure/machinery/r_n_d/circuit_imprinter/dismantle() - for(var/obj/I in component_parts) - // This will distribute all reagents amongst the contained beakers - if(istype(I, /obj/item/reagent_containers/glass/beaker)) - reagents.trans_to_obj(I, reagents.total_volume) - SSmaterials.normalize_material_amounts(materials) - for(var/f in materials) - if(materials[f] >= SHEET_MATERIAL_AMOUNT) - var/path = getMaterialType(f) - if(path) - var/obj/item/stack/S = new path(loc) - S.amount = round(materials[f] / SHEET_MATERIAL_AMOUNT) +/obj/structure/machinery/r_n_d/fabricator/circuit_imprinter/dismantle() + for(var/obj/item/reagent_containers/glass/beaker in component_parts) + reagents.trans_to_obj(beaker, reagents.total_volume) ..() -/obj/structure/machinery/r_n_d/circuit_imprinter/attackby(obj/item/attacking_item, mob/user) - //No touch the machine while it's working! - if(build_callback_timer) - to_chat(user, SPAN_NOTICE("\The [src] is busy. Please wait for completion of previous operation.")) - return 1 - - if(default_deconstruction_screwdriver(user, attacking_item)) - if(linked_console) - linked_console.linked_imprinter = null - linked_console = null - return - - if(default_deconstruction_crowbar(user, attacking_item)) - return - - if(default_part_replacement(user, attacking_item)) - return - - if(panel_open) - to_chat(user, SPAN_NOTICE("You can't load \the [src] while it's opened.")) - return 1 - - if(!linked_console) - to_chat(user, "\The [src] must be linked to an R&D console first.") - return 1 - - if(attacking_item.is_open_container()) - return 0 - - if(!istype(attacking_item, /obj/item/stack/material)) - to_chat(user, SPAN_NOTICE("You cannot insert this item into \the [src]!")) - return 1 - - if(stat) - return 1 - - - if(TotalMaterials() + SHEET_MATERIAL_AMOUNT > max_material_storage) - to_chat(user, SPAN_NOTICE("\The [src]'s material bin is full. Please remove material before adding more.")) - return 1 - - var/obj/item/stack/material/stack = attacking_item - if(!stack.default_type) - to_chat(user, SPAN_WARNING("This stack cannot be used!")) - return - - var/max_value = min(stack.get_amount(), round((max_material_storage - TotalMaterials()) / SHEET_MATERIAL_AMOUNT)) - var/amount = tgui_input_number(user, "How many sheets do you want to add?", "Add sheets", min(10, max_value), max_value = max_value, min_value = 1, round_value = TRUE) - - if(!attacking_item) - return - if(!Adjacent(user)) - to_chat(user, SPAN_NOTICE("\The [src] is too far away for you to insert this.")) - return - if(amount <= 0)//No negative numbers - return - - use_power_oneoff(max(1000, (SHEET_MATERIAL_AMOUNT * amount / 10))) - if(do_after(user, 16)) - if(stack.use(amount)) - to_chat(user, SPAN_NOTICE("You add [amount] sheets to \the [src].")) - SSmaterials.add_material_amount(materials, stack.default_type, amount * SHEET_MATERIAL_AMOUNT) - - //In case there's things queued up, we run the queue handler - handle_queue() - - updateUsrDialog() - -/** - * Adds a design to the queue - * - * * design_to_add: The design to add - */ -/obj/structure/machinery/r_n_d/circuit_imprinter/proc/addToQueue(datum/design/design_to_add) - queue += design_to_add - - //Wake up, we have things to do - handle_queue() - - -/** - * Removes a design from the queue - * - * * index: The index of the design to remove - */ -/obj/structure/machinery/r_n_d/circuit_imprinter/proc/removeFromQueue(index) - queue.Cut(index, index + 1) - - //Wake up, we have things to do - handle_queue() - - -/** - * Handle the construction queue - */ -/obj/structure/machinery/r_n_d/circuit_imprinter/proc/handle_queue() - //No work to do or already busy, stop - if(!length(queue) || build_callback_timer) - update_icon() - return - - //If there's no power, there's no building - if(stat & NOPOWER) - queue = list() - return - - //Get the first design in the queue - var/datum/design/D = queue[1] - - //If we can build it, process the request - if(canBuild(D)) - build_callback_timer = addtimer(CALLBACK(src, PROC_REF(build), D), (D.time / production_speed), TIMER_UNIQUE) - removeFromQueue(1) - - else - visible_message(SPAN_NOTICE("[icon2html(src, viewers(get_turf(src)))] \The [src] flashes: insufficient materials: [getLackingMaterials(D)].")) - if(linked_console) - linked_console.updateUsrDialog() - - update_icon() - -/** - * Checks if the protolathe can build the given design - * - * * design_to_check: The design to check - * - * Returns `TRUE` if the design can be built, `FALSE` otherwise - */ -/obj/structure/machinery/r_n_d/circuit_imprinter/proc/canBuild(datum/design/design_to_check) - SSmaterials.normalize_material_amounts(materials) - for(var/M in design_to_check.materials) - var/material = SSmaterials.material_to_path(M, FALSE) - if(!material) - material = M - if((materials[material] || 0) < design_to_check.materials[M]) - return FALSE - - for(var/C in design_to_check.chemicals) - if(!reagents.has_reagent(C, design_to_check.chemicals[C])) - return FALSE - - return TRUE - -/** - * Get what materials (chemicals included) are lacking from being able to build the given design - * - * * design_to_check: The design to check - * - * Returns a string of the materials that are missing - */ -/obj/structure/machinery/r_n_d/circuit_imprinter/proc/getLackingMaterials(datum/design/design_to_check) - SSmaterials.normalize_material_amounts(materials) - var/ret = "" - - for(var/M in design_to_check.materials) - var/amount = SSmaterials.get_material_amount(materials, M) - if(amount < design_to_check.materials[M]) - if(ret != "") - ret += ", " - ret += "[design_to_check.materials[M] - amount] [SSmaterials.material_display_name(M)]" - - for(var/C in design_to_check.chemicals) - if(!reagents.has_reagent(C, design_to_check.chemicals[C])) - var/singleton/reagent/R = GET_SINGLETON(C) - if(ret != "") - ret += ", " - ret += "[R.name]" - - return ret - -/** - * Builds the given design, assuming all the necessary conditions are met - * - * * design_to_build: The design to build - */ -/obj/structure/machinery/r_n_d/circuit_imprinter/proc/build(datum/design/design_to_build) - //Consume some power - var/power = active_power_usage - for(var/M in design_to_build.materials) - power += round(design_to_build.materials[M] / 5) - power = max(active_power_usage, power) - use_power_oneoff(power) - - //Consume the materials - SSmaterials.normalize_material_amounts(materials) - for(var/M in design_to_build.materials) - SSmaterials.remove_material_amount(materials, M, design_to_build.materials[M] * mat_efficiency) - - for(var/C in design_to_build.chemicals) - reagents.remove_reagent(C, design_to_build.chemicals[C] * mat_efficiency) - - if(design_to_build.build_path) - var/obj/new_item = design_to_build.Fabricate(src, src) - if(product_offset) - new_item.forceMove(get_step(src, dir)) - else - new_item.forceMove(src.loc) - if(mat_efficiency != 1) // No matter out of nowhere - if(new_item.matter && new_item.matter.len > 0) - for(var/i in new_item.matter) - new_item.matter[i] = new_item.matter[i] * mat_efficiency - - //We finished building, clear the timer - build_callback_timer = null - - //Do the queue handling for the next item, or to stop - handle_queue() - - -/obj/structure/machinery/r_n_d/circuit_imprinter/is_open_container() +/obj/structure/machinery/r_n_d/fabricator/circuit_imprinter/is_open_container() . = ..() - - //This is to recheck the queue when acid is added to the reagent container that we have - //yes this should be done with signals, we don't have those signals yet - //cope - addtimer(CALLBACK(src, PROC_REF(handle_queue)), 1 SECOND, TIMER_UNIQUE) + linked_console?.dispatch_fabrication_jobs() diff --git a/code/modules/research/designs/circuit/circuits.dm b/code/modules/research/designs/circuit/circuits.dm index 1881e6b806a..af10934741e 100644 --- a/code/modules/research/designs/circuit/circuits.dm +++ b/code/modules/research/designs/circuit/circuits.dm @@ -9,7 +9,7 @@ if(ispath(build_path, /obj/item/circuitboard)) var/obj/item/circuitboard/CB = build_path var/atom/machine = initial(CB.build_path) - desc = "Used in the construction of a: [capitalize_first_letters(initial(machine.name))], [initial(machine.desc)]" + desc = "Used in the construction of a: [capitalize_first_letters(initial(machine.name))], [initial(machine.desc)]" else var/atom/A = build_path desc = initial(A.desc) diff --git a/code/modules/research/designs/circuit/machine_circuits.dm b/code/modules/research/designs/circuit/machine_circuits.dm index 8204f6a5c35..12d876e115b 100644 --- a/code/modules/research/designs/circuit/machine_circuits.dm +++ b/code/modules/research/designs/circuit/machine_circuits.dm @@ -213,7 +213,7 @@ /datum/design/circuit/machine/aicore name = "AI Core" - desc = "Used in the construction of an: AI core, Secure housing for an AI, it provides power and protection to its inhabitant." + desc = "Used in the construction of an: AI core, Secure housing for an AI, it provides power and protection to its inhabitant." req_tech = list(TECH_DATA = 4, TECH_BIO = 3) build_path = /obj/item/circuitboard/aicore diff --git a/code/modules/research/designs/mechfab/prosthetics/external.dm b/code/modules/research/designs/mechfab/prosthetics/external.dm index 73d81db3976..72eb008dd4a 100644 --- a/code/modules/research/designs/mechfab/prosthetics/external.dm +++ b/code/modules/research/designs/mechfab/prosthetics/external.dm @@ -4,9 +4,9 @@ //if the fabricator is a mech fab we need to construct the proper typepath for the manufacturer /datum/design/item/mechfab/prosthetic/Fabricate(var/newloc, var/fabricator) - if(istype(fabricator, /obj/structure/machinery/mecha_part_fabricator)) + if(istype(fabricator, /obj/structure/machinery/r_n_d/fabricator/mecha_part_fabricator)) if(!use_direct_buildpath) - var/obj/structure/machinery/mecha_part_fabricator/mechfab = fabricator + var/obj/structure/machinery/r_n_d/fabricator/mecha_part_fabricator/mechfab = fabricator for(var/model_path in subtypesof(build_path)) var/obj/item/organ/external/E = new model_path if(E.robotize_type == mechfab.limb_manufacturer) diff --git a/code/modules/research/designs/mechfab/robot/robot.dm b/code/modules/research/designs/mechfab/robot/robot.dm index b5f7e49939c..7cbee8d18d4 100644 --- a/code/modules/research/designs/mechfab/robot/robot.dm +++ b/code/modules/research/designs/mechfab/robot/robot.dm @@ -7,8 +7,8 @@ //if the fabricator is a mech fab pass the manufacturer info over to the robot part constructor /datum/design/item/mechfab/robot/Fabricate(var/newloc, var/fabricator) - if(istype(fabricator, /obj/structure/machinery/mecha_part_fabricator)) - var/obj/structure/machinery/mecha_part_fabricator/mechfab = fabricator + if(istype(fabricator, /obj/structure/machinery/r_n_d/fabricator/mecha_part_fabricator)) + var/obj/structure/machinery/r_n_d/fabricator/mecha_part_fabricator/mechfab = fabricator return new build_path(newloc, mechfab.manufacturer) return ..() diff --git a/code/modules/research/material_silo.dm b/code/modules/research/material_silo.dm new file mode 100644 index 00000000000..a6e09a40bf5 --- /dev/null +++ b/code/modules/research/material_silo.dm @@ -0,0 +1,176 @@ +/obj/structure/machinery/r_n_d/material_silo + name = "material silo" + desc = "A centralized material store used by linked research fabricators." + icon_state = "silo" + + idle_power_usage = 30 WATTS + + /// Total material capacity, measured in material units. + var/max_material_storage = 800000 + + /// Materials shared by every fabricator connected to the same R&D console. + var/list/materials = list( + MATERIAL_STEEL = 0, + MATERIAL_GLASS = 0, + MATERIAL_GOLD = 0, + MATERIAL_SILVER = 0, + MATERIAL_PHORON = 0, + MATERIAL_URANIUM = 0, + MATERIAL_DIAMOND = 0, + MATERIAL_PLASTEEL = 0, + MATERIAL_ALUMINIUM = 0, + MATERIAL_LEAD = 0 + ) + +/obj/structure/machinery/r_n_d/material_silo/upgrade_hints(mob/user, distance, is_adjacent) + . += ..() + . += SPAN_NOTICE("\t- The shared storage capacity is [max_material_storage / SHEET_MATERIAL_AMOUNT] sheets.") + +/obj/structure/machinery/r_n_d/material_silo/Initialize(mapload, d, populate_components, is_internal) + . = ..() + update_icon() + +/obj/structure/machinery/r_n_d/material_silo/update_icon() + . = ..() + ClearOverlays() + if(!(stat & (NOPOWER | BROKEN))) + AddOverlays(emissive_appearance(icon, "[icon_state]_lights")) + AddOverlays("[icon_state]_lights") + +/obj/structure/machinery/r_n_d/material_silo/proc/TotalMaterials() + SSmaterials.normalize_material_amounts(materials) + var/total = 0 + for(var/material in materials) + total += materials[material] + return total + +/obj/structure/machinery/r_n_d/material_silo/proc/get_material_amount(material_id) + SSmaterials.normalize_material_amounts(materials) + var/material = SSmaterials.material_to_path(material_id, FALSE) + if(!material) + material = material_id + return materials[material] || 0 + +/obj/structure/machinery/r_n_d/material_silo/proc/has_material(material_id, amount) + return get_material_amount(material_id) >= amount + +/obj/structure/machinery/r_n_d/material_silo/proc/add_material(material_id, amount) + if(amount <= 0) + return 0 + var/material = SSmaterials.material_to_path(material_id, FALSE) + if(!material) + material = material_id + if(!(material in materials)) + return 0 + var/accepted = min(amount, max_material_storage - TotalMaterials()) + if(accepted <= 0) + return 0 + SSmaterials.add_material_amount(materials, material, accepted) + update_linked_uis() + return accepted + +/obj/structure/machinery/r_n_d/material_silo/proc/remove_material(material_id, amount) + if(amount <= 0) + return TRUE + var/material = SSmaterials.material_to_path(material_id, FALSE) + if(!material) + material = material_id + if(!has_material(material, amount)) + return FALSE + SSmaterials.remove_material_amount(materials, material, amount) + update_linked_uis() + return TRUE + +/obj/structure/machinery/r_n_d/material_silo/proc/eject_material(material_id, requested_sheets) + if(requested_sheets < 1) + return FALSE + var/material = SSmaterials.material_to_path(material_id, FALSE) + if(!material) + return FALSE + var/available = round(get_material_amount(material) / SHEET_MATERIAL_AMOUNT) + var/amount = min(requested_sheets, available) + if(amount < 1) + return FALSE + var/stack_type = SSmaterials.material_stack_type(material) + if(!stack_type) + return FALSE + var/obj/item/stack/material/stack = new stack_type(loc, amount) + stack.update_icon() + SSmaterials.remove_material_amount(materials, material, amount * SHEET_MATERIAL_AMOUNT) + update_linked_uis() + return TRUE + +/obj/structure/machinery/r_n_d/material_silo/proc/wake_linked_fabricators() + linked_console?.dispatch_fabrication_jobs() + +/obj/structure/machinery/r_n_d/material_silo/proc/update_linked_uis() + if(!linked_console) + return + SStgui.update_uis(linked_console) + for(var/obj/structure/machinery/r_n_d/fabricator/fabricator as anything in linked_console.linked_fabricators) + SStgui.update_uis(fabricator) + +/obj/structure/machinery/r_n_d/material_silo/attackby(obj/item/attacking_item, mob/user) + if(user.a_intent == I_HURT) + return ..() + if(default_deconstruction_screwdriver(user, attacking_item)) + disconnect_console() + return TRUE + if(default_deconstruction_crowbar(user, attacking_item)) + return TRUE + if(default_part_replacement(user, attacking_item)) + return TRUE + if(panel_open) + to_chat(user, SPAN_NOTICE("You can't load \the [src] while it's opened.")) + return TRUE + if(!istype(attacking_item, /obj/item/stack/material)) + return ..() + if(stat) + return TRUE + + var/obj/item/stack/material/stack = attacking_item + if(!stack.default_type || !stack.material) + to_chat(user, SPAN_WARNING("This stack cannot be stored.")) + return TRUE + + var/material = SSmaterials.material_to_path(stack.default_type, FALSE) + if(!material) + material = stack.default_type + if(!(material in materials)) + to_chat(user, SPAN_WARNING("\The [src] cannot hold [stack.material.name].")) + return TRUE + + var/max_value = min(stack.get_amount(), round((max_material_storage - TotalMaterials()) / SHEET_MATERIAL_AMOUNT)) + if(max_value < 1) + to_chat(user, SPAN_NOTICE("\The [src] is full.")) + return TRUE + + var/amount = tgui_input_number(user, "How many sheets do you want to add?", "Add sheets", min(10, max_value), max_value = max_value, min_value = 1, round_value = TRUE) + if(!amount || !Adjacent(user)) + return TRUE + + use_power_oneoff(max(1000, SHEET_MATERIAL_AMOUNT * amount / 10)) + if(do_after(user, 1.6 SECONDS) && stack.use(amount)) + SSmaterials.add_material_amount(materials, material, amount * SHEET_MATERIAL_AMOUNT) + to_chat(user, SPAN_NOTICE("You add [amount] sheet\s of [stack.material.name] to \the [src].")) + update_linked_uis() + wake_linked_fabricators() + return TRUE + +/obj/structure/machinery/r_n_d/material_silo/dismantle() + SSmaterials.normalize_material_amounts(materials) + for(var/material in materials) + var/sheets = round(materials[material] / SHEET_MATERIAL_AMOUNT) + if(sheets < 1) + continue + var/stack_type = SSmaterials.material_stack_type(material) + if(!stack_type) + continue + var/obj/item/stack/material/stack = new stack_type(loc, sheets) + stack.update_icon() + ..() + +/obj/structure/machinery/r_n_d/material_silo/proc/disconnect_console() + if(linked_console) + linked_console.linked_silo = null + linked_console = null diff --git a/code/modules/research/mecha_fabricator.dm b/code/modules/research/mecha_fabricator.dm new file mode 100644 index 00000000000..8405ae75c9c --- /dev/null +++ b/code/modules/research/mecha_fabricator.dm @@ -0,0 +1,156 @@ +/obj/structure/machinery/r_n_d/fabricator/mecha_part_fabricator + name = "synthetic fabricator" + desc = "A general purpose fabricator that can be used to fabricate equipment for synthetics or exosuits." + icon = 'icons/obj/machinery/robotics_fabricator.dmi' + icon_state = "fab" + idle_power_usage = 20 + active_power_usage = 5000 + req_access = list(ACCESS_ROBOTICS) + component_types = list( + /obj/item/circuitboard/mechfab, + /obj/item/stock_parts/manipulator, + /obj/item/stock_parts/micro_laser, + /obj/item/stock_parts/console_screen + ) + manufacturer = "hephaestus" + build_type = MECHFAB + product_offset = TRUE + + fabrication_loop_type = /datum/looping_sound/synth_fab + + /// Manufacturer used for the currently assigned prosthetic job. + var/limb_manufacturer = PROSTHETIC_IND + +/obj/structure/machinery/r_n_d/fabricator/mecha_part_fabricator/LateInitialize() + . = ..() + if(!linked_console) + for(var/obj/structure/machinery/computer/rdconsole/console in range(3, src)) + console.SyncRDevices() + if(linked_console) + break + +/obj/structure/machinery/r_n_d/fabricator/mecha_part_fabricator/upgrade_hints(mob/user, distance, is_adjacent) + . += ..() + . += "- Materials are drawn from the research material silo linked to the same R&D console." + . += "- Upgraded micro-lasers will increase fabrication speed." + . += SPAN_NOTICE(" - The current speed increase is [round((1 - (1 / production_speed)) * 100)]%") + . += "- Upgraded manipulators will improve material use efficiency." + . += SPAN_NOTICE(" - The current cost reduction is [round((1 - mat_efficiency) * 100)]%") + +/obj/structure/machinery/r_n_d/fabricator/mecha_part_fabricator/antagonist_hints(mob/user, distance, is_adjacent) + . += ..() + . += "Click-drag someone with long hair onto this machine to start feeding it their hair! This will hurt and piss them off!" + +/obj/structure/machinery/r_n_d/fabricator/mecha_part_fabricator/Initialize() + . = ..() + + update_icon() + +/obj/structure/machinery/r_n_d/fabricator/mecha_part_fabricator/update_icon() + ClearOverlays() + if(panel_open) + AddOverlays("[icon_state]_panel") + if(!(stat & (NOPOWER | BROKEN))) + AddOverlays(emissive_appearance(icon, "[icon_state]_lights")) + AddOverlays("[icon_state]_lights") + if(build_callback_timer) + AddOverlays("[icon_state]_working") + AddOverlays(emissive_appearance(icon, "[icon_state]_lights_working")) + AddOverlays("[icon_state]_lights_working") + +/obj/structure/machinery/r_n_d/fabricator/mecha_part_fabricator/RefreshParts() + ..() + var/manipulator_rating = 0 + for(var/obj/item/stock_parts/manipulator/manipulator in component_parts) + manipulator_rating += manipulator.rating + mat_efficiency = 1 - (manipulator_rating - 1) / 4 + + var/laser_rating = 0 + for(var/obj/item/stock_parts/micro_laser/laser in component_parts) + laser_rating += laser.rating + production_speed = max(1, laser_rating) + +/obj/structure/machinery/r_n_d/fabricator/mecha_part_fabricator/attack_hand(mob/user) + if(..()) + return + if(!allowed(user)) + to_chat(user, SPAN_WARNING("Access denied.")) + return + do_hair_pull(user) + + var/cancelled = FALSE + SEND_SIGNAL(user, COMSIG_USE_MECH_FAB, &cancelled) + if(cancelled) + return + +/obj/structure/machinery/r_n_d/fabricator/mecha_part_fabricator/on_product_completed(obj/new_item, datum/design/design_to_build, datum/research_fabrication_job/job) + visible_message("\The [src] pings, indicating that \the [new_item] is complete.", "You hear a ping.", intent_message = PING_SOUND) + +/obj/structure/machinery/r_n_d/fabricator/mecha_part_fabricator/mouse_drop_receive(atom/dropped, mob/user, params) + var/mob/living/carbon/human/target = dropped + if (!istype(target) || target.buckled_to || get_dist(user, src) > 1 || get_dist(user, target) > 1 || user.stat || istype(user, /mob/living/silicon/ai)) + return + if(target == user) + return + src.add_fingerprint(user) + var/target_loc = target.loc + + if(target != user && !user.restrained() && !user.stat && !user.weakened && !user.stunned && !user.paralysis) + if(isskrell(target) || isunathi(target)) + return + + for(var/obj/item/protection in list(target.head)) + if(protection && (protection.flags_inv & BLOCKHAIR)) + return + + var/datum/sprite_accessory/hair/hair_style = GLOB.hair_styles_list[target.h_style] + if(hair_style.length < 4) + return + + user.visible_message(SPAN_WARNING("[user] starts feeding [target]'s hair into \the [src]!"), SPAN_WARNING("You start feeding [target]'s hair into \the [src]!")) + if(!do_after(user, 5 SECONDS, target, DO_UNIQUE)) + return + if(target_loc != target.loc) + return + if(target != user && !user.restrained() && !user.stat && !user.weakened && !user.stunned && !user.paralysis) + user.visible_message(SPAN_WARNING("[user] feeds the [target]'s hair into the [src] and flicks it on!"), SPAN_ALERT("You turn the [src] on!")) + do_hair_pull(target) + user.attack_log += "\[[time_stamp()]\] Has fed [target.name]'s ([target.ckey]) hair into a [src]." + target.attack_log += "\[[time_stamp()]\] Has had their hair fed into [src] by [user.name] ([user.ckey])" + msg_admin_attack("[key_name_admin(user)] fed [key_name_admin(target)] in a [src]. (JMP)",ckey=key_name(user),ckey_target=key_name(target)) + else + return + if(!do_after(user, 3.5 SECONDS, target, DO_UNIQUE)) + return + if(target_loc != target.loc) + return + if(target != user && !user.restrained() && !user.stat && !user.weakened && !user.stunned && !user.paralysis) + user.visible_message(SPAN_ALERT("[user] starts tugging on [target]'s head as the [src] keeps running!"), SPAN_ALERT("You start tugging on [target]'s head!")) + do_hair_pull(target) + spawn(10) + user.visible_message(SPAN_ALERT("[user] stops the [src] and leaves [target] resting as they are."), SPAN_ALERT("You turn the [src] off and let go of [target].")) + +/obj/structure/machinery/r_n_d/fabricator/mecha_part_fabricator/emag_act(var/remaining_charges, var/mob/user) + switch(emagged) + if(0) + emagged = 0.5 + visible_message("[icon2html(src, viewers(get_turf(src)))] [src] beeps: \"DB error \[Code 0x00F1\]\"") + sleep(10) + visible_message("[icon2html(src, viewers(get_turf(src)))] [src] beeps: \"Attempting auto-repair\"") + sleep(15) + visible_message("[icon2html(src, viewers(get_turf(src)))] [src] beeps: \"User DB corrupted \[Code 0x00FA\]. Truncating data structure...\"") + sleep(30) + visible_message("[icon2html(src, viewers(get_turf(src)))] [src] beeps: \"User DB truncated. Please contact your [SSatlas.current_map.company_name] system operator for future assistance.\"") + req_access = null + emagged = 1 + return 1 + if(0.5) + visible_message("[icon2html(src, viewers(get_turf(src)))] [src] beeps: \"DB not responding \[Code 0x0003\]...\"") + if(1) + visible_message("[icon2html(src, viewers(get_turf(src)))] [src] beeps: \"No records in User DB\"") + +/obj/structure/machinery/r_n_d/fabricator/mecha_part_fabricator/on_job_started(datum/research_fabrication_job/job) + limb_manufacturer = job?.manufacturer + +/obj/structure/machinery/r_n_d/fabricator/mecha_part_fabricator/on_job_cleared() + limb_manufacturer = PROSTHETIC_IND diff --git a/code/modules/research/protolathe.dm b/code/modules/research/protolathe.dm index 4ba23700636..9914a463175 100644 --- a/code/modules/research/protolathe.dm +++ b/code/modules/research/protolathe.dm @@ -1,102 +1,56 @@ -/obj/structure/machinery/r_n_d/protolathe +/obj/structure/machinery/r_n_d/fabricator/protolathe name = "protolathe" desc = "An upgraded variant of a common Autolathe, this can only be operated via a nearby RnD console, but can manufacture cutting edge technology, provided it has the design and the correct materials." icon_state = "protolathe" atom_flags = ATOM_FLAG_OPEN_CONTAINER - idle_power_usage = 30 WATTS active_power_usage = 25 KILO WATTS - - var/max_material_storage = 100000 - var/list/materials = list(MATERIAL_STEEL = 0, MATERIAL_GLASS = 0, MATERIAL_GOLD = 0, MATERIAL_SILVER = 0, MATERIAL_PHORON = 0, MATERIAL_URANIUM = 0, MATERIAL_DIAMOND = 0) - - /** - * A `/list` of enqueued `/datum/design` to be printed, processed in the queue - */ - var/list/datum/design/queue = list() - - /** - * How much efficient (or inefficient) the protolathe is at manufacturing things - */ - var/mat_efficiency = 1 - - /** - * The production speed of this specific protolathe, a factor - */ - var/production_speed = 1 - - ///The timer id for the build callback, if we're building something - var/build_callback_timer - + build_type = PROTOLATHE + uses_reagents = TRUE + fabrication_loop_type = /datum/looping_sound/synth_fab component_types = list( /obj/item/circuitboard/protolathe, - /obj/item/stock_parts/matter_bin = 2, /obj/item/stock_parts/manipulator = 2, /obj/item/reagent_containers/glass/beaker = 2 ) -/obj/structure/machinery/r_n_d/protolathe/upgrade_hints(mob/user, distance, is_adjacent) +/obj/structure/machinery/r_n_d/fabricator/protolathe/upgrade_hints(mob/user, distance, is_adjacent) . += ..() - . += "- Upgraded matter bins will increase material storage capacity." - . += SPAN_NOTICE(" - The current storage capacity is [max_material_storage / 2000] sheets") + . += "- Materials are drawn from the research material silo linked to the same R&D console." . += "- Upgraded manipulators will improve material use efficiency and increase fabrication speed." . += SPAN_NOTICE(" - The current speed increase is [round((1 - (1 / production_speed)) * 100)]%") . += SPAN_NOTICE(" - The current cost reduction is [round((1 - mat_efficiency) * 100)]%") -///Returns the total of all the stored materials -/obj/structure/machinery/r_n_d/protolathe/proc/TotalMaterials() - SSmaterials.normalize_material_amounts(materials) - var/t = 0 - for(var/f in materials) - t += materials[f] - return t - -/obj/structure/machinery/r_n_d/protolathe/RefreshParts() +/obj/structure/machinery/r_n_d/fabricator/protolathe/RefreshParts() ..() - // Adjust reagent container volume to match combined volume of the inserted beakers - var/T = 0 - for(var/obj/item/reagent_containers/glass/G in component_parts) - T += G.reagents.maximum_volume - create_reagents(T) - // Transfer all reagents from the beakers to internal reagent container - for(var/obj/item/reagent_containers/glass/G in component_parts) - G.reagents.trans_to_obj(src, G.reagents.total_volume) + var/total_volume = 0 + for(var/obj/item/reagent_containers/glass/beaker in component_parts) + total_volume += beaker.reagents.maximum_volume + create_reagents(total_volume) + for(var/obj/item/reagent_containers/glass/beaker in component_parts) + beaker.reagents.trans_to_obj(src, beaker.reagents.total_volume) - // Adjust material storage capacity to scale with matter bin rating - max_material_storage = 0 - for(var/obj/item/stock_parts/matter_bin/M in component_parts) - max_material_storage += M.rating * 75000 - - // Adjust production speed to increase with manipulator rating - T = 0 - for(var/obj/item/stock_parts/manipulator/M in component_parts) - T += M.rating - mat_efficiency = 1 - (T - 2) / 8 - production_speed = T / 2 + var/manipulator_rating = 0 + for(var/obj/item/stock_parts/manipulator/manipulator in component_parts) + manipulator_rating += manipulator.rating + mat_efficiency = 1 - (manipulator_rating - 2) / 8 + production_speed = manipulator_rating / 2 update_icon() -/obj/structure/machinery/r_n_d/protolathe/dismantle() - for(var/obj/I in component_parts) - if(istype(I, /obj/item/reagent_containers/glass/beaker)) - reagents.trans_to_obj(I, reagents.total_volume) - SSmaterials.normalize_material_amounts(materials) - for(var/f in materials) - if(materials[f] >= SHEET_MATERIAL_AMOUNT) - var/path = getMaterialType(f) - if(path) - var/obj/item/stack/S = new path(loc) - S.amount = round(materials[f] / SHEET_MATERIAL_AMOUNT) +/obj/structure/machinery/r_n_d/fabricator/protolathe/dismantle() + for(var/obj/item/reagent_containers/glass/beaker in component_parts) + reagents.trans_to_obj(beaker, reagents.total_volume) ..() -/obj/structure/machinery/r_n_d/protolathe/power_change() +/obj/structure/machinery/r_n_d/fabricator/protolathe/power_change() . = ..() update_icon() -/obj/structure/machinery/r_n_d/protolathe/update_icon() +/obj/structure/machinery/r_n_d/fabricator/protolathe/update_icon() ClearOverlays() if(panel_open) AddOverlays("[icon_state]_panel") - if(!(stat & (NOPOWER|BROKEN))) + if(!(stat & (NOPOWER | BROKEN))) AddOverlays(emissive_appearance(icon, "[icon_state]_lights")) AddOverlays("[icon_state]_lights") if(build_callback_timer) @@ -104,214 +58,6 @@ AddOverlays(emissive_appearance(icon, "[icon_state]_lights_working")) AddOverlays("[icon_state]_lights_working") -/obj/structure/machinery/r_n_d/protolathe/attackby(obj/item/attacking_item, mob/user) - if(user.a_intent == I_HURT) - return ..() - - if(build_callback_timer) - to_chat(user, SPAN_NOTICE("\The [src] is busy. Please wait for completion of previous operation.")) - return TRUE - if(default_deconstruction_screwdriver(user, attacking_item)) - if(linked_console) - linked_console.linked_lathe = null - linked_console = null - return TRUE - if(default_deconstruction_crowbar(user, attacking_item)) - return TRUE - if(default_part_replacement(user, attacking_item)) - return TRUE - if(attacking_item.is_open_container()) - return TRUE - if(panel_open) - to_chat(user, SPAN_NOTICE("You can't load \the [src] while it's opened.")) - return TRUE - if(!linked_console) - to_chat(user, SPAN_NOTICE("The [src] must be linked to an R&D console first!")) - return TRUE - if(!istype(attacking_item, /obj/item/stack/material)) - to_chat(user, SPAN_NOTICE("You cannot insert this item into \the [src]!")) - return TRUE - if(stat) - return TRUE - - if(TotalMaterials() + SHEET_MATERIAL_AMOUNT > max_material_storage) - to_chat(user, SPAN_NOTICE("The [src]'s material bin is full. Please remove material before adding more.")) - return TRUE - - var/obj/item/stack/material/stack = attacking_item - if(!stack.default_type) - to_chat(user, SPAN_WARNING("This stack cannot be used!")) - return - - var/max_value = min(stack.get_amount(), round((max_material_storage - TotalMaterials()) / SHEET_MATERIAL_AMOUNT)) - var/amount = tgui_input_number(user, "How many sheets do you want to add?", "Add sheets", min(10, max_value), max_value = max_value, min_value = 1, round_value = TRUE) - - if(!attacking_item) - return - if(!Adjacent(user)) - to_chat(user, SPAN_NOTICE("The [src] is too far away for you to insert this.")) - return - if(amount <= 0)//No negative numbers, no nulls - return - - var/mutable_appearance/M = mutable_appearance(icon, "material_insertion") - M.color = stack.material.icon_colour - //first play the insertion animation - flick_overlay_view(M, 1 SECONDS) - - //now play the progress bar animation - flick_overlay_view(mutable_appearance(icon, "protolathe_progress"), 1 SECONDS) - - //Use some power and add the materials - use_power_oneoff(max(1000, (SHEET_MATERIAL_AMOUNT * amount / 10))) - if(do_after(user, 1.6 SECONDS)) - if(stack.use(amount)) - if(amount>1) - to_chat(user, SPAN_NOTICE("You add [amount] [stack.material.sheet_plural_name] of [stack.material.name] to \the [src].")) - else - to_chat(user, SPAN_NOTICE("You add [amount] [stack.material.sheet_singular_name] of [stack.material.name] to \the [src].")) - SSmaterials.add_material_amount(materials, stack.default_type, amount * SHEET_MATERIAL_AMOUNT) - - //In case there's things queued up, we run the queue handler - handle_queue() - - //Give an update on the UIs - updateUsrDialog() - if(linked_console) - linked_console.updateUsrDialog() - -/** - * Adds a design to the queue - * - * * design_to_add: The design to add - */ -/obj/structure/machinery/r_n_d/protolathe/proc/addToQueue(datum/design/design_to_add) - queue += design_to_add - - //Wake up, we have things to do - handle_queue() - -/** - * Removes a design from the queue - * - * * index: The index of the design to remove - */ -/obj/structure/machinery/r_n_d/protolathe/proc/removeFromQueue(index) - queue.Cut(index, index + 1) - - //Wake up, we have things to do - handle_queue() - -/** - * Handle the construction queue - */ -/obj/structure/machinery/r_n_d/protolathe/proc/handle_queue() - - //No work to do or already busy, stop - if(!length(queue) || build_callback_timer) - update_icon() - return - - //If there's no power, there's no building - if(stat & NOPOWER) - queue = list() - update_icon() - return - - //Get the first design in the queue - var/datum/design/D = queue[1] - - //If we can build it, process the request - if(canBuild(D)) - build_callback_timer = addtimer(CALLBACK(src, PROC_REF(build), D), (D.time / production_speed), TIMER_UNIQUE) - removeFromQueue(1) - - else - visible_message(SPAN_NOTICE("[icon2html(src, viewers(get_turf(src)))] \The [src] flashes: Insufficient materials: [getLackingMaterials(D)].")) - if(linked_console) - linked_console.updateUsrDialog() - - update_icon() - - -/** - * Checks if the protolathe can build the given design - * - * * design_to_check: The design to check - * - * Returns `TRUE` if the design can be built, `FALSE` otherwise - */ -/obj/structure/machinery/r_n_d/protolathe/proc/canBuild(datum/design/design_to_check) - SSmaterials.normalize_material_amounts(materials) - for(var/M in design_to_check.materials) - var/material = SSmaterials.material_to_path(M, FALSE) - if(!material) - material = M - if((materials[material] || 0) < design_to_check.materials[M]) - return FALSE - - for(var/C in design_to_check.chemicals) - if(!reagents.has_reagent(C, design_to_check.chemicals[C])) - return FALSE - - return TRUE - -/** - * Get what materials (chemicals included) are lacking from being able to build the given design - * - * * design_to_check: The design to check - * - * Returns a string of the materials that are missing - */ -/obj/structure/machinery/r_n_d/protolathe/proc/getLackingMaterials(var/datum/design/design_to_check) - SSmaterials.normalize_material_amounts(materials) - var/ret = "" - for(var/M in design_to_check.materials) - var/amount = SSmaterials.get_material_amount(materials, M) - if(amount < design_to_check.materials[M]) - if(ret != "") - ret += ", " - ret += "[design_to_check.materials[M] - amount] [SSmaterials.material_display_name(M)]" - for(var/C in design_to_check.chemicals) - if(!reagents.has_reagent(C, design_to_check.chemicals[C])) - var/singleton/reagent/R = GET_SINGLETON(C) - if(ret != "") - ret += ", " - ret += "[R.name]" - return ret - -/** - * Builds the given design, assuming all the necessary conditions are met - * - * * design_to_build: The design to build - */ -/obj/structure/machinery/r_n_d/protolathe/proc/build(datum/design/design_to_build) - //Consume some power - var/power = active_power_usage - for(var/M in design_to_build.materials) - power += round(design_to_build.materials[M] / 2) - power = max(active_power_usage, power) - use_power_oneoff(power) - - //Consume the materials - SSmaterials.normalize_material_amounts(materials) - for(var/M in design_to_build.materials) - SSmaterials.remove_material_amount(materials, M, design_to_build.materials[M] * mat_efficiency) - for(var/C in design_to_build.chemicals) - reagents.remove_reagent(C, design_to_build.chemicals[C] * mat_efficiency) - - intent_message(MACHINE_SOUND) - - if(design_to_build.build_path) - var/obj/new_item = design_to_build.Fabricate(src, src) - new_item.forceMove(loc) - if(mat_efficiency != 1) // No matter out of nowhere - if(new_item.matter && new_item.matter.len > 0) - for(var/i in new_item.matter) - new_item.matter[i] = new_item.matter[i] * mat_efficiency - - //We finished building, clear the timer - build_callback_timer = null - - //Do the queue handling for the next item, or to stop - handle_queue() +/obj/structure/machinery/r_n_d/fabricator/protolathe/is_open_container() + . = ..() + linked_console?.dispatch_fabrication_jobs() diff --git a/code/modules/research/rdconsole.dm b/code/modules/research/rdconsole.dm index b123651af9f..a77809fd944 100644 --- a/code/modules/research/rdconsole.dm +++ b/code/modules/research/rdconsole.dm @@ -35,29 +35,82 @@ won't update every console in existence) but it's more of a hassle to do. Also, icon_keyboard_emis = "purple_key_mask" light_color = LIGHT_COLOR_PURPLE - circuit = /obj/item/circuitboard/rdconsole - var/datum/research/files //Stores all the collected research data. - var/obj/item/disk/tech_disk/t_disk = null //Stores the technology disk. - var/obj/item/disk/design_disk/d_disk = null //Stores the design disk. + manufacturer = "nanotrasen" - var/obj/structure/machinery/r_n_d/destructive_analyzer/linked_destroy = null //Linked Destructive Analyzer - var/obj/structure/machinery/r_n_d/protolathe/linked_lathe = null //Linked Protolathe - var/obj/structure/machinery/r_n_d/circuit_imprinter/linked_imprinter = null //Linked Circuit Imprinter + circuit = /obj/item/circuitboard/rdconsole + //Stores all the collected research data. + var/datum/research/files + //Stores the technology disk. + var/obj/item/disk/tech_disk/t_disk = null + //Stores the design disk. + var/obj/item/disk/design_disk/d_disk = null + + /// All fabrication machinery connected to this console. + var/list/obj/structure/machinery/r_n_d/fabricator/linked_fabricators = list() + + /// Console-owned production queues. + var/list/datum/research_fabrication_job/protolathe_queue = list() + var/list/datum/research_fabrication_job/imprinter_queue = list() + var/list/datum/research_fabrication_job/mechfab_queue = list() + + //Linked Destructive Analyzer + var/obj/structure/machinery/r_n_d/destructive_analyzer/linked_destroy = null + //Shared material storage + var/obj/structure/machinery/r_n_d/material_silo/linked_silo = null + + var/selected_mech_manufacturer + + var/device_sync_range = 7 var/allow_analyzer = TRUE var/allow_lathe = TRUE var/allow_imprinter = TRUE + var/allow_mechfab = TRUE + var/allow_silo = TRUE - var/screen = 1.0 //Which screen is currently showing. - var/id = 0 //ID of the computer (for server restrictions). - var/sync = 1 //If sync = 0, it doesn't show up on Server Control Console + //Which screen is currently showing. + var/screen = 1.0 + //ID of the computer (for server restrictions). + var/id = 0 + //If sync = 0, it doesn't show up on Server Control Console + var/sync = 1 + /// Number of copies to queue when adding a design. + var/queue_amount = 1 + /// Message displayed while a delayed console action is processing. + var/busy_message = "Processing request. Please wait." var/protolathe_category = "All" var/imprinter_category = "All" var/ref_for_ui - req_access = list(ACCESS_TOX) //Data and setting manipulation requires scientist access. + //Data and setting manipulation requires scientist access. + req_access = list(ACCESS_TOX) + +/datum/research_fabrication_job + /// Design being produced. + var/datum/design/design + + /// Machine currently producing this job. + var/obj/structure/machinery/assigned_machine + + /// Manufacturer selected for mech-fabricator products. + var/manufacturer + + /// Materials removed from the silo and reserved for this job. + var/list/reserved_materials = list() + +/datum/research_fabrication_job/New(datum/design/new_design, new_manufacturer) + . = ..() + + design = new_design + manufacturer = new_manufacturer + +/datum/research_fabrication_job/Destroy() + design = null + assigned_machine = null + reserved_materials = null + return ..() /obj/structure/machinery/computer/rdconsole/proc/CallMaterialName(var/ID) return SSmaterials.material_display_name(ID) @@ -66,24 +119,77 @@ won't update every console in existence) but it's more of a hassle to do. Also, var/singleton/reagent/R = GET_SINGLETON(ID) return R ? R.name : "(none)" -/obj/structure/machinery/computer/rdconsole/proc/SyncRDevices() //Makes sure it is properly sync'ed up with the devices attached to it (if any). - for(var/obj/structure/machinery/r_n_d/D in range(3, src)) - if(D.linked_console != null || D.panel_open) +/obj/structure/machinery/computer/rdconsole/proc/SyncRDevices() + linked_fabricators ||= list() + + for(var/obj/structure/machinery/r_n_d/device in range(device_sync_range, src)) + if(device.panel_open) continue - if(istype(D, /obj/structure/machinery/r_n_d/destructive_analyzer) && allow_analyzer) - if(linked_destroy == null) - linked_destroy = D - D.linked_console = src - else if(istype(D, /obj/structure/machinery/r_n_d/protolathe) && allow_lathe) - if(linked_lathe == null) - linked_lathe = D - D.linked_console = src - else if(istype(D, /obj/structure/machinery/r_n_d/circuit_imprinter) && allow_imprinter) - if(linked_imprinter == null) - linked_imprinter = D - D.linked_console = src + + if(istype(device, /obj/structure/machinery/r_n_d/destructive_analyzer) && allow_analyzer) + if(!linked_destroy && !device.linked_console) + linked_destroy = device + device.linked_console = src + else if(istype(device, /obj/structure/machinery/r_n_d/material_silo) && allow_silo) + if(!linked_silo && !device.linked_console) + linked_silo = device + device.linked_console = src + else if(istype(device, /obj/structure/machinery/r_n_d/fabricator)) + var/obj/structure/machinery/r_n_d/fabricator/fabricator = device + if(!fabricator_type_allowed(fabricator)) + continue + if(fabricator.linked_console && fabricator.linked_console != src) + continue + fabricator.linked_console = src + linked_fabricators |= fabricator + + cleanup_fabricators() + dispatch_fabrication_jobs() return +/obj/structure/machinery/computer/rdconsole/proc/fabricator_type_allowed(obj/structure/machinery/r_n_d/fabricator/fabricator) + if(fabricator.build_type & PROTOLATHE) + return allow_lathe + if(fabricator.build_type & IMPRINTER) + return allow_imprinter + if(fabricator.build_type & MECHFAB) + return allow_mechfab + return FALSE + +/obj/structure/machinery/computer/rdconsole/proc/cleanup_fabricators() + for(var/obj/structure/machinery/r_n_d/fabricator/fabricator as anything in linked_fabricators.Copy()) + if(QDELETED(fabricator) || fabricator.linked_console != src) + linked_fabricators -= fabricator + +/obj/structure/machinery/computer/rdconsole/proc/remove_fabricator(obj/structure/machinery/r_n_d/fabricator/fabricator) + if(!fabricator) + return + linked_fabricators -= fabricator + if(fabricator.assigned_job) + fabricator.assigned_job.assigned_machine = null + fabricator.assigned_job = null + fabricator.current_design = null + SStgui.update_uis(src) + dispatch_fabrication_jobs() + +/obj/structure/machinery/computer/rdconsole/proc/get_fabricators(build_flag) + var/list/result = list() + for(var/obj/structure/machinery/r_n_d/fabricator/fabricator as anything in linked_fabricators) + if(fabricator.build_type & build_flag) + result += fabricator + return result + +/obj/structure/machinery/computer/rdconsole/proc/get_primary_fabricator(build_flag) + for(var/obj/structure/machinery/r_n_d/fabricator/fabricator as anything in linked_fabricators) + if(fabricator.build_type & build_flag) + return fabricator + return null + +/obj/structure/machinery/computer/rdconsole/proc/disconnect_fabricators(build_flag) + for(var/obj/structure/machinery/r_n_d/fabricator/fabricator as anything in linked_fabricators.Copy()) + if(fabricator.build_type & build_flag) + fabricator.disconnect_console() + /obj/structure/machinery/computer/rdconsole/proc/SyncTechs() var/turf/turf = get_turf(src) for(var/obj/structure/machinery/r_n_d/server/S in SSmachinery.machinery) @@ -103,16 +209,91 @@ won't update every console in existence) but it's more of a hassle to do. Also, screen = 1.6 updateUsrDialog() -/obj/structure/machinery/computer/rdconsole/proc/griefProtection() //Have it automatically push research to the centcomm server so wild griffins can't fuck up R&D's work +//Have it automatically push research to the centcomm server so wild griffins can't fuck up R&D's work +/obj/structure/machinery/computer/rdconsole/proc/griefProtection() for(var/obj/structure/machinery/r_n_d/server/centcom/C in SSmachinery.machinery) for(var/tech_id in files.known_tech) var/datum/tech/T = files.known_tech[tech_id] C.files.AddTech2Known(files.known_tech[T]) C.files.RefreshResearch() +/obj/structure/machinery/computer/rdconsole/proc/dispatch_fabrication_jobs() + cleanup_fabricators() + dispatch_queue_to_machines(protolathe_queue, PROTOLATHE) + dispatch_queue_to_machines(imprinter_queue, IMPRINTER) + dispatch_queue_to_machines(mechfab_queue, MECHFAB) + SStgui.update_uis(src) + +/obj/structure/machinery/computer/rdconsole/proc/dispatch_queue_to_machines(list/job_queue, build_flag) + if(!length(job_queue)) + return + + for(var/datum/research_fabrication_job/job as anything in job_queue) + if(job.assigned_machine) + continue + for(var/obj/structure/machinery/r_n_d/fabricator/fabricator as anything in linked_fabricators) + if(!(fabricator.build_type & build_flag) || !fabricator.is_available()) + continue + if(!fabricator.can_produce_job(job)) + continue + if(fabricator.begin_console_job(job)) + break + +/obj/structure/machinery/computer/rdconsole/proc/get_job_queue(build_flag) + if(build_flag & PROTOLATHE) + return protolathe_queue + if(build_flag & IMPRINTER) + return imprinter_queue + if(build_flag & MECHFAB) + return mechfab_queue + return null + +/obj/structure/machinery/computer/rdconsole/proc/refund_job_materials(datum/research_fabrication_job/job) + if(!job || !length(job.reserved_materials) || !linked_silo) + return + + for(var/material_id in job.reserved_materials) + SSmaterials.add_material_amount(linked_silo.materials, material_id, job.reserved_materials[material_id]) + + job.reserved_materials.Cut() + linked_silo.update_linked_uis() + +/obj/structure/machinery/computer/rdconsole/proc/refund_all_queued_materials() + for(var/datum/research_fabrication_job/job as anything in protolathe_queue) + refund_job_materials(job) + for(var/datum/research_fabrication_job/job as anything in imprinter_queue) + refund_job_materials(job) + for(var/datum/research_fabrication_job/job as anything in mechfab_queue) + refund_job_materials(job) + +/obj/structure/machinery/computer/rdconsole/proc/remove_fabrication_job(list/job_queue, index) + if(index < 1 || index > length(job_queue)) + return FALSE + var/datum/research_fabrication_job/job = job_queue[index] + if(job.assigned_machine) + return FALSE + refund_job_materials(job) + job_queue.Cut(index, index + 1) + qdel(job) + dispatch_fabrication_jobs() + return TRUE + +/obj/structure/machinery/computer/rdconsole/proc/finish_fabrication_job(datum/research_fabrication_job/job) + if(!job) + return + if(job in protolathe_queue) + protolathe_queue -= job + else if(job in imprinter_queue) + imprinter_queue -= job + else if(job in mechfab_queue) + mechfab_queue -= job + qdel(job) + SStgui.update_uis(src) + /obj/structure/machinery/computer/rdconsole/Initialize() ..() files = new /datum/research(src) //Setup the research data holder. + selected_mech_manufacturer = GLOB.basic_robolimb.company if(!id) for(var/obj/structure/machinery/r_n_d/server/centcom/S in SSmachinery.machinery) S.setup() @@ -127,12 +308,14 @@ won't update every console in existence) but it's more of a hassle to do. Also, screen = 1.0 /obj/structure/machinery/computer/rdconsole/Destroy() - if(linked_destroy != null) + refund_all_queued_materials() + if(linked_destroy) linked_destroy.linked_console = null - if(linked_lathe != null) - linked_lathe.linked_console = null - if(linked_imprinter != null) - linked_imprinter.linked_console = null + if(linked_silo) + linked_silo.linked_console = null + for(var/obj/structure/machinery/r_n_d/fabricator/fabricator as anything in linked_fabricators.Copy()) + fabricator.linked_console = null + linked_fabricators.Cut() return ..() /obj/structure/machinery/computer/rdconsole/attackby(obj/item/attacking_item, mob/user) @@ -155,7 +338,7 @@ won't update every console in existence) but it's more of a hassle to do. Also, //The construction/deconstruction of the console code. ..() - src.updateUsrDialog() + SStgui.update_uis(src) return /obj/structure/machinery/computer/rdconsole/emag_act(remaining_charges, mob/user, emag_source) @@ -167,285 +350,6 @@ won't update every console in existence) but it's more of a hassle to do. Also, to_chat(usr, SPAN_NOTICE("You disable the security protocols.")) return 1 -/obj/structure/machinery/computer/rdconsole/Topic(href, href_list) - if(..()) - return 1 - - add_fingerprint(usr) - - usr.set_machine(src) - if(href_list["menu"]) //Switches menu screens. Converts a sent text string into a number. Saves a LOT of code. - var/temp_screen = text2num(href_list["menu"]) - if(temp_screen <= 1.1 || (3 <= temp_screen && 4.9 >= temp_screen) || allowed(usr) || emagged) //Unless you are making something, you need access. - screen = temp_screen - else - to_chat(usr, "Unauthorized Access.") - - else if(href_list["updt_tech"]) //Update the research holder with information from the technology disk. - screen = 0.0 - spawn(50) - screen = 1.2 - files.AddTech2Known(t_disk.stored) - updateUsrDialog() - griefProtection() //Update centcomm too - - else if(href_list["clear_tech"]) //Erase data on the technology disk. - t_disk.stored = null - - else if(href_list["eject_tech"]) //Eject the technology disk. - t_disk.forceMove(loc) - usr.put_in_hands(t_disk) - t_disk = null - screen = 1.0 - - else if(href_list["copy_tech"]) //Copys some technology data from the research holder to the disk. - var/datum/tech/T = files.known_tech[href_list["copy_tech_sent"]] - t_disk.stored = T - screen = 1.2 - - else if(href_list["updt_design"]) //Updates the research holder with design data from the design disk. - screen = 0.0 - spawn(50) - screen = 1.4 - files.AddDesign2Known(d_disk.blueprint) - updateUsrDialog() - griefProtection() //Update centcomm too - - else if(href_list["clear_design"]) //Erases data on the design disk. - d_disk.blueprint = null - - else if(href_list["eject_design"]) //Eject the design disk. - d_disk.forceMove(loc) - usr.put_in_hands(d_disk) - d_disk = null - screen = 1.0 - - else if(href_list["copy_design"]) //Copy design data from the research holder to the design disk. - var/path = text2path(href_list["copy_design_sent"]) - var/datum/design/D = files.known_designs[path] - d_disk.blueprint = D - screen = 1.4 - - else if(href_list["eject_item"]) //Eject the item inside the destructive analyzer. - if(linked_destroy) - if(linked_destroy.busy) - to_chat(usr, SPAN_NOTICE("The destructive analyzer is busy at the moment.")) - - else if(linked_destroy.loaded_item) - linked_destroy.loaded_item.forceMove(linked_destroy.loc) - if(linked_destroy.Adjacent(usr)) - usr.put_in_hands(linked_destroy.loaded_item) - linked_destroy.loaded_item = null - linked_destroy.icon_state = "d_analyzer" - screen = 2.1 - - else if(href_list["deconstruct"]) //Deconstruct the item in the destructive analyzer and update the research holder. - if(linked_destroy) - if(linked_destroy.busy) - to_chat(usr, SPAN_NOTICE("The destructive analyzer is busy at the moment.")) - else - if(alert("Proceeding will destroy loaded item. Continue?", "Destructive analyzer confirmation", "Yes", "No") == "No" || !linked_destroy) - return - linked_destroy.busy = 1 - screen = 0.1 - updateUsrDialog() - flick("d_analyzer_process", linked_destroy) - spawn(24) - if(linked_destroy) - linked_destroy.busy = 0 - if(!linked_destroy.loaded_item) - to_chat(usr, SPAN_NOTICE("The destructive analyzer appears to be empty.")) - screen = 1.0 - return - - for(var/T in linked_destroy.loaded_item.origin_tech) - files.UpdateTech(T, linked_destroy.loaded_item.origin_tech[T]) - if(linked_lathe && linked_destroy.loaded_item.matter) // Also sends salvaged materials to a linked protolathe, if any. - SSmaterials.normalize_material_amounts(linked_lathe.materials) - for(var/t in linked_destroy.loaded_item.matter) - var/material = SSmaterials.material_to_path(t, FALSE) - if(material) - var/salvage_amount = min(linked_lathe.max_material_storage - linked_lathe.TotalMaterials(), linked_destroy.loaded_item.matter[t] * linked_destroy.decon_mod) - if(salvage_amount > 0) - SSmaterials.add_material_amount(linked_lathe.materials, material, salvage_amount) - - linked_destroy.loaded_item = null - for(var/obj/I in linked_destroy.contents) - for(var/mob/M in I.contents) - M.death() - qdel(M) - if(istype(I,/obj/item/stack/material))//Only deconsturcts one sheet at a time instead of the entire stack - var/obj/item/stack/material/S = I - if(S.get_amount() > 1) - S.use(1) - linked_destroy.loaded_item = S - else - qdel(S) - linked_destroy.icon_state = "d_analyzer" - else - if(!(I in linked_destroy.component_parts)) - qdel(I) - linked_destroy.icon_state = "d_analyzer" - - use_power_oneoff(linked_destroy.active_power_usage) - screen = 1.0 - updateUsrDialog() - - else if(href_list["lock"]) //Lock the console from use by anyone without tox access. - if(allowed(usr)) - screen = text2num(href_list["lock"]) - else - to_chat(usr, "Unauthorized Access.") - - else if(href_list["sync"]) //Sync the research holder with all the R&D consoles in the game that aren't sync protected. - screen = 0.0 - if(!sync) - to_chat(usr, SPAN_NOTICE("You must connect to the network first.")) - else - griefProtection() //Putting this here because I dont trust the sync process - addtimer(CALLBACK(src, PROC_REF(SyncTechs)), 30) - - else if(href_list["togglesync"]) //Prevents the console from being synced by other consoles. Can still send data. - sync = !sync - - else if(href_list["protolathe_category"]) - var/choice = tgui_input_list(usr, "Which category do you wish to display?", "Protolathe Categories", GLOB.designs_protolathe_categories+"All") - if(!choice) - return - protolathe_category = choice - updateUsrDialog() - - else if(href_list["imprinter_category"]) - var/choice = tgui_input_list(usr, "Which category do you wish to display?", "Printer Categories", GLOB.designs_imprinter_categories+"All") - if(!choice) - return - imprinter_category = choice - updateUsrDialog() - - else if(href_list["build"]) //Causes the Protolathe to build something. - if(linked_lathe) - var/path = text2path(href_list["build"]) - var/datum/design/D = files.known_designs[path] - linked_lathe.addToQueue(D) - screen = 3.1 - updateUsrDialog() - - else if(href_list["imprint"]) //Causes the Circuit Imprinter to build something. - if(linked_imprinter) - var/path = text2path(href_list["imprint"]) - var/datum/design/D = files.known_designs[path] - linked_imprinter.addToQueue(D) - screen = 4.1 - updateUsrDialog() - - else if(href_list["disposeI"]) //Causes the circuit imprinter to dispose of a single reagent (all of it) - linked_imprinter?.reagents.del_reagent(href_list["disposeI"]) - - else if(href_list["disposeallI"]) //Causes the circuit imprinter to dispose of all it's reagents. - linked_imprinter?.reagents.clear_reagents() - - else if(href_list["removeI"]) - linked_imprinter?.removeFromQueue(text2num(href_list["removeI"])) - - else if(href_list["disposeP"]) //Causes the protolathe to dispose of a single reagent (all of it) - linked_lathe?.reagents.del_reagent(href_list["dispose"]) - - else if(href_list["disposeallP"]) //Causes the protolathe to dispose of all it's reagents. - linked_lathe?.reagents.clear_reagents() - - else if(href_list["removeP"]) - linked_lathe?.removeFromQueue(text2num(href_list["removeP"])) - - else if(href_list["lathe_ejectsheet"] && linked_lathe) //Causes the protolathe to eject a sheet of material - SSmaterials.normalize_material_amounts(linked_lathe.materials) - var/material = SSmaterials.material_to_path(href_list["lathe_ejectsheet"], FALSE) - if(!material) - return - - var/num_sheets = min(text2num(href_list["amount"]), round((linked_lathe.materials[material] || 0) / SHEET_MATERIAL_AMOUNT)) - - if(num_sheets < 1) - return - - var/mattype = linked_lathe.getMaterialType(material) - if(!mattype) - return - - var/obj/item/stack/material/M = new mattype(linked_lathe.loc) - M.amount = num_sheets - SSmaterials.remove_material_amount(linked_lathe.materials, material, num_sheets * SHEET_MATERIAL_AMOUNT) - - else if(href_list["imprinter_ejectsheet"] && linked_imprinter) //Causes the protolathe to eject a sheet of material - SSmaterials.normalize_material_amounts(linked_imprinter.materials) - var/material = SSmaterials.material_to_path(href_list["imprinter_ejectsheet"], FALSE) - if(!material) - return - - var/num_sheets = min(text2num(href_list["amount"]), round((linked_imprinter.materials[material] || 0) / SHEET_MATERIAL_AMOUNT)) - - if(num_sheets < 1) - return - - var/mattype = linked_imprinter.getMaterialType(material) - if(!mattype) - return - - var/obj/item/stack/material/M = new mattype(linked_imprinter.loc) - M.amount = num_sheets - SSmaterials.remove_material_amount(linked_imprinter.materials, material, num_sheets * SHEET_MATERIAL_AMOUNT) - - else if(href_list["find_device"]) //The R&D console looks for devices nearby to link up with. - screen = 0.0 - spawn(10) - SyncRDevices() - screen = 1.7 - updateUsrDialog() - - else if(href_list["disconnect"]) //The R&D console disconnects with a specific device. - switch(href_list["disconnect"]) - if("destroy") - linked_destroy.linked_console = null - linked_destroy = null - if("lathe") - linked_lathe.linked_console = null - linked_lathe = null - if("imprinter") - linked_imprinter.linked_console = null - linked_imprinter = null - - else if(href_list["reset"]) //Reset the R&D console's database. - griefProtection() - var/choice = alert("R&D Console Database Reset", "Are you sure you want to reset the R&D console's database? Data lost cannot be recovered.", "Continue", "Cancel") - if(choice == "Continue") - screen = 0.0 - qdel(files) - files = new /datum/research(src) - spawn(20) - screen = 1.6 - updateUsrDialog() - - else if (href_list["print"]) //Print research information - screen = 0.5 - spawn(20) - var/obj/item/paper/PR = new/obj/item/paper - var/pname = "list of researched technologies" - var/info = "
[station_name()] Science Laboratories" - info += "

[ (text2num(href_list["print"]) == 2) ? "Detailed" : null] Research Progress Report

" - info += "report prepared at [worldtime2text()] station time

" - if(text2num(href_list["print"]) == 2) - info += GetResearchListInfo() - else - info += GetResearchLevelsInfo() - - PR.set_content_unsafe(pname, info) - print(PR, user = usr) - spawn(10) - screen = ((text2num(href_list["print"]) == 2) ? 5.0 : 1.1) - updateUsrDialog() - - updateUsrDialog() - return - /obj/structure/machinery/computer/rdconsole/proc/GetResearchLevelsInfo() var/dat dat += "" return dat -/obj/structure/machinery/computer/rdconsole/attack_hand(mob/user as mob) - if(stat & (BROKEN|NOPOWER)) +/obj/structure/machinery/computer/rdconsole/attack_hand(mob/user) + if(..()) return + ui_interact(user) - user.set_machine(src) - var/dat = "" - files.RefreshResearch() - switch(screen) //A quick check to make sure you get the right screen when a device is disconnected. - if(2 to 2.9) - if(linked_destroy == null) - screen = 2.0 - else if(linked_destroy.loaded_item == null) - screen = 2.1 - else - screen = 2.2 - if(3 to 3.9) - if(linked_lathe == null) - screen = 3.0 - if(4 to 4.9) - if(linked_imprinter == null) - screen = 4.0 +/obj/structure/machinery/computer/rdconsole/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "ResearchConsole", "Research and Development Console", 1200, 800) + ui.open() +/obj/structure/machinery/computer/rdconsole/proc/tgui_screen_name() switch(screen) - - //////////////////////R&D CONSOLE SCREENS////////////////// - if(0.0) - dat += "Updating Database..." - - if(0.1) - dat += "Processing and Updating Database..." - - if(0.2) - dat += "SYSTEM LOCKED

" - dat += "Unlock" - - if(0.3) - dat += "Constructing Prototype. Please Wait..." - - if(0.4) - dat += "Imprinting Circuit. Please Wait..." - - if(0.5) - dat += "Printing Research Information. Please Wait..." - - if(1.0) //Main Menu - dat += "Main Menu:
" - - if(1.1) //Research viewer - dat += "Main Menu || " - dat += "Print This Page
" - dat += "Current Research Levels:
" - dat += GetResearchLevelsInfo() - dat += "" - - if(1.2) //Technology Disk Menu - - dat += "Main Menu
" - dat += "Disk Contents: (Technology Data Disk)

" - dat += "" - - if(1.3) //Technology Disk submenu - dat += "
Main Menu || " - dat += "Return to Disk Operations
" - dat += "" - - if(1.4) //Design Disk menu. - dat += "Main Menu
" - dat += "" - - if(1.5) //Technology disk submenu - dat += "Main Menu || " - dat += "Return to Disk Operations
" - dat += "" - - if(1.6) //R&D console settings - dat += "Main Menu
" - dat += "R&D Console Setting:
" - dat += "" - - if(1.7) //R&D device linkage - dat += "Main Menu || " - dat += "Settings Menu
" - dat += "R&D Console Device Linkage Menu:
" - dat += "" - - ////////////////////DESTRUCTIVE ANALYZER SCREENS//////////////////////////// - if(2.0) - dat += "Main Menu
" - dat += "NO DESTRUCTIVE ANALYZER LINKED TO CONSOLE

" - - if(2.1) - dat += "Main Menu
" - dat += "No Item Loaded. Standing-by...

" - - if(2.2) - dat += "Main Menu
" - dat += "Deconstruction Menu
" - dat += "Name: [linked_destroy.loaded_item.name]
" - dat += "Origin Tech:" - dat += "" - if(!istype(linked_destroy.loaded_item, /obj/item/stack)) - dat += "
Deconstruct Item || " - else - dat += "
Deconstruct One In Stack || " - dat += "Eject Item" - - /////////////////////PROTOLATHE SCREENS///////////////////////// - if(3.0) - dat += "Main Menu
" - dat += "NO PROTOLATHE LINKED TO CONSOLE

" - - if(3.1) - dat += "Main Menu || " - dat += "View Queue || " - dat += "Material Storage || " - dat += "Chemical Storage
" - dat += "Protolathe Menu:
" - dat += "Material Amount: [linked_lathe.TotalMaterials()] cm3 (MAX: [linked_lathe.max_material_storage])
" - dat += "Chemical Volume: [linked_lathe.reagents.total_volume] (MAX: [linked_lathe.reagents.maximum_volume])
" - dat += "Category: [protolathe_category]
" - dat += "
" - dat += "" - dat += "
" - - if(3.2) //Protolathe Material Storage Sub-menu - dat += "Main Menu || " - dat += "Protolathe Menu
" - dat += "Material Storage

" - dat += "" - - if(3.3) //Protolathe Chemical Storage Submenu - dat += "Main Menu || " - dat += "Protolathe Menu
" - dat += "Chemical Storage

" - for(var/_R in linked_lathe.reagents.reagent_volumes) - var/singleton/reagent/R = GET_SINGLETON(_R) - dat += "Name: [R.name] | Units: [linked_lathe.reagents.reagent_volumes[_R]] " - dat += "(Purge)
" - dat += "Disposal All Chemicals in Storage
" - - if(3.4) // Protolathe queue - dat += "Main Menu || " - dat += "Protolathe Menu
" - dat += "Queue

" - if(!linked_lathe.queue.len) - dat += "Empty" - else - var/tmp = 1 - for(var/datum/design/D in linked_lathe.queue) - if(tmp == 1) - if(linked_lathe.busy) - dat += "1: [D.name]
" - else - dat += "1: [D.name] (Awaiting materials) Remove
" - else - dat += "[tmp]: [D.name] Remove
" - ++tmp - - ///////////////////CIRCUIT IMPRINTER SCREENS//////////////////// - if(4.0) - dat += "Main Menu
" - dat += "NO CIRCUIT IMPRINTER LINKED TO CONSOLE

" - - if(4.1) - dat += "Main Menu || " - dat += "View Queue || " - dat += "Material Storage || " - dat += "Chemical Storage
" - dat += "Circuit Imprinter Menu:

" - dat += "Material Amount: [linked_imprinter.TotalMaterials()] cm3
" - dat += "Chemical Volume: [linked_imprinter.reagents.total_volume]
" - dat += "Category: [imprinter_category]
" - dat += "
" - dat += "" - dat += "
" - - if(4.2) - dat += "Main Menu || " - dat += "Imprinter Menu
" - dat += "Chemical Storage

" - for(var/_R in linked_imprinter.reagents.reagent_volumes) - var/singleton/reagent/R = GET_SINGLETON(_R) - dat += "Name: [R.name] | Units: [linked_imprinter.reagents.reagent_volumes[_R]] " - dat += "(Purge)
" - dat += "Disposal All Chemicals in Storage
" - - if(4.3) - dat += "Main Menu || " - dat += "Circuit Imprinter Menu
" - dat += "Material Storage

" - dat += "" - - if(4.4) - dat += "Main Menu || " - dat += "Circuit Imprinter Menu
" - dat += "Queue

" - if(linked_imprinter.queue.len == 0) - dat += "Empty" - else - var/tmp = 1 - for(var/datum/design/D in linked_imprinter.queue) - if(tmp == 1) - dat += "1: [D.name]
" - else - dat += "[tmp]: [D.name] Remove
" - ++tmp - - ///////////////////Research Information Browser//////////////////// + if(0.0 to 0.9) + return "busy" + if(1.1) + return "levels" + if(1.2) + return "tech_disk" + if(1.4) + return "design_disk" + if(1.6, 1.7) + return "settings" + if(2.0 to 2.9) + return "analyzer" + if(3.0 to 3.9) + return "protolathe" + if(4.0 to 4.9) + return "imprinter" if(5.0) - dat += "Main Menu || " - dat += "Print This Page
" - dat += "List of Researched Technologies and Designs:
" - dat += GetResearchListInfo() + return "designs" + if(6.0 to 6.9) + return "mechfab" + return "main" - var/datum/browser/rdconsole = new(user, "rdconsole", "Research and Development Console", 850, 600) - rdconsole.add_stylesheet("rdconsole", 'html/browser/rdconsole.css') - rdconsole.set_content(dat) - rdconsole.open() +/obj/structure/machinery/computer/rdconsole/proc/tgui_screen_number(screen_name) + switch(screen_name) + if("main") + return 1.0 + if("levels") + return 1.1 + if("tech_disk") + return 1.2 + if("design_disk") + return 1.4 + if("settings") + return 1.6 + if("analyzer") + return linked_destroy?.loaded_item ? 2.2 : 2.1 + if("protolathe") + return length(get_fabricators(PROTOLATHE)) ? 3.1 : 3.0 + if("imprinter") + return length(get_fabricators(IMPRINTER)) ? 4.1 : 4.0 + if("mechfab") + return length(get_fabricators(MECHFAB)) ? 6.1 : 6.0 + if("designs") + return 5.0 + return 1.0 + +/obj/structure/machinery/computer/rdconsole/proc/can_open_tgui_screen(screen_name, mob/user) + if(screen_name in list("main", "levels", "designs", "protolathe", "imprinter", "mechfab")) + return TRUE + return allowed(user) || emagged + +/obj/structure/machinery/computer/rdconsole/proc/get_technology_tgui_data() + var/list/result = list() + for(var/tech_id in files.known_tech) + var/datum/tech/T = files.known_tech[tech_id] + if(T.level < 1) + continue + result += list(list( + "id" = tech_id, + "name" = T.name, + "description" = T.desc, + "level" = T.level, + "progress" = T.next_level_progress, + "threshold" = T.next_level_threshold, + "complete" = T.level >= 15 + )) + return result + +/obj/structure/machinery/computer/rdconsole/proc/get_design_category(datum/design/D, build_flag = 0) + if((build_flag & MECHFAB) && D.category) + return D.category + if(D.p_category) + return D.p_category + if(D.category) + return D.category + return "Misc" + +/obj/structure/machinery/computer/rdconsole/proc/get_design_tgui_data() + var/list/result = list() + for(var/path in files.known_designs) + var/datum/design/D = files.known_designs[path] + if(!D.build_path) + continue + result += list(list( + "path" = "[D.type]", + "name" = D.name, + "description" = D.desc, + "category" = get_design_category(D) + )) + return result + +/obj/structure/machinery/computer/rdconsole/proc/get_analyzer_tgui_data() + var/list/result = list("linked" = !!linked_destroy) + if(!linked_destroy) + return result + result["busy"] = linked_destroy.busy + if(!linked_destroy.loaded_item) + return result + result["item"] = list( + "name" = linked_destroy.loaded_item.name, + "stack" = istype(linked_destroy.loaded_item, /obj/item/stack), + "technologies" = list() + ) + var/list/techs = result["item"]["technologies"] + for(var/tech_id in linked_destroy.loaded_item.origin_tech) + var/datum/tech/T = files.known_tech[tech_id] + if(!T) + continue + techs += list(list( + "name" = T.name, + "item_level" = linked_destroy.loaded_item.origin_tech[tech_id], + "current_level" = T.level, + "progress" = T.next_level_progress, + "threshold" = T.next_level_threshold + )) + return result + +/obj/structure/machinery/computer/rdconsole/proc/get_stored_reagent_amount(datum/reagents/reagent_holder, reagent_id) + if(!reagent_holder || !reagent_id) + return 0 + + var/list/reagent_volumes = reagent_holder.reagent_volumes + + if(!islist(reagent_volumes)) + return 0 + + /* + * Use keys obtained directly from reagent_volumes. This avoids attempting + * to index the list with a design chemical identifier that may not be in + * precisely the same representation as the holder's key. + */ + for(var/stored_reagent_id in reagent_volumes) + if(stored_reagent_id == reagent_id || "[stored_reagent_id]" == "[reagent_id]") + return reagent_volumes[stored_reagent_id] || 0 + + return 0 + +/obj/structure/machinery/computer/rdconsole/proc/get_research_fabricator_data(build_flag) + var/list/fabricators = get_fabricators(build_flag) + if(!length(fabricators)) + return null + + var/obj/structure/machinery/r_n_d/fabricator/primary = fabricators[1] + var/list/job_queue = get_job_queue(build_flag) + var/list/stored_materials = linked_silo?.materials || list() + var/datum/reagents/reagent_holder = primary.uses_reagents ? primary.reagents : null + var/list/data = list( + "linked" = TRUE, + "materials" = list(), + "reagents" = list(), + "recipes" = list(), + "categories" = list("All"), + "queue" = list(), + "sheet_material_amount" = SHEET_MATERIAL_AMOUNT, + "maximum_material_storage" = linked_silo?.max_material_storage || 0, + "maximum_reagent_volume" = reagent_holder?.maximum_volume || 0, + "reagent_volume" = reagent_holder?.total_volume || 0, + "supports_manufacturers" = build_flag & MECHFAB, + "manufacturers" = list(), + "selected_manufacturer" = selected_mech_manufacturer, + "supports_reagents" = FALSE + ) + + SSmaterials.normalize_material_amounts(stored_materials) + for(var/material in stored_materials) + data["materials"] += list(list( + "id" = "[material]", + "name" = CallMaterialName(material), + "amount" = stored_materials[material], + "maximum" = linked_silo?.max_material_storage || 0 + )) + + if(reagent_holder) + for(var/reagent_type in reagent_holder.reagent_volumes) + data["reagents"] += list(list( + "id" = "[reagent_type]", + "name" = CallReagentName(reagent_type), + "amount" = reagent_holder.reagent_volumes[reagent_type] + )) + + if(build_flag & MECHFAB) + for(var/manufacturer_id in GLOB.fabricator_robolimbs) + var/datum/robolimb/robolimb = GLOB.fabricator_robolimbs[manufacturer_id] + data["manufacturers"] += list(list("id" = manufacturer_id, "name" = robolimb.company)) + + for(var/path in files.known_designs) + var/datum/design/design = files.known_designs[path] + if(!design.build_path || !(design.build_type & build_flag)) + continue + + var/category = get_design_category(design, build_flag) + data["categories"] |= category + var/list/resources = list() + var/list/requirements = list() + + for(var/material_id in design.materials) + var/material_path = SSmaterials.material_to_path(material_id, FALSE) + if(!material_path) + material_path = material_id + var/required_material = primary.get_required_material_amount(design, material_id) + var/stored_material = stored_materials[material_path] || 0 + var/material_name = CallMaterialName(material_id) + resources += "[required_material] [material_name]" + requirements += list(list("name" = material_name, "required" = required_material, "stored" = stored_material, "missing" = stored_material < required_material, "type" = "material")) + + if(primary.uses_reagents && length(design.chemicals)) + data["supports_reagents"] = TRUE + for(var/reagent_id in design.chemicals) + var/required_reagent = primary.get_required_reagent_amount(design, reagent_id) + var/stored_reagent = get_stored_reagent_amount(reagent_holder, reagent_id) + var/reagent_name = CallReagentName(reagent_id) + resources += "[required_reagent] [reagent_name]" + requirements += list(list("name" = reagent_name, "required" = required_reagent, "stored" = stored_reagent, "missing" = stored_reagent < required_reagent, "type" = "reagent")) + + var/can_build_anywhere = FALSE + var/fastest_time = null + for(var/obj/structure/machinery/r_n_d/fabricator/fabricator as anything in fabricators) + can_build_anywhere ||= fabricator.can_build(design) + var/machine_time = round(design.time / fabricator.production_speed) + if(isnull(fastest_time) || machine_time < fastest_time) + fastest_time = machine_time + + data["recipes"] += list(list( + "name" = design.name, + "description" = design.desc, + "design" = "[path]", + "category" = category, + "resources" = english_list(resources), + "requirements" = requirements, + "can_build" = can_build_anywhere, + "build_time" = fastest_time || design.time + )) + + var/index = 1 + for(var/datum/research_fabrication_job/job as anything in job_queue) + var/obj/structure/machinery/r_n_d/fabricator/machine = job.assigned_machine + var/build_time = machine ? round(job.design.time / machine.production_speed) : round(job.design.time / primary.production_speed) + var/remaining_time = machine?.build_callback_timer ? max(0, timeleft(machine.build_callback_timer)) : 0 + data["queue"] += list(list( + "index" = index, + "name" = job.design.name, + "build_time" = build_time, + "active" = !!machine, + "machine" = machine?.name, + "remaining_time" = remaining_time + )) + index++ + + return data + +/obj/structure/machinery/computer/rdconsole/proc/validate_fabricator_screen() + cleanup_fabricators() + switch(tgui_screen_name()) + if("protolathe") + if(!length(get_fabricators(PROTOLATHE))) + screen = 1.0 + if("imprinter") + if(!length(get_fabricators(IMPRINTER))) + screen = 1.0 + if("mechfab") + if(!length(get_fabricators(MECHFAB))) + screen = 1.0 + +/obj/structure/machinery/computer/rdconsole/ui_data(mob/user) + validate_fabricator_screen() + files.RefreshResearch() + var/list/data = list( + "manufacturer" = manufacturer, + "screen" = tgui_screen_name(), + "authorized" = allowed(user) || emagged, + "emagged" = emagged, + "network_sync" = sync, + "busy_message" = busy_message, + "loaded_disk" = null, + "queue_amount" = queue_amount, + "devices" = list( + "analyzer" = linked_destroy ? 1 : 0, + "protolathe" = length(get_fabricators(PROTOLATHE)), + "imprinter" = length(get_fabricators(IMPRINTER)), + "mechfab" = length(get_fabricators(MECHFAB)), + "silo" = linked_silo ? 1 : 0 + ) + ) + if(t_disk) + data["loaded_disk"] = list("type" = "technology", "name" = t_disk.name, "stored_name" = t_disk.stored?.name, "stored_description" = t_disk.stored?.desc, "stored_level" = t_disk.stored?.level) + else if(d_disk) + data["loaded_disk"] = list("type" = "design", "name" = d_disk.name, "stored_name" = d_disk.blueprint?.name, "stored_description" = d_disk.blueprint?.desc) + switch(data["screen"]) + if("levels") + data["technologies"] = get_technology_tgui_data() + if("designs") + data["designs"] = get_design_tgui_data() + if("tech_disk") + data["technologies"] = get_technology_tgui_data() + if("design_disk") + data["designs"] = get_design_tgui_data() + if("analyzer") + data["analyzer"] = get_analyzer_tgui_data() + if("protolathe") + data["fabricator"] = get_research_fabricator_data(PROTOLATHE) + if("imprinter") + data["fabricator"] = get_research_fabricator_data(IMPRINTER) + if("mechfab") + data["fabricator"] = get_research_fabricator_data(MECHFAB) + return data + +/obj/structure/machinery/computer/rdconsole/proc/eject_fabricator_material(obj/structure/machinery/r_n_d/machine, material_id, requested_sheets) + return linked_silo?.eject_material(material_id, requested_sheets) + +/obj/structure/machinery/computer/rdconsole/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) + . = ..() + if(.) + return + add_fingerprint(usr) + usr.set_machine(src) + switch(action) + if("set_screen") + var/new_screen = params["screen"] + if(!can_open_tgui_screen(new_screen, usr)) + to_chat(usr, SPAN_WARNING("Unauthorized access.")) + return + screen = tgui_screen_number(new_screen) + . = TRUE + if("back") + screen = 1.0 + . = TRUE + if("upload_disk") + if(t_disk?.stored) + files.AddTech2Known(t_disk.stored) + griefProtection() + . = TRUE + else if(d_disk?.blueprint) + files.AddDesign2Known(d_disk.blueprint) + griefProtection() + . = TRUE + if("clear_disk") + if(t_disk) + t_disk.stored = null + . = TRUE + else if(d_disk) + d_disk.blueprint = null + . = TRUE + if("eject_disk") + var/obj/item/disk/disk = t_disk || d_disk + if(disk) + disk.forceMove(loc) + usr.put_in_hands(disk) + t_disk = null + d_disk = null + screen = 1.0 + . = TRUE + if("copy_tech") + if(t_disk) + t_disk.stored = files.known_tech[params["id"]] + . = TRUE + if("copy_design") + if(d_disk) + var/path = text2path(params["design"]) + d_disk.blueprint = files.known_designs[path] + . = TRUE + if("analyzer_eject") + if(linked_destroy && !linked_destroy.busy && linked_destroy.loaded_item) + linked_destroy.loaded_item.forceMove(linked_destroy.loc) + if(linked_destroy.Adjacent(usr)) + usr.put_in_hands(linked_destroy.loaded_item) + linked_destroy.loaded_item = null + linked_destroy.icon_state = "d_analyzer" + . = TRUE + if("analyzer_deconstruct") + if(linked_destroy && !linked_destroy.busy && linked_destroy.loaded_item) + linked_destroy.busy = TRUE + screen = 0.1 + flick("d_analyzer_process", linked_destroy) + addtimer(CALLBACK(src, PROC_REF(finish_analyzer_deconstruction)), 24) + . = TRUE + if("fabricator_build") + var/design_text = params["design"] + if(!design_text) + return FALSE + + var/path = text2path(design_text) + var/datum/design/design = files.known_designs[path] + + if(!design) + to_chat(usr, SPAN_WARNING("The selected design could not be found in the research database.")) + return FALSE + + switch(tgui_screen_name()) + if("protolathe") + if(!(design.build_type & PROTOLATHE)) + return FALSE + + for(var/i in 1 to queue_amount) + protolathe_queue += new /datum/research_fabrication_job(design) + + if("imprinter") + if(!(design.build_type & IMPRINTER)) + return FALSE + + for(var/i in 1 to queue_amount) + imprinter_queue += new /datum/research_fabrication_job(design) + + if("mechfab") + if(!(design.build_type & MECHFAB)) + return FALSE + + for(var/i in 1 to queue_amount) + mechfab_queue += new /datum/research_fabrication_job(design, selected_mech_manufacturer) + + else + return FALSE + + dispatch_fabrication_jobs() + . = TRUE + if("fabricator_remove") + var/index = text2num(params["index"]) + switch(tgui_screen_name()) + if("protolathe") + . = remove_fabrication_job(protolathe_queue, index) + if("imprinter") + . = remove_fabrication_job(imprinter_queue, index) + if("mechfab") + . = remove_fabrication_job(mechfab_queue, index) + if("fabricator_eject") + var/amount = text2num(params["amount"]) + . = linked_silo?.eject_material(params["material"], amount) + if("fabricator_eject_custom") + if(!linked_silo) + return FALSE + var/material_id = params["material"] + var/available_sheets = floor(linked_silo.get_material_amount(material_id) / SHEET_MATERIAL_AMOUNT) + if(available_sheets < 1) + return FALSE + var/amount = input(usr, "How many sheets should be ejected?", "Material Ejection", 1) as null|num + if(isnull(amount)) + return TRUE + amount = clamp(round(amount), 1, available_sheets) + . = linked_silo.eject_material(material_id, amount) + if("fabricator_purge") + var/build_flag = tgui_screen_name() == "protolathe" ? PROTOLATHE : IMPRINTER + var/obj/structure/machinery/r_n_d/fabricator/fabricator = get_primary_fabricator(build_flag) + if(fabricator?.reagents) + fabricator.reagents.del_reagent(params["reagent"]) + . = TRUE + if("fabricator_purge_all") + var/build_flag = tgui_screen_name() == "protolathe" ? PROTOLATHE : IMPRINTER + var/obj/structure/machinery/r_n_d/fabricator/fabricator = get_primary_fabricator(build_flag) + if(fabricator?.reagents) + fabricator.reagents.clear_reagents() + . = TRUE + if("fabricator_manufacturer") + var/manufacturer_id = params["manufacturer"] + if(manufacturer_id in GLOB.fabricator_robolimbs) + var/datum/robolimb/robolimb = GLOB.fabricator_robolimbs[manufacturer_id] + selected_mech_manufacturer = robolimb.company + . = TRUE + if("set_queue_amount") + var/new_amount = clamp(text2num(params["amount"]), 1, 100) + if(new_amount in list(1, 5, 10)) + queue_amount = new_amount + return TRUE + if("set_custom_queue_amount") + var/new_amount = input(usr, "How many copies should be queued at once?", "Queue Amount", queue_amount) as null|num + if(isnull(new_amount)) + return TRUE + queue_amount = clamp(round(new_amount), 1, 100) + return TRUE + if("find_devices") + if(allowed(usr) || emagged) + busy_message = "Scanning for nearby research devices..." + screen = 0.2 + addtimer(CALLBACK(src, PROC_REF(finish_find_devices)), 5) + . = TRUE + if("disconnect") + switch(params["device"]) + if("analyzer") + if(linked_destroy) + linked_destroy.linked_console = null + linked_destroy = null + if("protolathe") + disconnect_fabricators(PROTOLATHE) + if("imprinter") + disconnect_fabricators(IMPRINTER) + if("mechfab") + disconnect_fabricators(MECHFAB) + if("silo") + linked_silo?.disconnect_console() + . = TRUE + if("toggle_sync") + if(allowed(usr) || emagged) + sync = !sync + . = TRUE + if("sync_network") + if((allowed(usr) || emagged) && sync) + griefProtection() + busy_message = "Synchronizing research database with the network..." + screen = 0.3 + addtimer(CALLBACK(src, PROC_REF(finish_network_sync)), 30) + . = TRUE + if("reset_database") + if(allowed(usr) || emagged) + griefProtection() + qdel(files) + files = new /datum/research(src) + . = TRUE + if("print_research") + var/detailed = !!params["detailed"] + var/obj/item/paper/PR = new /obj/item/paper + var/info = "
[station_name()] Science Laboratories

[detailed ? "Detailed" : ""] Research Progress Report

report prepared at [worldtime2text()] station time

" + info += detailed ? GetResearchListInfo() : GetResearchLevelsInfo() + PR.set_content_unsafe("list of researched technologies", info) + print(PR, user = usr) + . = TRUE + if(.) + SStgui.update_uis(src) + + +/obj/structure/machinery/computer/rdconsole/proc/finish_find_devices() + SyncRDevices() + busy_message = "Processing request. Please wait." + screen = 1.6 + SStgui.update_uis(src) + +/obj/structure/machinery/computer/rdconsole/proc/finish_network_sync() + SyncTechs() + busy_message = "Processing request. Please wait." + screen = 1.6 + SStgui.update_uis(src) + +/obj/structure/machinery/computer/rdconsole/proc/finish_analyzer_deconstruction() + if(!linked_destroy) + screen = 1.0 + return + linked_destroy.busy = FALSE + if(!linked_destroy.loaded_item) + screen = 1.0 + SStgui.update_uis(src) + return + for(var/T in linked_destroy.loaded_item.origin_tech) + files.UpdateTech(T, linked_destroy.loaded_item.origin_tech[T]) + if(linked_silo && linked_destroy.loaded_item.matter) + for(var/t in linked_destroy.loaded_item.matter) + linked_silo.add_material(t, linked_destroy.loaded_item.matter[t] * linked_destroy.decon_mod) + linked_destroy.loaded_item = null + for(var/obj/I in linked_destroy.contents) + for(var/mob/M in I.contents) + M.death() + qdel(M) + if(istype(I, /obj/item/stack/material)) + var/obj/item/stack/material/S = I + if(S.get_amount() > 1) + S.use(1) + linked_destroy.loaded_item = S + else + qdel(S) + else if(!(I in linked_destroy.component_parts)) + qdel(I) + linked_destroy.icon_state = linked_destroy.loaded_item ? linked_destroy.icon_state : "d_analyzer" + use_power_oneoff(linked_destroy.active_power_usage) + griefProtection() + screen = 1.0 + SStgui.update_uis(src) /obj/structure/machinery/computer/rdconsole/robotics name = "robotics R&D console" id = 1 req_access = list(ACCESS_ROBOTICS) allow_analyzer = FALSE + manufacturer = "hephaestus" circuit = /obj/item/circuitboard/robotics_console /obj/structure/machinery/computer/rdconsole/robotics/terminal diff --git a/code/modules/research/rdmachines.dm b/code/modules/research/rdmachines.dm index fb716cecdd2..fbcfbdcb7bb 100644 --- a/code/modules/research/rdmachines.dm +++ b/code/modules/research/rdmachines.dm @@ -1,6 +1,6 @@ //This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:33 -//All devices that link into the R&D console fall into thise type for easy identification and some shared procs. +//All devices that link into the R&D console fall into this type for easy identification and shared procs. /obj/structure/machinery/r_n_d name = "R&D device" @@ -15,3 +15,351 @@ /obj/structure/machinery/r_n_d/proc/getMaterialType(var/name) return SSmaterials.material_stack_type(name) + +/obj/structure/machinery/r_n_d/fabricator + /// The design build flag accepted by this machine. + var/build_type + + /// Whether this machine checks and consumes design chemicals. + var/uses_reagents = FALSE + + /// Whether completed products are placed one tile in front of the machine. + var/product_offset = FALSE + + /// Material cost multiplier. + var/mat_efficiency = 1 + + /// Fabrication speed multiplier. + var/production_speed = 1 + + /// Job currently assigned by the research console. + var/datum/research_fabrication_job/assigned_job + + /// Design currently being fabricated. + var/datum/design/current_design + + /// Timer ID for the active fabrication. + var/build_callback_timer + + /// Looping-sound datum selected by this machine type. + var/fabrication_loop_type + + /// Active looping-sound controller for this machine. + var/datum/looping_sound/fabrication_loop + + /// Whether this fabricator's construction loop is currently playing. + var/fabrication_audio_playing = FALSE + +/obj/structure/machinery/r_n_d/fabricator/Initialize(mapload, d, populate_components, is_internal) + . = ..() + + if(fabrication_loop_type) + fabrication_loop = new fabrication_loop_type(src) + +/obj/structure/machinery/r_n_d/fabricator/proc/start_fabrication_audio() + if(!fabrication_loop || fabrication_audio_playing) + return + + fabrication_loop.start() + fabrication_audio_playing = TRUE + +/obj/structure/machinery/r_n_d/fabricator/proc/stop_fabrication_audio() + if(!fabrication_loop || !fabrication_audio_playing) + return + + fabrication_loop.stop() + fabrication_audio_playing = FALSE + +/obj/structure/machinery/r_n_d/fabricator/power_change() + . = ..() + if((stat & NOPOWER) && build_callback_timer) + if(build_callback_timer) + deltimer(build_callback_timer) + pause_console_job() + +/obj/structure/machinery/r_n_d/fabricator/proc/is_available() + return !assigned_job && !build_callback_timer && !(stat & (NOPOWER | BROKEN)) && !panel_open + +/obj/structure/machinery/r_n_d/fabricator/proc/get_material_silo() + return linked_console?.linked_silo + +/obj/structure/machinery/r_n_d/fabricator/proc/get_required_material_amount(datum/design/design_to_check, material_id) + return round(design_to_check.materials[material_id] * mat_efficiency) + +/obj/structure/machinery/r_n_d/fabricator/proc/get_required_reagent_amount(datum/design/design_to_check, reagent_id) + return round(design_to_check.chemicals[reagent_id] * mat_efficiency) + +/obj/structure/machinery/r_n_d/fabricator/proc/has_required_materials(datum/design/design_to_check) + if(!design_to_check) + return FALSE + + var/obj/structure/machinery/r_n_d/material_silo/silo = get_material_silo() + if(!silo) + return FALSE + + for(var/material_id in design_to_check.materials) + if(!silo.has_material(material_id, get_required_material_amount(design_to_check, material_id))) + return FALSE + + return TRUE + +/obj/structure/machinery/r_n_d/fabricator/proc/has_required_reagents(datum/design/design_to_check) + if(!design_to_check) + return FALSE + if(!uses_reagents) + return TRUE + + for(var/reagent_id in design_to_check.chemicals) + if(!reagents?.has_reagent(reagent_id, get_required_reagent_amount(design_to_check, reagent_id))) + return FALSE + + return TRUE + +/obj/structure/machinery/r_n_d/fabricator/proc/can_build(datum/design/design_to_check) + return has_required_materials(design_to_check) && has_required_reagents(design_to_check) + +/obj/structure/machinery/r_n_d/fabricator/proc/canBuild(datum/design/design_to_check) + return can_build(design_to_check) + +/obj/structure/machinery/r_n_d/fabricator/proc/getLackingMaterials(datum/design/design_to_check) + var/obj/structure/machinery/r_n_d/material_silo/silo = get_material_silo() + var/list/missing = list() + + for(var/material_id in design_to_check.materials) + var/required_amount = get_required_material_amount(design_to_check, material_id) + var/stored_amount = silo?.get_material_amount(material_id) || 0 + if(stored_amount < required_amount) + missing += "[required_amount - stored_amount] [SSmaterials.material_display_name(material_id)]" + + if(uses_reagents) + for(var/reagent_id in design_to_check.chemicals) + var/required_amount = get_required_reagent_amount(design_to_check, reagent_id) + if(!reagents?.has_reagent(reagent_id, required_amount)) + var/singleton/reagent/reagent = GET_SINGLETON(reagent_id) + missing += reagent?.name || "unknown reagent" + + return english_list(missing) + +/obj/structure/machinery/r_n_d/fabricator/proc/can_produce_job(datum/research_fabrication_job/job) + if(!job?.design) + return FALSE + if(!(job.design.build_type & build_type)) + return FALSE + if(!has_required_reagents(job.design)) + return FALSE + if(length(job.reserved_materials)) + return TRUE + return has_required_materials(job.design) + +/obj/structure/machinery/r_n_d/fabricator/proc/reserve_job_materials(datum/research_fabrication_job/job) + if(!job?.design) + return FALSE + if(length(job.reserved_materials)) + return TRUE + + var/obj/structure/machinery/r_n_d/material_silo/silo = get_material_silo() + if(!silo) + return FALSE + + for(var/material_id in job.design.materials) + var/required_amount = get_required_material_amount(job.design, material_id) + if(!silo.has_material(material_id, required_amount)) + return FALSE + + for(var/material_id in job.design.materials) + var/required_amount = get_required_material_amount(job.design, material_id) + var/material = SSmaterials.material_to_path(material_id, FALSE) + if(!material) + material = material_id + SSmaterials.remove_material_amount(silo.materials, material, required_amount) + job.reserved_materials[material] = required_amount + + silo.update_linked_uis() + return TRUE + +/obj/structure/machinery/r_n_d/fabricator/proc/begin_console_job(datum/research_fabrication_job/job) + if(!job || !is_available() || !can_produce_job(job)) + return FALSE + if(!reserve_job_materials(job)) + return FALSE + + assigned_job = job + current_design = job.design + job.assigned_machine = src + on_job_started(job) + update_use_power(POWER_USE_ACTIVE) + build_callback_timer = addtimer(CALLBACK(src, PROC_REF(complete_console_job)), current_design.time / production_speed, TIMER_UNIQUE | TIMER_STOPPABLE) + start_fabrication_audio() + update_icon() + SStgui.update_uis(linked_console) + return TRUE + +/obj/structure/machinery/r_n_d/fabricator/proc/on_job_started(datum/research_fabrication_job/job) + return + +/obj/structure/machinery/r_n_d/fabricator/proc/complete_console_job() + build_callback_timer = null + + var/datum/research_fabrication_job/job = assigned_job + var/datum/design/design_to_build = job?.design + if(!job || !design_to_build) + clear_console_job() + return + + if(!has_required_reagents(design_to_build)) + visible_message(SPAN_WARNING("\The [src] stops because the required reagents are no longer available.")) + pause_console_job() + return + + consume_reagents(design_to_build) + use_fabrication_power(design_to_build) + fabricate_design(design_to_build, job) + job.reserved_materials.Cut() + + var/obj/structure/machinery/computer/rdconsole/console = linked_console + console?.finish_fabrication_job(job) + + /* + * Keep construction audio running while the console attempts to assign + * another job. This prevents the end and startup sounds from playing + * between consecutive items. + */ + clear_console_job(FALSE, FALSE) + console?.dispatch_fabrication_jobs() + + /* + * If dispatch did not assign another job to this machine, construction + * has actually ended and the loop can now stop. + */ + if(!assigned_job) + stop_fabrication_audio() + +/obj/structure/machinery/r_n_d/fabricator/proc/pause_console_job() + var/datum/research_fabrication_job/job = assigned_job + if(job) + job.assigned_machine = null + + assigned_job = null + current_design = null + build_callback_timer = null + stop_fabrication_audio() + update_use_power(POWER_USE_IDLE) + update_icon() + on_job_cleared() + + if(linked_console) + SStgui.update_uis(linked_console) + linked_console.dispatch_fabrication_jobs() + +/obj/structure/machinery/r_n_d/fabricator/proc/clear_console_job(clear_assignment = TRUE, stop_audio = TRUE) + if(clear_assignment && assigned_job) + assigned_job.assigned_machine = null + + assigned_job = null + current_design = null + build_callback_timer = null + + if(stop_audio) + stop_fabrication_audio() + + update_use_power(POWER_USE_IDLE) + update_icon() + on_job_cleared() + +/obj/structure/machinery/r_n_d/fabricator/proc/consume_reagents(datum/design/design_to_build) + if(!uses_reagents) + return + + for(var/reagent_id in design_to_build.chemicals) + reagents.remove_reagent(reagent_id, get_required_reagent_amount(design_to_build, reagent_id)) + +/obj/structure/machinery/r_n_d/fabricator/proc/use_fabrication_power(datum/design/design_to_build) + var/power_cost = active_power_usage + for(var/material_id in design_to_build.materials) + power_cost += round(design_to_build.materials[material_id] / 2) + use_power_oneoff(max(active_power_usage, power_cost)) + +/obj/structure/machinery/r_n_d/fabricator/proc/fabricate_design(datum/design/design_to_build, datum/research_fabrication_job/job) + intent_message(MACHINE_SOUND) + if(!design_to_build.build_path) + return + + var/atom/product_location = product_offset ? get_step(src, dir) : src + var/obj/new_item = design_to_build.Fabricate(product_location, src) + if(!new_item) + return + + if(!product_offset) + new_item.forceMove(loc) + + if(mat_efficiency != 1 && new_item.matter?.len) + for(var/material_id in new_item.matter) + new_item.matter[material_id] *= mat_efficiency + + apply_product_data(new_item, design_to_build, job) + on_product_completed(new_item, design_to_build, job) + +/obj/structure/machinery/r_n_d/fabricator/proc/apply_product_data(obj/new_item, datum/design/design_to_build, datum/research_fabrication_job/job) + return + +/obj/structure/machinery/r_n_d/fabricator/proc/on_product_completed(obj/new_item, datum/design/design_to_build, datum/research_fabrication_job/job) + return + + +/obj/structure/machinery/r_n_d/fabricator/proc/wake_console_dispatch() + linked_console?.dispatch_fabrication_jobs() + +/obj/structure/machinery/r_n_d/fabricator/proc/disconnect_console() + if(!linked_console) + return + var/obj/structure/machinery/computer/rdconsole/console = linked_console + if(build_callback_timer) + deltimer(build_callback_timer) + if(assigned_job) + assigned_job.assigned_machine = null + assigned_job = null + current_design = null + build_callback_timer = null + stop_fabrication_audio() + update_use_power(POWER_USE_IDLE) + update_icon() + linked_console = null + console.remove_fabricator(src) + +/obj/structure/machinery/r_n_d/fabricator/attackby(obj/item/attacking_item, mob/user) + if(user.a_intent == I_HURT) + return ..() + if(build_callback_timer) + to_chat(user, SPAN_NOTICE("\The [src] is busy. Please wait for the current operation to finish.")) + return TRUE + if(default_deconstruction_screwdriver(user, attacking_item)) + disconnect_console() + return TRUE + if(default_deconstruction_crowbar(user, attacking_item)) + return TRUE + if(default_part_replacement(user, attacking_item)) + return TRUE + if(istype(attacking_item, /obj/item/stack/material)) + to_chat(user, SPAN_NOTICE("Materials must be inserted into the linked research material silo.")) + return TRUE + return ..() + +/obj/structure/machinery/r_n_d/fabricator/Destroy() + if(build_callback_timer) + deltimer(build_callback_timer) + + stop_fabrication_audio() + QDEL_NULL(fabrication_loop) + + if(assigned_job) + assigned_job.assigned_machine = null + + if(linked_console) + linked_console.remove_fabricator(src) + + assigned_job = null + current_design = null + return ..() + +/obj/structure/machinery/r_n_d/fabricator/proc/on_job_cleared() + return diff --git a/code/unit_tests/map_tests.dm b/code/unit_tests/map_tests.dm index e9d5e25747e..d769829db1a 100644 --- a/code/unit_tests/map_tests.dm +++ b/code/unit_tests/map_tests.dm @@ -495,5 +495,45 @@ return test_status +/datum/unit_test/map_test/research_fabricator_connections + name = "MAP: Research fabricator connections" + +/datum/unit_test/map_test/research_fabricator_connections/start_test() + var/fabricator_count = 0 + var/console_count = 0 + var/fabricators_without_console = 0 + var/consoles_without_silo = 0 + var/test_status = UNIT_TEST_PASSED + + for(var/obj/structure/machinery/r_n_d/fabricator/fabricator in world) + fabricator_count++ + + var/obj/structure/machinery/computer/rdconsole/nearby_console = locate() in range(7, fabricator) + if(nearby_console) + continue + + fabricators_without_console++ + TEST_FAIL("[fabricator.name] at ([fabricator.x], [fabricator.y], [fabricator.z]) in [get_area(fabricator)] has no research console within 7 turfs.") + + for(var/obj/structure/machinery/computer/rdconsole/console in world) + if(QDELETED(console) || !is_station_level(console.z)) + continue + + console_count++ + + var/obj/structure/machinery/r_n_d/material_silo/nearby_silo = locate() in range(7, console) + if(nearby_silo) + continue + + consoles_without_silo++ + TEST_FAIL("[console.name] at ([console.x], [console.y], [console.z]) in [get_area(console)] has no material silo within 7 turfs.") + + if(fabricators_without_console || consoles_without_silo) + test_status = TEST_FAIL("\[[fabricators_without_console] / [fabricator_count]\] research fabricators lacked a nearby research console. \[[consoles_without_silo] / [console_count]\] research consoles lacked a nearby material silo.") + else + TEST_PASS("All [fabricator_count] research fabricators had a nearby research console, and all [console_count] research consoles had a nearby material silo.") + + return test_status + #undef SUCCESS #undef FAILURE diff --git a/html/changelogs/TimeForMoreTGUIInScience.yml b/html/changelogs/TimeForMoreTGUIInScience.yml new file mode 100644 index 00000000000..647341b840d --- /dev/null +++ b/html/changelogs/TimeForMoreTGUIInScience.yml @@ -0,0 +1,7 @@ +author: TheGreyWolf + +delete-after: True + +changes: + - rscadd: "Added a material silo and mech fabricators to the research console." + - rscadd: "The research console now uses TGUI." diff --git a/icons/obj/machinery/research.dmi b/icons/obj/machinery/research.dmi index 05f9dc1141c..a5c1345fe0a 100644 Binary files a/icons/obj/machinery/research.dmi and b/icons/obj/machinery/research.dmi differ diff --git a/maps/away/away_site/abandoned_industrial/abandoned_industrial_station.dmm b/maps/away/away_site/abandoned_industrial/abandoned_industrial_station.dmm index 66193d8b2cf..786d613b7ef 100644 --- a/maps/away/away_site/abandoned_industrial/abandoned_industrial_station.dmm +++ b/maps/away/away_site/abandoned_industrial/abandoned_industrial_station.dmm @@ -2283,6 +2283,14 @@ /obj/random/dirt_75, /turf/simulated/floor/airless, /area/abandoned_industrial_station/exterior) +"hr" = ( +/obj/random/tool, +/obj/random/dirt_75, +/obj/structure/machinery/computer/rdconsole/robotics{ + dir = 8 + }, +/turf/simulated/floor/tiled, +/area/abandoned_industrial_station/hangar) "ht" = ( /obj/effect/floor_decal/corner/beige{ dir = 5 @@ -6886,7 +6894,7 @@ /turf/simulated/floor/plating, /area/abandoned_industrial_station/bridge) "vX" = ( -/obj/structure/machinery/mecha_part_fabricator{ +/obj/structure/machinery/r_n_d/fabricator/mecha_part_fabricator{ dir = 4; req_access = null }, @@ -15992,9 +16000,7 @@ /obj/effect/floor_decal/spline/plain{ dir = 4 }, -/obj/random/tech_supply{ - pixel_x = 5 - }, +/obj/structure/machinery/r_n_d/material_silo, /turf/simulated/floor/carpet/rubber, /area/abandoned_industrial_station/hangar) "YN" = ( @@ -16098,6 +16104,9 @@ pixel_y = -16 }, /obj/random/dirt_75, +/obj/random/tech_supply{ + pixel_x = 5 + }, /turf/simulated/floor/carpet/rubber, /area/abandoned_industrial_station/hangar) "Zb" = ( @@ -52583,7 +52592,7 @@ kI eA gX gM -sw +hr Sq Zv bX diff --git a/maps/away/away_site/assunzione/port_volturno/port_volturno.dmm b/maps/away/away_site/assunzione/port_volturno/port_volturno.dmm index cccf5a98e5c..1d04a930851 100644 --- a/maps/away/away_site/assunzione/port_volturno/port_volturno.dmm +++ b/maps/away/away_site/assunzione/port_volturno/port_volturno.dmm @@ -7614,16 +7614,13 @@ /turf/simulated/floor/grass1/no_edge, /area/port_volturno/exterior) "fjA" = ( -/obj/structure/table/steel, -/obj/item/flashlight/headlights, -/obj/item/cell/hyper{ - pixel_x = -4; - pixel_y = -10 - }, /obj/item/stack/nanopaste{ pixel_x = 8; pixel_y = -14 }, +/obj/structure/machinery/computer/rdconsole/robotics/terminal{ + dir = 4 + }, /turf/simulated/floor/tiled, /area/port_volturno/interior/robotics) "fjK" = ( @@ -8700,6 +8697,9 @@ dir = 8 }, /obj/item/storage/toolbox/mechanical, +/obj/item/cell/hyper{ + pixel_y = 7 + }, /turf/simulated/floor/tiled, /area/port_volturno/interior/robotics) "fYD" = ( @@ -12462,6 +12462,7 @@ pixel_y = 8 }, /obj/item/clothing/gloves/yellow, +/obj/item/flashlight/headlights, /turf/simulated/floor/tiled, /area/port_volturno/interior/robotics) "iNR" = ( @@ -19093,7 +19094,7 @@ /turf/simulated/floor/tiled/cargo/brass/full, /area/port_volturno/interior/chapel) "ntu" = ( -/obj/structure/machinery/mecha_part_fabricator{ +/obj/structure/machinery/r_n_d/fabricator/mecha_part_fabricator{ req_access = list(219) }, /obj/structure/machinery/light{ @@ -21610,6 +21611,10 @@ }, /turf/simulated/floor/tiled/white, /area/port_volturno/interior/minimart) +"oWJ" = ( +/obj/structure/machinery/r_n_d/material_silo, +/turf/simulated/floor/tiled, +/area/port_volturno/interior/robotics) "oWV" = ( /obj/structure/lattice/catwalk/indoor, /turf/simulated/floor, @@ -33804,7 +33809,7 @@ /turf/simulated/floor/tiled/slate, /area/port_volturno/interior/stafylia) "xLh" = ( -/obj/structure/machinery/mecha_part_fabricator{ +/obj/structure/machinery/r_n_d/fabricator/mecha_part_fabricator{ req_access = list(219) }, /obj/random/dirt_75, @@ -138077,7 +138082,7 @@ rGy xWS agK dvD -bhN +oWJ bhN bhN bhN diff --git a/maps/away/away_site/big_derelict/bigderelict.dmm b/maps/away/away_site/big_derelict/bigderelict.dmm index 50449835e66..8f9d4b4b5b2 100644 --- a/maps/away/away_site/big_derelict/bigderelict.dmm +++ b/maps/away/away_site/big_derelict/bigderelict.dmm @@ -243,7 +243,7 @@ /turf/simulated/floor/tiled/white/airless, /area/derelict) "bm" = ( -/obj/structure/machinery/mecha_part_fabricator, +/obj/structure/machinery/r_n_d/fabricator/mecha_part_fabricator, /obj/effect/floor_decal/corner/purple/diagonal, /turf/simulated/floor/tiled/white/airless, /area/derelict) @@ -757,7 +757,7 @@ /turf/simulated/floor/tiled/dark, /area/derelict) "cx" = ( -/obj/structure/machinery/r_n_d/circuit_imprinter, +/obj/structure/machinery/r_n_d/fabricator/circuit_imprinter, /obj/effect/floor_decal/corner/purple/diagonal, /turf/simulated/floor/tiled/white/airless, /area/derelict) @@ -771,7 +771,7 @@ /turf/simulated/floor/tiled/white/airless, /area/derelict) "cA" = ( -/obj/structure/machinery/r_n_d/protolathe{ +/obj/structure/machinery/r_n_d/fabricator/protolathe{ icon_state = "protolathe_t"; stat = 1 }, @@ -1785,6 +1785,18 @@ /obj/structure/closet/crate/loot, /turf/simulated/floor/airless, /area/derelict) +"DN" = ( +/obj/effect/floor_decal/corner/purple/diagonal, +/obj/structure/machinery/computer/rdconsole/core{ + dir = 4 + }, +/turf/simulated/floor/tiled/white/airless, +/area/derelict) +"JN" = ( +/obj/effect/floor_decal/corner/purple/diagonal, +/obj/structure/machinery/r_n_d/material_silo, +/turf/simulated/floor/tiled/white/airless, +/area/derelict) (1,1,1) = {" fI @@ -29878,7 +29890,7 @@ bU bU bU bH -bU +DN dO dO fk @@ -30393,7 +30405,7 @@ bU bU bH bU -bU +JN dO fk dO diff --git a/maps/away/away_site/blueriver/blueriver.dmm b/maps/away/away_site/blueriver/blueriver.dmm index e4610b504ea..5d1bd4ca040 100644 --- a/maps/away/away_site/blueriver/blueriver.dmm +++ b/maps/away/away_site/blueriver/blueriver.dmm @@ -368,7 +368,7 @@ /turf/simulated/floor/tiled/white, /area/bluespaceriver/ship) "si" = ( -/obj/structure/machinery/r_n_d/protolathe, +/obj/structure/machinery/r_n_d/fabricator/protolathe, /obj/item/paper/blueriver/expedition_log_4, /turf/simulated/floor/tiled/white, /area/bluespaceriver/ship) @@ -402,11 +402,11 @@ /turf/simulated/floor/tiled/white, /area/bluespaceriver/ship) "tS" = ( -/obj/structure/closet/emcloset, +/obj/structure/machinery/r_n_d/material_silo, /turf/simulated/floor/tiled/white, /area/bluespaceriver/ship) "uw" = ( -/obj/structure/machinery/r_n_d/circuit_imprinter, +/obj/structure/machinery/r_n_d/fabricator/circuit_imprinter, /obj/item/ano_scanner, /turf/simulated/floor/tiled/white, /area/bluespaceriver/ship) diff --git a/maps/away/away_site/first_aurora/first_aurora.dmm b/maps/away/away_site/first_aurora/first_aurora.dmm index f15d92726ee..9319faf56c7 100644 --- a/maps/away/away_site/first_aurora/first_aurora.dmm +++ b/maps/away/away_site/first_aurora/first_aurora.dmm @@ -3102,7 +3102,7 @@ /turf/template_noop, /area/template_noop) "kJ" = ( -/obj/structure/machinery/r_n_d/circuit_imprinter, +/obj/structure/machinery/r_n_d/fabricator/circuit_imprinter, /turf/simulated/floor/tiled/white/airless, /area/derelict/hallway/southeast) "kK" = ( @@ -3248,7 +3248,7 @@ /turf/simulated/floor/tiled/white/airless, /area/derelict/hallway/southeast) "ll" = ( -/obj/structure/machinery/mecha_part_fabricator, +/obj/structure/machinery/r_n_d/fabricator/mecha_part_fabricator, /turf/simulated/floor/tiled/white/airless, /area/derelict/hallway/southeast) "lm" = ( @@ -7235,6 +7235,11 @@ /obj/effect/decal/cleanable/dirt, /turf/simulated/floor/tiled/white/airless, /area/derelict/hallway/southeast) +"CV" = ( +/obj/effect/decal/cleanable/dirt, +/obj/structure/machinery/r_n_d/material_silo, +/turf/simulated/floor/tiled/white/airless, +/area/derelict/hallway/southeast) "Da" = ( /obj/structure/grille, /obj/structure/window/reinforced, @@ -7252,6 +7257,12 @@ "FM" = ( /turf/template_noop, /area/space) +"Hn" = ( +/obj/structure/machinery/computer/rdconsole/robotics{ + dir = 8 + }, +/turf/simulated/floor/tiled/white/airless, +/area/derelict/hallway/southeast) "HF" = ( /obj/structure/door_assembly, /turf/simulated/floor/tiled/white/airless, @@ -50076,7 +50087,7 @@ BS BS kJ jd -BS +CV lX ik aa @@ -51362,7 +51373,7 @@ kp ij ll Aa -Aa +Hn ik aa aa diff --git a/maps/away/away_site/konyang/point_verdant/point_verdant.dmm b/maps/away/away_site/konyang/point_verdant/point_verdant.dmm index 186194d531f..8ef358577e0 100644 --- a/maps/away/away_site/konyang/point_verdant/point_verdant.dmm +++ b/maps/away/away_site/konyang/point_verdant/point_verdant.dmm @@ -1445,6 +1445,30 @@ /obj/item/stack/material/plasteel/full{ pixel_x = 7 }, +/obj/item/stack/material/glass/full{ + pixel_x = -7 + }, +/obj/item/stack/material/glass/full{ + pixel_x = -7 + }, +/obj/item/stack/material/glass/full{ + pixel_x = -7 + }, +/obj/item/stack/material/glass/full{ + pixel_x = -7 + }, +/obj/item/stack/material/steel/full{ + pixel_x = 7 + }, +/obj/item/stack/material/steel/full{ + pixel_x = 7 + }, +/obj/item/stack/material/steel/full{ + pixel_x = 7 + }, +/obj/item/stack/material/steel/full{ + pixel_x = 7 + }, /turf/simulated/floor/tiled, /area/point_verdant/interior/robotics) "aQV" = ( @@ -22353,7 +22377,7 @@ /turf/simulated/floor/wood, /area/point_verdant/interior/police) "mGn" = ( -/obj/structure/machinery/mecha_part_fabricator{ +/obj/structure/machinery/r_n_d/fabricator/mecha_part_fabricator{ req_access = list(219) }, /obj/structure/machinery/light{ @@ -24957,31 +24981,7 @@ /turf/simulated/floor/tiled, /area/point_verdant/interior/maint_janitorial) "olp" = ( -/obj/structure/table/rack/retail_shelf, -/obj/item/stack/material/steel/full{ - pixel_x = 7 - }, -/obj/item/stack/material/glass/full{ - pixel_x = -7 - }, -/obj/item/stack/material/glass/full{ - pixel_x = -7 - }, -/obj/item/stack/material/glass/full{ - pixel_x = -7 - }, -/obj/item/stack/material/glass/full{ - pixel_x = -7 - }, -/obj/item/stack/material/steel/full{ - pixel_x = 7 - }, -/obj/item/stack/material/steel/full{ - pixel_x = 7 - }, -/obj/item/stack/material/steel/full{ - pixel_x = 7 - }, +/obj/structure/machinery/r_n_d/material_silo, /turf/simulated/floor/tiled, /area/point_verdant/interior/robotics) "olq" = ( @@ -32321,7 +32321,7 @@ /turf/simulated/floor/exoplanet/water/shallow/konyang/no_smooth, /area/point_verdant/interior/offices/kaf) "sKK" = ( -/obj/structure/machinery/mecha_part_fabricator{ +/obj/structure/machinery/r_n_d/fabricator/mecha_part_fabricator{ req_access = list(219) }, /obj/random/dirt_75, @@ -41913,6 +41913,9 @@ /obj/structure/machinery/light{ dir = 4 }, +/obj/structure/machinery/computer/rdconsole/robotics{ + dir = 8 + }, /turf/simulated/floor/tiled, /area/point_verdant/interior/robotics) "yhQ" = ( diff --git a/maps/away/away_site/overgrown_mining_station/overgrown_mining_station.dmm b/maps/away/away_site/overgrown_mining_station/overgrown_mining_station.dmm index 8fc0f76dee6..964b1c3aa43 100644 --- a/maps/away/away_site/overgrown_mining_station/overgrown_mining_station.dmm +++ b/maps/away/away_site/overgrown_mining_station/overgrown_mining_station.dmm @@ -1147,7 +1147,7 @@ /area/overgrown_mining_station) "yw" = ( /obj/structure/platform, -/obj/structure/machinery/mecha_part_fabricator, +/obj/structure/machinery/r_n_d/fabricator/mecha_part_fabricator, /turf/simulated/floor/tiled/dark/full/airless, /area/overgrown_mining_station) "yy" = ( @@ -1770,6 +1770,10 @@ }, /turf/simulated/floor/tiled/airless, /area/overgrown_mining_station) +"Mc" = ( +/obj/structure/machinery/r_n_d/material_silo, +/turf/simulated/floor/tiled/dark/full/airless, +/area/overgrown_mining_station) "Mh" = ( /obj/structure/table/reinforced/wood, /obj/item/trash/can, @@ -2089,6 +2093,7 @@ /area/overgrown_mining_station) "Uu" = ( /obj/structure/platform, +/obj/structure/machinery/computer/rdconsole/robotics, /turf/simulated/floor/tiled/dark/full/airless, /area/overgrown_mining_station) "UA" = ( @@ -42777,7 +42782,7 @@ eK Xe Fg oj -oj +Mc qK Uj jm diff --git a/maps/away/away_site/racers/racers.dmm b/maps/away/away_site/racers/racers.dmm index bfe8f96076d..c4b9d990e7a 100644 --- a/maps/away/away_site/racers/racers.dmm +++ b/maps/away/away_site/racers/racers.dmm @@ -676,6 +676,10 @@ /obj/structure/machinery/atm, /turf/simulated/wall/rusty, /area/racers) +"cHq" = ( +/obj/structure/machinery/pipedispenser, +/turf/simulated/floor/plating, +/area/racers) "cJe" = ( /obj/structure/reagent_dispensers/cookingoil, /turf/simulated/floor/tiled/freezer{ @@ -864,7 +868,7 @@ /turf/simulated/floor/plating, /area/racers) "dNX" = ( -/obj/structure/machinery/mecha_part_fabricator, +/obj/structure/machinery/r_n_d/fabricator/mecha_part_fabricator, /obj/structure/machinery/light/small/red{ dir = 4 }, @@ -1369,13 +1373,7 @@ /turf/template_noop, /area/space) "gyV" = ( -/obj/item/stack/material/steel{ - amount = 50 - }, -/obj/item/stack/material/plasteel{ - amount = 10 - }, -/obj/structure/table/rack, +/obj/structure/machinery/r_n_d/material_silo, /turf/simulated/floor/plating, /area/racers) "gAI" = ( @@ -1395,6 +1393,16 @@ }, /turf/simulated/floor/holofloor/tiled, /area/racers) +"gOv" = ( +/obj/structure/table/rack, +/obj/item/stack/material/plasteel{ + amount = 10 + }, +/obj/item/stack/material/steel{ + amount = 50 + }, +/turf/simulated/floor/plating, +/area/racers) "gPD" = ( /obj/structure/largecrate/hoverpod, /obj/effect/floor_decal/industrial/outline/yellow, @@ -2936,6 +2944,13 @@ }, /turf/simulated/floor/carpet/rubber, /area/racers) +"okb" = ( +/obj/structure/machinery/atmospherics/portables_connector{ + dir = 1 + }, +/obj/item/laser_pointer/red, +/turf/simulated/floor/plating, +/area/racers) "okF" = ( /obj/effect/decal/cleanable/dirt, /obj/effect/decal/remains/robot, @@ -3049,8 +3064,9 @@ /turf/simulated/floor/plating, /area/racers) "oNS" = ( -/obj/structure/machinery/pipedispenser, -/obj/item/laser_pointer/red, +/obj/structure/machinery/computer/rdconsole/robotics{ + dir = 4 + }, /turf/simulated/floor/plating, /area/racers) "oQI" = ( @@ -41187,7 +41203,7 @@ dGi owK wUg vhm -iVB +okb oNS qWG iVB @@ -41955,7 +41971,7 @@ qiM xjB jzb xjB -xjB +gOv xjB xjB gyV @@ -42216,7 +42232,7 @@ qVo jjB xjB dNX -xjB +cHq hRn xjB mDd diff --git a/maps/away/scenarios/enviro_testing_facility/enviro_testing_facility_submaps_main.dmm b/maps/away/scenarios/enviro_testing_facility/enviro_testing_facility_submaps_main.dmm index 99b84128f0c..89200c1029c 100644 --- a/maps/away/scenarios/enviro_testing_facility/enviro_testing_facility_submaps_main.dmm +++ b/maps/away/scenarios/enviro_testing_facility/enviro_testing_facility_submaps_main.dmm @@ -1106,6 +1106,12 @@ /obj/random/tech_supply, /turf/template_noop, /area/template_noop) +"bcW" = ( +/obj/structure/machinery/computer/rdconsole/robotics{ + dir = 8 + }, +/turf/simulated/floor/tiled/dark, +/area/enviro_testing_facility/inside/engineering/mech_lab) "bde" = ( /obj/structure/bed/stool/chair, /obj/random/dirt_75, @@ -2445,6 +2451,12 @@ }, /turf/simulated/floor/tiled, /area/enviro_testing_facility/inside/control/offices) +"cqI" = ( +/obj/structure/machinery/r_n_d/fabricator/mecha_part_fabricator{ + dir = 8 + }, +/turf/simulated/floor/carpet/rubber, +/area/enviro_testing_facility/inside/engineering/mech_lab) "cqK" = ( /obj/effect/map_effect/marker/mapmanip/submap/extract/enviro_testing_facility/misc/science_storage, /turf/template_noop, @@ -3506,6 +3518,9 @@ /obj/random/dirt_75, /turf/simulated/floor/exoplanet/crystal, /area/template_noop) +"dtW" = ( +/turf/simulated/floor/tiled/dark/full, +/area/enviro_testing_facility/inside/engineering/mech_lab) "dup" = ( /obj/effect/floor_decal/industrial/hatch_door/red{ dir = 1 @@ -4020,7 +4035,7 @@ /turf/simulated/floor/tiled/dark, /area/enviro_testing_facility/inside/engineering/reactors) "dVz" = ( -/obj/structure/machinery/mecha_part_fabricator{ +/obj/structure/machinery/r_n_d/fabricator/mecha_part_fabricator{ dir = 8 }, /turf/simulated/floor/tiled/dark/full, @@ -5822,7 +5837,7 @@ /turf/simulated/floor/carpet/green, /area/enviro_testing_facility/inside/medical/chief_office) "fQy" = ( -/obj/structure/machinery/r_n_d/circuit_imprinter, +/obj/structure/machinery/r_n_d/fabricator/circuit_imprinter, /turf/simulated/floor/carpet/rubber, /area/enviro_testing_facility/inside/engineering/mech_lab) "fQJ" = ( @@ -6686,7 +6701,7 @@ /turf/simulated/floor/tiled/white, /area/enviro_testing_facility/inside/research/meeting) "gBG" = ( -/obj/structure/machinery/r_n_d/protolathe, +/obj/structure/machinery/r_n_d/fabricator/protolathe, /obj/effect/floor_decal/spline/plain{ dir = 4 }, @@ -10752,6 +10767,7 @@ pixel_x = 8; pixel_y = 11 }, +/obj/item/stack/material/steel/full, /turf/simulated/floor/carpet/rubber, /area/enviro_testing_facility/inside/engineering/mech_lab) "kIM" = ( @@ -10909,11 +10925,11 @@ /area/enviro_testing_facility/inside/research/rnd) "kSl" = ( /obj/effect/floor_decal/industrial/outline/yellow, -/obj/item/stack/material/steel/full, /obj/random/dirt_75, /obj/structure/machinery/alarm/north{ req_one_access = null }, +/obj/structure/machinery/r_n_d/material_silo, /turf/simulated/floor/plating, /area/enviro_testing_facility/inside/engineering/mech_lab) "kUe" = ( @@ -11587,7 +11603,7 @@ /turf/simulated/floor/tiled/dark, /area/enviro_testing_facility/inside/engineering/reactors) "lDw" = ( -/obj/structure/machinery/mecha_part_fabricator{ +/obj/structure/machinery/r_n_d/fabricator/mecha_part_fabricator{ dir = 4 }, /turf/simulated/floor/tiled/dark/full, @@ -11600,10 +11616,10 @@ /turf/simulated/floor/reinforced, /area/enviro_testing_facility/inside/security/armory) "lDP" = ( -/obj/structure/table/steel, /obj/effect/floor_decal/corner/brown{ dir = 5 }, +/obj/structure/machinery/r_n_d/material_silo, /turf/simulated/floor/tiled, /area/enviro_testing_facility/inside/engineering/mech_lab) "lDW" = ( @@ -15696,7 +15712,7 @@ /turf/simulated/floor/tiled/dark, /area/enviro_testing_facility/inside/engineering/reactors) "pMv" = ( -/obj/structure/machinery/r_n_d/circuit_imprinter, +/obj/structure/machinery/r_n_d/fabricator/circuit_imprinter, /obj/effect/floor_decal/spline/plain{ dir = 9 }, @@ -16463,7 +16479,7 @@ /turf/simulated/floor/tiled/dark, /area/enviro_testing_facility/inside/security/storage) "qDv" = ( -/obj/structure/machinery/r_n_d/protolathe, +/obj/structure/machinery/r_n_d/fabricator/protolathe, /turf/simulated/floor/carpet/rubber, /area/enviro_testing_facility/inside/engineering/mech_lab) "qDB" = ( @@ -17487,6 +17503,7 @@ dir = 1 }, /obj/random/dirt_75, +/obj/structure/machinery/computer/rdconsole/robotics, /turf/simulated/floor/plating, /area/enviro_testing_facility/inside/engineering/mech_lab) "rOm" = ( @@ -21384,10 +21401,6 @@ dir = 9 }, /obj/structure/table/steel, -/obj/item/clothing/under/medical_gown/white{ - pixel_x = 5; - pixel_y = 8 - }, /turf/simulated/floor/reinforced, /area/enviro_testing_facility/inside/research/electronics) "vKp" = ( @@ -39367,7 +39380,7 @@ duD pos kSl toG -dVz +dtW dVz jdE dVz @@ -39569,7 +39582,7 @@ duD pos rNI toG -ydD +cqI ydD ydD jTa @@ -42601,7 +42614,7 @@ pif orb lDw lDw -eWD +bcW lDw lDw gPy diff --git a/maps/away/scenarios/nuclear_silo/nuclear_silo.dmm b/maps/away/scenarios/nuclear_silo/nuclear_silo.dmm index 354ef16d7fe..5122d1e7fad 100644 --- a/maps/away/scenarios/nuclear_silo/nuclear_silo.dmm +++ b/maps/away/scenarios/nuclear_silo/nuclear_silo.dmm @@ -64,6 +64,7 @@ /area/nuclear_silo/outside/cave/bunker) "ai" = ( /obj/random/dirt_75, +/obj/structure/heavy_vehicle_frame, /turf/simulated/floor/carpet/rubber, /area/nuclear_silo/lower_level/maintenance/east/robotics_bay) "aj" = ( @@ -7357,6 +7358,7 @@ /obj/random/dirt_75, /obj/random/dirt_75, /obj/structure/machinery/atmospherics/pipe/simple/hidden/scrubbers, +/obj/structure/machinery/r_n_d/material_silo, /turf/simulated/floor/plating, /area/nuclear_silo/lower_level/maintenance/east/robotics_bay) "qs" = ( @@ -12427,7 +12429,7 @@ /turf/simulated/floor/exoplanet/barren, /area/nuclear_silo/outside/buildings/town) "Bu" = ( -/obj/structure/heavy_vehicle_frame, +/obj/structure/machinery/computer/rdconsole/robotics, /turf/simulated/floor/carpet/rubber, /area/nuclear_silo/lower_level/maintenance/east/robotics_bay) "Bv" = ( @@ -18489,7 +18491,7 @@ /turf/simulated/floor/plating, /area/nuclear_silo/lower_level/maintenance/engimed) "Pb" = ( -/obj/structure/machinery/mecha_part_fabricator, +/obj/structure/machinery/r_n_d/fabricator/mecha_part_fabricator, /turf/simulated/floor/plating, /area/nuclear_silo/lower_level/maintenance/east/robotics_bay) "Pc" = ( diff --git a/maps/away/ships/biesel/tcaf_corvette/tcaf_corvette.dmm b/maps/away/ships/biesel/tcaf_corvette/tcaf_corvette.dmm index 93eb4c0db5a..501dd42b5e1 100644 --- a/maps/away/ships/biesel/tcaf_corvette/tcaf_corvette.dmm +++ b/maps/away/ships/biesel/tcaf_corvette/tcaf_corvette.dmm @@ -2463,9 +2463,6 @@ dir = 8 }, /obj/effect/floor_decal/corner/brown/diagonal, -/obj/structure/machinery/atmospherics/pipe/simple/hidden/scrubbers{ - dir = 10 - }, /obj/structure/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 9 }, @@ -2474,6 +2471,9 @@ d2 = 8; icon_state = "1-8" }, +/obj/structure/machinery/atmospherics/unary/vent_scrubber/on{ + dir = 8 + }, /turf/simulated/floor/tiled/gunmetal, /area/tcaf_corvette/workshop) "iM" = ( @@ -3552,7 +3552,7 @@ /turf/simulated/floor/tiled, /area/tcaf_corvette/lounge) "mT" = ( -/obj/structure/machinery/mecha_part_fabricator{ +/obj/structure/machinery/r_n_d/fabricator/mecha_part_fabricator{ dir = 8; req_access = list(111) }, @@ -4320,6 +4320,7 @@ /obj/structure/machinery/mech_recharger, /obj/effect/floor_decal/industrial/outline/yellow, /obj/effect/decal/cleanable/dirt, +/obj/structure/reagent_dispensers/fueltank, /turf/simulated/floor/tiled/gunmetal, /area/tcaf_corvette/workshop) "pJ" = ( @@ -9973,12 +9974,11 @@ /obj/structure/machinery/alarm/south{ req_one_access = list(111) }, -/obj/structure/machinery/atmospherics/unary/vent_scrubber/on{ - dir = 1 - }, /obj/structure/platform{ dir = 8 }, +/obj/effect/floor_decal/industrial/outline/operations, +/obj/structure/machinery/r_n_d/material_silo, /turf/simulated/floor/tiled/gunmetal, /area/tcaf_corvette/workshop) "MG" = ( @@ -12331,10 +12331,11 @@ /turf/simulated/floor/tiled/dark, /area/tcaf_corvette/autocannon) "Wq" = ( -/obj/structure/reagent_dispensers/fueltank, -/obj/effect/floor_decal/industrial/outline/operations, -/obj/effect/decal/cleanable/dirt, /obj/structure/machinery/light/small, +/obj/effect/decal/cleanable/dirt, +/obj/structure/machinery/computer/rdconsole/robotics{ + dir = 8 + }, /turf/simulated/floor/tiled/gunmetal/full, /area/tcaf_corvette/workshop) "Wt" = ( diff --git a/maps/away/ships/golden_deep/golden_deep.dmm b/maps/away/ships/golden_deep/golden_deep.dmm index 737ba4217f9..f56af383b1f 100644 --- a/maps/away/ships/golden_deep/golden_deep.dmm +++ b/maps/away/ships/golden_deep/golden_deep.dmm @@ -2494,6 +2494,10 @@ /obj/item/assembly/prox_sensor{ pixel_x = 5 }, +/obj/item/paint_sprayer{ + pixel_y = 5; + pixel_x = 2 + }, /turf/simulated/floor/tiled/dark/full, /area/golden_deep/workshop) "kw" = ( @@ -7059,7 +7063,7 @@ /obj/effect/floor_decal/spline/fancy/wood{ dir = 1 }, -/obj/structure/machinery/mecha_part_fabricator{ +/obj/structure/machinery/r_n_d/fabricator/mecha_part_fabricator{ req_access = list(229, 217) }, /obj/random/dirt_75, @@ -9089,11 +9093,7 @@ /obj/effect/floor_decal/spline/fancy/wood{ dir = 10 }, -/obj/structure/table/reinforced/steel, -/obj/item/paint_sprayer{ - pixel_y = 5; - pixel_x = 2 - }, +/obj/structure/machinery/r_n_d/material_silo, /turf/simulated/floor/tiled/dark/full, /area/golden_deep/workshop) "LD" = ( @@ -12399,8 +12399,10 @@ /obj/effect/floor_decal/spline/fancy/wood{ dir = 8 }, -/obj/structure/machinery/mech_recharger, /obj/effect/floor_decal/industrial/outline/yellow, +/obj/structure/machinery/computer/rdconsole/robotics{ + dir = 4 + }, /obj/random/dirt_75, /turf/simulated/floor/tiled/dark/full, /area/golden_deep/workshop) diff --git a/maps/away/ships/himeo/himeo_patrol_ship_submaps.dmm b/maps/away/ships/himeo/himeo_patrol_ship_submaps.dmm index 4d301041ea5..2ec938ce8cb 100644 --- a/maps/away/ships/himeo/himeo_patrol_ship_submaps.dmm +++ b/maps/away/ships/himeo/himeo_patrol_ship_submaps.dmm @@ -217,7 +217,7 @@ /turf/simulated/floor/carpet/rubber, /area/template_noop) "kp" = ( -/obj/structure/closet/crate/loot, +/obj/structure/reagent_dispensers/fueltank, /turf/simulated/floor/carpet/rubber, /area/template_noop) "kE" = ( @@ -319,7 +319,7 @@ /turf/simulated/floor/carpet/rubber, /area/template_noop) "py" = ( -/obj/structure/machinery/mecha_part_fabricator{ +/obj/structure/machinery/r_n_d/fabricator/mecha_part_fabricator{ req_access = null; dir = 1 }, @@ -332,7 +332,6 @@ /turf/simulated/floor/carpet/rubber, /area/template_noop) "qm" = ( -/obj/structure/closet/crate/loot, /obj/random/dirt_75, /obj/structure/machinery/alarm/south{ req_access = list(253); @@ -342,6 +341,9 @@ dir = 1; level = 2 }, +/obj/structure/machinery/computer/rdconsole/robotics{ + dir = 1 + }, /turf/simulated/floor/carpet/rubber, /area/template_noop) "qx" = ( @@ -532,8 +534,8 @@ /turf/simulated/floor/carpet/rubber, /area/template_noop) "Ab" = ( -/obj/structure/reagent_dispensers/fueltank, /obj/random/dirt_75, +/obj/structure/machinery/r_n_d/material_silo, /turf/simulated/floor/carpet/rubber, /area/template_noop) "Ae" = ( @@ -920,12 +922,12 @@ /turf/simulated/floor/tiled/dark/full, /area/template_noop) "Vn" = ( -/obj/structure/closet/crate/drop, /obj/item/paint_sprayer, /obj/item/mmi/digital/posibrain, /obj/item/stack/nanopaste, /obj/item/stack/nanopaste, /obj/item/stack/nanopaste, +/obj/structure/closet/crate/loot, /turf/simulated/floor/carpet/rubber, /area/template_noop) "Wv" = ( diff --git a/maps/away/ships/iac/iac_rescue_ship.dmm b/maps/away/ships/iac/iac_rescue_ship.dmm index c1190809788..398c17de25b 100644 --- a/maps/away/ships/iac/iac_rescue_ship.dmm +++ b/maps/away/ships/iac/iac_rescue_ship.dmm @@ -6900,7 +6900,7 @@ /turf/simulated/floor/tiled/dark, /area/ship/iac_rescue_ship/custodial) "pHr" = ( -/obj/structure/machinery/mecha_part_fabricator{ +/obj/structure/machinery/r_n_d/fabricator/mecha_part_fabricator{ req_access = list(211) }, /obj/effect/floor_decal/spline/plain/cee{ @@ -7176,6 +7176,7 @@ "qpf" = ( /obj/structure/machinery/firealarm/west, /obj/effect/floor_decal/industrial/warning, +/obj/structure/machinery/r_n_d/material_silo, /turf/simulated/floor, /area/ship/iac_rescue_ship/machinist) "qsF" = ( @@ -7672,6 +7673,9 @@ pixel_x = 22; pixel_y = 4 }, +/obj/structure/closet/crate/bin{ + name = "trashbin" + }, /turf/simulated/floor/tiled, /area/ship/iac_rescue_ship/machinist) "rBa" = ( @@ -7767,9 +7771,6 @@ /turf/simulated/floor/tiled/dark/full, /area/ship/iac_rescue_ship/bridge) "rMI" = ( -/obj/structure/closet/crate/bin{ - name = "trashbin" - }, /obj/effect/floor_decal/corner/black{ dir = 5 }, @@ -7779,6 +7780,9 @@ /obj/structure/machinery/atmospherics/pipe/simple/hidden/black{ dir = 5 }, +/obj/structure/machinery/computer/rdconsole/robotics/terminal{ + dir = 4 + }, /turf/simulated/floor/tiled, /area/ship/iac_rescue_ship/machinist) "rNe" = ( diff --git a/maps/away/ships/konyang/kasf_ship/kasf_ship.dmm b/maps/away/ships/konyang/kasf_ship/kasf_ship.dmm index c1aef3b8141..1c0080f7a16 100644 --- a/maps/away/ships/konyang/kasf_ship/kasf_ship.dmm +++ b/maps/away/ships/konyang/kasf_ship/kasf_ship.dmm @@ -637,8 +637,6 @@ /area/ship/kasf_corvette/starboardthrust) "dY" = ( /obj/effect/floor_decal/industrial/outline/yellow, -/obj/structure/machinery/mech_recharger, -/obj/effect/decal/cleanable/blood/oil, /obj/structure/machinery/button/remote/blast_door{ dir = 4; pixel_x = 24; @@ -647,9 +645,8 @@ name = "Mechbay Garage Shutters"; req_one_access = list(218) }, -/mob/living/heavy_vehicle/premade/light/recon{ - desc = "A light and nimble recon exosuit bearing the colour scheme of the Konyang Armed Forces, evidently a hand-me-down from the Solarian Armed Forces."; - e_color = "#767e8c" +/obj/structure/machinery/computer/rdconsole/robotics/terminal{ + dir = 1 }, /turf/simulated/floor/carpet/rubber, /area/ship/kasf_corvette/engie) @@ -1096,6 +1093,11 @@ /obj/effect/floor_decal/industrial/outline/yellow, /obj/structure/machinery/mech_recharger, /obj/structure/machinery/light, +/mob/living/heavy_vehicle/premade/light/recon{ + desc = "A light and nimble recon exosuit bearing the colour scheme of the Konyang Armed Forces, evidently a hand-me-down from the Solarian Armed Forces."; + e_color = "#767e8c" + }, +/obj/effect/decal/cleanable/blood/oil, /turf/simulated/floor/carpet/rubber, /area/ship/kasf_corvette/engie) "gu" = ( @@ -5411,7 +5413,6 @@ /turf/simulated/floor/plating, /area/ship/kasf_corvette/armory) "EY" = ( -/obj/structure/table/steel, /obj/effect/floor_decal/corner/orange/full{ dir = 4 }, @@ -5425,13 +5426,8 @@ /obj/structure/cable{ icon_state = "1-2" }, -/obj/item/reagent_containers/weldpack{ - pixel_y = 14; - pixel_x = 6 - }, -/obj/item/storage/box/lights/mixed{ - pixel_y = 12; - pixel_x = -6 +/obj/structure/machinery/vending/robotics{ + req_access = null }, /turf/simulated/floor/tiled/dark/full, /area/ship/kasf_corvette/engie) @@ -6279,10 +6275,8 @@ /turf/simulated/wall/shuttle/space_ship/mercenary, /area/shuttle/kasf_shuttle) "Jn" = ( -/obj/structure/machinery/vending/robotics{ - req_access = null - }, /obj/effect/floor_decal/industrial/outline/operations, +/obj/structure/machinery/r_n_d/material_silo, /turf/simulated/floor/carpet/rubber, /area/ship/kasf_corvette/engie) "Jq" = ( @@ -6427,7 +6421,7 @@ /turf/simulated/floor/carpet/rubber, /area/ship/kasf_corvette/cic) "Ko" = ( -/obj/structure/machinery/mecha_part_fabricator{ +/obj/structure/machinery/r_n_d/fabricator/mecha_part_fabricator{ req_access = null }, /obj/structure/machinery/light{ @@ -8582,6 +8576,14 @@ icon_state = "0-2" }, /obj/item/blueprints, +/obj/item/storage/box/lights/mixed{ + pixel_y = 12; + pixel_x = -6 + }, +/obj/item/reagent_containers/weldpack{ + pixel_y = 14; + pixel_x = 6 + }, /turf/simulated/floor/tiled/dark/full, /area/ship/kasf_corvette/engie) "Xu" = ( diff --git a/maps/away/ships/lone_spacer/lone_spacer_submaps.dmm b/maps/away/ships/lone_spacer/lone_spacer_submaps.dmm index dae7b2bbb3d..ccee1cd86df 100644 --- a/maps/away/ships/lone_spacer/lone_spacer_submaps.dmm +++ b/maps/away/ships/lone_spacer/lone_spacer_submaps.dmm @@ -89,20 +89,8 @@ pixel_y = -1; req_access = list(243) }, -/obj/structure/table/rack/folding_table, -/obj/item/storage/box/masks{ - pixel_x = -6; - pixel_y = 12 - }, -/obj/item/storage/box/gloves{ - pixel_x = -6; - pixel_y = 1 - }, -/obj/item/stack/nanopaste{ - pixel_y = 4; - pixel_x = 8 - }, /obj/effect/decal/cleanable/dirt, +/obj/structure/machinery/r_n_d/material_silo, /turf/simulated/floor/tiled/dark/full, /area/shuttle/lone_spacer/storage) "k" = ( @@ -110,8 +98,10 @@ /obj/structure/machinery/light/small{ dir = 4 }, -/obj/structure/closet/crate/loot, /obj/effect/decal/cleanable/dirt, +/obj/structure/machinery/computer/rdconsole/robotics/terminal{ + dir = 1 + }, /turf/simulated/floor/tiled/dark/full, /area/shuttle/lone_spacer/storage) "l" = ( @@ -180,6 +170,18 @@ /obj/item/robot_parts/r_arm, /obj/effect/decal/cleanable/dirt, /obj/item/paint_sprayer, +/obj/item/storage/box/masks{ + pixel_x = -6; + pixel_y = 12 + }, +/obj/item/storage/box/gloves{ + pixel_x = -6; + pixel_y = 1 + }, +/obj/item/stack/nanopaste{ + pixel_y = 4; + pixel_x = 8 + }, /turf/simulated/floor/tiled/dark/full, /area/shuttle/lone_spacer/storage) "t" = ( @@ -384,7 +386,7 @@ /area/shuttle/lone_spacer/storage) "O" = ( /obj/effect/floor_decal/industrial/outline/operations, -/obj/structure/machinery/mecha_part_fabricator{ +/obj/structure/machinery/r_n_d/fabricator/mecha_part_fabricator{ req_access = null; dir = 1 }, @@ -435,6 +437,13 @@ /obj/effect/decal/cleanable/dirt, /turf/simulated/floor/tiled/dark/full, /area/shuttle/lone_spacer/storage) +"U" = ( +/obj/structure/sign/poster{ + pixel_x = 32 + }, +/obj/structure/closet/crate/loot, +/turf/simulated/floor/tiled/dark/full, +/area/shuttle/lone_spacer/storage) "W" = ( /obj/structure/sink/kitchen{ dir = 8; @@ -727,7 +736,7 @@ a a r W -B +U k a a diff --git a/maps/away/ships/pra/headmaster/headmaster_ship.dmm b/maps/away/ships/pra/headmaster/headmaster_ship.dmm index b82f5462152..8d7142c8287 100644 --- a/maps/away/ships/pra/headmaster/headmaster_ship.dmm +++ b/maps/away/ships/pra/headmaster/headmaster_ship.dmm @@ -1557,6 +1557,31 @@ temperature = 278.15 }, /area/headmaster_ship/hallway_lower) +"eW" = ( +/obj/effect/floor_decal/corner/red/diagonal, +/obj/random/dirt_75, +/obj/item/spaceflare{ + pixel_y = 3; + pixel_x = 6 + }, +/obj/item/spaceflare{ + pixel_y = 1; + pixel_x = -5 + }, +/obj/item/tank/hydrogen/shuttle{ + pixel_x = 3; + pixel_y = 8 + }, +/obj/item/tank/hydrogen/shuttle{ + pixel_x = -3; + pixel_y = 5 + }, +/obj/structure/table/rack, +/obj/effect/floor_decal/industrial/outline/grey, +/turf/simulated/floor/tiled/dark{ + temperature = 278.15 + }, +/area/headmaster_ship/hangar) "eZ" = ( /obj/structure/closet/crate/hydroponics, /obj/item/reagent_containers/glass/bucket, @@ -4984,6 +5009,14 @@ temperature = 278.15 }, /area/headmaster_ship/cic) +"sF" = ( +/obj/effect/floor_decal/corner/red/diagonal, +/obj/random/dirt_75, +/obj/structure/machinery/r_n_d/material_silo, +/turf/simulated/floor/tiled/dark{ + temperature = 278.15 + }, +/area/headmaster_ship/hangar) "sK" = ( /obj/structure/machinery/atmospherics/pipe/simple/hidden/scrubbers{ dir = 5 @@ -7632,25 +7665,10 @@ pixel_y = -32; stand_icon = null }, -/obj/structure/table/rack, -/obj/effect/floor_decal/industrial/outline/grey, -/obj/item/tank/hydrogen/shuttle{ - pixel_x = -3; - pixel_y = 5 - }, -/obj/item/tank/hydrogen/shuttle{ - pixel_x = 3; - pixel_y = 8 - }, -/obj/item/spaceflare{ - pixel_y = 1; - pixel_x = -5 - }, -/obj/item/spaceflare{ - pixel_y = 3; - pixel_x = 6 - }, /obj/random/dirt_75, +/obj/structure/machinery/computer/rdconsole/robotics/terminal{ + dir = 1 + }, /turf/simulated/floor/tiled/dark{ temperature = 278.15 }, @@ -10341,7 +10359,7 @@ }, /area/headmaster_ship/helm) "QS" = ( -/obj/structure/machinery/mecha_part_fabricator{ +/obj/structure/machinery/r_n_d/fabricator/mecha_part_fabricator{ dir = 8; req_access = list(209) }, @@ -46485,7 +46503,7 @@ jG aT us nA -My +eW pf rm QL @@ -46999,7 +47017,7 @@ aT aT BA kZ -My +sF pf nD QL diff --git a/maps/away/ships/sol/sol_frigate/sol_frigate.dmm b/maps/away/ships/sol/sol_frigate/sol_frigate.dmm index bc5865c92b2..41156617f27 100644 --- a/maps/away/ships/sol/sol_frigate/sol_frigate.dmm +++ b/maps/away/ships/sol/sol_frigate/sol_frigate.dmm @@ -1500,6 +1500,7 @@ /obj/effect/floor_decal/corner/orange{ dir = 5 }, +/obj/structure/machinery/computer/rdconsole/robotics, /turf/simulated/floor/tiled/dark, /area/ship/sol_frigate/Exo_Bay) "gD" = ( @@ -1652,6 +1653,9 @@ /obj/structure/cable/blue{ icon_state = "4-8" }, +/obj/structure/machinery/atmospherics/unary/vent_scrubber/on{ + dir = 4 + }, /turf/simulated/floor/tiled/dark, /area/ship/sol_frigate/Exo_Bay) "hk" = ( @@ -4019,7 +4023,7 @@ /turf/simulated/floor/tiled/white, /area/ship/sol_frigate/canteen) "rT" = ( -/obj/structure/machinery/mecha_part_fabricator, +/obj/structure/machinery/r_n_d/fabricator/mecha_part_fabricator, /obj/structure/cable/blue{ icon_state = "4-8" }, @@ -6478,12 +6482,10 @@ /area/ship/sol_frigate/sickbay) "Dn" = ( /obj/structure/extinguisher_cabinet/east, -/obj/structure/machinery/atmospherics/unary/vent_scrubber/on{ - dir = 1 - }, /obj/effect/floor_decal/corner/orange{ dir = 6 }, +/obj/structure/machinery/r_n_d/material_silo, /turf/simulated/floor/tiled/dark, /area/ship/sol_frigate/Exo_Bay) "Dr" = ( @@ -11012,12 +11014,12 @@ /obj/structure/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 4 }, -/obj/structure/machinery/atmospherics/pipe/simple/hidden/scrubbers{ - dir = 6 - }, /obj/structure/cable/blue{ icon_state = "4-8" }, +/obj/structure/machinery/atmospherics/pipe/simple/hidden/scrubbers{ + dir = 4 + }, /turf/simulated/floor/tiled/dark, /area/ship/sol_frigate/Exo_Bay) "Za" = ( diff --git a/maps/away/ships/sol/sol_ssrm/ssrm_ship.dmm b/maps/away/ships/sol/sol_ssrm/ssrm_ship.dmm index 58ad3d293c8..4cd67bd8657 100644 --- a/maps/away/ships/sol/sol_ssrm/ssrm_ship.dmm +++ b/maps/away/ships/sol/sol_ssrm/ssrm_ship.dmm @@ -2472,12 +2472,10 @@ "eWz" = ( /obj/structure/railing/mapped, /obj/effect/floor_decal/corner/brown/diagonal, -/obj/structure/machinery/atmospherics/unary/vent_scrubber/on{ - dir = 8 - }, /obj/structure/machinery/light{ dir = 1 }, +/obj/structure/machinery/r_n_d/material_silo, /turf/simulated/floor/tiled/dark, /area/ship/ssrm_corvette/garage) "eYX" = ( @@ -4288,6 +4286,9 @@ }, /obj/effect/floor_decal/industrial/outline/red, /obj/structure/reagent_dispensers/fueltank, +/obj/structure/machinery/atmospherics/unary/vent_scrubber/on{ + dir = 8 + }, /turf/simulated/floor/tiled/dark/full, /area/ship/ssrm_corvette/garage) "iOV" = ( @@ -4918,7 +4919,7 @@ dir = 4 }, /obj/effect/floor_decal/industrial/hatch/operations, -/obj/structure/machinery/mecha_part_fabricator{ +/obj/structure/machinery/r_n_d/fabricator/mecha_part_fabricator{ dir = 8; req_access = list(203) }, @@ -8895,6 +8896,10 @@ pixel_y = 12 }, /obj/item/robotanalyzer, +/obj/structure/machinery/cell_charger{ + pixel_y = 4; + pixel_x = 1 + }, /turf/simulated/floor/tiled/dark, /area/ship/ssrm_corvette/garage) "uyh" = ( @@ -10242,10 +10247,8 @@ dir = 4 }, /obj/effect/floor_decal/industrial/outline/grey, -/obj/structure/table/reinforced/steel, -/obj/structure/machinery/cell_charger{ - pixel_y = 4; - pixel_x = 1 +/obj/structure/machinery/computer/rdconsole/robotics/terminal{ + dir = 4 }, /turf/simulated/floor/tiled/dark, /area/ship/ssrm_corvette/garage) @@ -10376,12 +10379,14 @@ "xJQ" = ( /obj/effect/floor_decal/corner/brown/diagonal, /obj/structure/machinery/atmospherics/pipe/simple/hidden/supply, -/obj/structure/machinery/atmospherics/pipe/simple/hidden/scrubbers, /obj/structure/cable{ icon_state = "1-2"; d1 = 1; d2 = 2 }, +/obj/structure/machinery/atmospherics/pipe/manifold/hidden/scrubbers{ + dir = 8 + }, /turf/simulated/floor/tiled/dark, /area/ship/ssrm_corvette/garage) "xLV" = ( diff --git a/maps/random_ruins/exoplanets/konyang/abandoned/factory_robotics.dmm b/maps/random_ruins/exoplanets/konyang/abandoned/factory_robotics.dmm index 4280bdb2389..6cdb49922ff 100644 --- a/maps/random_ruins/exoplanets/konyang/abandoned/factory_robotics.dmm +++ b/maps/random_ruins/exoplanets/konyang/abandoned/factory_robotics.dmm @@ -149,6 +149,11 @@ /obj/structure/machinery/smartfridge, /turf/simulated/floor/foundation, /area/konyang/robotics_factory) +"jl" = ( +/obj/random/dirt_75, +/obj/structure/machinery/r_n_d/material_silo, +/turf/simulated/floor/sidewalk/dark, +/area/konyang/robotics_factory) "jm" = ( /obj/structure/machinery/light_switch{ dir = 4; @@ -219,6 +224,7 @@ "lN" = ( /obj/random/dirt_75, /obj/random/tech_supply, +/mob/living/simple_animal/thinbug, /turf/simulated/floor/sidewalk/dark, /area/konyang/robotics_factory) "lT" = ( @@ -343,8 +349,10 @@ /area/konyang/robotics_factory) "rq" = ( /obj/random/dirt_75, -/mob/living/simple_animal/thinbug, /obj/structure/machinery/light/broken, +/obj/structure/machinery/computer/rdconsole/robotics/terminal{ + dir = 1 + }, /turf/simulated/floor/sidewalk/dark, /area/konyang/robotics_factory) "rT" = ( @@ -723,7 +731,7 @@ /turf/simulated/floor/sidewalk/dark, /area/konyang/robotics_factory) "Ku" = ( -/obj/structure/machinery/mecha_part_fabricator, +/obj/structure/machinery/r_n_d/fabricator/mecha_part_fabricator, /turf/simulated/floor/foundation, /area/konyang/robotics_factory) "KC" = ( @@ -939,7 +947,7 @@ /area/konyang/robotics_factory) "XD" = ( /obj/random/dirt_75, -/obj/structure/machinery/mecha_part_fabricator, +/obj/structure/machinery/r_n_d/fabricator/mecha_part_fabricator, /turf/simulated/floor/sidewalk/dark, /area/konyang/robotics_factory) "XI" = ( @@ -1899,8 +1907,8 @@ eM SY tf BH -Us CF +jl SY cP se diff --git a/maps/random_ruins/exoplanets/ouerea/ouerea_nt_ruin.dmm b/maps/random_ruins/exoplanets/ouerea/ouerea_nt_ruin.dmm index 924322b7d10..09262c951dc 100644 --- a/maps/random_ruins/exoplanets/ouerea/ouerea_nt_ruin.dmm +++ b/maps/random_ruins/exoplanets/ouerea/ouerea_nt_ruin.dmm @@ -110,7 +110,7 @@ /obj/effect/floor_decal/corner/mauve/full{ dir = 4 }, -/obj/item/melee/baton/stunrod, +/obj/structure/machinery/r_n_d/material_silo, /turf/simulated/floor/exoplanet/tiled/white{ temperature = 298.15 }, @@ -123,6 +123,7 @@ dir = 6 }, /obj/random/dirt_75, +/obj/item/melee/baton/stunrod, /turf/simulated/floor/exoplanet/tiled/white{ temperature = 298.15 }, diff --git a/maps/runtime/runtime.dmm b/maps/runtime/runtime.dmm index 699b20d108b..07d0f2e36a0 100644 --- a/maps/runtime/runtime.dmm +++ b/maps/runtime/runtime.dmm @@ -734,7 +734,7 @@ /obj/effect/floor_decal/corner/mauve/full{ dir = 1 }, -/obj/structure/machinery/r_n_d/circuit_imprinter, +/obj/structure/machinery/r_n_d/fabricator/circuit_imprinter, /obj/effect/floor_decal/industrial/outline/yellow, /obj/effect/floor_decal/industrial/warning{ dir = 5 @@ -760,7 +760,7 @@ /obj/effect/floor_decal/corner/mauve{ dir = 5 }, -/obj/structure/machinery/mecha_part_fabricator{ +/obj/structure/machinery/r_n_d/fabricator/mecha_part_fabricator{ dir = 8 }, /obj/effect/floor_decal/industrial/outline/yellow, @@ -2766,7 +2766,7 @@ /obj/effect/floor_decal/industrial/warning{ dir = 1 }, -/obj/structure/machinery/r_n_d/protolathe, +/obj/structure/machinery/r_n_d/fabricator/protolathe, /turf/simulated/floor/tiled/white, /area/runtime/floor_one/main) "mh" = ( @@ -8322,6 +8322,7 @@ /obj/effect/floor_decal/industrial/warning{ dir = 4 }, +/obj/structure/machinery/r_n_d/material_silo, /turf/simulated/floor/tiled/white, /area/runtime/floor_one/main) "SB" = ( diff --git a/maps/sccv_horizon/sccv_horizon.dmm b/maps/sccv_horizon/sccv_horizon.dmm index 9f2b3defb6c..63420df6f8f 100644 --- a/maps/sccv_horizon/sccv_horizon.dmm +++ b/maps/sccv_horizon/sccv_horizon.dmm @@ -2600,6 +2600,14 @@ pixel_x = 10; pixel_y = -3 }, +/obj/item/clothing/glasses/safety/goggles/science{ + pixel_y = 10 + }, +/obj/item/reagent_containers/glass/beaker/sulphuric{ + pixel_y = 3 + }, +/obj/item/stack/material/glass/full, +/obj/item/stack/material/steel/full, /turf/simulated/floor/tiled/white, /area/horizon/rnd/lab) "apc" = ( @@ -3063,28 +3071,11 @@ /turf/simulated/floor/wood, /area/horizon/service/library) "ato" = ( -/obj/item/stack/material/lead{ - amount = 10 - }, -/obj/item/reagent_containers/weldpack, -/obj/item/portable_whiteboard, -/obj/item/paint_sprayer, -/obj/structure/closet/crate/tool, -/obj/item/storage/bag/circuits/basic, -/obj/item/integrated_electronics/wirer, -/obj/item/integrated_electronics/debugger{ - pixel_x = 2; - pixel_y = 7 - }, -/obj/item/stack/material/phoron{ - amount = 20 - }, -/obj/item/stack/material/uranium{ - amount = 10 - }, /obj/effect/floor_decal/spline/plain/red{ dir = 5 }, +/obj/structure/crate_shelf, +/obj/structure/closet/crate/tool/machinist, /turf/simulated/floor/carpet/rubber, /area/horizon/operations/machinist) "atp" = ( @@ -10460,7 +10451,7 @@ dir = 5 }, /obj/effect/floor_decal/industrial/hatch/yellow, -/obj/structure/machinery/r_n_d/protolathe, +/obj/structure/machinery/r_n_d/fabricator/protolathe, /turf/simulated/floor/carpet/rubber, /area/horizon/rnd/lab) "bot" = ( @@ -21224,7 +21215,7 @@ dir = 9 }, /obj/effect/floor_decal/industrial/hatch/yellow, -/obj/structure/machinery/r_n_d/circuit_imprinter, +/obj/structure/machinery/r_n_d/fabricator/circuit_imprinter, /obj/structure/railing/mapped{ dir = 8 }, @@ -76550,7 +76541,7 @@ }, /area/centcom/shared_dream) "jSK" = ( -/obj/structure/machinery/mecha_part_fabricator{ +/obj/structure/machinery/r_n_d/fabricator/mecha_part_fabricator{ dir = 8 }, /obj/effect/floor_decal/industrial/outline/red, @@ -96059,7 +96050,7 @@ /turf/simulated/floor/tiled/white, /area/horizon/rnd/xenobiology) "mtu" = ( -/obj/structure/machinery/mecha_part_fabricator{ +/obj/structure/machinery/r_n_d/fabricator/mecha_part_fabricator{ dir = 8 }, /obj/effect/floor_decal/industrial/outline/red, @@ -103545,16 +103536,7 @@ /turf/simulated/floor, /area/horizon/maintenance/deck_3/aft/port/far) "ntL" = ( -/obj/structure/table/standard, /obj/item/firing_pin/test_range, -/obj/item/clothing/glasses/safety/goggles/science{ - pixel_y = 10 - }, -/obj/item/stack/material/steel/full, -/obj/item/stack/material/glass/full, -/obj/item/reagent_containers/glass/beaker/sulphuric{ - pixel_y = 3 - }, /obj/structure/platform{ dir = 1 }, @@ -103566,6 +103548,7 @@ pixel_x = 28; pixel_y = -5 }, +/obj/structure/machinery/r_n_d/material_silo, /turf/simulated/floor/tiled/white, /area/horizon/rnd/lab) "ntU" = ( @@ -106467,7 +106450,7 @@ /turf/simulated/floor/wood, /area/horizon/service/mess_hall) "nMY" = ( -/obj/structure/machinery/r_n_d/protolathe, +/obj/structure/machinery/r_n_d/fabricator/protolathe, /obj/structure/machinery/camera{ c_tag = "Operations - Machinist Workshop 3"; network = list("Operations"); @@ -110171,10 +110154,10 @@ network = list("Operations"); dir = 1 }, -/obj/structure/crate_shelf, /obj/effect/floor_decal/spline/plain/red{ dir = 1 }, +/obj/structure/machinery/r_n_d/material_silo, /turf/simulated/floor/carpet/rubber, /area/horizon/operations/machinist) "omQ" = ( @@ -111490,10 +111473,20 @@ pixel_y = 4; randpixel = 0 }, -/obj/structure/closet/crate/drop, /obj/effect/floor_decal/spline/plain/red{ dir = 9 }, +/obj/item/stack/material/phoron{ + amount = 20 + }, +/obj/item/stack/material/uranium{ + amount = 10 + }, +/obj/structure/closet/crate/drop, +/obj/structure/crate_shelf, +/obj/item/stack/material/lead{ + amount = 10 + }, /turf/simulated/floor/carpet/rubber, /area/horizon/operations/machinist) "ovc" = ( @@ -182570,7 +182563,7 @@ /obj/structure/machinery/light/small{ dir = 8 }, -/obj/structure/machinery/r_n_d/circuit_imprinter, +/obj/structure/machinery/r_n_d/fabricator/circuit_imprinter, /obj/effect/floor_decal/industrial/outline/research, /turf/simulated/floor/tiled/dark/full, /area/horizon/operations/machinist) diff --git a/tgui/packages/tgui/interfaces/ResearchConsole.tsx b/tgui/packages/tgui/interfaces/ResearchConsole.tsx new file mode 100644 index 00000000000..a3e2bd1be0c --- /dev/null +++ b/tgui/packages/tgui/interfaces/ResearchConsole.tsx @@ -0,0 +1,578 @@ +import { + Box, + Button, + LabeledList, + NoticeBox, + ProgressBar, + Section, + Stack, + Table, +} from 'tgui-core/components'; +import type { BooleanLike } from 'tgui-core/react'; +import { useBackend, useLocalState } from '../backend'; +import { Window } from '../layouts'; +import { SearchBar } from './common/SearchBar'; +import { + ResearchFabricator, + type ResearchFabricatorData, +} from './ResearchFabricator'; + +type ResearchConsoleData = { + manufacturer: string; + screen: string; + authorized: BooleanLike; + emagged: BooleanLike; + network_sync: BooleanLike; + busy_message: string; + devices: { + analyzer: number; + protolathe: number; + imprinter: number; + mechfab: number; + silo: number; + }; + loaded_disk: LoadedDisk | null; + technologies?: Technology[]; + designs?: Design[]; + analyzer?: AnalyzerData; + fabricator?: ResearchFabricatorData; + queue_amount: number; +}; + +type LoadedDisk = { + type: 'technology' | 'design'; + name: string; + stored_name?: string; + stored_description?: string; + stored_level?: number; +}; + +type Technology = { + id: string; + name: string; + description: string; + level: number; + progress: number; + threshold: number; + complete: BooleanLike; +}; + +type Design = { + path: string; + name: string; + description: string; + category: string; +}; + +type AnalyzerData = { + linked: BooleanLike; + busy?: BooleanLike; + item?: { + name: string; + stack: BooleanLike; + technologies: AnalyzerTechnology[]; + }; +}; + +type AnalyzerTechnology = { + name: string; + item_level: number; + current_level: number; + progress: number; + threshold: number; +}; + +export const ResearchConsole = () => { + const { data } = useBackend(); + + return ( + + + {data.screen === 'main' && } + {data.screen === 'busy' && ( +
+ {data.busy_message} + + Please wait... + +
+ )} + {data.screen === 'levels' && ( + + )} + {data.screen === 'designs' && ( + + )} + {data.screen === 'tech_disk' && ( + + )} + {data.screen === 'design_disk' && ( + + )} + {data.screen === 'analyzer' && } + {data.screen === 'protolathe' && data.fabricator && ( + + )} + {data.screen === 'imprinter' && data.fabricator && ( + + )} + {data.screen === 'mechfab' && data.fabricator && ( + + )} + {data.screen === 'settings' && } +
+
+ ); +}; + +const BackButton = () => { + const { act } = useBackend(); + return ( + + + ))} + + ); +}; + +const Manufacturers = ({ + fabricator, +}: { + fabricator: ResearchFabricatorData; +}) => { + const { act } = useBackend(); + + return ( +
+ {fabricator.manufacturers?.map((manufacturer) => ( +
+ ); +}; + +const Queue = ({ fabricator }: { fabricator: ResearchFabricatorData }) => { + const { act } = useBackend(); + + return ( +
+ {fabricator.queue.length ? ( + + {fabricator.queue.map((entry) => ( + + act('fabricator_remove', { index: entry.index }) + } + /> + } + > + {entry.active ? ( + <> + {entry.machine ?? 'Fabricator'} + + {formatDuration(entry.remaining_time)} remaining + + + ) : ( + {formatDuration(entry.build_time)} + )} + + ))} + + ) : ( + The queue is empty. + )} +
+ ); +}; + +const formatDuration = (deciseconds: number) => { + const totalSeconds = Math.max(0, Math.ceil(deciseconds / 10)); + const minutes = Math.floor(totalSeconds / 60); + const seconds = totalSeconds % 60; + + if (minutes > 0) { + return `${minutes}:${seconds.toString().padStart(2, '0')}`; + } + + return `${seconds}s`; +}; diff --git a/tgui/packages/tgui/styles/interfaces/ResearchFabricator.scss b/tgui/packages/tgui/styles/interfaces/ResearchFabricator.scss new file mode 100644 index 00000000000..b49beadb2e0 --- /dev/null +++ b/tgui/packages/tgui/styles/interfaces/ResearchFabricator.scss @@ -0,0 +1,26 @@ +.ResearchFabricator { + &__recipeRow { + &--odd { + background-color: rgba(255, 255, 255, 0.035); + } + + &--even { + background-color: rgba(0, 0, 0, 0.035); + } + + &--odd:hover, + &--even:hover { + background-color: rgba(255, 255, 255, 0.08); + } + } + + &__requirement { + &--missing { + background-color: rgba(160, 20, 20, 0.18); + } + + &--available { + background-color: rgba(20, 120, 20, 0.12); + } + } +} diff --git a/tgui/packages/tgui/styles/main.scss b/tgui/packages/tgui/styles/main.scss index 8b346bffa7a..47204490112 100644 --- a/tgui/packages/tgui/styles/main.scss +++ b/tgui/packages/tgui/styles/main.scss @@ -60,6 +60,7 @@ @include meta.load-css('./interfaces/PersonalCrafting.scss'); @include meta.load-css('./interfaces/Radio.scss'); @include meta.load-css('./interfaces/LoreConsole.scss'); +@include meta.load-css('./interfaces/ResearchFabricator.scss'); // Layouts @include meta.load-css('./layouts/Layout.scss');