From 2f5d7e84228d8c40eacd73f325026308ea2e4834 Mon Sep 17 00:00:00 2001
From: Casper3667 <8396443+Casper3667@users.noreply.github.com>
Date: Thu, 6 Aug 2026 11:34:34 +0200
Subject: [PATCH] Upgrades the research console to TGUI and adds the material
silo (#22987)
This updates the research console to be a TGUI and fixes several bugs
with it.
It now uses a material silo for materials, while the acid for the
circuit imprinter is still given directly to the machine.
Mech fabricators now also need to be linked to a research console.
It is also capable of connecting to multiple fabricators and distribute
tasks between them.
The sprite originates from TG station.
Unit tests were also made to ensure there is no fabricators missing
consoles and no console missing material silos.
---
aurorastation.dme | 3 +-
.../circuitboards/machinery/research.dm | 6 +-
.../structures/crates_lockers/crates.dm | 5 +
.../structures/machinery/mecha_fabricator.dm | 506 ------
.../spacesuits/rig/modules/computer.dm | 6 +-
code/modules/item_worth/worths_list.dm | 2 +-
code/modules/research/circuitprinter.dm | 316 +---
.../research/designs/circuit/circuits.dm | 2 +-
.../designs/circuit/machine_circuits.dm | 2 +-
.../designs/mechfab/prosthetics/external.dm | 4 +-
.../research/designs/mechfab/robot/robot.dm | 4 +-
code/modules/research/material_silo.dm | 176 ++
code/modules/research/mecha_fabricator.dm | 156 ++
code/modules/research/protolathe.dm | 308 +---
code/modules/research/rdconsole.dm | 1495 +++++++++--------
code/modules/research/rdmachines.dm | 350 +++-
code/unit_tests/map_tests.dm | 40 +
html/changelogs/TimeForMoreTGUIInScience.yml | 7 +
icons/obj/machinery/research.dmi | Bin 21883 -> 23029 bytes
.../abandoned_industrial_station.dmm | 19 +-
.../port_volturno/port_volturno.dmm | 23 +-
.../away_site/big_derelict/bigderelict.dmm | 22 +-
maps/away/away_site/blueriver/blueriver.dmm | 6 +-
.../away_site/first_aurora/first_aurora.dmm | 19 +-
.../konyang/point_verdant/point_verdant.dmm | 57 +-
.../overgrown_mining_station.dmm | 9 +-
maps/away/away_site/racers/racers.dmm | 42 +-
.../enviro_testing_facility_submaps_main.dmm | 43 +-
.../scenarios/nuclear_silo/nuclear_silo.dmm | 6 +-
.../biesel/tcaf_corvette/tcaf_corvette.dmm | 21 +-
maps/away/ships/golden_deep/golden_deep.dmm | 16 +-
.../ships/himeo/himeo_patrol_ship_submaps.dmm | 12 +-
maps/away/ships/iac/iac_rescue_ship.dmm | 12 +-
.../ships/konyang/kasf_ship/kasf_ship.dmm | 36 +-
.../ships/lone_spacer/lone_spacer_submaps.dmm | 41 +-
.../ships/pra/headmaster/headmaster_ship.dmm | 60 +-
.../ships/sol/sol_frigate/sol_frigate.dmm | 16 +-
maps/away/ships/sol/sol_ssrm/ssrm_ship.dmm | 23 +-
.../konyang/abandoned/factory_robotics.dmm | 16 +-
.../exoplanets/ouerea/ouerea_nt_ruin.dmm | 3 +-
maps/runtime/runtime.dmm | 7 +-
maps/sccv_horizon/sccv_horizon.dmm | 65 +-
.../tgui/interfaces/ResearchConsole.tsx | 578 +++++++
.../tgui/interfaces/ResearchFabricator.tsx | 553 ++++++
.../styles/interfaces/ResearchFabricator.scss | 26 +
tgui/packages/tgui/styles/main.scss | 1 +
46 files changed, 3094 insertions(+), 2026 deletions(-)
delete mode 100644 code/game/objects/structures/machinery/mecha_fabricator.dm
create mode 100644 code/modules/research/material_silo.dm
create mode 100644 code/modules/research/mecha_fabricator.dm
create mode 100644 html/changelogs/TimeForMoreTGUIInScience.yml
create mode 100644 tgui/packages/tgui/interfaces/ResearchConsole.tsx
create mode 100644 tgui/packages/tgui/interfaces/ResearchFabricator.tsx
create mode 100644 tgui/packages/tgui/styles/interfaces/ResearchFabricator.scss
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 = "
z8BnH_U*ViZdIByX4jjbL_fQ3r!X9{Gn}{m{oxKdopT@htnC<4i@np6EeCT{P)t`pPJtQUO^Ph@I&ecc8KEkE}_Qm^L7S8J>T}9r1)zQ zR&mE?th-$qb!qfA?M1v83n${I4hBOC;dt3xx#REgqX-H!&aSAOr?0g4aPr*e6=OdB zWCm+~V3z*DVkKlgc5l`1KJRI0Di;zyUIWiHQG_f~+dCPn9w5z$v%Xcb)W?m+*3}T( z1C?LQHR^B@_%cN)kL$**(-=kS8TwE(cE+@8$%u{FOv`>6!!CZQuG9r2vFz-%NN$bi zQXLg-rezB^%?<&HWK|m>rTZqD-n7KV*K)v(OHu+c4U(j0Lx59hvw zcxhxY5aR=pqeldOpzo((UX?B*Ri6&lRX@?jY-P-e@9ialdcJjrchmI5Kh7gH_#F8? zFNg1uu68C@BlD*MGQl8kPc2G>Z9YEcM#HIboxycM8y2BTd8*_};@+hLj*M|P^kGvQ zV*=QwLd&i)R`b^{HP>`tDXMnPUF8YfuCv pF0Ogd>Of#-v}OgBr<*<* zT@VSuJvQ3dqydVtm)SSzK)EY+#?lBU9E>F&;D c ZRzS^`_Kv@j922(&v2DZ-**XZm+0O9_^<0ct#8rpWX_HF>mGQhK*7FK%)|=e0 zPP6{ow0S`jr?Lu$!kDBxCB(yinMNmtFp1FPW_}qC5b4)in+tYF CK&RDkw4h?R|f`Ss&tJfNAb%4tO{voGoKrjb5 zR8&~fYwPf&AdQg^#2@KHJ+Y45;Rck5Ce`{(hlmV3%|OgI3Ct)TC=Ck-%eK9IUpv`> z>!d8zc2~S0N9a8LyR%GdKjTgo +ctI#4JzIu#wgn6U=s4qavbe=V6*_$)B@8q<)uAKhTF4 ziV-S1@JcMUmJBHw_r;yL#v7}RIYBa+P TP+s3|>cr&1AmCN5)`7P$d|hK~K!CH5ecf5##CE zvrSpc`w261ennpI_BdDq< (AXtf@!d2XVCLI2qspzO=FU`m}MUK@n$kG+O(B3NwHf3Ngml&V#&WemW`8XjV z;nT*;BlJ|3LnHxtKq3M>G1Y4Xi=RvgVgLh0Fi78BJC@HT+YUT zuW`%?*5 zuq#5N^k#SNh|03PyuZd3tc*dH?UGqjw+Oh-R- w;2>qT&+S}+3M0x^u6m{o+3@!irlgLb@rhU@O_v2 z#9Od{0|YlV`iE#Z<;7pJZp`%^v?T#%;rH{YHG$Xhw>$-&a${s$Z?Nc+bgi$?wtuHT z&p&t&_(}@-ep-2k@Vs*8?UehEA7hcRREhyoRchrIecxej;I#wi$|J9pCXc6G!a|!t zziyrN0q+C>nq(BWm7+ybkW5JrYmX?#-Ix!ZR{VAI1|5@cW0VQNa4+dv6w wP7~Eu|QdXe3%Bj;BY$)#SS>@M2kP?8R<9NwL(4x+X(l=mX~q z=?;;|JK)Sr7-8^-f$J6Y$Gf4G>AOk_)-S`4aDc;Kb2x1|_`BFi)(IEkZw{D*3$R=Z zo|eB&T--payL2`gi!?}LK{2-7TVy{-SXHZgGe0!k?eFmYiGdFeL%NBT3_6VKtl-t( zgd?78bt5_9CE#^Oo1WBOvrOG|KgNHs(cy?i$T72!p_Rn0Jhbi5{hn%Ul6P4VyccJ= zA$7)4v$ZHL#@coA;J6|~7CcMl<0h3e8$1W@ix{EqiI|HzTBujJcEYITOVC8d>u2H* zRpP;9_$px#tb;$Gf3L?6=2N#uv+T2$JY!FQ8k^4FU8ti{1XL|=QH(om`s1m{ss4V+ zOLsYum 1beH7I9P}5Yf zVeLfzKN{+Tl&G(N%Tx>{W_?~Fc6PwN8LT**fgnS!QbUqpiGSk-|J_XgNRs{|q4@YU zg{~SjD}5 X5kfPvaR`9o*`$uUXsWJcg2 zDAzpG6ZXrmx%;Qy-HV)Zwm%~hQ=Cmy{)x1eYo_7fz_|QBX@NjVQ78Eq-veB)%=e9) zmX=@mtanfDGRE$~YYCNSrx|^U@3GUjH#AYJ!V0TK=TS1s$8PKlM}ND8N#PsWF^&Gl zjEU!~-1u)w8jfXXjCvSM@acIB*OgAPRtB!M52iqqXknbX1}7F3E?@nMu=J$boRt>e z{PmsTm~U4xU--;+Ok=*$VKVrXH ^~T zf&p>n(9O-3JP2s4n*^|pBaQYZYJq(0SnM8}yhRvpUj?}!ZAY3GRa&q@Nu`j;I%|t1 z!Mr>dPgkPeT;wWVRaiLxCDuRsZ7A=12Hy82gp8uaQ-tNl@l32P3n+$e91q3bWU<(5 zZ5+?Q>ac*q=7!_7E**PJvZR!Wrq`B-cF1)V&8c3K@Md zJDg%vRdajdCMxfS_K~J`7npnQ2~`BB%Tq3MOUZtA{|}i6%J@Li6s(MawLB(qb)9=( z6;<<&h;Ljss`oVcz394E4`OV^a>4YV0JPyYkp7!LUPPt;s6LI$v7kuk@9b8<(7c`> z#jWrVo^2fd>*}?pVY5G*qCj33 ?{(S;C=EhhYthGsN?@|ZzVQht$AuOG5%IP2(6PgS*GpTu;nAmQ18es zhq9}C|4kiERlQ )mZazJ>p= z(fmcIEAviP=>fd_=o4bOGY$L-x`|xm%udQ25!$;|p|WP`Ua^E;^6qqLS$>Hv89snH ziN1odMl=2YS}1a_e;V=>!gC;F&!!& uk%sUS^O@lAP C1qd@7O37Qu!?u;hg9d*VpmyT`>dsR&MOnNW)EL@zT0&`|- zapzGv+xNB;S7Sal_f)Ul{?R&m4|)Jh-S;gJgX6we^Vd0w*~EiNspa&Vf10i|uusBU zY}0CoidU7NmM{^3V1k+pB5*`-9}KnpohcM{>yItK*Rss$Vmz;cNwioly^MYHB(C&c z?sw2!yKOpuBOPeKZ&UKA#1p8$QS@2Z%Y52l1=hu>mG0~L4PAeB3U*W3d5ey?yyx>2 zy`HJXmTG=M7mxqhS7M8q395OlRukUu_7BSf`I0+#biB^{+-CSYy@fE`t9;NlIjmj; zKRqI4R$=~+Q*t!V>h3qWi~a2zs8o>Wb_MzVEVS~ ((d15uT}y(*TLqK9n05y7 Iw z`bUCGgH21qRZyoJ#SS5MFbBPxoV0SpR;x^3N&EBC;y^@Q`|EO{^;F5+!WMfBs1$nP zC?Y}wnk+p4a;|g$SPpD@0@`$vjP>10$9;%0qf@;&eJ`Zqo*?s&HM77#Fm%GrFCT7k z7&r#1kta<(xz}*^b`{nG5b4X-jpqS632M7~6eEW9>}oi{&2`wj^B#CaG@S#^e|y!( zxlxoF%7x$rCM3qa_S(nUud4sOV2swHmn45dsN02xmzS40>aVn68W$=?y*3R!XPtZf z?O1Q`GfO?4meQOYsK>ECFAfBO)8q2pZ0S3sYfZ9VO!CZ98my8UPP^JMEvKltgB+;S zfH`A0)z06)oB=NHPeCFHu9W#9{;BRPLyIqEfm&M6vfZ_B-V|uM_Gfi|B{DH7g7QRb z>ngF``7jM9d*s0{(hhy+>-M&{ht@|u+EwgZEDCLJ4cUZQKrUR|m@8)LW)~ suU;$WLo2=yVonoe%%EJ zF?D=JE-qT2##an}z+~H}8?)Ttv}G5JSs5`e@RKg1`v}QVrF{D(yhOmxmaz5=>X777 zG_jzGgP7sA(zDrp#In9Rp^6Wz7{*yfPJ6X(UDsBnrQv`K0ZRzLJ2rS^LtN(LNp6z1 zci`b(!i*Shc?n3t(WLof1GC|l feJF)kD2n2gd&M@=k zuER1xt#@NlQUqtu%INK%7K~6I`j3|Qp29;4F~Bn3rXL%Q82&IDd^P8GR`?bjlg#z? ztT!{+$YIFx1t@8RY@d};*GfY5YA)@N`@f^!1k-_AnSq~{#plJUTHxeO AZq5-q)0 QT@Hu3!8xNXz!DE{`cUAwDs2_U$m+ADoh+Z9VcY7`yNr#!8-00piZ>do|9Y z7Gl$a$^#3TW3qo+Jc*hQIiR*V`Dd^4mR&kkLZw^uiW-TilvkyJjX5R-|6dy!Ubymi zeYI)%aN?tBiDPG_LK4{T#M#8?B|KQQc!&V^82%I34E)^OWJ+AHsb4v%?#2&3_#KRH zw_4UVdBB3&|D|92kBIswGCDp@366}6w5g}YU3S5+i(eN(UvnZ{Y& 67%k)onJAH~xY*;Bo~~ X?5d-TXhvGUo}0G58b zV0nkWkLFa&OPm$ VCM$(An3QSyo#g&OJ5tUXKwZQ58Y_0Dxy~g0)78=V<`Y zy!jIX{g_IsN?8~CzR&@=_lj>SxZ+3GbIif)0OLGbj_Zhkr23+Qcn*^HRFn<(4gfW= zV%TTMnU(r4RW8ZmO{cvV*?}uD4SA?h;wz02#Oyk5tNRIYZWrqxq`DGNI{c*f6hdDT zuv~d^AGg^SYv}M6TBA$MB9OAT6_9{nF9!Lm9ed~msk}dUwk;)x|DA@3iOCP*Y>5+C z?Eo?AM_^%xOFL?;xog!0Gar)kp{hzSl6N({YEVwFTM8M2-EL2gQNal|rUeC5OuK)v zLX*`d+uB!R2J#T!3#v;!p8s%}25Q2EoA6vOk)w1p!{iTUKh33Cc1d=^daol*Q9q-j zO%(yxNr?GtB|W?C(oi};3|=#|3ss;=F0=gNMV}_%M7QG&P?v%k$o~W<4t&8pd3B`2 za)({NxaKhBg>_H@JK)$)F&3WD{oXJl?m^&&ITLe(Ilz&V0~I66uzcZ=RU#ikRbUw+ zZn+H9 @GU3xDasJbyaVHhbr;x=i>wdcc(OIT z%YLa(;RTLBx?;J^6`YP2UaUhRlwPd$PH7OY-i(CFJG%leUIwRZ yB`_{H_PW zbNP||?EKCSXqXjnRWQ<#{Z#O^nI*I=ZDFC5tCZjN3vG7^6B&2q>Fm@=64HXQ )dos3rz?Kr!o+D^_*C5E`RvoX};Hc2eLz z>1N<{%HF$69t@|QM--(;3QpRjgxJU@$=R_Tk+N|7{PayGV!q3m_V%P+lV~k!=M?)@ zoX^trTtZjI*=eQUk3apPsQUG edb9p z!Do@=*Sf}oW6R0Z356Jt9!W}`qm77QIIWC3u4i}{F&w-L0FTwwJnV!S>A$uhd7kPa zZjX*`G=VZ!=YHq+o><+~{UB{s3n9yV Ru5eWX%00Z%a*RMb7 +seiYq=(cEX;EF z4=H( >vB| z%}d_Uo#fu`zg1(~78=k?E4Ty>(;@5BQW07AXfAyFC}1 rGma1Ab7yJ>4eE9uqpH*6sTmkeE=XEeg>O$`#_9 zPkR&G>aE#vnO52SbtFM(C8kQhG18qT3B#~hPes(B8@0MbXHi(zwvn*g@FjdEwi+XM zt{=;NRihUDikN}}hl*JxD&8Mipb6%t%n8(+5`UET(gA~5Ok~byh83ltwQrA)Hh0H& zCpWj8Zbwfges5^rzTJ4Zs3f+pgpYn3dZWhH|Ik9`*ch8~4g?lN(qwNUdzX=S{!brQ z9u8&Ow{Ig`s<9+Wk0l~mOJr>jAtXI^F;q_|S$bluGlWWsk|l~E*<+&Y%UGiD6e2r= zdF;#3U<|Xo*YwnPyx(_x@B7C+$IKjaT=#w5*FEQX{?6<7`)x)*X#`qPAT#RHa_XcC z{mkk>T*@Nm`b+#GX`Rd!4)mYO4QnMR6JYRky}@$j-F&KzQODb?$-RHQq{@v+(o>0+ zCXIXd`n=wIAs&Er65mU!>7~C|c~SFS*YPz9*_7RC_`>eP$J-5jOhES;B2U%7$;F$; zP1Lfwt;~?)EMPCs{Sja9^x#}))+@iBw{yH~VPTP9Sojd0u7|hB%Ud>=eb3n&9aod} zqrkLF3!fD7B R2wP+4m<0{P?iu;_&9k7HAIs!*s2T)Q$RhHPzZ9c&1zbr=1g-1?zkZIAx9b~M>J z)&YQ?ALQY&AjJ}oV_8T=Mas)k)ipKSkO<|#yO5`8EGPpj0m?hR@;b`BDJp`eKkI@S zWJRQ!rNEpj+uAxiRJI#KMAVK82iLbIW?Z=rJ27oHSjAtptiHy0EE 1q|IQD%phd3YAbF ztvV5~U%CTF1FT^}gFW~z>k~5VaXAq=w7DRj@u>^5*l!>94Pnr;L0O~_jJ9@&qz1!p_Vjd9Klp zp0}U6kowM)zk^(M=-YJELDq#5Ny aWnS*zN0H)8jfDdlwd_t;x!^&Q<$3BlGj~O+>v$Nw5mK>4wUi zy#70R=oH@^#_sl!77wRAn_c3o6biBeJTVML=<1HDH(ODyvpgmHD2mI@(k!DyUqvJq zakiJQI1lfk`}mws&Awc^?LvXtSOKCM&1u=vGkQR4q{)`PaRycY$FtdyHTDAkzL9;w zBh3YbE&?Wg0<0w%nv=H8GnfsT6MNv@r^M)x1?!D~7!9Ct6Zi(rWOUwR{Vw6Y!`6=3 zmg+q-W#>cbn&jWrUzMLb+ae0~eTwDadB1KDeoM`X1#OuH1w0n?+2&^i`98FKzNVK) zV2iJZj2=rTyrVBr9ZB!%fhr{R;f8qRsmJt4`I$3zLTN2l*|+!JpJy3fss!XWLX%-C zu8v|x2Xd_dk;5_C)AMvQC-Yk9^d|`_cM~h@WlpThR)*4Tfu2MySbDN`rKxrLMuYYk zTU7~bNm7L4>FxoJENBov%=kMQC(6hgfIoh$6MU )iG7I zbQZ|&^!ns-0R1yG#Ui8H8zHRzWh5lRbreQgSZL8E)d3`Pm;_|vcv|)m%VD74kdn^4 zql+3~I*G_1V)&C@057OhG)h=KcHV%S-_+l~e2I_y={+~zK%k?ovZ)|wUt70^!0Co8 zx!b*g5y%SWLGQQz zrsjTOj;UVK$e%iH)wstyDhPCnO``bSAq@6u0|wIwHp9JGCoE8VObT2e>uwxX0}$ zstQ|Af+O{YqM*S^UZ7S_?m9|^nV2>}DsBb`#kM 8z|cGwA2TV;-d$V zV6LZKo1;#Gkouu1#F=3nLdYOi`EN=e<`y%zKcjh&ykAW+^}aW3WnsZA LKFz zs{ri+uqTS8-6YXW*Hwi@5M}|FFgH;nh0*q9E76J6^YUAPDEMx&@svEWMmj4rJm&cm z^CL&uicm5bt$;n;(VW-4N4eizJ}9az-{Jun!zTD4YzFoJYX2A<^J`9He{rc_>Il-7 z{w>fNgUhFO*jeomy4+Yd<0Ow@!a?cF?6v_if6n}M@HuFM(&+m!E)gb>{LK}xf_+_} z-*Curf4W&po4^x+`&u&m=yK(tW6k)h##hEtlq}>3zvHJytN88TAKP?U-?Tipp_@2m zd7Pk#zuUh-xYjptm$epXMf>=eoE&>U>ZO)HxFxTgSc%GE#S%s1Y=p4Nqdg!lQCN c3JeZ-(Iv29SDe-gzni=N(A z203gh7eH2 ~qqD1gO;7SSQA|*$v;n)5NRz z1tblR%y^%@^nb&S)<@}?e9VrdG=Je4yN{AzaW7YFWZj;kTT?=zBON}l)ASeU+Pxe` zMm)HZXZ3ShLys!Qz0c8)s) {*y}<#8)EWFdKGVHPKZu(0+s4W5o*=u|817nS_wz%woI-JaqbW zUSKrKPn7Vhp2o~IVA}+5JIvdU1`*EIbTXDoH)_a^PEJH(G*!y#vc{pT$Q@#@lEb TG*c afN#B6&5Tr+23HS Q}w 1TBvd06uM-E(|0nQ~So-#(q zcYm)$g+5?i1irkE9g=IO%niJ tIt! o?6= z9aB^^@|-te74e!RG7jp=`ObAqc1{;MHP&v{?Uq;zIEr%vX)9s3LEXxfk5c^|&pR`J zSq3TH@(5%HMI36=_f0{u#5#svcC^O<%a9%qW3oefVBoaHq&Q_Y1&W8p%l=|%g6wIg zmXyHNUyLy$HwR*y?!LaLT*ghec9PHyying^^;30j8s=OeLzfdD+|VB)&p;8F5EhG; zgeHc4kTE9ib{&h2+9Zz#Mg>Zlb%6gxZ$n|OxqA7J@|d9ATGc|C1IIy>6g}hHmw&m% z4OE|CRJMw?we97|Nt6JZJ65y1h055#H`=5qHdlRDW&6O0oopK+J?O%$mJC*X<&np` z@j{ukmBJ8T%$=+Kd5D;<%NL^o!DLf%ETAg`513@sj98mK_gY5c0buqT52xJyI(0FQ z_RNVpAQQ`InzHn!YOTrVVc8}MI}c2@yK^yaud&V`JH|z-k;;gzjT3Kqa!=6%K_;*L zQIu 8?CNA6|8jT1DM;L0|iWG{dRCDjdeyaI|v%3MUp00C;6ZW@Z!yAqWBV*QLd-L zH&qewPS^KOv{SAw7Bkw@H6h<`-{oIf?7%-f11L;3spq84m6r~b$4@mzIIZ=Lrf+$! zTrdZ_Mj3@PgXUMk1C0qNljMMlKtHH5d%L_l=P!+k-Jxa2`+ 8(mI{Cb3m z#NEt0`Yz37?X->0GT?|v4(esFd#Xc{hWkO&Ph~vqWQ!0m@K?4>2>HUtfo}MQ8T&?v zfi+5rW_+q%Ss)oEEjE)Hm~3CpkYf|wzSpgcyXaI?2Q=*z@ka{RZ`-zgI~xu*7TR}R zx}g7Wg3Az6*)Vsi1q2PM+NSDR1Bnl<8XDZSP9WEkFi?F^qh7Pkc+oTO+@nYXTHJNz zIhHx1^hT$3HXSuiIR(nFDMiTisEuUPjJ$QU?L~oGC(Y}kt{ng(ehks t$=z?L1f$QCW0UgVK>;M1& literal 21883 zcmbTe2|Sd4`!+nbB0^*-TZ^cWBoZ+~_N5|3wo1ravNJP6c8Y9SlSpFhV;{ThyRnZo z>)08_Ebld{`~KbU^S__>dEVdeqnWv`nd`dddwtLIIL_ldz7u?3Tjk`5vnN0x&`C8_ zrH3F8#T5CE_873mXP_nqc#-6(XZS|R()rnI8>crmjt(G@ds5S*b_c)f%y(wthL;Rj z0x~Yo)qcJ3{k1A1fuj8cqv54e%gVb;DD3<1J#WLaKj{w{DSVnp@X1f9pWU-@gV`Y! zIlkXy<$mZMFM9ue(fG&r^)G|pMir*tL@?P0eSTlh-{K#=j8kUI`$ |Jr$t z=?VRAy}QNclalsRffrBGgbal(=$|C1UyHf?aTh)8ea4@Er)r$1DauK@ileLXev@jz z8I;}`f&B@Ip^vI{1Z?lEJEdxG%A^vJA;~#zc$+uY->>nWnKb8JnZKRr@eZ%lbw_&n zXBv-HSnXrAtMqpYSg?I5+Qc7*s?v9Or~=eRztlu1Vq@OSygq*qGvVU(7Cf}M$xNT< zKR;E4ZLF6IxBd)`8*7paz?}C3hH?FCa1#2Vl_NU{bPlAZbXU(k2~+Lktv8>HCDAd{ zs?kO>^D6v^p?#X3>*_K3@QGiHz1Ze8qIHaC9D8Vmv5D8Sn3vgWxEy`sUXhi4hkGAm zy%2_9XGAC}i!Y74mhjsQ=JFqVWnGfs`~IiA0252=I8?K1uA}3&RoASG)-Uf#II5~E zVcV%1iAqS=mPOr0-Ht{L57R2pp7#&33|j>OkIdkDYE9r#6-F1&tU&vrR?71EO6j|J z)y+?XE*{I+oqioKVW)pxkR)}lhPan_Dp)DtCzbD0JMYEKcou447kU3-XSZ&TKeIv* zb+e@@wY3>E(Y|gxoN3H74uV_CN`;gYQ7H6FuL&J{{D)`ARh-w-EzMeAc`?TE4L7MX z!SU=vC{Z0xQX6h8-+DXJUSgg6A>2~j0@|(N 5BkRBt6}*Al9e1>(3P zZwHzl2UR&wW_7S1eK#FwEbjQRW16Be2aOLvwXJ<;@(wIf=n&|E@o^By>)Ml#uWb60 z^D{LQ-V%w#FhzJ8`P#C~BB0(7xp#8X(XSTS3Rl)+&~GT$*4COEgIUEHtKa_q; %cvI>*g8Z8hd*!#;c}I1P?uT3i=rlkd)^B}#c1ItlwP!Cs6^J6*O(IyxP3 zb?dlJN@H!p`7~4#(ruAJT2Ln(c}Ch#Tu>)s88^1661W0w^oMeTlrEeqF24CWH&?>u z^YZf9u=RDtH2%r^Yr)E5*=55{_?>vlH*enXw{fEQ )P&EOb8!$7(z#kX(4&A2hI!WhkB_iW9T1TFd@k9 z9%2SKeD6f6F+}{(Z1+tTvEG-$`$r)I$Z2a%O*7@r?S}o~jHRq}D(lwI7{_K(YnwW# zmipWma4yHrf|)%ClvH!5LK~=%U8E)MvaENSoqeXEe`v_L0bX{9>) ~b*^*?82!#lwHPnbw z&IhqA7$;S?PCN g&VW1ujhiqjg?IHe1W*Xkz1*u!+#5 zNrp@3Dlcnoo?% t5r}X*KBXktv}WE1g Tk$?UCJeY}Gs^rG7`(;x=p*p$0J zF-p{*2tkKq99wetWG0A(1Xmkd+omjzuDZ2}^{|)7Rm^%OdgsgK+#7wA+77 s@=@Y$RK)QJ(i S^TJ*Zdd=7G@o-5tL)=NYV)|)^!_U)> zwndAn*G9TAqA2noI0ONe#O2zN&^m I zPAEU{?3G1AxEBw-Xs*c%FOxspyYC?_C@0Zns0SO`?Vo?ApcL@_Dpx#-zvuNuJEokx zf;j4;&qUM177F==0!C^|icXpV(%EVMf!ETJF8txYQ+cWh@j$0SoI4jtp{dpsd@sSY zB0HJJmvnFcNuY6Pwf0-Rl*HY|TqKHE)!eLXKn>*f*MTyUc412;47tp~N@;_$@z*I> z6=-{>BTjNX({4B48Vvk{Ae@dsSnEgjCJ2Lx4oiO4PNE z- 9*UP&? +Tk#F&3JrgYy@6DkEqn? zjKK-XC8Qz^(9nLmG9;pZE^4-5Etd)e)8e>x!?;QV;yC^b-@3NHHA{0)GR{1Qat__# zDzDzNdJQ1v>|UGW5GDW}GG+n>g*vzqh9=f E26iNPQ37G8^@;Gqlwp4Y=b*8GF#1W A`dsCMtWk_m0*~nEO&IB0?9AZ$mz}o2PClrvLJS*ZLvItt_2jHRv z5OEN32RQ!4gz0{6*~DJfQ%mSvDSEj^iep*ya2vaP2xbJ;PcuVGed1o6=lMX3!yUqi zawZ=>rRbi*t4#BQYWX4quJiN=%WyMZ3a^~janZR-mv;X>xKp;OiWa~CfAmk_!lTDD z$ms-9 R(mrQ(+u9$PK8OI`N+2-* z0=wXDg3`R#RvWj{@gZIc^9-7oc^{klo0+Tw?}!cw4LB$%Ex(MMl;oJcHTugGSLs YF-in5L
wY1wz1Q=B@4R zO;;}&5bdteuKxH1f_k+s(+Q>hl4X%LGef p76Gle$(NAkb%sOEsI) zf$oY(AN-Ny1LBCH;!i$KRp2^EBLu>}lQpewmiC}Yvk=tsW{9{|7s$ioAs#l46YAhS zs{nz>8NGSRyh(ubPPV*kG9OI+2+PT_eN$AEm?63Qn~r9o9f_|z#XrJ|onC+J9U2PR ze5Nx!>9yY3|LN8zf}fvXAD)U2!pYa#L%CNEmXZqUkia&lenU_rV>~M{2qVW^1lMIC z&s=lZPnHK0UP4E&EK Zs_ z^B`x~cgw%Yrcb<#V?fMA&mWr`G{(f0GyNPQM9Sd%H=J~baMc8S=K+OVsoy;HkDZMh z%bwv*!>`O;39OTlF>yBm=(Nmwql~)h1_z_#c3a4aX(F8?GQ8kf%MAXu^GqsI9$DBq z*q>~Hr_%Ep7`wA{C|a9Z(RmMdG_zkL6qhAAmQbU=(!tPme6zb5sRL5m-i?03;NU}& zU~$56Ozk@%R;+J^`1<;W&FaL&Hn&t(+&6O7Pfr2}7eIT`XJx-Y>bvlf?5ESp$~YI7 z6>gqoTZVowKHY15y6OA8$YFQUf`Oy+?weD@0sCux@RH8tgMPuKgWu^{62Ll}L?DS4 zbpoZp#4jMr*lj=gfqa ^QYv>Ho={5}aSYa!G#UoRe2L|JH^e $!A6vGOlrfeGC@0k^w zR{^HyPQPfoPLQzsiBIIZP`(-b3 @&m9z%pKEx+K(3bfFT|4DHgP2+cYKc0&gT1ZxTs- zUbTGs#a@Ltfw=Lz%KPs2Z&+1N2+Wutzc67Ta7>i&;_1d{>Xl=sAYV9@4tAe1FX=?q zX>}Y_ht>hrUOqIEpOmBsrvgphUe_&sqAeXj0}KdiIP7Z)ec#Znv4c WXgW zlqmFozDs&V*gtZQzjI}Mz2&i0a!foNk;FPcMB?W~Ib|HGroG{W7o~nEqUP9Lf3&qs z(AJ9m@edHhifs$Lgl7@aoi{N=3L^-iOo8ezQ!E))Sy?IeYDVdeo7=?_%g>rRJQX99 zY9geaw9Wr=Bp+A2(U>p9%8nG78gDuj4tC5!ryyqu`cEoxP_<3BD)hU;E8-gmLV2Z6 zAQvdQ &ve2DS$-w|@o7&zM$6$^U75P$75Wc5q XU zZL~Z)ulcZ3<16zYd^X lzFR>a z0}bN`RCBVbs<`Lp=QGE()6l8JS0I3r_Vo4nmzK_b3Ho2^Sx#2 o$(wEEN9PWeZyGe3B zXXmYA=&^|h!c}`z&8YzZ;j=p52FE@Q+M!xXz2^o^bt3VfZY03CkuNLGAnQYN^g*EI z `IkEJ!E#pgL)=_DrZqO`0htDo`x|D<{@4lXLWv*i66>-1Ha6fG}Q=zedra& zi1=h&@{Akw)q_`khdSOf(L0fpNMjTfvkx~RXpPkrQ&&x|@g*1)c&-|P IDMj&tUND{feEsabP<}8-PdU&L z8h=wQ1X(2tsVJ+?zaN6WCSx{CcMfu*@cGvB@v^;k5Uhsi8@6&)BsHovnxYXdo>ltp zqsLA^{QVJ{`g6Nvro2o)l6IDu0u}cstC{#GS4^TMLnm?BsFWov0(^zS*2M2sVri*< z7@yY(?4+0DXlKt+#=3zI9Ay%4_(B(`Xx&@puzG3d@ITAn6_qc4;nPZO2`#aTRss`P z@P$ayYXZ#mxCr99ZYhd*oOhi2unXbX<8xLo4+T!dpJkfa&zhbVXn1?<_^ex%O!Y5o z)Vc}7FT!M#dAMB&MFZ;X*Jk-3#gAU#hH2)v>%2;X FcR(3W+7%b{6Q}HYny(B?&)IBMLwN?9F~7;kw0JJ#aybxUuBC3BuLHZgi)u z*uzryszl(4Y*{OcbECExx5ckeV)R8t%RsXSV&Yfm7G4vIMp9E?-5O?#`x_V@03WHS zHhC@ng`Dc2Qlw4)6I%Ts-vtQ38~*@r#y+5jZRputN$ TI@RHE&7wx44x%Ll$K}IEc@-MHLR%IA(_Nh#n7z Y+6+vt3vkT%x-+0 znKQd_gt7N *H`9&4Hzv0vmkw14BV z<2zl!w~H$)RPW)HVG}&E1mdosQLX1f)r2%hN;dbAQt%ieLfi 1kA0AEWt+XWQOq< zP?@Mh3N}BtJWG(I{L7Ro&>JL}8>W *jTx}R`YD&}P!m(U&C@_Prn;}`JC4sbx>GR1Y4Rd0du4KiBgz~lCSfG$vE z7Op@^91czXO{^I{L@ab#A9S6~FOFCh2is)gY?&(IyB<*&K~6-;*lWM>&V_!_9VsHm zQo5?$@Exs%c!V5t5Q2z~zaW^FckWlevD)&&NN$BXtsJnruS)LFJ8|J?n$qT(csMu| zm60uCoVY<#UnI1IG?N+Hd%_JG8hkV?tN#YHFTVk8ZJ2?qE|(B4^lXQ|h1ioivUe5= z!PzubGvC=ju!@SD`%(oUP|x6C03N@YNv}6lL Kfwh7$m1>JrA5qqmH~u;-W{_ z?hn!B;Q*N7;9wK1CN&Dm*%;QA Ax_T!9V+34bMr3|`Nry-!z9Bzwb04=WY{WN?Z zbpUKQ2I>Gv2gX`HA?x^`yL{M`s#-hkM2oJ(`Zk6EoFV4HOaDpSw3Djazo<0%EkL%P z=^M2S4lpwM#7$$)_E#T^Hrtk)QstjX=+PYc_e7gG{^~N;^j*VwYP4+7cV|&mE`7ZY zCB+8%ib7!2m>C13)-FmI-~KD6Swb&RfQu}3CtYk&O_^G7O`&gg>R*4|KVP!I2~zUP zl~sxUP4#hvIh~gjTr{6mOp?92Co_(Ow^RsnpBz#B0;WJ#UaczUkh*FZsfang*%7Pt zj--6|NX-Wm%GXfU$Gm~mBS;Ys6lh1B*?9z>0Q*OZ`9u%;? BT|8QDX zV)pUz0c~l~0Q2*Y5U2qNXZS!cw`!PMGN pxh&lvNO4M`5y%y)?D@dP)zr0`lb@a9 z)^7O9;}p?s7fv}dLI1|SX1)JUKsS6Z{22}=?j)hm)*70J15JY %g!AK#`H)IwB d-+53ljL5z*r##Kl%HQ>4k$vLplV zVK;C0A5TJ%qG!+Y%9w>9k+<&Lk<^GHeG`IAP_GV(oO U>M0*A< z)x)kpkJKOOGb(|czOraii9OpnGzSzdmNJ~c>_Ep4Xg{u1eLGm$<+Z F{q*s zV}6 T^M`syOA$aHu(DTV_XXFITiq(~pC8Pq#1qn=4J{`rvGS*3g#>WI8Jc zZ5n)tkCQYFL75)% O z_3HdF6$nTE3fN=ox%Pfce}iwWYRmWfV4C11Pn6?CS)K;%DV+-xjzHO5IgNu;$j?xK zK-ZnW$ICBfp}UE{YaPq7u-+?kPEyaWo)Uk|8YRnL0yB70LJ7KW;Pz-H@a#jEGTn&% z7KC<>NKY%%_z(2uV-J4Q?KJ2c+{F(;)7jIdr9)ws@zu>-jO~~vb=u%)*OO&smk)f` z(LQnzlUL!MGt1M{XRuT^=CRk0^+O_`=Fefz9czcUFcjjqVWA5m(aUb3z|~xVB6udi zrZJpA($h3vi*>Yb{6f0(y3 X0j3Cc?z1lHr4Dqf>&Q4n)#*cFt_!ilUc9LF5@y zMs`U-%4~Q_W2^{KcCh_v``8{XkEmX4IcSyvVBos9g&=JIN*TcCU(Jq+QgM2yR-J15 zfP7v3vN>$YCf8RJA>Tuur6*J%Ma-AJTU ot&l%{dT}Iwe{h%Fv(X1>^ILsC|rgQ zjHwY&cMk6M73bcHwfqYH00Ss^joUg^@R@`>6-xz2pXz=ypgnDcRN!Z!_|(o^_w)V) zC9%FO{99gdqyefCjVx=Jm|SOivMZnBdHL)`QgF@aaQ)dd8)y{~Ij+kkB=QDv=}q@M zb}l wt*6BCAZPSg)3-BkcF(slod-t&Wds~(jg!D zLU4ptgKzqUITVk?!4YuuKJWJz5N=Y%y*1s0=RL(pv2`$3B%C7LLq1nfM|f0+G4ih$ zz ZEMy0 zO!6 2S$X^9SCG0VIsM z{b~-10_<~43VF5K ri`BP&IS~d=U&3Pnf-!e0*)I{v&6>sG3d-30K9saq# zxAa1UT{vXjn?v~RP3ShoR1T_1$0cM>s8vOuzy@%L&<$V!|D%)%Xv@@l$;kH5D}qwK z7&Yqr2FNGfkN{Qww}}qls6cp6QP7~o6i^Gk;a!1AEAM)JJ-+(^E!_B{{ZrJtOY3^m z#Bfr?$rX}SGpA?7tzQI8-%c|8Ov;^uk@#4WZw_w{rZ_5`xSg|lfYlQX<#FE;H+<6$ zov!_oTGv4XKR4h_w^oi%sRH!6=1+Tx*sL{t?miowC6Eo;4=LX@z-uqV_mcSt)S 0#_flQd7457#PCqb`zc>TbKL|t-^*HFRs@V z+#Ka XZd};aaR2M!l-E1NEDwka*$LXW;&KsYHtORV9#r5bw^A1juBJTT0 z=VTP0xb#*2`mlrhr 4h;UO140$g}X#oc0yw(36hN*{c=lg6n}=qWH>z# zd=7`hf#z1OJ#$l00a%!zps|F#y(2}f6VtEv(`9kryqH(Q7YfZL{2pO$_7@GE{KpaA z(d{!qrQUO{ON-H3E__K@yF@m=AisH@0vN~ywKsmzB2s3ROA)veTVMp?%AUGFqm}jK zJ0-G@#<6#mM(gN_#eWn_wL3?=DM+}~!M4X|stK^wq_{oZsqZE;kMkUEz<4?iRUbKP zGDz))=kAAvGs*I9-#7*bO0(w!o4ZhVk%EivxB?w|q>suwaH6Zc?5wgYSF{|?T5lb+ z#K!{(5Hz)IULA2}S!d#d9OsG+t$fyFkIbd^-sVwZkx6TTN@?sfzK4`UM(FOp6F0^u zrsC9Q#9}~X#SEZW!X2d9R3uEt^v$ao0u*_0Y_057$#v2*$LjFNRA=oOLMmxTaJ=Ms z&2 >gRviW8hmX*Y%#O1PrAKuX%e~Jb>15r((Zp?DJaiP6-yjcxrjtY z-Bw7U!o;9m>C2GV#f@QMLCDz8oyao6Qd}~K&=oMK`)CaMIiIH|TkM%H#Z*DKBY!5L zL4>0vD|AM7YTG$I7>B&`i@QK(%3;be)BO2ioI&WW8>PlVHfycN7io$E9%DMG+Y~`6 zrDm>_Q5Zpfmr;XB=GDRA_r!^eJPLLC+8A5^k1A3M`-BA8A(Ix}+owt;F4Uqe9P; zyY?N)3p$r~YOORiIsfN3r5BWX<2Rm`0eF9&`LZ^C1}yRUNqpt=MTR^AE^Z^-@${w4 zWPCoyB}FU$ru?~4omo|szY+mlx~~ZGtH GDu)4pIf71nqWAbiPXQcwU#oz{>RJ#S?<;*2E#;-{EV~ zbnYMPsV@ssP;Qb)_--GKXY$W@?+X>PhIH^S6L#9QVfvYsS^z wpU0L)Jd*Sxvv-nX3HXKmU&PEa?F=jcnrw0#D7A* zh{pRjf5-+-=}=vu={(MmfPY~GGd<`m1y%n*o~hbE3ZPX`D89rJ z`GUSD2d8q;q*jIRg5*GC`cJ9TT0J!(H$D-R%Y1&HB-M7f;+Wmu*|bC%UK*jSJJlP; zb@rs($ZGDyo3Ad!qA)GSUIw$yNO%)ck0A7}IZW*(h3EWnSbBQ;bLb|L^4ZPE*~K_K z;i7+8=vc0OCpF$RUvh771(qsDm1Gu^w9&yYpOrNlhJUfq23>h3+TtlE6~Sg;5}s~- z>K%l;sr!3MLq=h?8eLC2E>L<>5xXgK-OKgV k4j4^=@W?{*q_&aOxkVD_>Pk?NO9Q#=_3Dmqds!T7*7ilJ%2KS4Decyh> zi*GhurerI+F87h>FC}g*FqKIN_gFCay6z23kNM%2F6rZ`N#Akco>|AV5=^A~vW!_9 z)r_nP(;eIy;fi@=8t bHtc2sIlpAL#<4ACL5mM}t!-`Z0R|f2bP4gX zxJix0EVEcMHCSp-D_GZ)IOo9tBd$Y3I=6&PsCaEG8Tm6!BDQ_m76d14G2ZMGTW0c! zuhqL>WB};l3 T<)yen@i4(a__D*LH9ZLIWH^F7WnSk);^8};eVwP5^fK> z8OH2n1{Y@?T0vh#^v?J*&(QDWZR^7iPvlX fPVMR$WlIyPJVgx%s%l$z!CJ<$}0`4}&nyp>z#bZ5yAD~l*(C8$0U+~&F1=?21 zF7Gwxf;i$d@A%e^9|$&&ZONZqP#pFliLE|<7&4Xm*6=15=V80qFA=rJsMnNh9$)EX zvsrC9hC|1XL9}5B3!xe?TYc30ncWaTeFbkAS)2!@P3J5;vzc-u*95Dfc!`Z|v_(5Q zOl9!)qly(H^;$qD$k)3#!mcfx&siuN4^nxb0E(IH0%R6uWYJsz#}@h43h!r~ xb!^8I>3tf{Ui0xj^l6#1GDR5Z!tH${x>-j5BtmGkzU`*S@i)2_q)GfcezO zQ0k!gl)eY)WXuYb=!V+0ny}X&N^<{~Y$52+{FG5HsNn)c3)Y`$FtgL4GsS9^?5Cq$ zEhR=kMk0N&q%FZPCPJ@-!ET{tL20$u_Q%h*G>LN^z>VSp#!`K|h)B$XmhQ)Vwk83v zQ!sBovm)a6APz=I0d-%yqIh%EZ$>woS!C(%8QAB~cMVw6PW?&l_3Jc=p`isz62)y2 z;0on!nJsdCihF?6H@rO9jXSI)j?5UmODAYY!*!9W!h+^V!P>&l=RxuhiZ9DOlkVw9 ziwaXHUo-*1hI^z=-)la3cs!u&-a(8?NjBez08188LI(l^U`TD3Dxb&C7*Z)#wuE5y z17usVxpY3zd!mj{%Jh!RxfA_#QpPM(Edtt&TNMO%+x3^;YuJ+gxKJXS; lMH4YKB(`0a9Tv!@zLolaM%UR z`l~zs5a=OrqYd(tU<(7nsw*^OqkAfvuOx(VOM?FI$D1Eg)>v StwS^61E z_!_^KwC5uW8g{3E_6lGlWCJExZY~>ee=o2E^Ji)SNp|ygufts=lHkuAr7 ->(NIuUT) zZS9HZOe-xD2p`@-1+&{&A47 2W=7j zgEE2Y8 t6P8-8(MncaWbBHhU+;3Hni(=N< g=`!oG$2m z;NAG|H&w}}wxW+`j&c!B9H)G;T$L0X`(d+UJ}!+hF75MrOxTKj*#_s?NypZ*C(6i| z-Zu~8v>fhE1hDe701fq8U|a~=&wkEz+uGR`KLP-=taZr#WWm8UN4mqorM%d&{Dqj} zKp|_{m>}S`AC3U!-g6p24*(SA%(ebvUDr&NQ-DTb4K}XzfB$2;qGaVM*$-44@!D+u z2itD&ifvr#H}wuQx{QNd+J85=P1p(DDWcJ&Z9NMxr&Y&sdLfy*$3n}jv&%eu%; ^(0545G=yS=33d3y`CZig$Av!j9F~Q~=haT$H@NWRe;lMT+KD z$VrAna7Dx2hd?*)@x61*?~T}jb8V5iLt(`Cs|(#zt`Cm^TQ>f%338wBInUit9;@+d ze!!R82Lx>3gZwNX{_(*O^L(CeduZDxT1*Z>$i5>Zat61=F~1O 1wd z@ab|^?OBBYzMOxlvi~+g2qP=1?D7D6o^=2aD^Zgi9ob45hz8Gd?qXs!KuaUt1Np)w zdAe8iJKBN40f%XvDn-9!06b-lQ!!{e2B7QMI8_4HWdPccVu0!AKUMDM2Qi%LXb*)D zv>m~Pbt5cPD0)(-a)aCJLzVaqX(&+vw0SX1GXrPeGL??jXSjAK(lX_Xe#pRkpw%+< z8Lh{_dnn#~uGW#$?;O&Bfs*h^jCL=p>0DjvX+M4>OM`hV%FJDK@hFIrE0t{jSQo)L z_zDqoIOjeNA=ch_iuP~EwJ=r0yw2{bH!t$)N%igaJTQT{w|xkVy&Z+dHhksqf|2$< zg1B=y*1Zm>DH?VZ?Q)c40ty_KXt%La3Ijq%&C|?nTIpsLI=!e^ZdqbE*6gkHleh0` z3~uDbEhD)65+O2TWf;U+KMshHnEU*HGjyFBO+D+!fRsM=7;@ 0;>w?i4ZU_Ku}%e;^tG!Z+K((Tz=%G VJ CWZUSg80&?0NK%i2Qtp!b$V|3<6Zb$uz z7~m9HvUH(jnm^f~Z3w_z5cU6kuiu7r9AE2~_{=n@gNV5BuTe?e;s5a$-Awm4`!r<4 z9jO0w5V4_vi2CG#80(Sj1T;;I^#Qv|0R(`wj#T9J2d lUeZ zoZA~sZN+>=hm8p#A0vS2 MMH5#t)f*l!iTgU|ICM?I`|l+sbqOmX~tT8 zu(dn5rl#WKKd{hpcsFMvosjMD0kGs>{{J+}BUW4+)*CSc>jU=9 zaI5Ls3~#HCc1{@S(EMJY@+U$61;^+@bt&$){ma+?IVTuEs9Y0{%3{}quk|seCPn%K zf?bYgko2o!Q0T1bog&NV>_CgZYeo3*HYnJaKOlLx!{|XOr$H#2NAazf6#xZ2GW%ft z%!*itfKVI;0Cu6Xy7mpT5%)`QkWIhPsgtF~Mv?yxlref~Agl>|A)ekggS!PblBlnL zQrnX~+sWhHwA4xNJaPYSxublTRUQpKX?ZPZ`PavL?JxlRJDiTuNm*~!myG>m7r+Sl zSzZadJ^Y(E7AnldDUSPL+#L>eBRpXgwa#OyZAH%f|Mm;$c(JgPOZ2M1 1H|C)S)hoWF@D3kQB2?cs%4|==OX@q zo+7L`pKLTjUcgIc{0p;yE$@FQ?Axmz(Zm8Wp=ey7b)<0ojZ+~*EWo)Q2W+r!RMd#3 z&-+8@%YOXOG{5;Ll0;iX?;O3C>MNgxR@=_x T?_q^@Eq5+^k3HVj+l 0@g~{tKr?r zGjV6U+K2alvfjF+1@_+82G#y}9Fu~xL> %aKgRQO%8$ivvMGBcP1A$hnW}0>=mvma@3GkoYBzmD4($|fduH?)bs7+Sd-{@3x z9}2-WmPv0?Zq9K57 dx+l-aJq_Bx0MA zFS Z?y#5C>9N|BdNiMAXX*u3jYDITU#@bgdrfMZQsN^rU6vovk2@ zi*A9lPCOT6WcRm)ao%8~ooIlddGbgC<$BJw-P!@tU(iq2+3A@6?J{3(Zf<7mZoMe$ zQ4&mmATdxNFKI-kq^y077SI|ocp%oKt0OfYPTt^2jEZ8>osWOgQ3xz}I?9@s2ZU16 zAVK_HT6l!6>BqZ3D9>H^Bto`XeX4!=n1+TXuN?EQ{Lo|f?<6Kqb(F-O36YnNs|n`P ztSZmAJe>S+k;h?K1Zb5V#m0r7pR^4@H$@}< YN5+VDG BF8Uz$XOWl-6yXWLEy%(ig3*-~pV}1%5 z|H>=3|6ES{Mpg+>I6c4l@X;enDf9}DFk#1dH|x?=Ufwm`!mX{>(H4)jx<|4=AlA3! z-vuBaIc|aU>lV^x*`thn`voHl-#jE`W(trkkU&%B1-<)peZV`KrQkIr3DU3&GCxWq zWR#shXqmhrDYw E7QM9$%u z29tUtR!(A;AXr<0Df8<=_b+FZ*LNg{v(8RCaRN3mDeSm21xO{5w~0gl2MAPE<}9f1 z4WV(ZsbWo(w5J3CSw#Dm6K-NN#{{ShkT(69jXfdY5AKehl!3@y-jrOTFlef>2nt(Y zSZ_+Ea!eFz5pZ2$0zK1igu{If`2=U{){`|g`B>&!@=0CBCpP_54uuU(w&H$KfntTZ z0?}&()soBu>eWW@%~c&Z@_>5r8?hk+Leyto0t|Ik&s$k0rcPfaXAxwXv(;1Yg?5?# zUx@(_FUYQTEFe*72|i& JabodI}gT z>Anq7HF t zJf^}z%7-%iFrjqP15UM%=~iK>@q7}5)U(;lX+Fsa>SM`|o{agOWyn!<0HJw+Rmz;n zAOd3ss~dSGp6iBFDJRji60>p-qFJ1rw-Za}9mwI|o>fxvC Ue#=tVrFr;he;VnC(;S-G6hzOVD3yz z5`o4ys$0 nv7!Ji28fHAdaZJ@xDxqF4>^u9XKxpI0duc55cG{$ zYm&<`GP$}F@TdRbb(d8^mA&xg;P?1mZ#ds41tgZNve35P(9bnu%YnmNj`Mk^-A4ZS z!H}b$8{!e;zsNEBH-!KsNmI)K(IfNnsJ6o9r0z#5QFaZ?xzbl>0(uN^{bq#5)Qz)% zg!KFhz_NB0^2rK~LSfhuBdH5(2Q_DPVM>daIA*N?<>kHb-D+`CXE3Y0)SVm7?d;~5 z(S7%OGy-+|1LdzPxUQ!L-306+Z&i`(>3Hl!oGDhs7*@IVx~ba~`0b4-n#(fVO`#l- zn&X9*&~rMy1u-U*@}SJ{>@z5|_<0m(0p zK5~Iagwvu>Yg_)R%FwI-LT5NVApgKJ8^4 xgD2B331pFopR0T&@53gK%#7mKd*4&6SlZPV^D33Kk$EOEQQi-qZI8msVultY zcxLb?mMgNloTrYyv7K`DtN}|w2#|yd3k!?m^aU>%i_|gDZo*A+9=lmUT)3~{`lT)A zJrRy%WFy+`=9K>^K7Cn)Ct+iwL0(I1#$J4q3Rt*C(V6UXj`^nuRGmSAaK*)~9hj3Z zDoiQ^`g~+u&K}(iK0>yzv1|5M%On9V-2A>t^~vn--`kvOP-ad?$L?K=n>dkWmIO>e zGPg4-sH(bSZ=|amJ@4|HG-6BHOdmap*%V9M3B}r$!Z@xyu{MnF<*@(uE9}=! Wv4$!D(x1X{1g%7M-S)-9%?U}&8&Dfc!3*(GBZL~>+&c?gwI2?Nl#oAjlW zgj@jh!aHy4(z5xLkcNOB%6QC=HbCJMO=r?=aWP)Tz+Mz8(79XomVLP+Y4-V@{p#pa zKb}tFz?@JYdRBp?EJ%d35`(YZnVAfRMm DZ zNIx9R2h!Jxfc_ZMhWHOgw^)p|MhcfF?|VIcO(2Mw-%}e$ZV#v!KDfasqZ7QX(?NAj zI0!v~9jax%Cd}!FIQGu}Kybb_7km!8-OUE=BOsPPPvQrbq=Daz&`qu#SBI4n^q1{# zcINS^!oO75i62)MSU&O}(__EWNRJY-TT$8?!#0HE8%Bg>aZp=XauKB~aOSid;1pQm zVc`7)@Uc$95G#;b8}Fn&1AY!=yz4PVC1m#u$P#$w+lwPOpNwQlv$>-Vi`_U!VO0N3 zPM<04O^;^n3z-YnJQR#Ezh;j|?lqw=&wf1sb(s0N%p8CJ(H+BYAqcq@1@Fh@M3!N= zefk~IXx8YJ^~m#ArpMHX+IZG&nAx89$D0nj0B}Gb^0J6PEXS8AV5{qBO9_dK*kn2U z#?P>UrM9Kc0ZZM5u}zJ+!U4}KvnPzkRBVmI-r}+@nTyNP>@KdZQk(Xq!fPPufzO~> zPoP!{svvtP-p`%1b7oZh;whZZlw-UAiiFpLqR>kVOrSrs4|E@xo_nUBr*rNO5Q?wb z50B#o*oDhf-Bf{JlKGvu&-nE}t9u(7^;Z0QQZld<3rLgYs|InFJEou4vL1a>y!3eS z lY138{5^^Q;tsX0*n=x){57;>PNtw*!)>V*i6Z+BWLMAq^N=i%9 z0d^wxhLZ4 LdGFUDRNvMcIl}sv}ZTvQ9@aWto`@mE~G9AtvTp#~9Lt zS>CzNh(GS b8l<5AI1{e;88X-B&Mz<{3mjmXb8tWRSd &2kBhH%ZmNC#SZ%DeLrv z3htWp?t^_5y1RU8H*vXKHBOP?_ri)evN_e2uIF{L_N@SeYb2tI!}a6&CFF}iqUnK; z(GWSx;$4KPT_lD`VP^h4Bd?Iqp!;=R7L`vVHs(H`t}%E$Y#r}6HHgj&ya++){@`i{ zf 2dMy?y@ zk-RO2-P5lgARtSzWv-7y=TMu)Hwvfwh_sNkbX&u@w)R?^6!J?;cd?&Duj~J2dpN$x z7j* <2r{*}HgaDz1f8HzsgMxkF*Bwm z%34#Y{kDvx#N%P3GakXZhdtGab2IOPPp{^N)_=8t7(`hREDGtloEi=_Z0Rd9vcPdM zlQ8g#32w~#)NEb`l3+;;@jrrJdsa9axTM^G)3IF~X#o*dRZb9Urm;>dE;mE)_JK^< z@n?5rmInqx$?%5nk)B@6Ung~F&mtS$r(4|^56gWHnTQNOU132;EiNv;_GX7b|3rVD ztKvI+h;!^@tQ>BVbfa1f{y`#v1d%B6=P&Y^oHki1mbvkHb${g IgjJLFgKYD=<`{c_xOZPI3rl7GmeC+Dj z56OBtJfT21zZyXn;M9@zq`V83YiPz>g(zochhn? Yv;S%1sO>sPERGs(WssCP18+sB!&m|6S=Qm w8q)l zIpy?rCdQ6pvZrn8rMQI+yGKx@=W8bLi&WdJaenw|!9 C^N(_H%O%q?i9u^u{ zj_vYo5ZfmbLPIejAtCKQaeL>im5C;1=?S2*FDBm1^5Q!NUa`%cxDhoo(2J$b&&?12 z>mnK0aG)TYe&Oyek9;++Nx$mv YhgFqS;lzJ8ge%pkHv@Q8%b(`MPGzJ(x-|N1g}oTK zMLZS5N?Khyv_~CSG;sj}ovd(*Oaqp`%^@RyTVnDPDA$fzov7rnsp W bJFwHGn|9b5& z|2!r 1^3)w!Q{`?H9VYng9Zbw9~q2ZT0i?Ry^{w6?|f16~nj0W9` zSpT1EKj5j1-O;0|;OZG^gGkXcN|LKUccf-SCvicZdFYrU%gKQ {g&7s+AAFFZC@pUkrtS19niy%tyif=s}=M@rGX|qV(o0ApEVJVTy>j T(?@kq zW)d3i;!)a7k&?Xxn-AmlGo}U2j!qXlJ2T}Ju?pSY+XhXSf%8i*&~>{v{<+xgs~#b@ ztL5j&l~2xJ0s9lnol_l$3O3VfxpTc0PDbUWK8@_$waZm(3C7CLr;-$Z{*9D~&{E#| zM?6jc=+UFqqFrLAN!dnl^?Bf44EpFPnBIgRkmOcub!(>vhofIJ+%2VT@%aJSRxSR; z=| %~ES{dt~P&4jp3;b}v`kE;-CJX)oNU|<_3+`cHMg;-N%a`62r zXRxI`@nvG(3b6RkwGIgjm)>+e>%8L(MX4~pjQxllHW_b0(5y?x_wSALGUpcd9Er3T z50}>=V7T4q9kYL>+I
WHNAbvrr0z-`t}=WYNFE^Uy#08&nGn^5gt7Owo7 z-m$36vCh#^GjT4hbL%0PoP@c#woR*N4Zxg(?Hzr6sXarXCyO{L>J;_vEv8^0nYX~Q zj2Ww_cuG%P4NMel`&439- WVf6BMPqCBNx#ZFI}x3{;N z?;6LneZPNm3D^1z?Jag`*vb*sRoJfq`~MfRIR~HKX@>v^DpUaoW^5#eG}G5&wd%12 zzEXtYrwgp7I#CNVxeEluM{F$zv>_5;5QTN- v8 z>4>{bbjlHfKVd%K%7gyGSp#sEFTc^Wd30m+Ev(%mjdIBgcKZ1v3oZv3jEfbFe(4_# zV*{~_dE=cwabQ@{SUKt`F|lJ$!Wa<7rK@NZY(LSfC=V=A3^vG+NFp(lZD{F2d;yE9 Pq8tt!+Fx#S;?jQr;38-P 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 ( + + + ); +}; + +const BackButton = () => { + const { act } = useBackend(); + return ( ++ {data.screen === 'main' && +} + {data.screen === 'busy' && ( + + + )} + {data.screen === 'levels' && ( +{data.busy_message} ++ Please wait... + ++ )} + {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' && } +