diff --git a/baystation12.dme b/baystation12.dme
index 38fe37bdc94..d4100f4482e 100644
--- a/baystation12.dme
+++ b/baystation12.dme
@@ -1557,6 +1557,7 @@
#include "code\modules\research\circuitprinter.dm"
#include "code\modules\research\designs.dm"
#include "code\modules\research\destructive_analyzer.dm"
+#include "code\modules\research\mechfab_designs.dm"
#include "code\modules\research\message_server.dm"
#include "code\modules\research\protolathe.dm"
#include "code\modules\research\rd-readme.dm"
diff --git a/code/game/mecha/mech_fabricator.dm b/code/game/mecha/mech_fabricator.dm
index 07c54ceb2b9..3a0887b1b1f 100644
--- a/code/game/mecha/mech_fabricator.dm
+++ b/code/game/mecha/mech_fabricator.dm
@@ -1,126 +1,28 @@
-/////////////////////////////
-///// Part Fabricator ///////
-/////////////////////////////
-
/obj/machinery/mecha_part_fabricator
icon = 'icons/obj/robotics.dmi'
icon_state = "fab-idle"
name = "Exosuit Fabricator"
- desc = "Nothing is being built."
+ //desc = "Nothing is being built."
density = 1
anchored = 1
use_power = 1
idle_power_usage = 20
active_power_usage = 5000
req_access = list(access_robotics)
+
var/current_manufacturer
- var/time_coeff = 1.5 //can be upgraded with research
- var/resource_coeff = 1.5 //can be upgraded with research
- var/list/resources = list(
- DEFAULT_WALL_MATERIAL=0,
- "glass"=0,
- "gold"=0,
- "silver"=0,
- "diamond"=0,
- "phoron"=0,
- "uranium"=0,
- )
+ var/speed = 1
+ var/mat_efficiency = 1
+ var/list/materials = list(DEFAULT_WALL_MATERIAL = 0, "glass" = 0, "gold" = 0, "silver" = 0, "diamond" = 0, "phoron" = 0, "uranium" = 0)
var/res_max_amount = 200000
+
var/datum/research/files
- var/id
- var/sync = 0
- var/part_set
- var/obj/being_built
- var/list/queue = list()
- var/processing_queue = 0
- var/screen = "main"
- var/opened = 0
- var/temp
- var/output_dir = SOUTH //the direction relative to the fabber at which completed parts appear.
- var/list/part_sets = list( //set names must be unique
- "Robot"=list(
- /obj/item/robot_parts/robot_suit,
- /obj/item/robot_parts/chest,
- /obj/item/robot_parts/head,
- /obj/item/robot_parts/l_arm,
- /obj/item/robot_parts/r_arm,
- /obj/item/robot_parts/l_leg,
- /obj/item/robot_parts/r_leg,
- /obj/item/robot_parts/robot_component/binary_communication_device,
- /obj/item/robot_parts/robot_component/radio,
- /obj/item/robot_parts/robot_component/actuator,
- /obj/item/robot_parts/robot_component/diagnosis_unit,
- /obj/item/robot_parts/robot_component/camera,
- /obj/item/robot_parts/robot_component/armour
- ),
- "Ripley"=list(
- /obj/item/mecha_parts/chassis/ripley,
- /obj/item/mecha_parts/part/ripley_torso,
- /obj/item/mecha_parts/part/ripley_left_arm,
- /obj/item/mecha_parts/part/ripley_right_arm,
- /obj/item/mecha_parts/part/ripley_left_leg,
- /obj/item/mecha_parts/part/ripley_right_leg
- ),
-// "Hoverpod"=list(
-// /obj/structure/largecrate/hoverpod // Doesn't work, even with required vars. Why? - Gamerofthegame
-// ),
- "Odysseus"=list(
- /obj/item/mecha_parts/chassis/odysseus,
- /obj/item/mecha_parts/part/odysseus_torso,
- /obj/item/mecha_parts/part/odysseus_head,
- /obj/item/mecha_parts/part/odysseus_left_arm,
- /obj/item/mecha_parts/part/odysseus_right_arm,
- /obj/item/mecha_parts/part/odysseus_left_leg,
- /obj/item/mecha_parts/part/odysseus_right_leg
- ),
+ var/list/datum/design/queue = list()
+ var/progress = 0
+ var/busy = 0
- "Gygax"=list(
- /obj/item/mecha_parts/chassis/gygax,
- /obj/item/mecha_parts/part/gygax_torso,
- /obj/item/mecha_parts/part/gygax_head,
- /obj/item/mecha_parts/part/gygax_left_arm,
- /obj/item/mecha_parts/part/gygax_right_arm,
- /obj/item/mecha_parts/part/gygax_left_leg,
- /obj/item/mecha_parts/part/gygax_right_leg,
- /obj/item/mecha_parts/part/gygax_armour
- ),
- "Durand"=list(
- /obj/item/mecha_parts/chassis/durand,
- /obj/item/mecha_parts/part/durand_torso,
- /obj/item/mecha_parts/part/durand_head,
- /obj/item/mecha_parts/part/durand_left_arm,
- /obj/item/mecha_parts/part/durand_right_arm,
- /obj/item/mecha_parts/part/durand_left_leg,
- /obj/item/mecha_parts/part/durand_right_leg,
- /obj/item/mecha_parts/part/durand_armour
- ),
- "Exosuit Equipment"=list(
- /obj/item/mecha_parts/mecha_equipment/tool/hydraulic_clamp,
- /obj/item/mecha_parts/mecha_equipment/tool/drill,
- /obj/item/mecha_parts/mecha_equipment/tool/extinguisher,
- /obj/item/mecha_parts/mecha_equipment/tool/cable_layer,
- /obj/item/mecha_parts/mecha_equipment/tool/sleeper,
- /obj/item/mecha_parts/mecha_equipment/tool/syringe_gun,
- /obj/item/mecha_parts/mecha_equipment/tool/passenger,
- /obj/item/mecha_parts/chassis/firefighter,
- ///obj/item/mecha_parts/mecha_equipment/repair_droid,
- /obj/item/mecha_parts/mecha_equipment/generator,
- ///obj/item/mecha_parts/mecha_equipment/jetpack, //TODO MECHA JETPACK SPRITE MISSING
- /obj/item/mecha_parts/mecha_equipment/weapon/energy/taser,
- /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/lmg
- ),
-
- "Robotic Upgrade Modules" = list(
- /obj/item/borg/upgrade/reset,
- /obj/item/borg/upgrade/rename,
- /obj/item/borg/upgrade/restart,
- /obj/item/borg/upgrade/vtec,
- /obj/item/borg/upgrade/tasercooler,
- /obj/item/borg/upgrade/jetpack
- ),
-
- "Misc"=list(/obj/item/mecha_parts/mecha_tracking)
- )
+ var/list/categories = list()
+ var/category = null
/obj/machinery/mecha_part_fabricator/New()
..()
@@ -134,643 +36,102 @@
component_parts += new /obj/item/weapon/stock_parts/console_screen(src)
RefreshParts()
- // part_sets["Cyborg Upgrade Modules"] = typesof(/obj/item/borg/upgrade/) - /obj/item/borg/upgrade/ // Eh. This does it dymaically, but to support having the items referenced otherwhere in the code but not being constructable, going to do it manaully.
-
- for(var/part_set in part_sets)
- convert_part_set(part_set)
files = new /datum/research(src) //Setup the research data holder.
- /*
- if(!id)
- for(var/obj/machinery/r_n_d/server/centcom/S in world)
- S.initialize()
- break
- */
return
/obj/machinery/mecha_part_fabricator/initialize()
current_manufacturer = basic_robolimb.company
+ update_categories()
+
+/obj/machinery/mecha_part_fabricator/process()
+ ..()
+ if(stat)
+ return
+ if(busy)
+ progress += speed
+ finalize()
+ update_icon()
+
+/obj/machinery/mecha_part_fabricator/update_icon()
+ overlays.Cut()
+ if(panel_open)
+ icon_state = "fab-o"
+ else
+ icon_state = "fab-idle"
+ if(busy)
+ overlays += "fab-active"
/obj/machinery/mecha_part_fabricator/RefreshParts()
- var/T = 0
+ res_max_amount = 0
for(var/obj/item/weapon/stock_parts/matter_bin/M in component_parts)
+ res_max_amount += M.rating * 100000
+ var/T = 0
+ for(var/obj/item/weapon/stock_parts/manipulator/M in component_parts)
T += M.rating
- res_max_amount = (187500+(T * 37500))
- T = 0
- for(var/obj/item/weapon/stock_parts/micro_laser/Ma in component_parts)
- T += Ma.rating
- if(T >= 1)
- T -= 1
- var/diff
- diff = round(initial(resource_coeff) - (initial(resource_coeff)*(T))/25,0.01)
- if(resource_coeff!=diff)
- resource_coeff = diff
- T = 0
- for(var/obj/item/weapon/stock_parts/manipulator/Ml in component_parts)
- T += Ml.rating
- if(T>= 1)
- T -= 1
- diff = round(initial(time_coeff) - (initial(time_coeff)*(T))/25,0.01)
- if(time_coeff!=diff)
- time_coeff = diff
+ mat_efficiency = 1 - (T - 1) / 4
+ for(var/obj/item/weapon/stock_parts/micro_laser/M in component_parts) // Not resetting T is intended; speed is affected by both
+ T += M.rating
+ speed = T / 2
-/obj/machinery/mecha_part_fabricator/Destroy()
- for(var/atom/A in src)
- qdel(A)
- ..()
- return
-
-/obj/machinery/mecha_part_fabricator/proc/operation_allowed(mob/M)
- if(isrobot(M) || isAI(M))
- return 1
- if(!istype(req_access) || !req_access.len)
- return 1
- else if(istype(M, /mob/living/carbon/human))
- var/mob/living/carbon/human/H = M
- for(var/ID in list(H.get_active_hand(), H.wear_id, H.belt))
- if(src.check_access(ID))
- return 1
- M << "You don't have required permissions to use [src]"
- return 0
-
-/obj/machinery/mecha_part_fabricator/proc/convert_part_set(set_name as text)
- var/list/parts = part_sets[set_name]
- if(istype(parts, /list))
- for(var/i=1;i<=parts.len;i++)
- var/path = parts[i]
- var/part = new path(src)
- if(part)
- parts[i] = part
- //debug below
- if(!istype(parts[i],/obj/item)) return 0
- return
-
-
-/obj/machinery/mecha_part_fabricator/proc/add_part_set(set_name as text,parts=null)
- if(set_name in part_sets)//attempt to create duplicate set
- return 0
- if(isnull(parts))
- part_sets[set_name] = list()
- else
- part_sets[set_name] = parts
- convert_part_set(set_name)
- return 1
-
-/obj/machinery/mecha_part_fabricator/proc/add_part_to_set(set_name as text,part)
- if(!part) return 0
- src.add_part_set(set_name)//if no "set_name" set exists, create
- var/list/part_set = part_sets[set_name]
- var/atom/apart
- if(ispath(part))
- apart = new part(src)
- else
- apart = part
- if(!istype(apart)) return 0
- for(var/obj/O in part_set)
- if(O.type == apart.type)
- qdel(apart)
- return 0
- part_set[++part_set.len] = apart
- return 1
-
-/obj/machinery/mecha_part_fabricator/proc/remove_part_set(set_name as text)
- for(var/i=1,i<=part_sets.len,i++)
- if(part_sets[i]==set_name)
- part_sets.Cut(i,++i)
- return
-/*
- proc/sanity_check()
- for(var/p in resources)
- var/index = resources.Find(p)
- index = resources.Find(p, ++index)
- if(index) //duplicate resource
- world << "Duplicate resource definition for [src](\ref[src])"
- return 0
- for(var/set_name in part_sets)
- var/index = part_sets.Find(set_name)
- index = part_sets.Find(set_name, ++index)
- if(index) //duplicate part set
- world << "Duplicate part set definition for [src](\ref[src])"
- return 0
- return 1
-*/
-
-/obj/machinery/mecha_part_fabricator/proc/output_parts_list(set_name)
- var/output = ""
- var/list/part_set = listgetindex(part_sets, set_name)
- if(istype(part_set))
- for(var/obj/item/part in part_set)
- var/resources_available = check_resources(part)
- output += "
"
- return output
-
-/obj/machinery/mecha_part_fabricator/proc/output_part_info(var/obj/item/part)
- var/output = "[part.name] (Cost: [output_part_cost(part)]) [get_construction_time_w_coeff(part)/10]sec"
- return output
-
-/obj/machinery/mecha_part_fabricator/proc/output_part_cost(var/obj/item/part)
- var/i = 0
- var/output
- if(part.vars.Find("construction_time") && part.vars.Find("construction_cost"))//The most efficient way to go about this. Not all objects have these vars, but if they don't then they CANNOT be made by the mech fab. Doing it this way reduces a major amount of typecasting and switches, while cutting down maintenece for them as well -Sieve
- for(var/c in part:construction_cost)//The check should ensure that anything without the var doesn't make it to this point
- if(c in resources)
- output += "[i?" | ":null][get_resource_cost_w_coeff(part,c)] [c]"
- i++
- return output
- else
- return 0
-
-/obj/machinery/mecha_part_fabricator/proc/output_available_resources()
- var/output
- for(var/resource in resources)
- var/amount = min(res_max_amount, resources[resource])
- output += "[resource]: [amount] cm³"
- if(amount>0)
- output += " - Remove \[1\] | \[10\] | \[All\]"
- output += "
"
- return output
-
-/obj/machinery/mecha_part_fabricator/proc/remove_resources(var/obj/item/part)
-//Be SURE to add any new equipment to this switch, but don't be suprised if it spits out children objects
- if(part.vars.Find("construction_time") && part.vars.Find("construction_cost"))
- for(var/resource in part:construction_cost)
- if(resource in src.resources)
- src.resources[resource] -= get_resource_cost_w_coeff(part,resource)
- else
+/obj/machinery/mecha_part_fabricator/attack_hand(var/mob/user)
+ if(..())
+ return
+ if(!allowed(user))
return
-/obj/machinery/mecha_part_fabricator/proc/check_resources(var/obj/item/part)
-// if(istype(part, /obj/item/robot_parts) || istype(part, /obj/item/mecha_parts) || istype(part,/obj/item/borg/upgrade))
-//Be SURE to add any new equipment to this switch, but don't be suprised if it spits out children objects
- if(part.vars.Find("construction_time") && part.vars.Find("construction_cost"))
- for(var/resource in part:construction_cost)
- if(resource in src.resources)
- if(src.resources[resource] < get_resource_cost_w_coeff(part,resource))
- return 0
- return 1
- else
- return 0
+ ui_interact(user)
-/obj/machinery/mecha_part_fabricator/proc/build_part(var/obj/item/part)
- if(!part) return
+/obj/machinery/mecha_part_fabricator/ui_interact(var/mob/user, var/ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
+ var/data[0]
- // critical exploit prevention, do not remove unless you replace it -walter0o
- if( !(locate(part, src.contents)) || !(part.vars.Find("construction_time")) || !(part.vars.Find("construction_cost")) ) // these 3 are the current requirements for an object being buildable by the mech_fabricator
- return
+ 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["categories"] = categories
+ data["materials"] = get_materials()
+ data["maxres"] = res_max_amount
+ if(current)
+ data["builtperc"] = round((progress / current.time) * 100)
- if(current_manufacturer)
- src.being_built = new part.type(src, current_manufacturer)
- else
- src.being_built = new part.type(src, basic_robolimb.company)
-
- src.desc = "It's building [src.being_built]."
- src.remove_resources(part)
- src.overlays += "fab-active"
- src.use_power = 2
- src.updateUsrDialog()
- sleep(get_construction_time_w_coeff(part))
- src.use_power = 1
- src.overlays -= "fab-active"
- src.desc = initial(src.desc)
- if(being_built)
- src.being_built.Move(get_step(src,output_dir))
- src.visible_message("\icon[src] [src] beeps, \"The following has been completed: [src.being_built] is built\".")
- src.being_built = null
- src.updateUsrDialog()
- return 1
-
-/obj/machinery/mecha_part_fabricator/proc/update_queue_on_page()
- send_byjax(usr,"mecha_fabricator.browser","queue",src.list_queue())
- return
-
-/obj/machinery/mecha_part_fabricator/proc/add_part_set_to_queue(set_name)
- if(set_name in part_sets)
- var/list/part_set = part_sets[set_name]
- if(islist(part_set))
- for(var/obj/item/part in part_set)
- add_to_queue(part)
- return
-
-/obj/machinery/mecha_part_fabricator/proc/add_to_queue(part)
- if(!istype(queue))
- queue = list()
- if(part)
- queue[++queue.len] = part
- return queue.len
-
-/obj/machinery/mecha_part_fabricator/proc/remove_from_queue(index)
- if(!isnum(index) || !istype(queue) || (index<1 || index>queue.len))
- return 0
- queue.Cut(index,++index)
- return 1
-
-/obj/machinery/mecha_part_fabricator/proc/process_queue()
- var/obj/item/part = listgetindex(src.queue, 1)
- if(!part)
- remove_from_queue(1)
- if(src.queue.len)
- return process_queue()
- else
- return
- if(!(part.vars.Find("construction_time")) || !(part.vars.Find("construction_cost")))//If it shouldn't be printed
- remove_from_queue(1)//Take it out of the quene
- return process_queue()//Then reprocess it
- temp = null
- while(part)
- if(stat&(NOPOWER|BROKEN))
- return 0
- if(!check_resources(part))
- src.visible_message("\icon[src] [src] beeps, \"Not enough resources. Queue processing stopped\".")
- temp = {"Not enough resources to build next part.
- Try again | Return"}
- return 0
- remove_from_queue(1)
- build_part(part)
- part = listgetindex(src.queue, 1)
- src.visible_message("\icon[src] [src] beeps, \"Queue processing finished successfully\".")
- return 1
-
-/obj/machinery/mecha_part_fabricator/proc/list_queue()
- var/output = "Queue contains:"
- if(!istype(queue) || !queue.len)
- output += "
Nothing"
- else
- output += ""
- for(var/i=1;i<=queue.len;i++)
- var/obj/item/part = listgetindex(src.queue, i)
- if(istype(part))
- if(part.vars.Find("construction_time") && part.vars.Find("construction_cost"))
- output += "- [part.name] - [i>1?"↑":null] [i↓
":null] Remove"
- else//Prevents junk items from even appearing in the list, and they will be silently removed when the fab processes
- remove_from_queue(i)//Trash it
- return list_queue()//Rebuild it
- output += ""
- output += "\[Process queue | Clear queue\]"
- return output
-
-/obj/machinery/mecha_part_fabricator/proc/convert_designs()
- if(!files) return
- var/i = 0
- for(var/datum/design/D in files.known_designs)
- if(D.build_type&16)
- if(D.category in part_sets)//Checks if it's a valid category
- if(add_part_to_set(D.category, D.build_path))//Adds it to said category
- i++
- else
- if(add_part_to_set("Misc", D.build_path))//If in doubt, chunk it into the Misc
- i++
- return i
-
-/obj/machinery/mecha_part_fabricator/proc/update_tech()
- if(!files) return
- var/output
- for(var/datum/tech/T in files.known_tech)
- if(T && T.level > 1)
- var/diff
- switch(T.id) //bad, bad formulas
- if("materials")
- var/pmat = 0//Calculations to make up for the fact that these parts and tech modify the same thing
- for(var/obj/item/weapon/stock_parts/micro_laser/Ml in component_parts)
- pmat += Ml.rating
- if(pmat >= 1)
- pmat -= 1//So the equations don't have to be reworked, upgrading a single part from T1 to T2 is == to 1 tech level
- diff = round(initial(resource_coeff) - (initial(resource_coeff)*(T.level+pmat))/25,0.01)
- if(resource_coeff!=diff)
- resource_coeff = diff
- output+="Production efficiency increased.
"
- if("programming")
- var/ptime = 0
- for(var/obj/item/weapon/stock_parts/manipulator/Ma in component_parts)
- ptime += Ma.rating
- if(ptime >= 2)
- ptime -= 2
- diff = round(initial(time_coeff) - (initial(time_coeff)*(T.level+ptime))/25,0.1)
- if(time_coeff!=diff)
- time_coeff = diff
- output+="Production routines updated.
"
- return output
-
-
-/obj/machinery/mecha_part_fabricator/proc/sync(silent=null)
-/* if(queue.len)
- if(!silent)
- temp = "Error. Please clear processing queue before updating!"
- src.updateUsrDialog()
- return
-*/
- if(!silent)
- temp = "Updating local R&D database..."
- src.updateUsrDialog()
- sleep(30) //only sleep if called by user
- var/found = 0
- for(var/obj/machinery/computer/rdconsole/RDC in get_area_all_atoms(get_area(src)))
- if(!RDC.sync)
- continue
- found++
- for(var/datum/tech/T in RDC.files.known_tech)
- files.AddTech2Known(T)
- for(var/datum/design/D in RDC.files.known_designs)
- files.AddDesign2Known(D)
- files.RefreshResearch()
- var/i = src.convert_designs()
- var/tech_output = update_tech()
- if(!silent)
- temp = "Processed [i] equipment designs.
"
- temp += tech_output
- temp += "Return"
- src.updateUsrDialog()
- if(i || tech_output)
- src.visible_message("\icon[src] [src] beeps, \"Successfully synchronized with R&D server. New data processed.\"")
- if(found == 0)
- temp = "Couldn't contact R&D server.
"
- temp += "Return"
- src.updateUsrDialog()
- src.visible_message("\icon[src] [src] beeps, \"Error! Couldn't connect to R&D server.\"")
- return
-
-/obj/machinery/mecha_part_fabricator/proc/get_resource_cost_w_coeff(var/obj/item/part as obj,var/resource as text, var/roundto=1)
-//Be SURE to add any new equipment to this switch, but don't be suprised if it spits out children objects
- if(part.vars.Find("construction_time") && part.vars.Find("construction_cost"))
- return round(part:construction_cost[resource]*resource_coeff, roundto)
- else
- return 0
-
-/obj/machinery/mecha_part_fabricator/proc/get_construction_time_w_coeff(var/obj/item/part as obj, var/roundto=1)
-//Be SURE to add any new equipment to this switch, but don't be suprised if it spits out children objects
- if(part.vars.Find("construction_time") && part.vars.Find("construction_cost"))
- return round(part:construction_time*time_coeff, roundto)
- else
- return 0
-
-
-/obj/machinery/mecha_part_fabricator/attack_hand(mob/user as mob)
- var/dat, left_part
- if (..())
- return
- if(!operation_allowed(user))
- return
- user.set_machine(src)
- var/turf/exit = get_step(src,SOUTH)
- if(exit.density)
- src.visible_message("\icon[src] [src] beeps, \"Error! Part outlet is obstructed\".")
- return
- if(temp)
- left_part = temp
- else if(src.being_built)
- left_part = {"Building [src.being_built.name].
- Please wait until completion..."}
- else
- switch(screen)
- if("main")
- left_part = output_available_resources()+"
"
- left_part += "Sync with R&D servers | Set manufacturer ([current_manufacturer])
"
- for(var/part_set in part_sets)
- left_part += "[part_set] - \[Add all parts to queue\]
"
- if("parts")
- left_part += output_parts_list(part_set)
- left_part += "
Return"
- dat = {"
-
- [src.name]
-
-
-
-
-
-
- |
- [left_part]
- |
-
- [list_queue()]
- |
-
-
-
- "}
- user << browse(dat, "window=mecha_fabricator;size=1000x400")
- onclose(user, "mecha_fabricator")
- return
-
-/obj/machinery/mecha_part_fabricator/proc/exploit_prevention(var/obj/Part, mob/user as mob, var/desc_exploit)
-// critical exploit prevention, feel free to improve or replace this, but do not remove it -walter0o
-
- if(!Part || !user || !istype(Part) || !istype(user)) // sanity
- return 1
-
- if( !(locate(Part, src.contents)) || !(Part.vars.Find("construction_time")) || !(Part.vars.Find("construction_cost")) ) // these 3 are the current requirements for an object being buildable by the mech_fabricator
-
- var/turf/LOC = get_turf(user)
- message_admins("[key_name_admin(user)] tried to exploit an Exosuit Fabricator to [desc_exploit ? "get the desc of" : "duplicate"] [Part] ! ([LOC ? "JMP" : "null"])", 0)
- log_admin("EXPLOIT : [key_name(user)] tried to exploit an Exosuit Fabricator to [desc_exploit ? "get the desc of" : "duplicate"] [Part] !")
- return 1
-
- return null
+ ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open)
+ if(!ui)
+ ui = new(user, src, ui_key, "mechfab.tmpl", "Exosuit Fabricator UI", 800, 600)
+ ui.set_initial_data(data)
+ ui.open()
+ ui.set_auto_update(1)
/obj/machinery/mecha_part_fabricator/Topic(href, href_list)
-
- if(..()) // critical exploit prevention, do not remove unless you replace it -walter0o
+ if(..())
return
- var/datum/topic_input/filter = new /datum/topic_input(href,href_list)
- if(href_list["set_manufacturer"])
- var/choice = input(usr, "Which manufacturer do you wish to use for this fabricator?") as null|anything in all_robolimbs
- if(choice) current_manufacturer = choice
- if(href_list["part_set"])
- var/tpart_set = filter.getStr("part_set")
- if(tpart_set)
- if(tpart_set=="clear")
- src.part_set = null
- else
- src.part_set = tpart_set
- screen = "parts"
- if(href_list["part"])
- var/obj/part = filter.getObj("part")
+ if(href_list["build"])
+ add_to_queue(text2num(href_list["build"]))
- // critical exploit prevention, do not remove unless you replace it -walter0o
- if(src.exploit_prevention(part, usr))
- return
+ if(href_list["remove"])
+ remove_from_queue(text2num(href_list["remove"]))
- if(!processing_queue)
- build_part(part)
- else
- add_to_queue(part)
- if(href_list["add_to_queue"])
- var/obj/part = filter.getObj("add_to_queue")
+ if(href_list["category"])
+ if(href_list["category"] in categories)
+ category = href_list["category"]
+//TODO: eject and sync
+ return 1
- // critical exploit prevention, do not remove unless you replace it -walter0o
- if(src.exploit_prevention(part, usr))
- return
-
- add_to_queue(part)
-
- return update_queue_on_page()
- if(href_list["remove_from_queue"])
- remove_from_queue(filter.getNum("remove_from_queue"))
- return update_queue_on_page()
- if(href_list["partset_to_queue"])
- add_part_set_to_queue(filter.get("partset_to_queue"))
- return update_queue_on_page()
- if(href_list["process_queue"])
- spawn(-1)
- if(processing_queue || being_built)
- return 0
- processing_queue = 1
- process_queue()
- processing_queue = 0
-/*
- if(href_list["list_queue"])
- list_queue()
-*/
- if(href_list["clear_temp"])
- temp = null
- if(href_list["screen"])
- src.screen = href_list["screen"]
- if(href_list["queue_move"] && href_list["index"])
- var/index = filter.getNum("index")
- var/new_index = index + filter.getNum("queue_move")
- if(isnum(index) && isnum(new_index))
- if(InRange(new_index,1,queue.len))
- queue.Swap(index,new_index)
- return update_queue_on_page()
- if(href_list["clear_queue"])
- queue = list()
- return update_queue_on_page()
- if(href_list["sync"])
- queue = list()
- src.sync()
- return update_queue_on_page()
- if(href_list["part_desc"])
- var/obj/part = filter.getObj("part_desc")
-
- // critical exploit prevention, do not remove unless you replace it -walter0o
- if(src.exploit_prevention(part, usr, 1))
- return
-
- if(part)
- temp = {"[part] description:
- [part.desc]
- Return
- "}
- if(href_list["remove_mat"] && href_list["material"])
- temp = "Ejected [remove_material(href_list["material"],text2num(href_list["remove_mat"]))] of [href_list["material"]]
Return"
- src.updateUsrDialog()
- return
-
-/obj/machinery/mecha_part_fabricator/proc/remove_material(var/mat_string, var/amount)
- var/type
- switch(mat_string)
- if(DEFAULT_WALL_MATERIAL)
- type = /obj/item/stack/material/steel
- if("glass")
- type = /obj/item/stack/material/glass
- if("gold")
- type = /obj/item/stack/material/gold
- if("silver")
- type = /obj/item/stack/material/silver
- if("diamond")
- type = /obj/item/stack/material/diamond
- if("phoron")
- type = /obj/item/stack/material/phoron
- if("uranium")
- type = /obj/item/stack/material/uranium
- else
- return 0
- var/result = 0
- var/obj/item/stack/material/res = new type(src)
-
- // amount available to take out
- var/total_amount = round(resources[mat_string]/res.perunit)
-
- // number of stacks we're going to take out
- res.amount = round(min(total_amount,amount))
-
- if(res.amount>0)
- resources[mat_string] -= res.amount*res.perunit
- res.Move(src.loc)
- result = res.amount
- else
- qdel(res)
- return result
-
-/obj/machinery/mecha_part_fabricator/emag_act(var/remaining_charges, var/mob/user)
- sleep()
- switch(emagged)
- if(0)
- emagged = 0.5
- src.visible_message("\icon[src] [src] beeps: \"DB error \[Code 0x00F1\]\"")
- sleep(10)
- src.visible_message("\icon[src] [src] beeps: \"Attempting auto-repair\"")
- sleep(15)
- src.visible_message("\icon[src] [src] beeps: \"User DB corrupted \[Code 0x00FA\]. Truncating data structure...\"")
- sleep(30)
- src.visible_message("\icon[src] [src] beeps: \"User DB truncated. Please contact your Nanotrasen system operator for future assistance.\"")
- req_access = null
- emagged = 1
- return 1
- if(0.5)
- src.visible_message("\icon[src] [src] beeps: \"DB not responding \[Code 0x0003\]...\"")
- if(1)
- src.visible_message("\icon[src] [src] beeps: \"No records in User DB\"")
-
-/obj/machinery/mecha_part_fabricator/attackby(obj/W as obj, mob/user as mob)
- if(istype(W,/obj/item/weapon/screwdriver))
- if (!opened)
- opened = 1
- icon_state = "fab-o"
- user << "You open the maintenance hatch of [src]."
- else
- opened = 0
- icon_state = "fab-idle"
- user << "You close the maintenance hatch of [src]."
+/obj/machinery/mecha_part_fabricator/attackby(var/obj/item/I, var/mob/user)
+ if(busy)
+ user << "\The [src] is busy. Please wait for completion of previous operation."
+ return 1
+ if(default_deconstruction_screwdriver(user, I))
+ return
+ if(default_deconstruction_crowbar(user, I))
+ return
+ if(default_part_replacement(user, I))
return
- if (opened)
- if(istype(W, /obj/item/weapon/crowbar))
- playsound(src.loc, 'sound/items/Crowbar.ogg', 50, 1)
- var/obj/machinery/constructable_frame/machine_frame/M = new /obj/machinery/constructable_frame/machine_frame(src.loc)
- M.state = 2
- M.icon_state = "box_1"
- for(var/obj/I in component_parts)
- I.loc = src.loc
- if(src.resources[DEFAULT_WALL_MATERIAL] >= 3750)
- var/obj/item/stack/material/steel/G = new /obj/item/stack/material/steel(src.loc)
- G.amount = round(src.resources[DEFAULT_WALL_MATERIAL] / G.perunit)
- if(src.resources["glass"] >= 3750)
- var/obj/item/stack/material/glass/G = new /obj/item/stack/material/glass(src.loc)
- G.amount = round(src.resources["glass"] / G.perunit)
- if(src.resources["phoron"] >= 2000)
- var/obj/item/stack/material/phoron/G = new /obj/item/stack/material/phoron(src.loc)
- G.amount = round(src.resources["phoron"] / G.perunit)
- if(src.resources["silver"] >= 2000)
- var/obj/item/stack/material/silver/G = new /obj/item/stack/material/silver(src.loc)
- G.amount = round(src.resources["silver"] / G.perunit)
- if(src.resources["gold"] >= 2000)
- var/obj/item/stack/material/gold/G = new /obj/item/stack/material/gold(src.loc)
- G.amount = round(src.resources["gold"] / G.perunit)
- if(src.resources["uranium"] >= 2000)
- var/obj/item/stack/material/uranium/G = new /obj/item/stack/material/uranium(src.loc)
- G.amount = round(src.resources["uranium"] / G.perunit)
- if(src.resources["diamond"] >= 2000)
- var/obj/item/stack/material/diamond/G = new /obj/item/stack/material/diamond(src.loc)
- G.amount = round(src.resources["diamond"] / G.perunit)
- qdel(src)
- return 1
- else
- user << "You can't load the [src.name] while it's opened."
- return 1
var/material
- switch(W.type)
+ switch(I.type)
if(/obj/item/stack/material/gold)
material = "gold"
if(/obj/item/stack/material/silver)
@@ -788,30 +149,118 @@
else
return ..()
- if(src.being_built)
- user << "The fabricator is currently processing. Please wait until completion."
- return
-
- var/obj/item/stack/material/stack = W
-
+ var/obj/item/stack/material/stack = I
var/sname = "[stack.name]"
var/amnt = stack.perunit
- if(src.resources[material] < res_max_amount)
+
+ if(materials[material] + amnt <= res_max_amount)
if(stack && stack.amount >= 1)
var/count = 0
- src.overlays += "fab-load-[material]"//loading animation is now an overlay based on material type. No more spontaneous conversion of all ores to metal. -vey
- sleep(10)
-
- while(src.resources[material] < res_max_amount && stack.amount >= 1)
- src.resources[material] += amnt
+ overlays += "fab-load-metal"
+ spawn(10)
+ overlays -= "fab-load-metal"
+ while(materials[material] + amnt <= res_max_amount && stack.amount >= 1)
+ materials[material] += amnt
stack.use(1)
count++
- src.overlays -= "fab-load-[material]"
user << "You insert [count] [sname] into the fabricator."
- src.updateUsrDialog()
- else
- user << "The fabricator can only accept full sheets of [sname]."
- return
+ update_busy()
else
user << "The fabricator cannot hold more [sname]."
- return
+
+/obj/machinery/mecha_part_fabricator/emag_act(var/remaining_charges, var/mob/user)
+ switch(emagged)
+ if(0)
+ emagged = 0.5
+ visible_message("\icon[src] [src] beeps: \"DB error \[Code 0x00F1\]\"")
+ sleep(10)
+ visible_message("\icon[src] [src] beeps: \"Attempting auto-repair\"")
+ sleep(15)
+ visible_message("\icon[src] [src] beeps: \"User DB corrupted \[Code 0x00FA\]. Truncating data structure...\"")
+ sleep(30)
+ visible_message("\icon[src] [src] beeps: \"User DB truncated. Please contact your Nanotrasen system operator for future assistance.\"")
+ req_access = null
+ emagged = 1
+ return 1
+ if(0.5)
+ visible_message("\icon[src] [src] beeps: \"DB not responding \[Code 0x0003\]...\"")
+ if(1)
+ visible_message("\icon[src] [src] beeps: \"No records in User DB\"")
+
+/obj/machinery/mecha_part_fabricator/proc/update_busy()
+ if(queue.len)
+ if(can_build(queue[1]))
+ busy = 1
+ else
+ busy = 0
+ else
+ busy = 0
+
+/obj/machinery/mecha_part_fabricator/proc/add_to_queue(var/index)
+ var/datum/design/D = files.known_designs[index]
+ queue += D
+ update_busy()
+
+/obj/machinery/mecha_part_fabricator/proc/remove_from_queue(var/index)
+ if(index == 1)
+ progress = 0
+ queue.Cut(index, index + 1)
+ update_busy()
+
+/obj/machinery/mecha_part_fabricator/proc/can_build(var/datum/design/D)
+ for(var/M in D.materials)
+ if(materials[M] < D.materials[M])
+ return 0
+ return 1
+
+/obj/machinery/mecha_part_fabricator/proc/finalize()
+ if(!queue.len)
+ return
+ var/datum/design/D = queue[1]
+ if(D.time > progress)
+ return
+ if(!can_build(D))
+ return
+ for(var/M in D.materials)
+ materials[M] = max(0, materials[M] - D.materials[M] * mat_efficiency)
+ if(D.build_path)
+ new D.build_path(loc)//var/obj/new_item = //TODO
+ queue.Cut(1, 2)
+ update_busy()
+
+/obj/machinery/mecha_part_fabricator/proc/get_queue_names()
+ . = list()
+ for(var/i = 2 to queue.len)
+ var/datum/design/D = queue[i]
+ . += D.name
+
+/obj/machinery/mecha_part_fabricator/proc/get_build_options()
+ . = list()
+ for(var/i = 1 to files.known_designs.len)
+ var/datum/design/D = files.known_designs[i]
+ if(!D.build_path || !(D.build_type & MECHFAB))
+ continue
+ . += list(list("name" = D.name, "id" = i, "category" = D.category, "resourses" = get_design_resourses(D), "time" = get_design_time(D)))
+
+/obj/machinery/mecha_part_fabricator/proc/get_design_resourses(var/datum/design/D)
+ var/list/F = list()
+ for(var/T in D.materials)
+ F += "[capitalize(T)]: [D.materials[T] * mat_efficiency]"
+ return english_list(F, and_text = ", ")
+
+/obj/machinery/mecha_part_fabricator/proc/get_design_time(var/datum/design/D)
+ return time2text(round(10 * D.time / speed), "mm:ss")
+
+/obj/machinery/mecha_part_fabricator/proc/update_categories()
+ categories = list()
+ for(var/datum/design/D in files.known_designs)
+ if(!D.build_path || !(D.build_type & MECHFAB))
+ continue
+ categories |= D.category
+ if(!category || !(category in categories))
+ category = categories[1]
+
+/obj/machinery/mecha_part_fabricator/proc/get_materials()
+ . = list()
+ for(var/T in materials)
+ . += list(list("mat" = capitalize(T), "amt" = materials[T]))
diff --git a/code/game/objects/items/robot/robot_parts.dm b/code/game/objects/items/robot/robot_parts.dm
index 96ddeccec64..5c4ae785d23 100644
--- a/code/game/objects/items/robot/robot_parts.dm
+++ b/code/game/objects/items/robot/robot_parts.dm
@@ -69,8 +69,6 @@
desc = "A heavily reinforced case containing cyborg logic boards, with space for a standard power cell."
icon_state = "chest"
part = list("groin","chest")
- construction_time = 350
- construction_cost = list(DEFAULT_WALL_MATERIAL=40000)
var/wires = 0.0
var/obj/item/weapon/cell/cell = null
@@ -79,8 +77,6 @@
desc = "A standard reinforced braincase, with spine-plugged neural socket and sensor gimbals."
icon_state = "head"
part = list("head")
- construction_time = 350
- construction_cost = list(DEFAULT_WALL_MATERIAL=25000)
var/obj/item/device/flash/flash1 = null
var/obj/item/device/flash/flash2 = null
@@ -88,8 +84,6 @@
name = "endoskeleton"
desc = "A complex metal backbone with standard limb sockets and pseudomuscle anchors."
icon_state = "robo_suit"
- construction_time = 500
- construction_cost = list(DEFAULT_WALL_MATERIAL=50000)
var/obj/item/robot_parts/l_arm/l_arm = null
var/obj/item/robot_parts/r_arm/r_arm = null
var/obj/item/robot_parts/l_leg/l_leg = null
diff --git a/code/modules/research/designs.dm b/code/modules/research/designs.dm
index c0841e95eba..3d26f3f3555 100644
--- a/code/modules/research/designs.dm
+++ b/code/modules/research/designs.dm
@@ -1438,154 +1438,6 @@ CIRCUITS BELOW
build_path = /obj/item/weapon/cartridge/captain
sort_string = "VBAAO"
-/*
-MECHAS BELOW
-*/
-
-/datum/design/item/mecha
- build_type = MECHFAB
- req_tech = list(TECH_COMBAT = 3)
- category = "Exosuit Equipment"
-
-/datum/design/item/mecha/AssembleDesignName()
- ..()
- name = "Exosuit module design ([item_name])"
-
-/datum/design/item/mecha/weapon/AssembleDesignName()
- ..()
- name = "Exosuit weapon design ([item_name])"
-
-/datum/design/item/mecha/AssembleDesignDesc()
- if(!desc)
- desc = "Allows for the construction of \a '[item_name]' exosuit module."
-
-// *** Weapon modules
-/datum/design/item/mecha/weapon/scattershot
- id = "mech_scattershot"
- req_tech = list(TECH_COMBAT = 4)
- build_path = /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/scattershot
-
-/datum/design/item/mecha/weapon/laser
- id = "mech_laser"
- req_tech = list(TECH_COMBAT = 3, TECH_MAGNET = 3)
- build_path = /obj/item/mecha_parts/mecha_equipment/weapon/energy/laser
-
-/datum/design/item/mecha/weapon/laser_rigged
- desc = "Allows for the construction of a welder-laser assembly package for non-combat exosuits."
- id = "mech_laser_rigged"
- req_tech = list(TECH_COMBAT = 2, TECH_MAGNET = 2)
- build_path = /obj/item/mecha_parts/mecha_equipment/weapon/energy/riggedlaser
-
-/datum/design/item/mecha/weapon/laser_heavy
- id = "mech_laser_heavy"
- req_tech = list(TECH_COMBAT = 4, TECH_MAGNET = 4)
- build_path = /obj/item/mecha_parts/mecha_equipment/weapon/energy/laser/heavy
-
-/datum/design/item/mecha/weapon/ion
- id = "mech_ion"
- req_tech = list(TECH_COMBAT = 4, TECH_MAGNET = 4)
- build_path = /obj/item/mecha_parts/mecha_equipment/weapon/energy/ion
-
-/datum/design/item/mecha/weapon/grenade_launcher
- id = "mech_grenade_launcher"
- req_tech = list(TECH_COMBAT = 3)
- build_path = /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/missile_rack/flashbang
-
-/datum/design/item/mecha/weapon/clusterbang_launcher
- desc = "A weapon that violates the Geneva Convention at 6 rounds per minute."
- id = "clusterbang_launcher"
- req_tech = list(TECH_COMBAT= 5, TECH_MATERIAL = 5, TECH_ILLEGAL = 3)
- build_path = /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/missile_rack/flashbang/clusterbang/limited
-
-// *** Nonweapon modules
-/datum/design/item/mecha/wormhole_gen
- desc = "An exosuit module that can generate small quasi-stable wormholes."
- id = "mech_wormhole_gen"
- req_tech = list(TECH_BLUESPACE = 3, TECH_MAGNET = 2)
- build_path = /obj/item/mecha_parts/mecha_equipment/wormhole_generator
-
-/datum/design/item/mecha/teleporter
- desc = "An exosuit module that allows teleportation to any position in view."
- id = "mech_teleporter"
- req_tech = list(TECH_BLUESPACE = 10, TECH_MAGNET = 5)
- build_path = /obj/item/mecha_parts/mecha_equipment/teleporter
-
-/datum/design/item/mecha/rcd
- desc = "An exosuit-mounted rapid construction device."
- id = "mech_rcd"
- req_tech = list(TECH_MATERIAL = 4, TECH_BLUESPACE = 3, TECH_MAGNET = 4, TECH_POWER=4, TECH_ENGINERING = 4)
- build_path = /obj/item/mecha_parts/mecha_equipment/tool/rcd
-
-/datum/design/item/mecha/gravcatapult
- desc = "An exosuit-mounted gravitational catapult."
- id = "mech_gravcatapult"
- req_tech = list(TECH_BLUESPACE = 2, TECH_MAGNET = 3, TECH_ENGINERING = 3)
- build_path = /obj/item/mecha_parts/mecha_equipment/gravcatapult
-
-/datum/design/item/mecha/repair_droid
- desc = "Automated repair droid, exosuits' best companion. BEEP BOOP"
- id = "mech_repair_droid"
- req_tech = list(TECH_MAGNET = 3, TECH_DATA = 3, TECH_ENGINERING = 3)
- build_path = /obj/item/mecha_parts/mecha_equipment/repair_droid
-
-/datum/design/item/mecha/phoron_generator
- desc = "Exosuit-mounted phoron generator."
- id = "mech_phoron_generator"
- req_tech = list(TECH_PHORON = 2, TECH_POWER= 2, TECH_ENGINERING = 2)
- build_path = /obj/item/mecha_parts/mecha_equipment/generator
-
-/datum/design/item/mecha/energy_relay
- id = "mech_energy_relay"
- req_tech = list(TECH_MAGNET = 4, TECH_POWER = 3)
- build_path = /obj/item/mecha_parts/mecha_equipment/tesla_energy_relay
-
-/datum/design/item/mecha/ccw_armor
- desc = "Exosuit close-combat armor booster."
- id = "mech_ccw_armor"
- req_tech = list(TECH_MATERIAL = 5, TECH_COMBAT = 4)
- build_path = /obj/item/mecha_parts/mecha_equipment/anticcw_armor_booster
-
-/datum/design/item/mecha/proj_armor
- desc = "Exosuit projectile armor booster."
- id = "mech_proj_armor"
- req_tech = list(TECH_MATERIAL = 5, TECH_COMBAT = 5, TECH_ENGINERING=3)
- build_path = /obj/item/mecha_parts/mecha_equipment/antiproj_armor_booster
-
-/datum/design/item/mecha/syringe_gun
- desc = "Exosuit-mounted syringe gun and chemical synthesizer."
- id = "mech_syringe_gun"
- req_tech = list(TECH_MATERIAL = 3, TECH_BIO=4, TECH_MAGNET=4, TECH_DATA=3)
- build_path = /obj/item/mecha_parts/mecha_equipment/tool/syringe_gun
-
-/datum/design/item/mecha/diamond_drill
- desc = "A diamond version of the exosuit drill. It's harder, better, faster, stronger."
- id = "mech_diamond_drill"
- req_tech = list(TECH_MATERIAL = 4, TECH_ENGINERING = 3)
- build_path = /obj/item/mecha_parts/mecha_equipment/tool/drill/diamonddrill
-
-/datum/design/item/mecha/generator_nuclear
- desc = "Exosuit-held nuclear reactor. Converts uranium and everyone's health to energy."
- id = "mech_generator_nuclear"
- req_tech = list(TECH_POWER= 3, TECH_ENGINERING = 3, TECH_MATERIAL = 3)
- build_path = /obj/item/mecha_parts/mecha_equipment/generator/nuclear
-
-/datum/design/item/synthetic_flash
- id = "sflash"
- req_tech = list(TECH_MAGNET = 3, TECH_COMBAT = 2)
- build_type = MECHFAB
- materials = list("metal" = 750, "glass" = 750)
- build_path = /obj/item/device/flash/synthetic
- category = "Misc"
-
-/datum/design/item/borg_syndicate_module
- name = "Cyborg lethal weapons upgrade"
- desc = "Allows for the construction of lethal upgrades for cyborgs."
- id = "borg_syndicate_module"
- build_type = MECHFAB
- req_tech = list(TECH_COMBAT = 4, TECH_ILLEGAL = 3)
- build_path = /obj/item/borg/upgrade/syndicate
- category = "Cyborg Upgrade Modules"
-
/* Uncomment if someone makes these buildable
/datum/design/circuit/general_alert
name = "general alert console"
diff --git a/code/modules/research/mechfab_designs.dm b/code/modules/research/mechfab_designs.dm
new file mode 100644
index 00000000000..ce2813131c1
--- /dev/null
+++ b/code/modules/research/mechfab_designs.dm
@@ -0,0 +1,257 @@
+/datum/design/item/mechfab
+ build_type = MECHFAB
+ category = "Misc"
+
+/datum/design/item/mechfab/robot
+ category = "Robot"
+ req_tech = list(TECH_MATERIAL = 1)
+
+/datum/design/item/mechfab/robot/exoskeleton
+ name = "Robot Exoskeleton"
+ id = "robot_exoskeleton"
+ build_path = /obj/item/robot_parts/robot_suit
+ time = 50
+ materials = list(DEFAULT_WALL_MATERIAL = 50000)
+
+/datum/design/item/mechfab/robot/torso
+ name = "Robot Torso"
+ id = "robot_torso"
+ build_path = /obj/item/robot_parts/chest
+ time = 35
+ materials = list(DEFAULT_WALL_MATERIAL = 40000)
+
+/datum/design/item/mechfab/robot/head
+ name = "Robot Head"
+ id = "robot_head"
+ build_path = /obj/item/robot_parts/head
+ time = 35
+ materials = list(DEFAULT_WALL_MATERIAL = 25000)
+
+/*
+ var/list/part_sets = list( //set names must be unique
+ "Robot"=list(
+ /obj/item/robot_parts/robot_suit,
+ /obj/item/robot_parts/chest,
+ /obj/item/robot_parts/head,
+ /obj/item/robot_parts/l_arm,
+ /obj/item/robot_parts/r_arm,
+ /obj/item/robot_parts/l_leg,
+ /obj/item/robot_parts/r_leg,
+ /obj/item/robot_parts/robot_component/binary_communication_device,
+ /obj/item/robot_parts/robot_component/radio,
+ /obj/item/robot_parts/robot_component/actuator,
+ /obj/item/robot_parts/robot_component/diagnosis_unit,
+ /obj/item/robot_parts/robot_component/camera,
+ /obj/item/robot_parts/robot_component/armour
+ ),
+ "Ripley"=list(
+ /obj/item/mecha_parts/chassis/ripley,
+ /obj/item/mecha_parts/part/ripley_torso,
+ /obj/item/mecha_parts/part/ripley_left_arm,
+ /obj/item/mecha_parts/part/ripley_right_arm,
+ /obj/item/mecha_parts/part/ripley_left_leg,
+ /obj/item/mecha_parts/part/ripley_right_leg
+ ),
+ "Odysseus"=list(
+ /obj/item/mecha_parts/chassis/odysseus,
+ /obj/item/mecha_parts/part/odysseus_torso,
+ /obj/item/mecha_parts/part/odysseus_head,
+ /obj/item/mecha_parts/part/odysseus_left_arm,
+ /obj/item/mecha_parts/part/odysseus_right_arm,
+ /obj/item/mecha_parts/part/odysseus_left_leg,
+ /obj/item/mecha_parts/part/odysseus_right_leg
+ ),
+
+ "Gygax"=list(
+ /obj/item/mecha_parts/chassis/gygax,
+ /obj/item/mecha_parts/part/gygax_torso,
+ /obj/item/mecha_parts/part/gygax_head,
+ /obj/item/mecha_parts/part/gygax_left_arm,
+ /obj/item/mecha_parts/part/gygax_right_arm,
+ /obj/item/mecha_parts/part/gygax_left_leg,
+ /obj/item/mecha_parts/part/gygax_right_leg,
+ /obj/item/mecha_parts/part/gygax_armour
+ ),
+ "Durand"=list(
+ /obj/item/mecha_parts/chassis/durand,
+ /obj/item/mecha_parts/part/durand_torso,
+ /obj/item/mecha_parts/part/durand_head,
+ /obj/item/mecha_parts/part/durand_left_arm,
+ /obj/item/mecha_parts/part/durand_right_arm,
+ /obj/item/mecha_parts/part/durand_left_leg,
+ /obj/item/mecha_parts/part/durand_right_leg,
+ /obj/item/mecha_parts/part/durand_armour
+ ),
+ "Exosuit Equipment"=list(
+ /obj/item/mecha_parts/mecha_equipment/tool/hydraulic_clamp,
+ /obj/item/mecha_parts/mecha_equipment/tool/drill,
+ /obj/item/mecha_parts/mecha_equipment/tool/extinguisher,
+ /obj/item/mecha_parts/mecha_equipment/tool/cable_layer,
+ /obj/item/mecha_parts/mecha_equipment/tool/sleeper,
+ /obj/item/mecha_parts/mecha_equipment/tool/syringe_gun,
+ /obj/item/mecha_parts/mecha_equipment/tool/passenger,
+ /obj/item/mecha_parts/chassis/firefighter,
+ ///obj/item/mecha_parts/mecha_equipment/repair_droid,
+ /obj/item/mecha_parts/mecha_equipment/generator,
+ ///obj/item/mecha_parts/mecha_equipment/jetpack, //TODO MECHA JETPACK SPRITE MISSING
+ /obj/item/mecha_parts/mecha_equipment/weapon/energy/taser,
+ /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/lmg
+ ),
+
+ "Robotic Upgrade Modules" = list(
+ /obj/item/borg/upgrade/reset,
+ /obj/item/borg/upgrade/rename,
+ /obj/item/borg/upgrade/restart,
+ /obj/item/borg/upgrade/vtec,
+ /obj/item/borg/upgrade/tasercooler,
+ /obj/item/borg/upgrade/jetpack
+ ),
+
+ "Misc"=list(/obj/item/mecha_parts/mecha_tracking)
+ )
+ */
+
+
+/datum/design/item/mecha
+ build_type = MECHFAB
+ req_tech = list(TECH_COMBAT = 3)
+ category = "Exosuit Equipment"
+
+/datum/design/item/mecha/AssembleDesignName()
+ ..()
+ name = "Exosuit module design ([item_name])"
+
+/datum/design/item/mecha/weapon/AssembleDesignName()
+ ..()
+ name = "Exosuit weapon design ([item_name])"
+
+/datum/design/item/mecha/AssembleDesignDesc()
+ if(!desc)
+ desc = "Allows for the construction of \a '[item_name]' exosuit module."
+
+// *** Weapon modules
+/datum/design/item/mecha/weapon/scattershot
+ id = "mech_scattershot"
+ req_tech = list(TECH_COMBAT = 4)
+ build_path = /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/scattershot
+
+/datum/design/item/mecha/weapon/laser
+ id = "mech_laser"
+ req_tech = list(TECH_COMBAT = 3, TECH_MAGNET = 3)
+ build_path = /obj/item/mecha_parts/mecha_equipment/weapon/energy/laser
+
+/datum/design/item/mecha/weapon/laser_rigged
+ desc = "Allows for the construction of a welder-laser assembly package for non-combat exosuits."
+ id = "mech_laser_rigged"
+ req_tech = list(TECH_COMBAT = 2, TECH_MAGNET = 2)
+ build_path = /obj/item/mecha_parts/mecha_equipment/weapon/energy/riggedlaser
+
+/datum/design/item/mecha/weapon/laser_heavy
+ id = "mech_laser_heavy"
+ req_tech = list(TECH_COMBAT = 4, TECH_MAGNET = 4)
+ build_path = /obj/item/mecha_parts/mecha_equipment/weapon/energy/laser/heavy
+
+/datum/design/item/mecha/weapon/ion
+ id = "mech_ion"
+ req_tech = list(TECH_COMBAT = 4, TECH_MAGNET = 4)
+ build_path = /obj/item/mecha_parts/mecha_equipment/weapon/energy/ion
+
+/datum/design/item/mecha/weapon/grenade_launcher
+ id = "mech_grenade_launcher"
+ req_tech = list(TECH_COMBAT = 3)
+ build_path = /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/missile_rack/flashbang
+
+/datum/design/item/mecha/weapon/clusterbang_launcher
+ desc = "A weapon that violates the Geneva Convention at 6 rounds per minute."
+ id = "clusterbang_launcher"
+ req_tech = list(TECH_COMBAT= 5, TECH_MATERIAL = 5, TECH_ILLEGAL = 3)
+ build_path = /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/missile_rack/flashbang/clusterbang/limited
+
+// *** Nonweapon modules
+/datum/design/item/mecha/wormhole_gen
+ desc = "An exosuit module that can generate small quasi-stable wormholes."
+ id = "mech_wormhole_gen"
+ req_tech = list(TECH_BLUESPACE = 3, TECH_MAGNET = 2)
+ build_path = /obj/item/mecha_parts/mecha_equipment/wormhole_generator
+
+/datum/design/item/mecha/teleporter
+ desc = "An exosuit module that allows teleportation to any position in view."
+ id = "mech_teleporter"
+ req_tech = list(TECH_BLUESPACE = 10, TECH_MAGNET = 5)
+ build_path = /obj/item/mecha_parts/mecha_equipment/teleporter
+
+/datum/design/item/mecha/rcd
+ desc = "An exosuit-mounted rapid construction device."
+ id = "mech_rcd"
+ req_tech = list(TECH_MATERIAL = 4, TECH_BLUESPACE = 3, TECH_MAGNET = 4, TECH_POWER=4, TECH_ENGINERING = 4)
+ build_path = /obj/item/mecha_parts/mecha_equipment/tool/rcd
+
+/datum/design/item/mecha/gravcatapult
+ desc = "An exosuit-mounted gravitational catapult."
+ id = "mech_gravcatapult"
+ req_tech = list(TECH_BLUESPACE = 2, TECH_MAGNET = 3, TECH_ENGINERING = 3)
+ build_path = /obj/item/mecha_parts/mecha_equipment/gravcatapult
+
+/datum/design/item/mecha/repair_droid
+ desc = "Automated repair droid, exosuits' best companion. BEEP BOOP"
+ id = "mech_repair_droid"
+ req_tech = list(TECH_MAGNET = 3, TECH_DATA = 3, TECH_ENGINERING = 3)
+ build_path = /obj/item/mecha_parts/mecha_equipment/repair_droid
+
+/datum/design/item/mecha/phoron_generator
+ desc = "Exosuit-mounted phoron generator."
+ id = "mech_phoron_generator"
+ req_tech = list(TECH_PHORON = 2, TECH_POWER= 2, TECH_ENGINERING = 2)
+ build_path = /obj/item/mecha_parts/mecha_equipment/generator
+
+/datum/design/item/mecha/energy_relay
+ id = "mech_energy_relay"
+ req_tech = list(TECH_MAGNET = 4, TECH_POWER = 3)
+ build_path = /obj/item/mecha_parts/mecha_equipment/tesla_energy_relay
+
+/datum/design/item/mecha/ccw_armor
+ desc = "Exosuit close-combat armor booster."
+ id = "mech_ccw_armor"
+ req_tech = list(TECH_MATERIAL = 5, TECH_COMBAT = 4)
+ build_path = /obj/item/mecha_parts/mecha_equipment/anticcw_armor_booster
+
+/datum/design/item/mecha/proj_armor
+ desc = "Exosuit projectile armor booster."
+ id = "mech_proj_armor"
+ req_tech = list(TECH_MATERIAL = 5, TECH_COMBAT = 5, TECH_ENGINERING=3)
+ build_path = /obj/item/mecha_parts/mecha_equipment/antiproj_armor_booster
+
+/datum/design/item/mecha/syringe_gun
+ desc = "Exosuit-mounted syringe gun and chemical synthesizer."
+ id = "mech_syringe_gun"
+ req_tech = list(TECH_MATERIAL = 3, TECH_BIO=4, TECH_MAGNET=4, TECH_DATA=3)
+ build_path = /obj/item/mecha_parts/mecha_equipment/tool/syringe_gun
+
+/datum/design/item/mecha/diamond_drill
+ desc = "A diamond version of the exosuit drill. It's harder, better, faster, stronger."
+ id = "mech_diamond_drill"
+ req_tech = list(TECH_MATERIAL = 4, TECH_ENGINERING = 3)
+ build_path = /obj/item/mecha_parts/mecha_equipment/tool/drill/diamonddrill
+
+/datum/design/item/mecha/generator_nuclear
+ desc = "Exosuit-held nuclear reactor. Converts uranium and everyone's health to energy."
+ id = "mech_generator_nuclear"
+ req_tech = list(TECH_POWER= 3, TECH_ENGINERING = 3, TECH_MATERIAL = 3)
+ build_path = /obj/item/mecha_parts/mecha_equipment/generator/nuclear
+
+/datum/design/item/synthetic_flash
+ id = "sflash"
+ req_tech = list(TECH_MAGNET = 3, TECH_COMBAT = 2)
+ build_type = MECHFAB
+ materials = list("metal" = 750, "glass" = 750)
+ build_path = /obj/item/device/flash/synthetic
+ category = "Misc"
+
+/datum/design/item/borg_syndicate_module
+ name = "Cyborg lethal weapons upgrade"
+ desc = "Allows for the construction of lethal upgrades for cyborgs."
+ id = "borg_syndicate_module"
+ build_type = MECHFAB
+ req_tech = list(TECH_COMBAT = 4, TECH_ILLEGAL = 3)
+ build_path = /obj/item/borg/upgrade/syndicate
+ category = "Cyborg Upgrade Modules"
\ No newline at end of file
diff --git a/maps/exodus-1.dmm b/maps/exodus-1.dmm
index bac04445db7..9be5ac20524 100644
--- a/maps/exodus-1.dmm
+++ b/maps/exodus-1.dmm
@@ -7061,7 +7061,7 @@
"cFX" = (/obj/structure/grille,/obj/structure/window/reinforced,/obj/structure/window/reinforced{dir = 4},/obj/structure/window/reinforced{dir = 1},/turf/simulated/floor/plating,/area/engineering/drone_fabrication)
"cFY" = (/obj/structure/grille,/obj/structure/window/reinforced,/obj/structure/window/reinforced{dir = 8},/turf/simulated/floor/plating,/area/engineering/drone_fabrication)
"cFZ" = (/obj/machinery/computer/drone_control,/turf/simulated/floor/plating{dir = 2; icon_state = "warnplate"},/area/engineering/drone_fabrication)
-"cGa" = (/obj/machinery/alarm{dir = 1; icon_state = "alarm0"; pixel_y = -22},/obj/machinery/light,/obj/machinery/mecha_part_fabricator{output_dir = 1},/turf/simulated/floor/plating{icon_state = "warnplate"; dir = 10},/area/engineering/drone_fabrication)
+"cGa" = (/obj/machinery/alarm{dir = 1; icon_state = "alarm0"; pixel_y = -22},/obj/machinery/light,/obj/machinery/mecha_part_fabricator,/turf/simulated/floor/plating{icon_state = "warnplate"; dir = 10},/area/engineering/drone_fabrication)
"cGb" = (/obj/machinery/light,/obj/machinery/power/apc{dir = 2; name = "south bump"; pixel_y = -24},/obj/structure/cable{icon_state = "0-4"; d2 = 4},/obj/machinery/power/sensor{name = "Powernet Sensor - Master Grid"; name_tag = "Master"},/turf/simulated/floor/plating{icon_state = "warnplate"; dir = 6},/area/engineering/drone_fabrication)
"cGc" = (/obj/structure/bed/chair{dir = 8},/turf/simulated/floor/plating{dir = 2; icon_state = "warnplate"},/area/engineering/drone_fabrication)
"cGd" = (/obj/structure/cable{d1 = 1; d2 = 8; icon_state = "1-8"},/obj/structure/cable{d1 = 1; d2 = 2; icon_state = "1-2"},/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers,/obj/machinery/atmospherics/pipe/simple/hidden/supply,/turf/simulated/floor/plating,/area/engineering/drone_fabrication)
diff --git a/nano/templates/mechfab.tmpl b/nano/templates/mechfab.tmpl
new file mode 100644
index 00000000000..78e33705073
--- /dev/null
+++ b/nano/templates/mechfab.tmpl
@@ -0,0 +1,51 @@
+Exosuit Fabricator
+
+
+ {{for data.categories}}
+ {{:helper.link(value, null, {'category' : value}, value == data.category ? 'selected' : null)}}
+ {{empty}}
+ There are no known designs
+ {{/for}}
+
+
+ {{for data.buildable}}
+ {{if value.category == data.category}}
+
+ {{:helper.link(value.name, null, {'build' : value.id})}} - {{:value.resourses}}, {{:value.time}}
+
+ {{/if}}
+ {{/for}}
+
+
+
+
+
+ Queue contains:
+
+ {{if data.current}}
+
+ Now: {{:data.current}} ({{:data.builtperc}}% ready) - {{:helper.link('Cancel', null, {'remove' : 1})}}
+
+ {{for data.queue}}
+
+ {{:index + 2}}: {{:value}} - {{:helper.link('Remove', null, {'remove' : index + 2})}}
+
+ {{empty}}
+
+ The queue is empty
+
+ {{/for}}
+ {{else}}
+ Nothing
+ {{/if}}
+
+
+
Materials:
+ {{for data.materials}}
+
+ {{:value.mat}}: {{:value.amt}}/{{:data.maxres}}
+
+ {{/for}}
+
+
+