diff --git a/code/__HELPERS/maths.dm b/code/__HELPERS/maths.dm
index 82128de15ffd9..7f6d114ebab70 100644
--- a/code/__HELPERS/maths.dm
+++ b/code/__HELPERS/maths.dm
@@ -20,6 +20,10 @@ var/list/sqrtTable = list(1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4,
/proc/Ceiling(x)
return -round(-x)
+/proc/Floor(x)
+ return -round(-x)-1
+
+
///proc/Clamp(val, min, max)
// return max(min, min(val, max))
diff --git a/code/_macros.dm b/code/_macros.dm
new file mode 100644
index 0000000000000..b3359678236e7
--- /dev/null
+++ b/code/_macros.dm
@@ -0,0 +1,53 @@
+#define Clamp(x, y, z) (x <= y ? y : (x >= z ? z : x))
+
+#define CLAMP01(x) (Clamp(x, 0, 1))
+
+//Already defined
+
+//#define get_turf(A) get_step(A,0)
+
+//#define isAI(A) istype(A, /mob/living/silicon/ai)
+
+//#define isalien(A) istype(A, /mob/living/carbon/alien)
+
+//#define isanimal(A) istype(A, /mob/living/simple_animal)
+
+//#define isairlock(A) istype(A, /obj/machinery/door/airlock)
+
+//#define isbrain(A) istype(A, /mob/living/carbon/brain)
+
+//#define iscarbon(A) istype(A, /mob/living/carbon)
+
+//#define iscorgi(A) istype(A, /mob/living/simple_animal/corgi)
+
+//#define isEye(A) istype(A, /mob/observer/eye)
+
+//#define ishuman(A) istype(A, /mob/living/carbon/human)
+
+//#define isliving(A) istype(A, /mob/living)
+
+//#define ismouse(A) istype(A, /mob/living/simple_animal/mouse)
+
+//#define isnewplayer(A) istype(A, /mob/new_player)
+
+//#define isobserver(A) istype(A, /mob/observer/dead)
+
+//#define isorgan(A) istype(A, /obj/item/organ/external)
+
+//#define ispAI(A) istype(A, /mob/living/silicon/pai)
+
+//#define isrobot(A) istype(A, /mob/living/silicon/robot)
+
+//#define issilicon(A) istype(A, /mob/living/silicon)
+
+//#define isslime(A) istype(A, /mob/living/carbon/slime)
+
+//#define isxeno(A) istype(A, /mob/living/simple_animal/xeno)
+
+#define isweakref(A) istype(A, /weakref)
+
+#define RANDOM_BLOOD_TYPE pick(4;"O-", 36;"O+", 3;"A-", 28;"A+", 1;"B-", 20;"B+", 1;"AB-", 5;"AB+")
+
+#define to_chat(target, message) target << message
+
+#define CanInteract(user,src) user.Adjacent(src) //changed so you need to be next to it is all
\ No newline at end of file
diff --git a/code/datums/EPv2.dm b/code/datums/EPv2.dm
new file mode 100644
index 0000000000000..f0e912fa55a95
--- /dev/null
+++ b/code/datums/EPv2.dm
@@ -0,0 +1,127 @@
+/*
+Exonet Protocol Version 2
+
+This is designed to be a fairly simple fake-networking system, allowing you to send and receive messages
+between the exonet_protocol datums, and for atoms to react to those messages, based on the contents of the message.
+Hopefully, this can evolve to be a more robust fake-networking system and allow for some devious network hacking in the future.
+
+Version 1 never existed.
+
+*Setting up*
+
+To set up the exonet link, define a variable on your desired atom it is like this;
+ var/datum/exonet_protocol/exonet = null
+Afterwards, before you want to do networking, call exonet = New(src), then exonet.make_address(string), and give it a string to hash into the new IP.
+The reason it needs a string is so you can have the addresses be persistant, assuming no-one already took it first.
+
+When you're no longer wanting to use the address and want to free it up, like when you want to Destroy() it, you need to call remove_address()
+
+*Sending messages*
+
+To send a message to another datum, you need to know it's EPv2 (fake IP) address. Once you know that, call send_message(), place your
+intended address in the first argument, then the message in the second. For example, send_message(exonet.address, "ping") will make you
+ping yourself.
+
+*Receiving messages*
+You don't need to do anything special to receive the messages, other than give your target exonet datum an address as well. Once something hits
+your datum with send_message(), receive_message() is called, and the default action is to call receive_exonet_message() on the datum's holder.
+You'll want to override receive_exonet_message() on your atom, and define what will occur when the message is received.
+The receiving atom will receive the origin atom (the atom that sent the message), the origin address, and finally the message itself.
+It's suggested to start with an if or switch statement for the message, to determine what to do.
+*/
+
+var/global/list/all_exonet_connections = list()
+
+/datum/exonet_protocol
+ var/address = "" //Resembles IPv6, but with only five 'groups', e.g. XXXX:XXXX:XXXX:XXXX:XXXX
+ var/atom/movable/holder = null
+
+/datum/exonet_protocol/New(var/atom/holder)
+ src.holder = holder
+ ..()
+
+
+// Proc: make_address()
+// Parameters: 1 (string - used to make into a hash that will be part of the new address)
+// Description: Allocates a new address based on the string supplied. It results in consistant addresses for each round assuming it is not already taken..
+/datum/exonet_protocol/proc/make_address(var/string)
+ if(string)
+ var/new_address = null
+ while(new_address == find_address(new_address)) //Collision test.
+ var/hash = md5(string)
+ var/raw_address = copytext(hash,1,25)
+ var/addr_0 = "fc00" //Used for unique local address in real-life IPv6.
+ var/addr_1 = hexadecimal_to_EPv2(raw_address)
+
+ new_address = "[addr_0]:[addr_1]"
+ string = "[string]0" //If we did get a collision, this should make the next attempt not have one.
+ sleep(1)
+ address = new_address
+ all_exonet_connections |= src
+
+
+// Proc: make_arbitrary_address()
+// Parameters: 1 (new_address - the desired address)
+// Description: Allocates that specific address, if it is available.
+/datum/exonet_protocol/proc/make_arbitrary_address(var/new_address)
+ if(new_address)
+ if(new_address == find_address(new_address) ) //Collision test.
+ return 0
+ address = new_address
+ all_exonet_connections |= src
+ return 1
+
+// Proc: hexadecimal_to_EPv2()
+// Parameters: 1 (hex - a string of hexadecimals to convert)
+// Description: Helper proc to add colons to a string in the right places.
+/proc/hexadecimal_to_EPv2(var/hex)
+ if(!hex)
+ return null
+ var/addr_1 = copytext(hex,1,5)
+ var/addr_2 = copytext(hex,5,9)
+ var/addr_3 = copytext(hex,9,13)
+ var/addr_4 = copytext(hex,13,17)
+ var/new_address = "[addr_1]:[addr_2]:[addr_3]:[addr_4]"
+ return new_address
+
+
+// Proc: remove_address()
+// Parameters: None
+// Description: Deallocates the address, freeing it for use.
+/datum/exonet_protocol/proc/remove_address()
+ address = ""
+ all_exonet_connections.Remove(src)
+
+
+// Proc: find_address()
+// Parameters: 1 (target_address - the desired address to find)
+// Description: Searches the global list all_exonet_connections for a specific address, and returns it if found, otherwise returns null.
+/datum/exonet_protocol/proc/find_address(var/target_address)
+ for(var/datum/exonet_protocol/exonet in all_exonet_connections)
+ if(exonet.address == target_address)
+ return exonet.address
+ return null
+
+// Proc: send_message()
+// Parameters: 2 (target_address - the desired address to send the message to, message - the message to send)
+// Description: Sends the message to target_address, by calling receive_message() on the desired datum.
+/datum/exonet_protocol/proc/send_message(var/target_address, var/message)
+ if(!address)
+ return 0
+ for(var/datum/exonet_protocol/exonet in all_exonet_connections)
+ if(exonet.address == target_address)
+ exonet.receive_message(holder, address, message)
+ break
+
+// Proc: receive_message()
+// Parameters: 3 (origin_atom - the origin datum's holder, origin_address - the address the message originated from, message - the message that was sent)
+// Description: Called when send_message() successfully reaches the intended datum. By default, calls receive_exonet_message() on the holder atom.
+/datum/exonet_protocol/proc/receive_message(var/atom/origin_atom, var/origin_address, var/message)
+ holder.receive_exonet_message(origin_atom, origin_address, message)
+ return
+
+// Proc: receive_exonet_message()
+// Parameters: 3 (origin_atom - the origin datum's holder, origin_address - the address the message originated from, message - the message that was sent)
+// Description: Override this to make your atom do something when a message is received.
+/atom/proc/receive_exonet_message(var/atom/origin_atom, var/origin_address, var/message)
+ return
diff --git a/code/datums/weakref.dm b/code/datums/weakref.dm
new file mode 100644
index 0000000000000..d225de991fb68
--- /dev/null
+++ b/code/datums/weakref.dm
@@ -0,0 +1,33 @@
+#define QDELETED(X) (!X || X.gc_destroyed)
+
+/datum
+ var/weakref/weakref
+
+/datum/Destroy()
+ weakref = null // Clear this reference to ensure it's kept for as brief duration as possible.
+ . = ..()
+
+//obtain a weak reference to a datum
+/proc/weakref(datum/D)
+ if(D.gc_destroyed)
+ return
+ if(!D.weakref)
+ D.weakref = new/weakref(D)
+ return D.weakref
+
+/weakref
+ var/ref
+
+/weakref/New(datum/D)
+ ref = "\ref[D]"
+
+/weakref/Destroy()
+ // A weakref datum should not be manually destroyed as it is a shared resource,
+ // rather it should be automatically collected by the BYOND GC when all references are gone.
+ return 0
+
+/weakref/proc/resolve()
+ var/datum/D = locate(ref)
+ if(D && D.weakref == src)
+ return D
+ return null
\ No newline at end of file
diff --git a/code/game/machinery/exonet_node.dm b/code/game/machinery/exonet_node.dm
new file mode 100644
index 0000000000000..682df50e60050
--- /dev/null
+++ b/code/game/machinery/exonet_node.dm
@@ -0,0 +1,173 @@
+/obj/machinery/exonet_node
+ name = "exonet node"
+ desc = "This machine is one of many, many nodes inside Vir's section of the Exonet, connecting the Northern Star to the rest of the system, at least \
+ electronically."
+ icon = 'icons/obj/stationobjs.dmi'
+ icon_state = "exonet_node"
+ idle_power_usage = 2500
+ density = 1
+ anchored = 1
+ var/on = 1
+ var/toggle = 1
+
+ var/allow_external_PDAs = 1
+ var/allow_external_communicators = 1
+ var/allow_external_newscasters = 1
+
+ var/opened = 0
+
+// Proc: New()
+// Parameters: None
+// Description: Adds components to the machine for deconstruction.
+/obj/machinery/exonet_node/New()
+ ..()
+
+ component_parts = list()
+ component_parts += new /obj/item/weapon/circuitboard/telecomms/exonet_node(src)
+ component_parts += new /obj/item/weapon/stock_parts/subspace/ansible(src)
+ component_parts += new /obj/item/weapon/stock_parts/subspace/filter(src)
+ component_parts += new /obj/item/weapon/stock_parts/manipulator(src)
+ component_parts += new /obj/item/weapon/stock_parts/manipulator(src)
+ component_parts += new /obj/item/weapon/stock_parts/micro_laser(src)
+ component_parts += new /obj/item/weapon/stock_parts/subspace/crystal(src)
+ component_parts += new /obj/item/weapon/stock_parts/subspace/treatment(src)
+ component_parts += new /obj/item/weapon/stock_parts/subspace/treatment(src)
+ component_parts += new /obj/item/stack/cable_coil(src, 2)
+ RefreshParts()
+
+// Proc: update_icon()
+// Parameters: None
+// Description: Self explanatory.
+/obj/machinery/exonet_node/update_icon()
+ if(on)
+ if(!allow_external_PDAs && !allow_external_communicators && !allow_external_newscasters)
+ icon_state = "[initial(icon_state)]_idle"
+ else
+ icon_state = initial(icon_state)
+ else
+ icon_state = "[initial(icon_state)]_off"
+
+// Proc: update_power()
+// Parameters: None
+// Description: Sets the device on/off and adjusts power draw based on stat and toggle variables.
+/obj/machinery/exonet_node/proc/update_power()
+ if(toggle)
+ if(stat & (BROKEN|NOPOWER|EMPED))
+ on = 0
+ idle_power_usage = 0
+ else
+ on = 1
+ idle_power_usage = 2500
+ else
+ on = 0
+ idle_power_usage = 0
+
+// Proc: emp_act()
+// Parameters: 1 (severity - how strong the EMP is, with lower numbers being stronger)
+// Description: Shuts off the machine for awhile if an EMP hits it. Ion anomalies also call this to turn it off.
+/obj/machinery/exonet_node/emp_act(severity)
+ if(!(stat & EMPED))
+ stat |= EMPED
+ var/duration = (300 * 10)/severity
+ spawn(rand(duration - 20, duration + 20))
+ stat &= ~EMPED
+ update_icon()
+ ..()
+
+// Proc: process()
+// Parameters: None
+// Description: Calls the procs below every tick.
+/obj/machinery/exonet_node/process()
+ update_power()
+
+// Proc: attackby()
+// Parameters: 2 (I - the item being whacked against the machine, user - the person doing the whacking)
+// Description: Handles deconstruction.
+/obj/machinery/exonet_node/attackby(obj/item/I, mob/user)
+ if(istype(I, /obj/item/weapon/screwdriver))
+ default_deconstruction_screwdriver(user, I)
+ else if(istype(I, /obj/item/weapon/crowbar))
+ default_deconstruction_crowbar(user, I)
+ else
+ ..()
+
+// Proc: attack_ai()
+// Parameters: 1 (user - the AI clicking on the machine)
+// Description: Redirects to attack_hand()
+/obj/machinery/exonet_node/attack_ai(mob/user)
+ attack_hand(user)
+
+// Proc: attack_hand()
+// Parameters: 1 (user - the person clicking on the machine)
+// Description: Opens the NanoUI interface with ui_interact()
+/obj/machinery/exonet_node/attack_hand(mob/user)
+ ui_interact(user)
+
+// Proc: ui_interact()
+// Parameters: 4 (standard NanoUI arguments)
+// Description: Allows the user to turn the machine on or off, or open or close certain 'ports' for things like external PDA messages, newscasters, etc.
+/obj/machinery/exonet_node/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
+ // this is the data which will be sent to the ui
+ var/data = list()
+
+
+ data["on"] = on ? 1 : 0
+ data["allowPDAs"] = allow_external_PDAs
+ data["allowCommunicators"] = allow_external_communicators
+ data["allowNewscasters"] = allow_external_newscasters
+
+ // update the ui if it exists, returns null if no ui is passed/found
+ ui = SSnano.try_update_ui(user, src, ui_key, ui, data, force_open)
+ if(!ui)
+ // the ui does not exist, so we'll create a new() one
+ // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm
+ ui = new(user, src, ui_key, "exonet_node.tmpl", "Exonet Node #157", 400, 400)
+ // when the ui is first opened this is the data it will use
+ ui.set_initial_data(data)
+ // open the new ui window
+ ui.open()
+ // auto update every Master Controller tick
+ ui.set_auto_update(1)
+
+// Proc: Topic()
+// Parameters: 2 (standard Topic arguments)
+// Description: Responds to button presses on the NanoUI interface.
+/obj/machinery/exonet_node/Topic(href, href_list)
+ if(..())
+ return 1
+ if(href_list["toggle_power"])
+ toggle = !toggle
+ update_power()
+ if(!toggle)
+ var/msg = "[usr.client.key] ([usr]) has turned [src] off, at [x],[y],[z]."
+ message_admins(msg)
+ log_game(msg)
+
+ if(href_list["toggle_PDA_port"])
+ allow_external_PDAs = !allow_external_PDAs
+
+ if(href_list["toggle_communicator_port"])
+ allow_external_communicators = !allow_external_communicators
+ if(!allow_external_communicators)
+ var/msg = "[usr.client.key] ([usr]) has turned [src]'s communicator port off, at [x],[y],[z]."
+ message_admins(msg)
+ log_game(msg)
+
+ if(href_list["toggle_newscaster_port"])
+ allow_external_newscasters = !allow_external_newscasters
+ if(!allow_external_newscasters)
+ var/msg = "[usr.client.key] ([usr]) has turned [src]'s newscaster port off, at [x],[y],[z]."
+ message_admins(msg)
+ log_game(msg)
+
+ update_icon()
+ SSnano.update_uis(src)
+ add_fingerprint(usr)
+
+// Proc: get_exonet_node()
+// Parameters: None
+// Description: Helper proc to get a reference to an Exonet node.
+/proc/get_exonet_node()
+ for(var/obj/machinery/exonet_node/E in machines)
+ if(E.on)
+ return E
diff --git a/code/game/machinery/telecomms/machines/relay.dm b/code/game/machinery/telecomms/machines/relay.dm
index 51d76d47255a4..1326e569d5f3e 100644
--- a/code/game/machinery/telecomms/machines/relay.dm
+++ b/code/game/machinery/telecomms/machines/relay.dm
@@ -84,3 +84,17 @@
hide = 1
toggled = 0
autolinkers = list("r_relay")
+
+//This isn't a real telecomms board but I don't want to make a whole file to hold only one circuitboard.
+/obj/item/weapon/circuitboard/telecomms/exonet_node
+ name = "exonet node"
+ build_path = "/obj/machinery/exonet_node"
+ origin_tech = list(TECH_DATA = 5, TECH_ENGINEERING = 5, TECH_BLUESPACE = 4)
+ req_components = list(
+ "/obj/item/weapon/stock_parts/subspace/ansible" = 1,
+ "/obj/item/weapon/stock_parts/subspace/filter" = 1,
+ "/obj/item/weapon/stock_parts/manipulator" = 2,
+ "/obj/item/weapon/stock_parts/micro_laser" = 1,
+ "/obj/item/weapon/stock_parts/subspace/crystal" = 1,
+ "/obj/item/weapon/stock_parts/subspace/treatment" = 2,
+ "/obj/item/stack/cable_coil" = 2)
diff --git a/code/game/objects/items/weapons/implants/implant.dm b/code/game/objects/items/weapons/implants/implant.dm
index e1c44eac0cd66..0bfa2a47bdee2 100644
--- a/code/game/objects/items/weapons/implants/implant.dm
+++ b/code/game/objects/items/weapons/implants/implant.dm
@@ -70,6 +70,17 @@
removed(imp_in)
..()
+/obj/item/weapon/implant/attackby(obj/item/I, mob/user)
+ if(istype(I, /obj/item/weapon/implanter))
+ var/obj/item/weapon/implanter/implanter = I
+ if(implanter.imp)
+ return // It's full.
+ user.remove_from_mob(src)
+ forceMove(implanter)
+ implanter.imp = src
+ implanter.update_icon()
+ else
+ ..()
/obj/item/weapon/implant/proc/get_data()
return "No information available"
diff --git a/code/game/objects/items/weapons/implants/implantcircuits.dm b/code/game/objects/items/weapons/implants/implantcircuits.dm
new file mode 100644
index 0000000000000..0dc18fff7eac2
--- /dev/null
+++ b/code/game/objects/items/weapons/implants/implantcircuits.dm
@@ -0,0 +1,48 @@
+/obj/item/weapon/implant/integrated_circuit
+ name = "electronic implant"
+ desc = "It's a case, for building very tiny electronics with."
+ icon = 'icons/obj/electronic_assemblies.dmi'
+ icon_state = "setup_implant"
+ var/obj/item/device/electronic_assembly/implant/IC = null
+
+/obj/item/weapon/implant/integrated_circuit/proc/islegal()
+ return TRUE
+
+/obj/item/weapon/implant/integrated_circuit/New()
+ ..()
+ IC = new(src)
+ IC.implant = src
+
+/obj/item/weapon/implant/integrated_circuit/Destroy()
+ IC.implant = null
+ qdel(IC)
+ ..()
+
+/obj/item/weapon/implant/integrated_circuit/get_data()
+ var/dat = {"
+ Implant Specifications:
+ Name: Modular Implant
+ Life: 3 years.
+ Important Notes: EMP can cause malfunctions in the internal electronics of this implant.
+
+ Implant Details:
+ Function: Contains no innate functions until other components are added.
+ Special Features:
+ Modular Circuitry- Can be loaded with specific modular circuitry in order to fulfill a wide possibility of functions.
+ Integrity: Implant is not shielded from electromagnetic interferance, otherwise it is independant of subject's status."}
+ return dat
+
+/obj/item/weapon/implant/integrated_circuit/emp_act(severity)
+ IC.emp_act(severity)
+
+/obj/item/weapon/implant/integrated_circuit/examine(mob/user)
+ IC.examine(user)
+
+/obj/item/weapon/implant/integrated_circuit/attackby(var/obj/item/O, var/mob/user)
+ if(istype(O, /obj/item/weapon/crowbar) || istype(O, /obj/item/device/integrated_electronics) || istype(O, /obj/item/integrated_circuit) || istype(O, /obj/item/weapon/screwdriver) )
+ IC.attackby(O, user)
+ else
+ ..()
+
+/obj/item/weapon/implant/integrated_circuit/attack_self(mob/user)
+ IC.attack_self(user)
\ No newline at end of file
diff --git a/code/game/objects/items/weapons/storage/belt.dm b/code/game/objects/items/weapons/storage/belt.dm
index c3d4af1d51679..fa39d528fdb0d 100644
--- a/code/game/objects/items/weapons/storage/belt.dm
+++ b/code/game/objects/items/weapons/storage/belt.dm
@@ -29,7 +29,8 @@
/obj/item/stack/cable_coil,
/obj/item/device/t_scanner,
/obj/item/device/analyzer,
- /obj/item/weapon/extinguisher/mini
+ /obj/item/weapon/extinguisher/mini,
+ /obj/item/device/integrated_electronics/wirer
)
/obj/item/weapon/storage/belt/utility/full/New()
diff --git a/code/game/objects/items/weapons/storage/storage.dm b/code/game/objects/items/weapons/storage/storage.dm
index 1a4e1f3224f52..42ddd910ca6a7 100644
--- a/code/game/objects/items/weapons/storage/storage.dm
+++ b/code/game/objects/items/weapons/storage/storage.dm
@@ -502,3 +502,13 @@
if(verbs.Find(/obj/item/weapon/storage/verb/quick_empty))
quick_empty()
+/obj/item/weapon/storage/proc/make_exact_fit()
+ storage_slots = contents.len
+
+ can_hold.Cut()
+ max_w_class = 0
+ max_combined_w_class = 0
+ for(var/obj/item/I in src)
+ can_hold[I.type]++
+ max_w_class = max(I.w_class, max_w_class)
+ max_combined_w_class += I.w_class
diff --git a/code/modules/error_handler/~defines.dm b/code/modules/error_handler/~defines.dm
new file mode 100644
index 0000000000000..98712eaa091bd
--- /dev/null
+++ b/code/modules/error_handler/~defines.dm
@@ -0,0 +1,6 @@
+// Settings for the error handler and error viewer
+#undef ERROR_COOLDOWN
+#undef ERROR_LIMIT
+#undef ERROR_MAX_COOLDOWN
+#undef ERROR_SILENCE_TIME
+#undef ERROR_MSG_DELAY
diff --git a/code/modules/integrated_electronics/_defines.dm b/code/modules/integrated_electronics/_defines.dm
new file mode 100644
index 0000000000000..219cac711e467
--- /dev/null
+++ b/code/modules/integrated_electronics/_defines.dm
@@ -0,0 +1,130 @@
+#define IC_INPUT "input"
+#define IC_OUTPUT "output"
+#define IC_ACTIVATOR "activator"
+
+// Pin functionality.
+#define DATA_CHANNEL "data channel"
+#define PULSE_CHANNEL "pulse channel"
+
+// Methods of obtaining a circuit.
+#define IC_SPAWN_DEFAULT 1 // If the circuit comes in the default circuit box and able to be printed in the IC printer.
+#define IC_SPAWN_RESEARCH 2 // If the circuit design will be available in the IC printer after upgrading it.
+
+// Displayed along with the pin name to show what type of pin it is.
+#define IC_FORMAT_ANY "\"
+#define IC_FORMAT_STRING "\"
+#define IC_FORMAT_CHAR "\"
+#define IC_FORMAT_COLOR "\"
+#define IC_FORMAT_NUMBER "\"
+#define IC_FORMAT_DIR "\"
+#define IC_FORMAT_BOOLEAN "\"
+#define IC_FORMAT_REF "\["
+#define IC_FORMAT_LIST "\]"
+#define IC_FORMAT_ANY "\"
+#define IC_FORMAT_PULSE "\"
+
+// Used inside input/output list to tell the constructor what pin to make.
+#define IC_PINTYPE_ANY /datum/integrated_io
+#define IC_PINTYPE_STRING /datum/integrated_io/string
+#define IC_PINTYPE_CHAR /datum/integrated_io/char
+#define IC_PINTYPE_COLOR /datum/integrated_io/color
+#define IC_PINTYPE_NUMBER /datum/integrated_io/number
+#define IC_PINTYPE_DIR /datum/integrated_io/dir
+#define IC_PINTYPE_BOOLEAN /datum/integrated_io/boolean
+#define IC_PINTYPE_REF /datum/integrated_io/ref
+#define IC_PINTYPE_LIST /datum/integrated_io/list
+
+#define IC_PINTYPE_PULSE_IN /datum/integrated_io/activate
+#define IC_PINTYPE_PULSE_OUT /datum/integrated_io/activate/out
+
+// Data limits.
+#define IC_MAX_LIST_LENGTH 200
+
+//Some colors
+
+#define COLOR_WHITE "#ffffff"
+#define COLOR_SILVER "#c0c0c0"
+#define COLOR_GRAY "#808080"
+#define COLOR_FLOORTILE_GRAY "#8D8B8B"
+#define COLOR_BLACK "#000000"
+#define COLOR_RED "#ff0000"
+#define COLOR_RED_LIGHT "#b00000"
+#define COLOR_MAROON "#800000"
+#define COLOR_YELLOW "#ffff00"
+#define COLOR_AMBER "#ffbf00"
+#define COLOR_OLIVE "#808000"
+#define COLOR_LIME "#00ff00"
+#define COLOR_GREEN "#008000"
+#define COLOR_CYAN "#00ffff"
+#define COLOR_TEAL "#008080"
+#define COLOR_BLUE "#0000ff"
+#define COLOR_BLUE_LIGHT "#33ccff"
+#define COLOR_NAVY "#000080"
+#define COLOR_PINK "#ff00ff"
+#define COLOR_PURPLE "#800080"
+#define COLOR_ORANGE "#ff9900"
+#define COLOR_LUMINOL "#66ffff"
+#define COLOR_BEIGE "#ceb689"
+#define COLOR_BLUE_GRAY "#6a97b0"
+#define COLOR_BROWN "#b19664"
+#define COLOR_DARK_BROWN "#917448"
+#define COLOR_DARK_ORANGE "#b95a00"
+#define COLOR_GREEN_GRAY "#8daf6a"
+#define COLOR_RED_GRAY "#aa5f61"
+#define COLOR_PALE_BLUE_GRAY "#8bbbd5"
+#define COLOR_PALE_GREEN_GRAY "#aed18b"
+#define COLOR_PALE_RED_GRAY "#cc9090"
+#define COLOR_PALE_PURPLE_GRAY "#bda2ba"
+#define COLOR_PURPLE_GRAY "#a2819e"
+#define COLOR_SUN "#ec8b2f"
+
+//Color defines used by the assembly detailer.
+#define COLOR_ASSEMBLY_BLACK "#545454"
+#define COLOR_ASSEMBLY_BGRAY "#9497AB"
+#define COLOR_ASSEMBLY_WHITE "#E2E2E2"
+#define COLOR_ASSEMBLY_RED "#CC4242"
+#define COLOR_ASSEMBLY_ORANGE "#E39751"
+#define COLOR_ASSEMBLY_BEIGE "#AF9366"
+#define COLOR_ASSEMBLY_BROWN "#97670E"
+#define COLOR_ASSEMBLY_GOLD "#AA9100"
+#define COLOR_ASSEMBLY_YELLOW "#CECA2B"
+#define COLOR_ASSEMBLY_GURKHA "#999875"
+#define COLOR_ASSEMBLY_LGREEN "#789876"
+#define COLOR_ASSEMBLY_GREEN "#44843C"
+#define COLOR_ASSEMBLY_LBLUE "#5D99BE"
+#define COLOR_ASSEMBLY_BLUE "#38559E"
+#define COLOR_ASSEMBLY_PURPLE "#6F6192"
+
+var/global/list/alphabet_uppercase = list("A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z")
+
+var/list/all_integrated_circuits = list()
+
+/proc/initialize_integrated_circuits_list()
+ for(var/thing in typesof(/obj/item/integrated_circuit))
+ all_integrated_circuits += new thing()
+
+/obj/item/integrated_circuit
+ name = "integrated circuit"
+ desc = "It's a tiny chip! This one doesn't seem to do much, however."
+ icon = 'icons/obj/electronic_assemblies.dmi'
+ icon_state = "template"
+ w_class = 1
+ var/obj/item/device/electronic_assembly/assembly = null // Reference to the assembly holding this circuit, if any.
+ var/extended_desc = null
+ var/list/inputs = list()
+ var/list/inputs_default = list() // Assoc list which will fill a pin with data upon creation. e.g. "2" = 0 will set input pin 2 to equal 0 instead of null.
+ var/list/outputs = list()
+ var/list/outputs_default = list() // Ditto, for output.
+ var/list/activators = list()
+ var/next_use = 0 //Uses world.time
+ var/complexity = 1 //This acts as a limitation on building machines, more resource-intensive components cost more 'space'.
+ var/size = null //This acts as a limitation on building machines, bigger components cost more 'space'. -1 for size 0
+ var/cooldown_per_use = 1 SECONDS // Circuits are limited in how many times they can be work()'d by this variable.
+ var/power_draw_per_use = 0 // How much power is drawn when work()'d.
+ var/power_draw_idle = 0 // How much power is drawn when doing nothing.
+ var/spawn_flags = null // Used for world initializing, see the #defines above.
+ var/category_text = "NO CATEGORY THIS IS A BUG" // To show up on circuit printer, and perhaps other places.
+ var/removable = TRUE // Determines if a circuit is removable from the assembly.
+ var/displayed_name = ""
+ var/allow_multitool = 1 // Allows additional multitool functionality
+ // Used as a global var, (Do not set manually in children).
diff --git a/code/modules/integrated_electronics/coordinate.dm b/code/modules/integrated_electronics/coordinate.dm
new file mode 100644
index 0000000000000..732a97dd3b14c
--- /dev/null
+++ b/code/modules/integrated_electronics/coordinate.dm
@@ -0,0 +1,50 @@
+//This circuit gives information on where the machine is.
+/obj/item/integrated_circuit/gps
+ name = "global positioning system"
+ desc = "This allows you to easily know the position of a machine containing this device."
+ icon_state = "gps"
+ complexity = 4
+ inputs = list()
+ outputs = list("X (abs)", "Y (abs)")
+ activators = list("get coordinates")
+
+/obj/item/integrated_circuit/gps/do_work()
+ var/turf/T = get_turf(src)
+ var/datum/integrated_io/result_x = outputs[1]
+ var/datum/integrated_io/result_y = outputs[2]
+
+ result_x.data = null
+ result_y.data = null
+ if(!T)
+ return
+
+ result_x.data = T.x
+ result_y.data = T.y
+
+ for(var/datum/integrated_io/O in outputs)
+ O.push_data()
+
+/obj/item/integrated_circuit/abs_to_rel_coords
+ name = "abs to rel coordinate converter"
+ desc = "Easily convert absolute coordinates to relative coordinates with this."
+ complexity = 4
+ inputs = list("X1 (abs)", "Y1 (abs)", "X2 (abs)", "Y2 (abs)")
+ outputs = list("X (rel)", "Y (rel)")
+ activators = list("compute rel coordinates")
+
+/obj/item/integrated_circuit/abs_to_rel_coords/do_work()
+ var/datum/integrated_io/x1 = inputs[1]
+ var/datum/integrated_io/y1 = inputs[2]
+
+ var/datum/integrated_io/x2 = inputs[3]
+ var/datum/integrated_io/y2 = inputs[4]
+
+ var/datum/integrated_io/result_x = outputs[1]
+ var/datum/integrated_io/result_y = outputs[2]
+
+ if(x1.data && y1.data && x2.data && y2.data)
+ result_x.data = x1.data - x2.data
+ result_y.data = y1.data - y2.data
+
+ for(var/datum/integrated_io/O in outputs)
+ O.push_data()
\ No newline at end of file
diff --git a/code/modules/integrated_electronics/core/assemblies.dm b/code/modules/integrated_electronics/core/assemblies.dm
new file mode 100644
index 0000000000000..2e6bfa53f5314
--- /dev/null
+++ b/code/modules/integrated_electronics/core/assemblies.dm
@@ -0,0 +1,506 @@
+#define IC_COMPONENTS_BASE 20
+#define IC_COMPLEXITY_BASE 80
+
+/obj/item/device/electronic_assembly
+ name = "electronic assembly"
+ desc = "It's a case, for building electronics with."
+ w_class = 2
+ icon = 'icons/obj/assemblies/electronic_setups.dmi'
+ icon_state = "setup_small"
+ var/show_messages = TRUE
+ var/max_components = IC_COMPONENTS_BASE
+ var/max_complexity = IC_COMPLEXITY_BASE
+ var/opened = 0
+ var/detail_color = COLOR_ASSEMBLY_BLACK
+ var/list/color_whitelist = list( //This is just for checking that hacked colors aren't in the save data.
+ COLOR_ASSEMBLY_BLACK,
+ COLOR_FLOORTILE_GRAY,
+ COLOR_ASSEMBLY_BGRAY,
+ COLOR_ASSEMBLY_WHITE,
+ COLOR_ASSEMBLY_RED,
+ COLOR_ASSEMBLY_ORANGE,
+ COLOR_ASSEMBLY_BEIGE,
+ COLOR_ASSEMBLY_BROWN,
+ COLOR_ASSEMBLY_GOLD,
+ COLOR_ASSEMBLY_YELLOW,
+ COLOR_ASSEMBLY_GURKHA,
+ COLOR_ASSEMBLY_LGREEN,
+ COLOR_ASSEMBLY_GREEN,
+ COLOR_ASSEMBLY_LBLUE,
+ COLOR_ASSEMBLY_BLUE,
+ COLOR_ASSEMBLY_PURPLE
+ )
+ var/obj/item/weapon/stock_parts/cell/battery = null // Internal cell which most circuits need to work.
+
+/obj/item/device/electronic_assembly/default //The /default electronic_assemblys are to allow the introduction of the new naming scheme without breaking old saves.
+ name = "type-a electronic assembly"
+
+/obj/item/device/electronic_assembly/calc
+ name = "type-b electronic assembly"
+ icon_state = "setup_small_calc"
+ desc = "It's a case, for building small electronics with. This one resembles a pocket calculator."
+
+/obj/item/device/electronic_assembly/clam
+ name = "type-c electronic assembly"
+ icon_state = "setup_small_clam"
+ desc = "It's a case, for building small electronics with. This one has a clamshell design."
+
+/obj/item/device/electronic_assembly/simple
+ name = "type-d electronic assembly"
+ icon_state = "setup_small_simple"
+ desc = "It's a case, for building small electronics with. This one has a simple design."
+
+/obj/item/device/electronic_assembly/hook
+ name = "type-e electronic assembly"
+ icon_state = "setup_small_hook"
+ desc = "It's a case, for building small electronics with. This one looks like it has a belt clip, but it's purely decorative."
+
+/obj/item/device/electronic_assembly/pda
+ name = "type-f electronic assembly"
+ icon_state = "setup_small_pda"
+ desc = "It's a case, for building small electronics with. This one resembles a PDA."
+
+/obj/item/device/electronic_assembly/medium
+ name = "electronic mechanism"
+ icon_state = "setup_medium"
+ desc = "It's a case, for building medium-sized electronics with."
+ w_class = 3
+ max_components = IC_COMPONENTS_BASE * 2
+ max_complexity = IC_COMPLEXITY_BASE * 2
+
+/obj/item/device/electronic_assembly/medium/default
+ name = "type-a electronic mechanism"
+
+/obj/item/device/electronic_assembly/medium/box
+ name = "type-b electronic mechanism"
+ icon_state = "setup_medium_box"
+ desc = "It's a case, for building medium-sized electronics with. This one has a boxy design."
+
+/obj/item/device/electronic_assembly/medium/clam
+ name = "type-c electronic mechanism"
+ icon_state = "setup_medium_clam"
+ desc = "It's a case, for building medium-sized electronics with. This one has a clamshell design."
+
+/obj/item/device/electronic_assembly/medium/medical
+ name = "type-d electronic mechanism"
+ icon_state = "setup_medium_med"
+ desc = "It's a case, for building medium-sized electronics with. This one resembles some type of medical apparatus."
+
+/obj/item/device/electronic_assembly/medium/gun
+ name = "type-e electronic mechanism"
+ icon_state = "setup_medium_gun"
+ desc = "It's a case, for building medium-sized electronics with. This one resembles a gun, or some type of tool, if you're feeling optimistic."
+
+/obj/item/device/electronic_assembly/medium/radio
+ name = "type-f electronic mechanism"
+ icon_state = "setup_medium_radio"
+ desc = "It's a case, for building medium-sized electronics with. This one resembles an old radio."
+
+/obj/item/device/electronic_assembly/large
+ name = "electronic machine"
+ icon_state = "setup_large"
+ desc = "It's a case, for building large electronics with."
+ w_class = 5
+ max_components = IC_COMPONENTS_BASE * 4
+ max_complexity = IC_COMPLEXITY_BASE * 4
+
+/obj/item/device/electronic_assembly/large/default
+ name = "type-a electronic machine"
+
+/obj/item/device/electronic_assembly/large/scope
+ name = "type-b electronic machine"
+ icon_state = "setup_large_scope"
+ desc = "It's a case, for building large electronics with. This one resembles an oscilloscope."
+
+/obj/item/device/electronic_assembly/large/terminal
+ name = "type-c electronic machine"
+ icon_state = "setup_large_terminal"
+ desc = "It's a case, for building large electronics with. This one resembles a computer terminal."
+
+/obj/item/device/electronic_assembly/large/arm
+ name = "type-d electronic machine"
+ icon_state = "setup_large_arm"
+ desc = "It's a case, for building large electronics with. This one resembles a robotic arm."
+
+/obj/item/device/electronic_assembly/large/tall
+ name = "type-e electronic machine"
+ icon_state = "setup_large_tall"
+ desc = "It's a case, for building large electronics with. This one has a tall design."
+
+/obj/item/device/electronic_assembly/large/industrial
+ name = "type-f electronic machine"
+ icon_state = "setup_large_industrial"
+ desc = "It's a case, for building large electronics with. This one resembles some kind of industrial machinery."
+
+/obj/item/device/electronic_assembly/drone
+ name = "electronic drone"
+ icon_state = "setup_drone"
+ desc = "It's a case, for building mobile electronics with."
+ w_class = 4
+ max_components = IC_COMPONENTS_BASE * 3
+ max_complexity = IC_COMPLEXITY_BASE * 3
+
+/obj/item/device/electronic_assembly/drone/can_move()
+ return TRUE
+
+/obj/item/device/electronic_assembly/drone/default
+ name = "type-a electronic drone"
+
+/obj/item/device/electronic_assembly/drone/arms
+ name = "type-b electronic drone"
+ icon_state = "setup_drone_arms"
+ desc = "It's a case, for building mobile electronics with. This one is armed and dangerous."
+
+/obj/item/device/electronic_assembly/drone/secbot
+ name = "type-c electronic drone"
+ icon_state = "setup_drone_secbot"
+ desc = "It's a case, for building mobile electronics with. This one resembles a Securitron."
+
+/obj/item/device/electronic_assembly/drone/medbot
+ name = "type-d electronic drone"
+ icon_state = "setup_drone_medbot"
+ desc = "It's a case, for building mobile electronics with. This one resembles a Medibot."
+
+/obj/item/device/electronic_assembly/drone/genbot
+ name = "type-e electronic drone"
+ icon_state = "setup_drone_genbot"
+ desc = "It's a case, for building mobile electronics with. This one has a generic bot design."
+
+/obj/item/device/electronic_assembly/drone/android
+ name = "type-f electronic drone"
+ icon_state = "setup_drone_android"
+ desc = "It's a case, for building mobile electronics with. This one has a hominoid design."
+
+/obj/item/device/electronic_assembly/wallmount
+ name = "wall-mounted electronic assembly"
+ icon_state = "setup_wallmount_medium"
+ desc = "It's a case, for building medium-sized electronics with. It has a magnetized backing to allow it to stick to walls, but you'll still need to wrench the anchoring bolts in place to keep it on."
+ w_class = 3
+ max_components = IC_COMPONENTS_BASE * 2
+ max_complexity = IC_COMPLEXITY_BASE * 2
+
+/obj/item/device/electronic_assembly/wallmount/heavy
+ name = "heavy wall-mounted electronic assembly"
+ icon_state = "setup_wallmount_large"
+ desc = "It's a case, for building large electronics with. It has a magnetized backing to allow it to stick to walls, but you'll still need to wrench the anchoring bolts in place to keep it on."
+ w_class = 5
+ max_components = IC_COMPONENTS_BASE * 4
+ max_complexity = IC_COMPLEXITY_BASE * 4
+
+/obj/item/device/electronic_assembly/wallmount/light
+ name = "light wall-mounted electronic assembly"
+ icon_state = "setup_wallmount_small"
+ desc = "It's a case, for building small electronics with. It has a magnetized backing to allow it to stick to walls, but you'll still need to wrench the anchoring bolts in place to keep it on."
+ w_class = 2
+ max_components = IC_COMPONENTS_BASE
+ max_complexity = IC_COMPLEXITY_BASE
+
+/obj/item/device/electronic_assembly/implant
+ name = "electronic implant"
+ icon_state = "setup_implant"
+ desc = "It's a case, for building very tiny electronics with."
+ w_class = 1
+ max_components = IC_COMPONENTS_BASE / 2
+ max_complexity = IC_COMPLEXITY_BASE / 2
+ var/obj/item/weapon/implant/integrated_circuit/implant = null
+
+/obj/item/device/electronic_assembly/New()
+ ..()
+ battery = new(src)
+ SSobj.processing |= src
+
+/obj/item/device/electronic_assembly/Destroy()
+ battery = null
+ SSobj.processing -= src
+ for(var/atom/movable/AM in contents)
+ qdel(AM)
+ ..()
+
+/obj/item/device/electronic_assembly/process()
+ handle_idle_power()
+
+/obj/item/device/electronic_assembly/proc/handle_idle_power()
+ // First we generate power.
+ for(var/obj/item/integrated_circuit/passive/power/P in contents)
+ P.make_energy()
+
+ // Now spend it.
+ for(var/obj/item/integrated_circuit/IC in contents)
+ if(IC.power_draw_idle)
+ if(!draw_power(IC.power_draw_idle))
+ IC.power_fail()
+
+/obj/item/device/electronic_assembly/implant/update_icon()
+ ..()
+ implant.icon_state = icon_state
+ overlays.Cut()
+ if(detail_color == COLOR_ASSEMBLY_BLACK) //Black colored overlay looks almost but not exactly like the base sprite, so just cut the overlay and avoid it looking kinda off.
+ return
+ var/image/detail_overlay = image('icons/obj/assemblies/electronic_setups.dmi', "[icon_state]-color")
+ detail_overlay.color = detail_color
+ overlays.Add(detail_overlay)
+
+
+/obj/item/device/electronic_assembly/implant/proc/nano_host()
+ return implant
+
+/obj/item/device/electronic_assembly/proc/resolve_nano_host()
+ return src
+
+/obj/item/device/electronic_assembly/implant/resolve_nano_host()
+ return implant
+
+/obj/item/device/electronic_assembly/proc/check_interactivity(mob/user)
+ if(!CanInteract(user, src))
+ return 0
+ return 1
+
+/obj/item/device/electronic_assembly/interact(mob/user)
+ if(!check_interactivity(user))
+ return
+
+ var/total_parts = 0
+ var/total_complexity = 0
+ for(var/obj/item/integrated_circuit/part in contents)
+ total_parts += part.size
+ total_complexity = total_complexity + part.complexity
+ var/HTML = list()
+
+ HTML += "[src.name]"
+ HTML += "
\[Refresh\] | "
+ HTML += "\[Rename\]
"
+ HTML += "[total_parts]/[max_components] ([round((total_parts / max_components) * 100, 0.1)]%) space taken up in the assembly.
"
+ HTML += "[total_complexity]/[max_complexity] ([round((total_complexity / max_complexity) * 100, 0.1)]%) maximum complexity.
"
+ if(battery)
+ HTML += "[round(battery.charge, 0.1)]/[battery.maxcharge] ([round(battery.percent(), 0.1)]%) cell charge. \[Remove\]"
+ else
+ HTML += "No powercell detected!"
+ HTML += "
"
+ HTML += "Components:
"
+ HTML += "Built in:
"
+
+
+//Put removable circuits in separate categories from non-removable
+ for(var/obj/item/integrated_circuit/circuit in contents)
+ if(!circuit.removable)
+ HTML += "[circuit.displayed_name] | "
+ HTML += "\[Rename\] | "
+ HTML += "\[Scan with Debugger\] | "
+ HTML += "\[Move to Bottom\]"
+ HTML += "
"
+
+ HTML += "
"
+ HTML += "Removable:
"
+
+ for(var/obj/item/integrated_circuit/circuit in contents)
+ if(circuit.removable)
+ HTML += "[circuit.displayed_name] | "
+ HTML += "\[Rename\] | "
+ HTML += "\[Scan with Debugger\] | "
+ HTML += "\[Remove\] | "
+ HTML += "\[Move to Bottom\]"
+ HTML += "
"
+
+ HTML += ""
+ user << browse(jointext(HTML,null), "window=assembly-\ref[src];size=600x350;border=1;can_resize=1;can_close=1;can_minimize=1")
+
+/obj/item/device/electronic_assembly/Topic(href, href_list[])
+ if(..())
+ return 1
+
+ if(href_list["rename"])
+ rename(usr)
+
+ if(href_list["remove_cell"])
+ if(!battery)
+ to_chat(usr, "There's no power cell to remove from \the [src].")
+ else
+ var/turf/T = get_turf(src)
+ battery.forceMove(T)
+ playsound(T, 'sound/items/Crowbar.ogg', 50, 1)
+ to_chat(usr, "You pull \the [battery] out of \the [src]'s power supplier.")
+ battery = null
+
+ interact(usr) // To refresh the UI.
+
+/obj/item/device/electronic_assembly/verb/rename()
+ set name = "Rename Circuit"
+ set category = "Object"
+ set desc = "Rename your circuit, useful to stay organized."
+
+ var/mob/M = usr
+ if(!check_interactivity(M))
+ return
+
+ var/input = sanitize_text(input("What do you want to name this?", "Rename", src.name) as null|text, MAX_NAME_LEN)
+ if(src && input)
+ M << "The machine now has a label reading '[input]'."
+ name = input
+
+/obj/item/device/electronic_assembly/proc/can_move()
+ return FALSE
+
+/obj/item/device/electronic_assembly/drone/can_move()
+ return TRUE
+
+/obj/item/device/electronic_assembly/update_icon()
+ if(opened)
+ icon_state = initial(icon_state) + "-open"
+ else
+ icon_state = initial(icon_state)
+ overlays.Cut()
+ if(detail_color == COLOR_ASSEMBLY_BLACK) //Black colored overlay looks almost but not exactly like the base sprite, so just cut the overlay and avoid it looking kinda off.
+ return
+ var/image/detail_overlay = image('icons/obj/assemblies/electronic_setups.dmi', "[icon_state]-color")
+ detail_overlay.color = detail_color
+ overlays.Add(detail_overlay)
+
+/obj/item/device/electronic_assembly/examine(mob/user)
+ . = ..(user, 1)
+ if(.)
+ for(var/obj/item/integrated_circuit/IC in contents)
+ IC.external_examine(user)
+ // for(var/obj/item/integrated_circuit/output/screen/S in contents)
+ // if(S.stuff_to_display)
+ // to_chat(user, "There's a little screen labeled '[S.name]', which displays '[S.stuff_to_display]'.")
+ if(opened)
+ interact(user)
+
+/obj/item/device/electronic_assembly/proc/get_part_complexity()
+ . = 0
+ for(var/obj/item/integrated_circuit/part in contents)
+ . += part.complexity
+
+/obj/item/device/electronic_assembly/proc/get_part_size()
+ . = 0
+ for(var/obj/item/integrated_circuit/part in contents)
+ . += part.size
+
+// Returns true if the circuit made it inside.
+/obj/item/device/electronic_assembly/proc/add_circuit(var/obj/item/integrated_circuit/IC, var/mob/user)
+ if(!opened)
+ to_chat(user, "\The [src] isn't opened, so you can't put anything inside. Try using a crowbar.")
+ return FALSE
+
+ if(IC.w_class > src.w_class)
+ to_chat(user, "\The [IC] is way too big to fit into \the [src].")
+ return FALSE
+
+ var/total_part_size = get_part_size()
+ var/total_complexity = get_part_complexity()
+
+ if((total_part_size + IC.size) > max_components)
+ to_chat(user, "You can't seem to add the '[IC.name]', as there's insufficient space.")
+ return FALSE
+ if((total_complexity + IC.complexity) > max_complexity)
+ to_chat(user, "You can't seem to add the '[IC.name]', since this setup's too complicated for the case.")
+ return FALSE
+
+ if(!IC.forceMove(src))
+ return FALSE
+
+ IC.assembly = src
+
+ return TRUE
+
+/obj/item/device/electronic_assembly/afterattack(atom/target, mob/user, proximity)
+ if(proximity)
+ var/scanned = FALSE
+ for(var/obj/item/integrated_circuit/input/sensor/S in contents)
+// S.set_pin_data(IC_OUTPUT, 1, weakref(target))
+// S.check_then_do_work()
+ if(S.scan(target))
+ scanned = TRUE
+ if(scanned)
+ visible_message("\The [user] waves \the [src] around [target].")
+
+/obj/item/device/electronic_assembly/attackby(var/obj/item/I, var/mob/user)
+ if(istype(I, /obj/item/integrated_circuit))
+ if(!user.unEquip(I))
+ return FALSE
+ if(add_circuit(I, user))
+ to_chat(user, "You slide \the [I] inside \the [src].")
+ playsound(get_turf(src), 'sound/items/Deconstruct.ogg', 50, 1)
+ interact(user)
+ return TRUE
+ else if(istype(I, /obj/item/weapon/crowbar))
+ playsound(get_turf(src), 'sound/items/Crowbar.ogg', 50, 1)
+ opened = !opened
+ user << "You [opened ? "opened" : "closed"] \the [src]."
+ update_icon()
+ return TRUE
+ else if(istype(I, /obj/item/device/integrated_electronics/wirer) || istype(I, /obj/item/device/integrated_electronics/debugger) || istype(I, /obj/item/weapon/screwdriver))
+ if(opened)
+ interact(user)
+ else
+ to_chat(user, "\The [src] isn't opened, so you can't fiddle with the internal components. \
+ Try using a crowbar.")
+ else if(istype(I, /obj/item/weapon/stock_parts/cell))
+ if(!opened)
+ to_chat(user, "\The [src] isn't opened, so you can't put anything inside. Try using a crowbar.")
+ return FALSE
+ if(battery)
+ to_chat(user, "\The [src] already has \a [battery] inside. Remove it first if you want to replace it.")
+ return FALSE
+ var/obj/item/weapon/stock_parts/cell = I
+ user.drop_item(cell)
+ cell.forceMove(src)
+ battery = cell
+ playsound(get_turf(src), 'sound/items/Deconstruct.ogg', 50, 1)
+ to_chat(user, "You slot \the [cell] inside \the [src]'s power supplier.")
+ interact(user)
+ return TRUE
+ else if(istype(I, /obj/item/device/integrated_electronics/detailer))
+ var/obj/item/device/integrated_electronics/detailer/D = I
+ detail_color = D.detail_color
+ update_icon()
+ else
+ return ..()
+
+/obj/item/device/electronic_assembly/attack_self(mob/user)
+ if(!check_interactivity(user))
+ return
+ if(opened)
+ interact(user)
+
+ var/list/input_selection = list()
+ var/list/available_inputs = list()
+ for(var/obj/item/integrated_circuit/input/input in contents)
+ if(input.can_be_asked_input)
+ available_inputs.Add(input)
+ var/i = 0
+ for(var/obj/item/integrated_circuit/s in available_inputs)
+ if(s.name == input.name && s.displayed_name == input.displayed_name && s != input)
+ i++
+ var/disp_name= "[input.displayed_name] \[[input.name]\]"
+ if(i)
+ disp_name += " ([i+1])"
+ input_selection.Add(disp_name)
+
+ var/obj/item/integrated_circuit/input/choice
+ if(available_inputs)
+ var/selection = input(user, "What do you want to interact with?", "Interaction") as null|anything in input_selection
+ if(selection)
+ var/index = input_selection.Find(selection)
+ choice = available_inputs[index]
+
+ if(choice)
+ choice.ask_for_input(user)
+
+/obj/item/device/electronic_assembly/emp_act(severity)
+ ..()
+ for(var/atom/movable/AM in contents)
+ AM.emp_act(severity)
+
+// Returns true if power was successfully drawn.
+/obj/item/device/electronic_assembly/proc/draw_power(amount)
+ if(battery && battery.use(amount * CELLRATE))
+ return TRUE
+ return FALSE
+
+// Ditto for giving.
+/obj/item/device/electronic_assembly/proc/give_power(amount)
+ if(battery && battery.give(amount * CELLRATE))
+ return TRUE
+ return FALSE
+
diff --git a/code/modules/integrated_electronics/core/detailer.dm b/code/modules/integrated_electronics/core/detailer.dm
new file mode 100644
index 0000000000000..a63c174c2846d
--- /dev/null
+++ b/code/modules/integrated_electronics/core/detailer.dm
@@ -0,0 +1,46 @@
+/obj/item/device/integrated_electronics/detailer
+ name = "assembly detailer"
+ desc = "A combination autopainter and flash anodizer designed to give electronic assemblies a colorful, wear-resistant finish."
+ icon = 'icons/obj/assemblies/electronic_tools.dmi'
+ icon_state = "detailer"
+ w_class = 2
+ var/data_to_write = null
+ var/accepting_refs = FALSE
+ var/detail_color = COLOR_ASSEMBLY_WHITE
+ var/list/color_list = list(
+ "black" = COLOR_ASSEMBLY_BLACK,
+ "gray" = COLOR_FLOORTILE_GRAY,
+ "machine gray" = COLOR_ASSEMBLY_BGRAY,
+ "white" = COLOR_ASSEMBLY_WHITE,
+ "red" = COLOR_ASSEMBLY_RED,
+ "orange" = COLOR_ASSEMBLY_ORANGE,
+ "beige" = COLOR_ASSEMBLY_BEIGE,
+ "brown" = COLOR_ASSEMBLY_BROWN,
+ "gold" = COLOR_ASSEMBLY_GOLD,
+ "yellow" = COLOR_ASSEMBLY_YELLOW,
+ "gurkha" = COLOR_ASSEMBLY_GURKHA,
+ "light green" = COLOR_ASSEMBLY_LGREEN,
+ "green" = COLOR_ASSEMBLY_GREEN,
+ "light blue" = COLOR_ASSEMBLY_LBLUE,
+ "blue" = COLOR_ASSEMBLY_BLUE,
+ "purple" = COLOR_ASSEMBLY_PURPLE
+ )
+
+/obj/item/device/integrated_electronics/detailer/New()
+ .=..()
+ update_icon()
+
+/obj/item/device/integrated_electronics/detailer/update_icon()
+ overlays.Cut()
+ var/image/detail_overlay = image('icons/obj/assemblies/electronic_tools.dmi', "detailer-color")
+ detail_overlay.color = detail_color
+ overlays.Add(detail_overlay)
+
+/obj/item/device/integrated_electronics/detailer/attack_self(mob/user)
+ var/color_choice = input(user, "Select color.", "Assembly Detailer") as null|anything in color_list
+ if(!color_list[color_choice])
+ return
+ if(!in_range(src, user))
+ return
+ detail_color = color_list[color_choice]
+ update_icon()
\ No newline at end of file
diff --git a/code/modules/integrated_electronics/core/device.dm b/code/modules/integrated_electronics/core/device.dm
new file mode 100644
index 0000000000000..38044595b7b35
--- /dev/null
+++ b/code/modules/integrated_electronics/core/device.dm
@@ -0,0 +1,84 @@
+/obj/item/device/assembly/electronic_assembly
+ name = "electronic device"
+ desc = "It's a case for building electronics with. It can be attached to other small devices."
+ icon_state = "setup_device"
+ var/opened = 0
+
+ var/obj/item/device/electronic_assembly/device/EA
+
+/obj/item/device/assembly/electronic_assembly/New()
+ EA = new(src)
+ EA.holder = src
+ ..()
+
+/obj/item/device/assembly/electronic_assembly/attackby(obj/item/I as obj, mob/user as mob)
+ if (istype(I, /obj/item/weapon/crowbar))
+ toggle_open(user)
+ else if (opened)
+ EA.attackby(I, user)
+ else
+ ..()
+
+/obj/item/device/assembly/electronic_assembly/proc/toggle_open(mob/user)
+ playsound(get_turf(src), 'sound/items/Crowbar.ogg', 50, 1)
+ opened = !opened
+ EA.opened = opened
+ to_chat(user, "You [opened ? "opened" : "closed"] \the [src].")
+ secured = 1
+ update_icon()
+
+/obj/item/device/assembly/electronic_assembly/update_icon()
+ if(EA)
+ icon_state = initial(icon_state)
+ else
+ icon_state = initial(icon_state)+"0"
+ if(opened)
+ icon_state = icon_state + "-open"
+
+/obj/item/device/assembly/electronic_assembly/attack_self(mob/user as mob)
+ if(EA)
+ EA.attack_self(user)
+
+/obj/item/device/assembly/electronic_assembly/pulsed(var/radio = 0) //Called when another assembly acts on this one, var/radio will determine where it came from for wire calcs
+ if(EA)
+ for(var/obj/item/integrated_circuit/built_in/device_input/I in EA.contents)
+ I.do_work()
+ return
+
+/obj/item/device/assembly/electronic_assembly/examine(mob/user)
+ .=..(user, 1)
+ if(EA)
+ for(var/obj/item/integrated_circuit/IC in EA.contents)
+ IC.external_examine(user)
+
+/obj/item/device/assembly/electronic_assembly/verb/toggle()
+ set src in usr
+ set category = "Object"
+ set name = "Open/Close Device Assembly"
+ set desc = "Open or close device assembly!"
+
+ toggle_open(usr)
+
+
+/obj/item/device/electronic_assembly/device
+ name = "electronic device"
+ icon_state = "setup_device"
+ desc = "It's a tiny electronic device with specific use for attaching to other devices."
+ var/obj/item/device/assembly/electronic_assembly/holder
+ w_class = 1
+ max_components = IC_COMPONENTS_BASE * 3/4
+ max_complexity = IC_COMPLEXITY_BASE * 3/4
+
+
+/obj/item/device/electronic_assembly/device/New()
+ ..()
+ var/obj/item/integrated_circuit/built_in/device_input/input = new(src)
+ var/obj/item/integrated_circuit/built_in/device_output/output = new(src)
+ input.assembly = src
+ output.assembly = src
+
+/obj/item/device/electronic_assembly/device/check_interactivity(mob/user)
+ if(!CanInteract(user,src))
+ return 0
+ return 1
+
diff --git a/code/modules/integrated_electronics/core/helpers.dm b/code/modules/integrated_electronics/core/helpers.dm
new file mode 100644
index 0000000000000..a0f60b6390eb0
--- /dev/null
+++ b/code/modules/integrated_electronics/core/helpers.dm
@@ -0,0 +1,85 @@
+/obj/item/integrated_circuit/proc/setup_io(var/list/io_list, var/io_type, var/list/io_default_list)
+ var/list/io_list_copy = io_list.Copy()
+ io_list.Cut()
+ var/i = 0
+ for(var/io_entry in io_list_copy)
+ var/default_data = null
+ var/io_type_override = null
+ // Override the default data.
+ if(io_default_list && io_default_list.len) // List containing special pin types that need to be added.
+ default_data = io_default_list["[i]"] // This is deliberately text because the index is a number in text form.
+ // Override the pin type.
+ if(io_list_copy[io_entry])
+ io_type_override = io_list_copy[io_entry]
+
+ if(io_type_override)
+ // world << "io_type_override is now [io_type_override] on [src]."
+ io_list.Add(new io_type_override(src, io_entry, default_data))
+ else
+ io_list.Add(new io_type(src, io_entry, default_data))
+
+/obj/item/integrated_circuit/proc/set_pin_data(var/pin_type, var/pin_number, var/new_data)
+ var/datum/integrated_io/pin = get_pin_ref(pin_type, pin_number)
+ return pin.write_data_to_pin(new_data)
+
+/obj/item/integrated_circuit/proc/get_pin_data(var/pin_type, var/pin_number)
+ var/datum/integrated_io/pin = get_pin_ref(pin_type, pin_number)
+ return pin.get_data()
+
+/obj/item/integrated_circuit/proc/get_pin_data_as_type(var/pin_type, var/pin_number, var/as_type)
+ var/datum/integrated_io/pin = get_pin_ref(pin_type, pin_number)
+ return pin.data_as_type(as_type)
+
+/obj/item/integrated_circuit/proc/activate_pin(var/pin_number)
+ var/datum/integrated_io/activate/A = activators[pin_number]
+ A.push_data()
+
+/datum/integrated_io/proc/get_data()
+ if(isnull(data))
+ return
+ if(isweakref(data))
+ return data.resolve()
+ return data
+
+/obj/item/integrated_circuit/proc/get_pin_ref(var/pin_type, var/pin_number)
+ switch(pin_type)
+ if(IC_INPUT)
+ if(pin_number > inputs.len)
+ return null
+ return inputs[pin_number]
+ if(IC_OUTPUT)
+ if(pin_number > outputs.len)
+ return null
+ return outputs[pin_number]
+ if(IC_ACTIVATOR)
+ if(pin_number > activators.len)
+ return null
+ return activators[pin_number]
+ return null
+
+/obj/item/integrated_circuit/proc/handle_wire(var/datum/integrated_io/pin, var/obj/item/device/integrated_electronics/tool)
+ if(istype(tool, /obj/item/device/integrated_electronics/wirer))
+ var/obj/item/device/integrated_electronics/wirer/wirer = tool
+ if(pin)
+ wirer.wire(pin, usr)
+ return 1
+
+ else if(istype(tool, /obj/item/device/integrated_electronics/debugger))
+ var/obj/item/device/integrated_electronics/debugger/debugger = tool
+ if(pin)
+ debugger.write_data(pin, usr)
+ return 1
+ return 0
+
+/mob/living/proc/isSynthetic()
+ return 0
+
+/mob/living/carbon/human/isSynthetic()
+ // If they are 100% robotic, they count as synthetic.
+ for(var/obj/item/organ/limb/E in organsystem)
+ if(E.organtype < ORGAN_ROBOTIC)
+ return FALSE
+ return TRUE
+
+/proc/get_random_colour()
+ return pick(list("FF0000", "FF7F00", "FFFF00", "00FF00", "0000FF", "4B0082", "8F00FF"))
\ No newline at end of file
diff --git a/code/modules/integrated_electronics/core/integrated_circuit.dm b/code/modules/integrated_electronics/core/integrated_circuit.dm
new file mode 100644
index 0000000000000..1be2a0af8657c
--- /dev/null
+++ b/code/modules/integrated_electronics/core/integrated_circuit.dm
@@ -0,0 +1,396 @@
+/*
+ Integrated circuits are essentially modular machines. Each circuit has a specific function, and combining them inside Electronic Assemblies allows
+a creative player the means to solve many problems. Circuits are held inside an electronic assembly, and are wired using special tools.
+*/
+
+/obj/item/integrated_circuit/examine(mob/user)
+ interact(user)
+ external_examine(user)
+ . = ..()
+
+// This should be used when someone is examining while the case is opened.
+/obj/item/integrated_circuit/proc/internal_examine(mob/user)
+ to_chat(user, "This board has [inputs.len] input pin\s, [outputs.len] output pin\s and [activators.len] activation pin\s.")
+ for(var/datum/integrated_io/I in inputs)
+ if(I.linked.len)
+ to_chat(user, "The '[I]' is connected to [I.get_linked_to_desc()].")
+ for(var/datum/integrated_io/O in outputs)
+ if(O.linked.len)
+ to_chat(user, "The '[O]' is connected to [O.get_linked_to_desc()].")
+ for(var/datum/integrated_io/activate/A in activators)
+ if(A.linked.len)
+ to_chat(user, "The '[A]' is connected to [A.get_linked_to_desc()].")
+ any_examine(user)
+ interact(user)
+
+// This should be used when someone is examining from an 'outside' perspective, e.g. reading a screen or LED.
+/obj/item/integrated_circuit/proc/external_examine(mob/user)
+ any_examine(user)
+
+/obj/item/integrated_circuit/proc/any_examine(mob/user)
+ return
+
+/obj/item/integrated_circuit/New()
+ displayed_name = name
+ if(!size) size = w_class
+ if(size == -1) size = 0
+ setup_io(inputs, /datum/integrated_io, inputs_default)
+ setup_io(outputs, /datum/integrated_io, outputs_default)
+ setup_io(activators, /datum/integrated_io/activate)
+ ..()
+
+/obj/item/integrated_circuit/proc/on_data_written() //Override this for special behaviour when new data gets pushed to the circuit.
+ return
+
+/obj/item/integrated_circuit/Destroy()
+ for(var/datum/integrated_io/I in inputs)
+ qdel(I)
+ for(var/datum/integrated_io/O in outputs)
+ qdel(O)
+ for(var/datum/integrated_io/A in activators)
+ qdel(A)
+ . = ..()
+
+/obj/item/integrated_circuit/proc/nano_host()
+ if(istype(src.loc, /obj/item/device/electronic_assembly))
+ var/obj/item/device/electronic_assembly/assembly = loc
+ return assembly.resolve_nano_host()
+ return ..()
+
+/obj/item/integrated_circuit/emp_act(severity)
+ for(var/datum/integrated_io/io in inputs + outputs + activators)
+ io.scramble()
+
+/obj/item/integrated_circuit/proc/check_interactivity(mob/user)
+ if(assembly)
+ return assembly.check_interactivity(user)
+ else if(!CanInteract(user, src))
+ return 0
+ return 1
+
+/obj/item/integrated_circuit/verb/rename_component()
+ set name = "Rename Circuit"
+ set category = "Object"
+ set desc = "Rename your circuit, useful to stay organized."
+
+ var/mob/M = usr
+ if(!check_interactivity(M))
+ return
+
+ var/input = sanitize_text(input("What do you want to name the circuit?", "Rename", src.name) as null|text, MAX_NAME_LEN)
+ if(src && input && assembly.check_interactivity(M))
+ to_chat(M, "The circuit '[src.name]' is now labeled '[input]'.")
+ displayed_name = input
+
+/obj/item/integrated_circuit/interact(mob/user)
+ if(!check_interactivity(user))
+ return
+// if(!assembly)
+// return
+
+ var/window_height = 350
+ var/window_width = 600
+
+ //var/table_edge_width = "[(window_width - window_width * 0.1) / 4]px"
+ //var/table_middle_width = "[(window_width - window_width * 0.1) - (table_edge_width * 2)]px"
+ var/table_edge_width = "30%"
+ var/table_middle_width = "40%"
+
+ var/HTML = list()
+ HTML += "[src.displayed_name]"
+ HTML += ""
+ HTML += "
"
+ HTML += "
"
+
+// HTML += "
Meta Variables;" // If more meta vars get introduced, uncomment this.
+// HTML += "
"
+
+ HTML += "
Complexity: [complexity]"
+ if(power_draw_idle)
+ HTML += "
Power Draw: [power_draw_idle] W (Idle)"
+ if(power_draw_per_use)
+ HTML += "
Power Draw: [power_draw_per_use] W (Active)" // Borgcode says that powercells' checked_use() takes joules as input.
+ HTML += "
[extended_desc]"
+
+ HTML += ""
+ if(src.assembly)
+ user << browse(jointext(HTML, null), "window=assembly-\ref[src.assembly];size=[window_width]x[window_height];border=1;can_resize=1;can_close=1;can_minimize=1")
+ else
+ user << browse(jointext(HTML, null), "window=circuit-\ref[src];size=[window_width]x[window_height];border=1;can_resize=1;can_close=1;can_minimize=1")
+
+ onclose(user, "assembly-\ref[src.assembly]")
+
+/obj/item/integrated_circuit/Topic(href, href_list)
+ if(!check_interactivity(usr))
+ return
+ if(..())
+ return 1
+
+ var/update = 1
+ var/obj/item/device/electronic_assembly/A = src.assembly
+ var/update_to_assembly = 0
+ var/datum/integrated_io/pin = locate(href_list["pin"]) in inputs + outputs + activators
+ var/datum/integrated_io/linked = null
+ if(href_list["link"])
+ linked = locate(href_list["link"]) in pin.linked
+
+ var/obj/held_item = usr.get_active_hand()
+
+ if(href_list["rename"])
+ rename_component(usr)
+ if(href_list["from_assembly"])
+ update = 0
+ var/obj/item/device/electronic_assembly/ea = loc
+ if(istype(ea))
+ ea.interact(usr)
+
+ if(href_list["pin_name"])
+ if (!istype(held_item, /obj/item/device/multitool) || !allow_multitool)
+ href_list["wire"] = 1
+ else
+ var/obj/item/device/multitool/M = held_item
+ M.wire(pin,usr)
+
+
+
+ if(href_list["pin_data"])
+ if (!istype(held_item, /obj/item/device/multitool) || !allow_multitool)
+ href_list["wire"] = 1
+
+ else
+ var/datum/integrated_io/io = pin
+ io.ask_for_pin_data(usr) // The pins themselves will determine how to ask for data, and will validate the data.
+ /*
+ if(io.io_type == DATA_CHANNEL)
+
+ var/type_to_use = input("Please choose a type to use.","[src] type setting") as null|anything in list("string","number", "null")
+ if(!check_interactivity(usr))
+ return
+
+ var/new_data = null
+ switch(type_to_use)
+ if("string")
+ new_data = input("Now type in a string.","[src] string writing") as null|text
+ to_chat(usr, "You input [new_data] into the pin.")
+ //to_chat(user, "You write '[new_data]' to the '[io]' pin of \the [io.holder].")
+ if("number")
+ new_data = input("Now type in a number.","[src] number writing") as null|num
+ if(isnum(new_data) && check_interactivity(usr) )
+ to_chat(usr, "You input [new_data] into the pin.")
+ if("null")
+ if(check_interactivity(usr))
+ to_chat(usr, "You clear the pin's memory.")
+
+ io.write_data_to_pin(new_data)
+
+ else if(io.io_type == PULSE_CHANNEL)
+ io.holder.check_then_do_work(ignore_power = TRUE)
+ to_chat(usr, "You pulse \the [io.holder]'s [io] pin.")
+ */
+
+
+ if(href_list["pin_unwire"])
+ if (!istype(held_item, /obj/item/device/multitool) || !allow_multitool)
+ href_list["wire"] = 1
+ else
+ var/obj/item/device/multitool/M = held_item
+ M.unwire(pin, linked, usr)
+
+ if(href_list["wire"])
+ if(istype(held_item, /obj/item/device/integrated_electronics/wirer))
+ var/obj/item/device/integrated_electronics/wirer/wirer = held_item
+ if(linked)
+ wirer.wire(linked, usr)
+ else if(pin)
+ wirer.wire(pin, usr)
+
+ else if(istype(held_item, /obj/item/device/integrated_electronics/debugger))
+ var/obj/item/device/integrated_electronics/debugger/debugger = held_item
+ if(pin)
+ debugger.write_data(pin, usr)
+ else
+ to_chat(usr, "You can't do a whole lot without the proper tools.")
+
+ if(href_list["examine"])
+ var/obj/item/integrated_circuit/examined
+ if(href_list["examined"])
+ examined = href_list["examined"]
+ else
+ examined = src
+ examined.interact(usr)
+ update = 0
+
+ if(href_list["bottom"])
+ var/obj/item/integrated_circuit/circuit = locate(href_list["bottom"]) in src.assembly.contents
+ var/assy = circuit.assembly
+ if(!circuit)
+ return
+ circuit.loc = null
+ circuit.loc = assy
+ . = 1
+ update_to_assembly = 1
+
+ if(href_list["scan"])
+ if(istype(held_item, /obj/item/device/integrated_electronics/debugger))
+ var/obj/item/device/integrated_electronics/debugger/D = held_item
+ if(D.accepting_refs)
+ D.afterattack(src, usr, TRUE)
+ else
+ to_chat(usr, "The Debugger's 'ref scanner' needs to be on.")
+ else
+ to_chat(usr, "You need a multitool/debugger set to 'ref' mode to do that.")
+
+ if(href_list["return"])
+ if(A)
+ update_to_assembly = 1
+ usr << browse(null, "window=circuit-\ref[src];border=1;can_resize=1;can_close=1;can_minimize=1")
+ else
+ to_chat(usr, "This circuit is not in an assembly!")
+
+
+ if(href_list["remove"])
+ if(!A)
+ to_chat(usr, "This circuit is not in an assembly!")
+ return
+ if(!removable)
+ to_chat(usr, "\The [src] seems to be permanently attached to the case.")
+ return
+ var/obj/item/device/electronic_assembly/ea = loc
+ disconnect_all()
+ var/turf/T = get_turf(src)
+ forceMove(T)
+ assembly = null
+ playsound(T, 'sound/items/Crowbar.ogg', 50, 1)
+ to_chat(usr, "You pop \the [src] out of the case, and slide it out.")
+
+ if(istype(ea))
+ ea.interact(usr)
+ update = 0
+ return
+
+ if(update)
+ if(A && istype(A) && update_to_assembly)
+ A.interact(usr)
+ else
+ interact(usr) // To refresh the UI.
+
+
+
+/obj/item/integrated_circuit/proc/push_data()
+ for(var/datum/integrated_io/O in outputs)
+ O.push_data()
+
+/obj/item/integrated_circuit/proc/pull_data()
+ for(var/datum/integrated_io/I in inputs)
+ I.push_data()
+
+/obj/item/integrated_circuit/proc/draw_idle_power()
+ if(assembly)
+ return assembly.draw_power(power_draw_idle)
+
+// Override this for special behaviour when there's no power left.
+/obj/item/integrated_circuit/proc/power_fail()
+ return
+
+// Returns true if there's enough power to work().
+/obj/item/integrated_circuit/proc/check_power()
+ if(!assembly)
+ return FALSE // Not in an assembly, therefore no power.
+ if(assembly.draw_power(power_draw_per_use))
+ return TRUE // Battery has enough.
+ return FALSE // Not enough power.
+
+/obj/item/integrated_circuit/proc/check_then_do_work(var/ignore_power = FALSE)
+ if(world.time < next_use) // All intergrated circuits have an internal cooldown, to protect from spam.
+ return
+ if(power_draw_per_use && !ignore_power)
+ if(!check_power())
+ power_fail()
+ return
+ next_use = world.time + cooldown_per_use
+ do_work()
+
+/obj/item/integrated_circuit/proc/do_work()
+ return
+
+/obj/item/integrated_circuit/proc/disconnect_all()
+ for(var/datum/integrated_io/I in inputs)
+ I.disconnect()
+ for(var/datum/integrated_io/O in outputs)
+ O.disconnect()
+ for(var/datum/integrated_io/activate/A in activators)
+ A.disconnect()
diff --git a/code/modules/integrated_electronics/core/pins.dm b/code/modules/integrated_electronics/core/pins.dm
new file mode 100644
index 0000000000000..6789834df2d73
--- /dev/null
+++ b/code/modules/integrated_electronics/core/pins.dm
@@ -0,0 +1,189 @@
+/*
+ Pins both hold data for circuits, as well move data between them. Some also cause circuits to do their function. DATA_CHANNEL pins are the data holding/moving kind,
+where as PULSE_CHANNEL causes circuits to work() when their pulse hits them.
+
+
+A visualization of how pins work is below. Imagine the below image involves an addition circuit.
+When the bottom pin, the activator, receives a pulse, all the numbers on the left (input) get added, and the answer goes on the right side (output).
+
+Inputs Outputs
+
+A [2]\ /[8] result
+B [1]-\|++|/
+C [4]-/|++|
+D [1]/ ||
+ ||
+ Activator
+
+
+
+*/
+/datum/integrated_io
+ var/name = "input/output"
+ var/obj/item/integrated_circuit/holder = null
+ var/weakref/data = null // This is a weakref, to reduce typecasts. Note that oftentimes numbers and text may also occupy this.
+ var/list/linked = list()
+ var/io_type = DATA_CHANNEL
+
+/datum/integrated_io/New(var/newloc, var/name, var/new_data)
+ ..()
+ src.name = name
+ if(new_data)
+ src.data = new_data
+ holder = newloc
+ if(!istype(holder))
+ message_admins("ERROR: An integrated_io ([src.name]) spawned without a valid holder! This is a bug.")
+
+/datum/integrated_io/Destroy()
+ disconnect()
+ data = null
+ holder = null
+ . = ..()
+
+/datum/integrated_io/proc/nano_host()
+ return holder.nano_host()
+
+
+/datum/integrated_io/proc/data_as_type(var/as_type)
+ if(!isweakref(data))
+ return
+ var/weakref/w = data
+ var/output = w.resolve()
+ return istype(output, as_type) ? output : null
+
+/datum/integrated_io/proc/display_data(var/input)
+ if(isnull(input))
+ return "(null)" // Empty data means nothing to show.
+
+ if(istext(input))
+ return "(\"[input]\")" // Wraps the 'string' in escaped quotes, so that people know it's a 'string'.
+
+/*
+list[](
+ "A",
+ "B",
+ "C"
+)
+*/
+
+ if(islist(input))
+ var/list/my_list = input
+ var/result = "list\[[my_list.len]\]("
+ if(my_list.len)
+ result += "
"
+ var/pos = 0
+ for(var/line in my_list)
+ result += "[display_data(line)]"
+ pos++
+ if(pos != my_list.len)
+ result += ",
"
+ result += "
"
+ result += ")"
+ return result
+
+ if(isweakref(input))
+ var/weakref/w = input
+ var/atom/A = w.resolve()
+ //return A ? "([A.name] \[Ref\])" : "(null)" // For refs, we want just the name displayed.
+ return A ? "(\ref[A] \[Ref\])" : "(null)"
+
+ return "([input])" // Nothing special needed for numbers or other stuff.
+
+/datum/integrated_io/activate/display_data()
+ return "(\[pulse\])"
+
+/datum/integrated_io/proc/display_pin_type()
+ return IC_FORMAT_ANY
+
+/datum/integrated_io/activate/display_pin_type()
+ return IC_FORMAT_PULSE
+
+/datum/integrated_io/proc/scramble()
+ if(isnull(data))
+ return
+ if(isnum(data))
+ write_data_to_pin(rand(-10000, 10000))
+ if(istext(data))
+ write_data_to_pin("ERROR")
+ push_data()
+
+/datum/integrated_io/activate/scramble()
+ push_data()
+
+/datum/integrated_io/proc/write_data_to_pin(var/new_data)
+ if(isnull(new_data) || isnum(new_data) || istext(new_data) || isweakref(new_data)) // Anything else is a type we don't want.
+ if(istext(new_data))
+ new_data = sanitize_text(new_data, MAX_MESSAGE_LEN, 0, 0)
+ data = new_data
+ holder.on_data_written()
+
+/datum/integrated_io/proc/push_data()
+ for(var/datum/integrated_io/io in linked)
+ io.write_data_to_pin(data)
+
+/datum/integrated_io/activate/push_data()
+ for(var/datum/integrated_io/io in linked)
+ io.holder.check_then_do_work()
+
+/datum/integrated_io/proc/pull_data()
+ for(var/datum/integrated_io/io in linked)
+ write_data_to_pin(io.data)
+
+/datum/integrated_io/proc/get_linked_to_desc()
+ if(linked.len)
+ return "the [english_list(linked)]"
+ return "nothing"
+
+/datum/integrated_io/proc/disconnect()
+ //First we iterate over everything we are linked to.
+ for(var/datum/integrated_io/their_io in linked)
+ //While doing that, we iterate them as well, and disconnect ourselves from them.
+ for(var/datum/integrated_io/their_linked_io in their_io.linked)
+ if(their_linked_io == src)
+ their_io.linked.Remove(src)
+ else
+ continue
+ //Now that we're removed from them, we gotta remove them from us.
+ src.linked.Remove(their_io)
+
+/datum/integrated_io/proc/ask_for_data_type(mob/user, var/default, var/list/allowed_data_types = list("string","number","null"))
+ var/type_to_use = input("Please choose a type to use.","[src] type setting") as null|anything in allowed_data_types
+ if(!holder.check_interactivity(user))
+ return
+
+ var/new_data = null
+ switch(type_to_use)
+ if("string")
+ new_data = input("Now type in a string.","[src] string writing", istext(default) ? default : null) as null|text
+ if(istext(new_data) && holder.check_interactivity(user) )
+ to_chat(user, "You input [new_data] into the pin.")
+ return new_data
+ if("number")
+ new_data = input("Now type in a number.","[src] number writing", isnum(default) ? default : null) as null|num
+ if(isnum(new_data) && holder.check_interactivity(user) )
+ to_chat(user, "You input [new_data] into the pin.")
+ return new_data
+ if("null")
+ if(holder.check_interactivity(user))
+ to_chat(user, "You clear the pin's memory.")
+ return new_data
+
+// Basically a null check
+/datum/integrated_io/proc/is_valid()
+ return !isnull(data)
+
+// This proc asks for the data to write, then writes it.
+/datum/integrated_io/proc/ask_for_pin_data(mob/user)
+ var/new_data = ask_for_data_type(user)
+ write_data_to_pin(new_data)
+
+/datum/integrated_io/activate/ask_for_pin_data(mob/user) // This just pulses the pin.
+ holder.check_then_do_work(ignore_power = TRUE)
+ to_chat(user, "You pulse \the [holder]'s [src] pin.")
+
+/datum/integrated_io/activate
+ name = "activation pin"
+ io_type = PULSE_CHANNEL
+
+/datum/integrated_io/activate/out // All this does is just make the UI say 'out' instead of 'in'
+ data = 1
diff --git a/code/modules/integrated_electronics/core/printer.dm b/code/modules/integrated_electronics/core/printer.dm
new file mode 100644
index 0000000000000..75809dbf1867b
--- /dev/null
+++ b/code/modules/integrated_electronics/core/printer.dm
@@ -0,0 +1,230 @@
+
+/obj/item/device/integrated_circuit_printer
+ name = "integrated circuit printer"
+ desc = "A portable(ish) machine made to print tiny modular circuitry out of metal."
+ icon = 'icons/obj/electronic_assemblies.dmi'
+ icon_state = "circuit_printer"
+ w_class = 4
+ var/metal = 0
+ var/max_metal = 100
+ var/metal_per_sheet = 10 // One sheet equals this much metal.
+
+ var/upgraded = FALSE // When hit with an upgrade disk, will turn true, allowing it to print the higher tier circuits.
+ var/can_clone = FALSE // Same for above, but will allow the printer to duplicate a specific assembly.
+ var/static/list/recipe_list = list()
+ var/current_category = null
+ var/obj/item/device/electronic_assembly/assembly_to_clone = null
+ initialize()
+
+/obj/item/device/integrated_circuit_printer/upgraded
+ upgraded = TRUE
+ can_clone = TRUE
+
+/obj/item/device/integrated_circuit_printer/New()
+ ..()
+ initialize()
+
+/obj/item/device/integrated_circuit_printer/initialize()
+ ..()
+ if(!recipe_list.len)
+ // Unfortunately this needed a lot of loops, but it should only be run once at init.
+
+ // First loop is to seperate the actual circuits from base circuits.
+ var/list/circuits_to_use = list()
+ for(var/obj/item/integrated_circuit/IC in all_integrated_circuits)
+ if((IC.spawn_flags & IC_SPAWN_DEFAULT) || (IC.spawn_flags & IC_SPAWN_RESEARCH))
+ circuits_to_use.Add(IC)
+
+ // Second loop is to find all categories.
+ var/list/found_categories = list()
+ for(var/obj/item/integrated_circuit/IC in circuits_to_use)
+ if(!(IC.category_text in found_categories))
+ found_categories.Add(IC.category_text)
+
+ // Third loop is to initialize lists by category names, then put circuits matching the category inside.
+ for(var/category in found_categories)
+ recipe_list[category] = list()
+ var/list/current_list = recipe_list[category]
+ for(var/obj/item/integrated_circuit/IC in circuits_to_use)
+ if(IC.category_text == category)
+ current_list.Add(IC)
+
+ // Now for non-circuit things.
+ var/list/assembly_list = list()
+ assembly_list.Add(
+ new /obj/item/device/electronic_assembly/default(null),
+ new /obj/item/device/electronic_assembly/calc(null),
+ new /obj/item/device/electronic_assembly/clam(null),
+ new /obj/item/device/electronic_assembly/simple(null),
+ new /obj/item/device/electronic_assembly/hook(null),
+ new /obj/item/device/electronic_assembly/pda(null),
+ new /obj/item/device/electronic_assembly/medium/default(null),
+ new /obj/item/device/electronic_assembly/medium/box(null),
+ new /obj/item/device/electronic_assembly/medium/clam(null),
+ new /obj/item/device/electronic_assembly/medium/medical(null),
+ new /obj/item/device/electronic_assembly/medium/gun(null),
+ new /obj/item/device/electronic_assembly/medium/radio(null),
+ new /obj/item/device/electronic_assembly/large/default(null),
+ new /obj/item/device/electronic_assembly/large/scope(null),
+ new /obj/item/device/electronic_assembly/large/terminal(null),
+ new /obj/item/device/electronic_assembly/large/arm(null),
+ new /obj/item/device/electronic_assembly/large/tall(null),
+ new /obj/item/device/electronic_assembly/large/industrial(null),
+ new /obj/item/device/electronic_assembly/drone/default(null),
+ new /obj/item/device/electronic_assembly/drone/arms(null),
+ new /obj/item/device/electronic_assembly/drone/secbot(null),
+ new /obj/item/device/electronic_assembly/drone/medbot(null),
+ new /obj/item/device/electronic_assembly/drone/genbot(null),
+ new /obj/item/device/electronic_assembly/drone/android(null),
+ new /obj/item/device/electronic_assembly/wallmount/light(null),
+ new /obj/item/device/electronic_assembly/wallmount(null),
+ new /obj/item/device/electronic_assembly/wallmount/heavy(null),
+ new /obj/item/weapon/implant/integrated_circuit(null),
+ new /obj/item/device/assembly/electronic_assembly(null)
+ )
+ recipe_list["Assemblies"] = assembly_list
+
+ var/list/tools_list = list()
+ tools_list.Add(
+ new /obj/item/device/integrated_electronics/wirer(null),
+ new /obj/item/device/integrated_electronics/debugger(null),
+ new /obj/item/device/integrated_electronics/detailer(null)
+ )
+ recipe_list["Tools"] = tools_list
+
+
+/obj/item/device/integrated_circuit_printer/attackby(var/obj/item/O, var/mob/user)
+ if(istype(O,/obj/item/stack/sheet/metal))
+ var/obj/item/stack/sheet/metal/stack = O
+ var/num = min((max_metal - metal) / metal_per_sheet, stack.amount)
+ if(num < 1)
+ to_chat(user, "\The [src] is too full to add more metal.")
+ return
+ if(stack.amount >= num)
+ stack.use(num)
+ to_chat(user, "You add [num] sheet\s to \the [src].")
+ metal += num * metal_per_sheet
+ interact(user)
+ return TRUE
+
+ if(istype(O,/obj/item/integrated_circuit))
+ to_chat(user, "You insert the circuit into \the [src]. ")
+ user.unEquip(O)
+ metal = min(metal + O.w_class, max_metal)
+ qdel(O)
+ interact(user)
+ return TRUE
+
+ if(istype(O,/obj/item/weapon/disk/integrated_circuit/upgrade/advanced))
+ if(upgraded)
+ to_chat(user, "\The [src] already has this upgrade. ")
+ return TRUE
+ to_chat(user, "You install \the [O] into \the [src]. ")
+ upgraded = TRUE
+ interact(user)
+ return TRUE
+
+ if(istype(O,/obj/item/weapon/disk/integrated_circuit/upgrade/clone))
+ if(can_clone)
+ to_chat(user, "\The [src] already has this upgrade. ")
+ return TRUE
+ to_chat(user, "You install \the [O] into \the [src]. ")
+ can_clone = TRUE
+ interact(user)
+ return TRUE
+
+ return ..()
+
+/obj/item/device/integrated_circuit_printer/attack_self(var/mob/user)
+ interact(user)
+
+/obj/item/device/integrated_circuit_printer/interact(mob/user)
+ var/window_height = 600
+ var/window_width = 500
+
+ if(isnull(current_category))
+ current_category = recipe_list[1]
+
+ var/HTML = list()
+
+ HTML += "Integrated Circuit Printer
"
+ HTML += "Metal: [metal/metal_per_sheet]/[max_metal/metal_per_sheet] sheets.
"
+ HTML += "Circuits available: [upgraded ? "Regular":"Advanced"]."
+ HTML += "Assembly Cloning: [can_clone ? "Available": "Unavailable"]."
+ if(assembly_to_clone)
+ HTML += "Assembly '[assembly_to_clone.name]' loaded."
+ HTML += "Crossed out circuits mean that the printer is not sufficentally upgraded to create that circuit.
"
+ HTML += "
"
+ HTML += "Categories:"
+ for(var/category in recipe_list)
+ if(category != current_category)
+ HTML += " \[[category]\] "
+ else // Bold the button if it's already selected.
+ HTML += " \[[category]\] "
+ HTML += "
"
+ HTML += "[current_category]
"
+
+ var/list/current_list = recipe_list[current_category]
+ for(var/obj/O in current_list)
+ var/can_build = TRUE
+ if(istype(O, /obj/item/integrated_circuit))
+ var/obj/item/integrated_circuit/IC = O
+ if((IC.spawn_flags & IC_SPAWN_RESEARCH) && (!(IC.spawn_flags & IC_SPAWN_DEFAULT)) && !upgraded)
+ can_build = FALSE
+ if(can_build)
+ HTML += "\[[O.name]\]: [O.desc]
"
+ else
+ HTML += "\[[O.name]\]: [O.desc]
"
+
+ user << browse(jointext(HTML, null), "window=integrated_printer;size=[window_width]x[window_height];border=1;can_resize=1;can_close=1;can_minimize=1")
+
+/obj/item/device/integrated_circuit_printer/Topic(href, href_list)
+ if(..())
+ return 1
+
+ add_fingerprint(usr)
+
+ if(href_list["category"])
+ current_category = href_list["category"]
+
+ if(href_list["build"])
+ var/build_type = text2path(href_list["build"])
+ if(!build_type || !ispath(build_type))
+ return 1
+
+ var/cost = 1
+ if(ispath(build_type, /obj/item/device/electronic_assembly))
+ var/obj/item/device/electronic_assembly/E = build_type
+ cost = round( (initial(E.max_complexity) + initial(E.max_components) ) / 4)
+ else if(ispath(build_type, /obj/item/integrated_circuit))
+ var/obj/item/integrated_circuit/IC = build_type
+ cost = initial(IC.w_class)
+
+ if(metal - cost < 0)
+ to_chat(usr, "You need [cost] metal to build that!.")
+ return 1
+ metal -= cost
+ new build_type(get_turf(loc))
+
+ interact(usr)
+
+// FUKKEN UPGRADE DISKS
+/obj/item/weapon/disk/integrated_circuit/upgrade
+ name = "integrated circuit printer upgrade disk"
+ desc = "Install this into your integrated circuit printer to enhance it."
+ icon = 'icons/obj/electronic_assemblies.dmi'
+ icon_state = "upgrade_disk"
+ item_state = "card-id"
+ w_class = 2
+ origin_tech = list(TECH_ENGINEERING = 3, TECH_DATA = 4)
+
+/obj/item/weapon/disk/integrated_circuit/upgrade/advanced
+ name = "integrated circuit printer upgrade disk - advanced designs"
+ desc = "Install this into your integrated circuit printer to enhance it. This one adds new, advanced designs to the printer."
+
+// To be implemented later.
+/obj/item/weapon/disk/integrated_circuit/upgrade/clone
+ name = "integrated circuit printer upgrade disk - circuit cloner"
+ desc = "Install this into your integrated circuit printer to enhance it. This one allows the printer to duplicate assemblies."
+ icon_state = "upgrade_disk_clone"
+ origin_tech = list(TECH_ENGINEERING = 5, TECH_DATA = 6)
\ No newline at end of file
diff --git a/code/modules/integrated_electronics/core/special_pins/boolean_pin.dm b/code/modules/integrated_electronics/core/special_pins/boolean_pin.dm
new file mode 100644
index 0000000000000..baf3d029daebf
--- /dev/null
+++ b/code/modules/integrated_electronics/core/special_pins/boolean_pin.dm
@@ -0,0 +1,25 @@
+// These pins only contain 0 or 1. Null is not allowed.
+/datum/integrated_io/boolean
+ name = "boolean pin"
+ data = FALSE
+
+/datum/integrated_io/boolean/ask_for_pin_data(mob/user) // 'Ask' is a bit misleading, acts more like a toggle.
+ var/new_data = !data
+ write_data_to_pin(new_data)
+
+/datum/integrated_io/boolean/write_data_to_pin(var/new_data)
+ if(new_data == FALSE || new_data == TRUE)
+ data = new_data
+ holder.on_data_written()
+
+/datum/integrated_io/boolean/scramble()
+ write_data_to_pin(rand(FALSE,TRUE))
+ push_data()
+
+/datum/integrated_io/boolean/display_pin_type()
+ return IC_FORMAT_BOOLEAN
+
+/datum/integrated_io/boolean/display_data(var/input)
+ if(data == TRUE)
+ return "(True)"
+ return "(False)"
\ No newline at end of file
diff --git a/code/modules/integrated_electronics/core/special_pins/char_pin.dm b/code/modules/integrated_electronics/core/special_pins/char_pin.dm
new file mode 100644
index 0000000000000..bd36d18495ec3
--- /dev/null
+++ b/code/modules/integrated_electronics/core/special_pins/char_pin.dm
@@ -0,0 +1,27 @@
+// These pins can only contain a 1 character string or null.
+/datum/integrated_io/char
+ name = "char pin"
+
+/datum/integrated_io/char/ask_for_pin_data(mob/user)
+ var/new_data = input("Please type in one character.","[src] char writing") as null|text
+ if(holder.check_interactivity(user) )
+ to_chat(user, "You input [new_data ? "new_data" : "NULL"] into the pin.")
+ write_data_to_pin(new_data)
+
+/datum/integrated_io/char/write_data_to_pin(var/new_data)
+ if(isnull(new_data) || istext(new_data))
+ if(length(new_data) > 1)
+ return
+ data = new_data
+ holder.on_data_written()
+
+// This makes the text go from "A" to "%".
+/datum/integrated_io/char/scramble()
+ if(!is_valid())
+ return
+ var/list/options = list("!","@","#","$","%","^","&","*") + alphabet_uppercase
+ data = pick(options)
+ push_data()
+
+/datum/integrated_io/char/display_pin_type()
+ return IC_FORMAT_CHAR
\ No newline at end of file
diff --git a/code/modules/integrated_electronics/core/special_pins/color_pin.dm b/code/modules/integrated_electronics/core/special_pins/color_pin.dm
new file mode 100644
index 0000000000000..2d20560fb6e7d
--- /dev/null
+++ b/code/modules/integrated_electronics/core/special_pins/color_pin.dm
@@ -0,0 +1,44 @@
+// These pins can only contain a color (in the form of #FFFFFF) or null.
+/datum/integrated_io/color
+ name = "color pin"
+
+/datum/integrated_io/color/ask_for_pin_data(mob/user)
+ var/new_data = input("Please select a color.","[src] color writing") as null|color
+ if(holder.check_interactivity(user) )
+ to_chat(user, "You input a new color into the pin.")
+ write_data_to_pin(new_data)
+
+/datum/integrated_io/color/write_data_to_pin(var/new_data)
+ // Since this is storing the color as a string hex color code, we need to make sure it's actually one.
+ if(isnull(new_data) || istext(new_data))
+ if(istext(new_data))
+ new_data = uppertext(new_data)
+ if(length(new_data) != 7) // We can hex if we want to, we can leave your strings behind
+ return // Cause your strings don't hex and if they don't hex
+ var/friends = copytext(new_data, 2, 8) // Well they're are no strings of mine
+ // I say, we can go where we want to, a place where they will never find
+ var/safety_dance = 1
+ while(safety_dance >= 7) // And we can act like we come from out of this world.log
+ var/hex = copytext(friends, safety_dance, safety_dance+1)
+ if(!(hex in list("0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F")))
+ return // Leave the fake one far behind,
+ safety_dance++
+
+ data = new_data // And we can hex
+ holder.on_data_written()
+
+// This randomizes the color.
+/datum/integrated_io/color/scramble()
+ if(!is_valid())
+ return
+ var/new_data = get_random_colour(simple = FALSE, lower = 0, upper = 255)
+ data = new_data
+ push_data()
+
+/datum/integrated_io/color/display_pin_type()
+ return IC_FORMAT_COLOR
+
+/datum/integrated_io/color/display_data(var/input)
+ if(!isnull(data))
+ return "([data])"
+ return ..()
\ No newline at end of file
diff --git a/code/modules/integrated_electronics/core/special_pins/dir_pin.dm b/code/modules/integrated_electronics/core/special_pins/dir_pin.dm
new file mode 100644
index 0000000000000..bbb00faa278a7
--- /dev/null
+++ b/code/modules/integrated_electronics/core/special_pins/dir_pin.dm
@@ -0,0 +1,33 @@
+// These pins can only contain directions (1,2,4,8...) or null.
+/datum/integrated_io/dir
+ name = "dir pin"
+
+/datum/integrated_io/dir/ask_for_pin_data(mob/user)
+ var/new_data = input("Please type in a valid dir number. \
+ Valid dirs are;\n\
+ North/Fore = [NORTH],\n\
+ South/Aft = [SOUTH],\n\
+ East/Starboard = [EAST],\n\
+ West/Port = [WEST],\n\
+ Northeast = [NORTHEAST],\n\
+ Northwest = [NORTHWEST],\n\
+ Southeast = [SOUTHEAST],\n\
+ Southwest = [SOUTHWEST],\n\
+ Up = [UP],\n\
+ Down = [DOWN]","[src] dir writing") as null|num
+ if(isnum(new_data) && holder.check_interactivity(user) )
+ to_chat(user, "You input [new_data] into the pin.")
+ write_data_to_pin(new_data)
+
+/datum/integrated_io/dir/write_data_to_pin(var/new_data)
+ if(isnull(new_data) || new_data in alldirs + list(UP, DOWN))
+ data = new_data
+ holder.on_data_written()
+
+/datum/integrated_io/dir/display_pin_type()
+ return IC_FORMAT_DIR
+
+/datum/integrated_io/dir/display_data(var/input)
+ if(!isnull(data))
+ return "([dir2text(data)])"
+ return ..()
\ No newline at end of file
diff --git a/code/modules/integrated_electronics/core/special_pins/list_pin.dm b/code/modules/integrated_electronics/core/special_pins/list_pin.dm
new file mode 100644
index 0000000000000..e3c10b25df4c3
--- /dev/null
+++ b/code/modules/integrated_electronics/core/special_pins/list_pin.dm
@@ -0,0 +1,149 @@
+// These pins contain a list. Null is not allowed.
+/datum/integrated_io/list
+ name = "list pin"
+ data = list()
+
+
+/datum/integrated_io/list/ask_for_pin_data(mob/user)
+ interact(user)
+
+/datum/integrated_io/list/proc/interact(mob/user)
+ var/list/my_list = data
+ var/t = "[src]
"
+ t += "List length: [my_list.len]
"
+ t += "\[Refresh\] | "
+ t += "\[Add\] | "
+ t += "\[Swap\] | "
+ t += "\[Clear\]
"
+ t += "
"
+ var/i = 0
+ for(var/line in my_list)
+ i++
+ t += "#[i] | [display_data(line)] | "
+ t += "\[Edit\] | "
+ t += "\[Remove\]
"
+ user << browse(t, "window=list_pin_\ref[src];size=500x400")
+
+/datum/integrated_io/list/proc/add_to_list(mob/user, var/new_entry)
+ if(!new_entry && user)
+ new_entry = ask_for_data_type(user)
+ if(is_valid(new_entry))
+ Add(new_entry)
+
+/datum/integrated_io/list/proc/Add(var/new_entry)
+ var/list/my_list = data
+ if(my_list.len > IC_MAX_LIST_LENGTH)
+ my_list.Cut(Start=1,End=2)
+ my_list.Add(new_entry)
+
+/datum/integrated_io/list/proc/remove_from_list_by_position(mob/user, var/position)
+ var/list/my_list = data
+ if(!my_list.len)
+ to_chat(user, "The list is empty, there's nothing to remove.")
+ return
+ if(!position)
+ return
+ var/target_entry = my_list.Find(position)
+ if(target_entry)
+ my_list.Remove(target_entry)
+
+/datum/integrated_io/list/proc/remove_from_list(mob/user, var/target_entry)
+ var/list/my_list = data
+ if(!my_list.len)
+ to_chat(user, "The list is empty, there's nothing to remove.")
+ return
+ if(!target_entry)
+ target_entry = input("Which piece of data do you want to remove?", "Remove") as null|anything in my_list
+ if(target_entry)
+ my_list.Remove(target_entry)
+
+/datum/integrated_io/list/proc/edit_in_list(mob/user, var/target_entry)
+ var/list/my_list = data
+ if(!my_list.len)
+ to_chat(user, "The list is empty, there's nothing to modify.")
+ return
+ if(!target_entry)
+ target_entry = input("Which piece of data do you want to edit?", "Edit") as null|anything in my_list
+ if(target_entry)
+ var/edited_entry = ask_for_data_type(user, target_entry)
+ if(edited_entry)
+ target_entry = edited_entry
+
+/datum/integrated_io/list/proc/edit_in_list_by_position(mob/user, var/position)
+ var/list/my_list = data
+ if(!my_list.len)
+ to_chat(user, "The list is empty, there's nothing to modify.")
+ return
+ if(!position)
+ return
+ var/target_entry = my_list.Find(position)
+ if(target_entry)
+ var/edited_entry = ask_for_data_type(user, target_entry)
+ if(edited_entry)
+ target_entry = edited_entry
+
+/datum/integrated_io/list/proc/swap_inside_list(mob/user, var/first_target, var/second_target)
+ var/list/my_list = data
+ if(my_list.len <= 1)
+ to_chat(user, "The list is empty, or too small to do any meaningful swapping.")
+ return
+ if(!first_target)
+ first_target = input("Which piece of data do you want to swap? (1)", "Swap") as null|anything in my_list
+
+ if(first_target)
+ if(!second_target)
+ second_target = input("Which piece of data do you want to swap? (2)", "Swap") as null|anything in my_list - first_target
+
+ if(second_target)
+ var/first_pos = my_list.Find(first_target)
+ var/second_pos = my_list.Find(second_target)
+ my_list.Swap(first_pos, second_pos)
+
+/datum/integrated_io/list/proc/clear_list(mob/user)
+ var/list/my_list = data
+ my_list.Cut()
+
+/datum/integrated_io/list/scramble()
+ var/list/my_list = data
+ my_list = shuffle(my_list)
+ push_data()
+
+/datum/integrated_io/list/write_data_to_pin(var/new_data)
+ if(islist(new_data))
+ var/list/new_list = new_data
+ data = new_list.Copy()
+ holder.on_data_written()
+
+/datum/integrated_io/list/display_pin_type()
+ return IC_FORMAT_LIST
+
+/datum/integrated_io/list/Topic(href, href_list)
+ if(!holder.check_interactivity(usr))
+ return
+ if(..())
+ return 1
+
+ if(href_list["add"])
+ add_to_list(usr)
+
+ if(href_list["swap"])
+ swap_inside_list(usr)
+
+ if(href_list["clear"])
+ clear_list(usr)
+
+ if(href_list["remove"])
+ if(href_list["pos"])
+ remove_from_list_by_position(usr, text2num(href_list["pos"]))
+ else
+ remove_from_list(usr)
+
+ if(href_list["edit"])
+ if(href_list["pos"])
+ edit_in_list_by_position(usr, text2num(href_list["pos"]))
+ else
+ edit_in_list(usr)
+
+ holder.interact(usr) // Refresh the main UI,
+ interact(usr) // and the list UI.
+
diff --git a/code/modules/integrated_electronics/core/special_pins/number_pin.dm b/code/modules/integrated_electronics/core/special_pins/number_pin.dm
new file mode 100644
index 0000000000000..319ac2de06bd2
--- /dev/null
+++ b/code/modules/integrated_electronics/core/special_pins/number_pin.dm
@@ -0,0 +1,18 @@
+// These pins can only contain numbers (int and floating point) or null.
+/datum/integrated_io/number
+ name = "number pin"
+// data = 0
+
+/datum/integrated_io/number/ask_for_pin_data(mob/user)
+ var/new_data = input("Please type in a number.","[src] number writing") as null|num
+ if(isnum(new_data) && holder.check_interactivity(user) )
+ to_chat(user, "You input [new_data] into the pin.")
+ write_data_to_pin(new_data)
+
+/datum/integrated_io/number/write_data_to_pin(var/new_data)
+ if(isnull(new_data) || isnum(new_data))
+ data = new_data
+ holder.on_data_written()
+
+/datum/integrated_io/number/display_pin_type()
+ return IC_FORMAT_NUMBER
\ No newline at end of file
diff --git a/code/modules/integrated_electronics/core/special_pins/ref_pin.dm b/code/modules/integrated_electronics/core/special_pins/ref_pin.dm
new file mode 100644
index 0000000000000..461965f254bcf
--- /dev/null
+++ b/code/modules/integrated_electronics/core/special_pins/ref_pin.dm
@@ -0,0 +1,14 @@
+// These pins only contain weakrefs or null.
+/datum/integrated_io/ref
+ name = "ref pin"
+
+/datum/integrated_io/ref/ask_for_pin_data(mob/user) // This clears the pin.
+ write_data_to_pin(null)
+
+/datum/integrated_io/ref/write_data_to_pin(var/new_data)
+ if(isnull(new_data) || isweakref(new_data))
+ data = new_data
+ holder.on_data_written()
+
+/datum/integrated_io/ref/display_pin_type()
+ return IC_FORMAT_REF
\ No newline at end of file
diff --git a/code/modules/integrated_electronics/core/special_pins/string_pin.dm b/code/modules/integrated_electronics/core/special_pins/string_pin.dm
new file mode 100644
index 0000000000000..8f297fa340fcc
--- /dev/null
+++ b/code/modules/integrated_electronics/core/special_pins/string_pin.dm
@@ -0,0 +1,32 @@
+// These pins can only contain text and null.
+/datum/integrated_io/string
+ name = "string pin"
+
+/datum/integrated_io/string/ask_for_pin_data(mob/user)
+ var/new_data = input("Please type in a string.","[src] string writing") as null|text
+ new_data = sanitize_text(new_data, MAX_MESSAGE_LEN, 0, 0)
+
+ if(new_data && holder.check_interactivity(user) )
+ to_chat(user, "You input [new_data ? "new_data" : "NULL"] into the pin.")
+ write_data_to_pin(new_data)
+
+/datum/integrated_io/string/write_data_to_pin(var/new_data)
+ new_data = sanitize_text(new_data, MAX_MESSAGE_LEN, 0, 0)
+ if(isnull(new_data) || istext(new_data))
+ data = new_data
+ holder.on_data_written()
+
+// This makes the text go "from this" to "#G&*!HD$%L"
+/datum/integrated_io/string/scramble()
+ if(!is_valid())
+ return
+ var/string_length = length(data)
+ var/list/options = list("!","@","#","$","%","^","&","*") + alphabet_uppercase
+ var/new_data = ""
+ while(string_length)
+ new_data += pick(options)
+ string_length--
+ push_data()
+
+/datum/integrated_io/string/display_pin_type()
+ return IC_FORMAT_STRING
diff --git a/code/modules/integrated_electronics/core/tools.dm b/code/modules/integrated_electronics/core/tools.dm
new file mode 100644
index 0000000000000..a77c933befe83
--- /dev/null
+++ b/code/modules/integrated_electronics/core/tools.dm
@@ -0,0 +1,463 @@
+#define WIRE "wire"
+#define WIRING "wiring"
+#define UNWIRE "unwire"
+#define UNWIRING "unwiring"
+
+
+/obj/item/device/integrated_electronics/wirer
+ name = "circuit wirer"
+ desc = "It's a small wiring tool, with a wire roll, electric soldering iron, wire cutter, and more in one package. \
+ The wires used are generally useful for small electronics, such as circuitboards and breadboards, as opposed to larger wires \
+ used for power or data transmission."
+ icon = 'icons/obj/electronic_assemblies.dmi'
+ icon_state = "wirer-wire"
+ item_state = "wirer"
+ flags = CONDUCT
+ w_class = 2
+ var/datum/integrated_io/selected_io = null
+ var/mode = WIRE
+
+/obj/item/device/integrated_electronics/wirer/update_icon()
+ icon_state = "wirer-[mode]"
+
+/obj/item/device/integrated_electronics/wirer/proc/wire(var/datum/integrated_io/io, mob/user)
+ if(!io.holder.assembly)
+ to_chat(user, "\The [io.holder] needs to be secured inside an assembly first.")
+ return
+ if(mode == WIRE)
+ selected_io = io
+ user << "You attach a data wire to \the [selected_io.holder]'s [selected_io.name] data channel."
+ mode = WIRING
+ update_icon()
+ else if(mode == WIRING)
+ if(io == selected_io)
+ user << "Wiring \the [selected_io.holder]'s [selected_io.name] into itself is rather pointless."
+ return
+ if(io.io_type != selected_io.io_type)
+ user << "Those two types of channels are incompatable. The first is a [selected_io.io_type], \
+ while the second is a [io.io_type]."
+ return
+ selected_io.linked |= io
+ io.linked |= selected_io
+
+ user << "You connect \the [selected_io.holder]'s [selected_io.name] to \the [io.holder]'s [io.name]."
+ mode = WIRE
+ update_icon()
+ selected_io.holder.interact(user) // This is to update the UI.
+ selected_io = null
+
+ else if(mode == UNWIRE)
+ selected_io = io
+ if(!io.linked.len)
+ user << "There is nothing connected to \the [selected_io] data channel."
+ selected_io = null
+ return
+ user << "You prepare to detach a data wire from \the [selected_io.holder]'s [selected_io.name] data channel."
+ mode = UNWIRING
+ update_icon()
+ return
+
+ else if(mode == UNWIRING)
+ if(io == selected_io)
+ user << "You can't wire a pin into each other, so unwiring \the [selected_io.holder] from \
+ the same pin is rather moot."
+ return
+ if(selected_io in io.linked)
+ io.linked.Remove(selected_io)
+ selected_io.linked.Remove(io)
+ user << "You disconnect \the [selected_io.holder]'s [selected_io.name] from \
+ \the [io.holder]'s [io.name]."
+ selected_io.holder.interact(user) // This is to update the UI.
+ selected_io = null
+ mode = UNWIRE
+ update_icon()
+ else
+ user << "\The [selected_io.holder]'s [selected_io.name] and \the [io.holder]'s \
+ [io.name] are not connected."
+ return
+ return
+
+/obj/item/device/integrated_electronics/wirer/attack_self(mob/user)
+ switch(mode)
+ if(WIRE)
+ mode = UNWIRE
+ if(WIRING)
+ if(selected_io)
+ user << "You decide not to wire the data channel."
+ selected_io = null
+ mode = WIRE
+ if(UNWIRE)
+ mode = WIRE
+ if(UNWIRING)
+ if(selected_io)
+ user << "You decide not to disconnect the data channel."
+ selected_io = null
+ mode = UNWIRE
+ update_icon()
+ user << "You set \the [src] to [mode]."
+
+#undef WIRE
+#undef WIRING
+#undef UNWIRE
+#undef UNWIRING
+
+/obj/item/device/integrated_electronics/debugger
+ name = "circuit debugger"
+ desc = "This small tool allows one working with custom machinery to directly set data to a specific pin, useful for writing \
+ settings to specific circuits, or for debugging purposes. It can also pulse activation pins."
+ icon = 'icons/obj/electronic_assemblies.dmi'
+ icon_state = "debugger"
+ flags = CONDUCT
+ w_class = 2
+ var/data_to_write = null
+ var/accepting_refs = 0
+
+/obj/item/device/integrated_electronics/debugger/attack_self(mob/user)
+ var/type_to_use = input("Please choose a type to use.","[src] type setting") as null|anything in list("string","number","ref", "null")
+ if(!CanInteract(user,src))
+ return
+
+ var/new_data = null
+ switch(type_to_use)
+ if("string")
+ accepting_refs = 0
+ new_data = input("Now type in a string.","[src] string writing") as null|text
+ new_data = sanitize_text(new_data, MAX_MESSAGE_LEN, 0, 0)
+ if(istext(new_data) && CanInteract(user,src))
+ data_to_write = new_data
+ user << "You set \the [src]'s memory to \"[new_data]\"."
+ if("number")
+ accepting_refs = 0
+ new_data = input("Now type in a number.","[src] number writing") as null|num
+ if(isnum(new_data))
+ data_to_write = new_data
+ user << "You set \the [src]'s memory to [new_data]."
+ if("ref")
+ accepting_refs = 1
+ user << "You turn \the [src]'s ref scanner on. Slide it across \
+ an object for a ref of that object to save it in memory."
+ if("null")
+ data_to_write = null
+ user << "You set \the [src]'s memory to absolutely nothing."
+
+/obj/item/device/integrated_electronics/debugger/afterattack(atom/target, mob/living/user, proximity)
+ if(accepting_refs && proximity)
+ data_to_write = target
+ visible_message("[user] slides \a [src]'s over \the [target].")
+ user << "You set \the [src]'s memory to a reference to [target.name] \[Ref\]. The ref scanner is \
+ now off."
+ accepting_refs = 0
+
+/obj/item/device/integrated_electronics/debugger/proc/write_data(var/datum/integrated_io/io, mob/user)
+ if(io.io_type == DATA_CHANNEL)
+ io.write_data_to_pin(data_to_write)
+ var/data_to_show = data_to_write
+ if(isweakref(data_to_write))
+ var/weakref/w = data_to_write
+ var/atom/A = w.resolve()
+ data_to_show = A.name
+ user << "You write [data_to_write ? data_to_show : "NULL"]' to the '[io]' pin of \the [io.holder]."
+ else if(io.io_type == PULSE_CHANNEL)
+ io.holder.check_then_do_work(ignore_power = TRUE)
+ to_chat(user, "You pulse \the [io.holder]'s [io].")
+
+ io.holder.interact(user) // This is to update the UI.
+
+
+
+
+/obj/item/device/multitool
+ var/datum/integrated_io/selected_io = null
+ var/mode = 0
+
+/obj/item/device/multitool/attack_self(mob/user)
+ if(selected_io)
+ selected_io = null
+ to_chat(user, "You clear the wired connection from the multitool.")
+ else
+ ..()
+ update_icon()
+
+/obj/item/device/multitool/update_icon()
+ if(selected_io)
+ if(buffer)
+ icon_state = "multitool_tracking"
+ else
+ icon_state = "multitool_red"
+ else
+ if(buffer)
+ icon_state = "multitool_tracking_fail"
+ else
+ icon_state = "multitool"
+
+/obj/item/device/multitool/proc/wire(var/datum/integrated_io/io, mob/user)
+ if(!io.holder.assembly)
+ to_chat(user, "\The [io.holder] needs to be secured inside an assembly first.")
+ return
+
+ if(selected_io)
+ if(io == selected_io)
+ to_chat(user, "Wiring \the [selected_io.holder]'s [selected_io.name] into itself is rather pointless.")
+ return
+ if(io.io_type != selected_io.io_type)
+ to_chat(user, "Those two types of channels are incompatable. The first is a [selected_io.io_type], \
+ while the second is a [io.io_type].")
+ return
+ if(io.holder.assembly && io.holder.assembly != selected_io.holder.assembly)
+ to_chat(user, "Both \the [io.holder] and \the [selected_io.holder] need to be inside the same assembly.")
+ return
+ selected_io.linked |= io
+ io.linked |= selected_io
+
+ to_chat(user, "You connect \the [selected_io.holder]'s [selected_io.name] to \the [io.holder]'s [io.name].")
+ selected_io.holder.interact(user) // This is to update the UI.
+ selected_io = null
+
+ else
+ selected_io = io
+ to_chat(user, "You link \the multitool to \the [selected_io.holder]'s [selected_io.name] data channel.")
+
+ update_icon()
+
+
+/obj/item/device/multitool/proc/unwire(var/datum/integrated_io/io1, var/datum/integrated_io/io2, mob/user)
+ if(!io1.linked.len || !io2.linked.len)
+ to_chat(user, "There is nothing connected to the data channel.")
+ return
+
+ if(!(io1 in io2.linked) || !(io2 in io1.linked) )
+ to_chat(user, "These data pins aren't connected!")
+ return
+ else
+ io1.linked.Remove(io2)
+ io2.linked.Remove(io1)
+ to_chat(user, "You clip the data connection between the [io1.holder.displayed_name]'s \
+ [io1.name] and the [io2.holder.displayed_name]'s [io2.name].")
+ io1.holder.interact(user) // This is to update the UI.
+ update_icon()
+
+
+
+
+
+
+
+
+
+
+
+/obj/item/weapon/storage/bag/circuits
+ name = "circuit kit"
+ desc = "This kit's essential for any circuitry projects."
+ icon = 'icons/obj/electronic_assemblies.dmi'
+ icon_state = "circuit_kit"
+ w_class = 3
+ display_contents_with_number = 0
+ can_hold = list(
+ /obj/item/integrated_circuit,
+ /obj/item/weapon/storage/bag/circuits/mini,
+ /obj/item/device/electronic_assembly,
+ /obj/item/device/integrated_electronics,
+ /obj/item/weapon/crowbar,
+ /obj/item/weapon/screwdriver
+ )
+
+/obj/item/weapon/storage/bag/circuits/basic/New()
+ ..()
+ spawn(2 SECONDS) // So the list has time to initialize.
+// for(var/obj/item/integrated_circuit/IC in all_integrated_circuits)
+// if(IC.spawn_flags & IC_SPAWN_DEFAULT)
+// for(var/i = 1 to 3)
+// new IC.type(src)
+ new /obj/item/weapon/storage/bag/circuits/mini/arithmetic(src)
+ new /obj/item/weapon/storage/bag/circuits/mini/trig(src)
+ new /obj/item/weapon/storage/bag/circuits/mini/input(src)
+ new /obj/item/weapon/storage/bag/circuits/mini/output(src)
+ new /obj/item/weapon/storage/bag/circuits/mini/memory(src)
+ new /obj/item/weapon/storage/bag/circuits/mini/logic(src)
+ new /obj/item/weapon/storage/bag/circuits/mini/time(src)
+ new /obj/item/weapon/storage/bag/circuits/mini/reagents(src)
+ new /obj/item/weapon/storage/bag/circuits/mini/transfer(src)
+ new /obj/item/weapon/storage/bag/circuits/mini/converter(src)
+// new /obj/item/weapon/storage/bag/circuits/mini/power(src)
+
+ new /obj/item/device/electronic_assembly(src)
+ new /obj/item/device/integrated_electronics/wirer(src)
+ new /obj/item/device/integrated_electronics/debugger(src)
+ new /obj/item/weapon/crowbar(src)
+ new /obj/item/weapon/screwdriver(src)
+ make_exact_fit()
+
+/obj/item/weapon/storage/bag/circuits/all/New()
+ ..()
+ spawn(2 SECONDS) // So the list has time to initialize.
+// new /obj/item/weapon/storage/bag/circuits/mini/arithmetic/all(src)
+// new /obj/item/weapon/storage/bag/circuits/mini/trig/all(src)
+// new /obj/item/weapon/storage/bag/circuits/mini/input/all(src)
+// new /obj/item/weapon/storage/bag/circuits/mini/output/all(src)
+// new /obj/item/weapon/storage/bag/circuits/mini/memory/all(src)
+// new /obj/item/weapon/storage/bag/circuits/mini/logic/all(src)
+// new /obj/item/weapon/storage/bag/circuits/mini/smart/all(src)
+// new /obj/item/weapon/storage/bag/circuits/mini/manipulation/all(src)
+// new /obj/item/weapon/storage/bag/circuits/mini/time/all(src)
+// new /obj/item/weapon/storage/bag/circuits/mini/reagents/all(src)
+// new /obj/item/weapon/storage/bag/circuits/mini/transfer/all(src)
+// new /obj/item/weapon/storage/bag/circuits/mini/converter/all(src)
+// new /obj/item/weapon/storage/bag/circuits/mini/power/all(src)
+
+ new /obj/item/device/electronic_assembly(src)
+ new /obj/item/device/electronic_assembly/medium(src)
+ new /obj/item/device/electronic_assembly/large(src)
+ new /obj/item/device/electronic_assembly/drone(src)
+ new /obj/item/device/integrated_electronics/wirer(src)
+ new /obj/item/device/integrated_electronics/debugger(src)
+ new /obj/item/weapon/crowbar(src)
+ new /obj/item/weapon/screwdriver(src)
+ make_exact_fit()
+
+/obj/item/weapon/storage/bag/circuits/mini/
+ name = "circuit box"
+ desc = "Used to partition categories of circuits, for a neater workspace."
+ w_class = 2
+ display_contents_with_number = 1
+ can_hold = list(/obj/item/integrated_circuit)
+
+/obj/item/weapon/storage/bag/circuits/mini/arithmetic
+ name = "arithmetic circuit box"
+ desc = "Warning: Contains math."
+ icon_state = "box_arithmetic"
+
+/obj/item/weapon/storage/bag/circuits/mini/arithmetic/New()
+ ..()
+ for(var/obj/item/integrated_circuit/arithmetic/IC in all_integrated_circuits)
+ if(IC.spawn_flags & IC_SPAWN_DEFAULT)
+ for(var/i = 1 to 3)
+ new IC.type(src)
+ make_exact_fit()
+
+
+/obj/item/weapon/storage/bag/circuits/mini/trig
+ name = "trig circuit box"
+ desc = "Danger: Contains more math."
+ icon_state = "box_trig"
+
+/obj/item/weapon/storage/bag/circuits/mini/trig/New()
+ ..()
+ for(var/obj/item/integrated_circuit/trig/IC in all_integrated_circuits)
+ if(IC.spawn_flags & IC_SPAWN_DEFAULT)
+ for(var/i = 1 to 3)
+ new IC.type(src)
+ make_exact_fit()
+
+
+/obj/item/weapon/storage/bag/circuits/mini/input
+ name = "input circuit box"
+ desc = "Tell these circuits everything you know."
+ icon_state = "box_input"
+
+/obj/item/weapon/storage/bag/circuits/mini/input/New()
+ ..()
+ for(var/obj/item/integrated_circuit/input/IC in all_integrated_circuits)
+ if(IC.spawn_flags & IC_SPAWN_DEFAULT)
+ for(var/i = 1 to 3)
+ new IC.type(src)
+ make_exact_fit()
+
+
+/obj/item/weapon/storage/bag/circuits/mini/output
+ name = "output circuit box"
+ desc = "Circuits to interface with the world beyond itself."
+ icon_state = "box_output"
+
+/obj/item/weapon/storage/bag/circuits/mini/output/New()
+ ..()
+ for(var/obj/item/integrated_circuit/output/IC in all_integrated_circuits)
+ if(IC.spawn_flags & IC_SPAWN_DEFAULT)
+ for(var/i = 1 to 3)
+ new IC.type(src)
+ make_exact_fit()
+
+
+/obj/item/weapon/storage/bag/circuits/mini/memory
+ name = "memory circuit box"
+ desc = "Machines can be quite forgetful without these."
+ icon_state = "box_memory"
+
+/obj/item/weapon/storage/bag/circuits/mini/memory/New()
+ ..()
+ for(var/obj/item/integrated_circuit/memory/IC in all_integrated_circuits)
+ if(IC.spawn_flags & IC_SPAWN_DEFAULT)
+ for(var/i = 1 to 3)
+ new IC.type(src)
+ make_exact_fit()
+
+
+
+/obj/item/weapon/storage/bag/circuits/mini/logic
+ name = "logic circuit box"
+ desc = "May or may not be Turing complete."
+ icon_state = "box_logic"
+
+/obj/item/weapon/storage/bag/circuits/mini/logic/New()
+ ..()
+ for(var/obj/item/integrated_circuit/logic/IC in all_integrated_circuits)
+ if(IC.spawn_flags & IC_SPAWN_DEFAULT)
+ for(var/i = 1 to 3)
+ new IC.type(src)
+ make_exact_fit()
+
+
+/obj/item/weapon/storage/bag/circuits/mini/time
+ name = "time circuit box"
+ desc = "No time machine parts, sadly."
+ icon_state = "box_time"
+
+/obj/item/weapon/storage/bag/circuits/mini/time/New()
+ ..()
+ for(var/obj/item/integrated_circuit/time/IC in all_integrated_circuits)
+ if(IC.spawn_flags & IC_SPAWN_DEFAULT)
+ for(var/i = 1 to 3)
+ new IC.type(src)
+ make_exact_fit()
+
+
+/obj/item/weapon/storage/bag/circuits/mini/reagents
+ name = "reagent circuit box"
+ desc = "Unlike most electronics, these circuits are supposed to come in contact with liquids."
+ icon_state = "box_reagents"
+
+/obj/item/weapon/storage/bag/circuits/mini/reagents/New()
+ ..()
+ for(var/obj/item/integrated_circuit/reagent/IC in all_integrated_circuits)
+ if(IC.spawn_flags & IC_SPAWN_DEFAULT)
+ for(var/i = 1 to 3)
+ new IC.type(src)
+ make_exact_fit()
+
+
+/obj/item/weapon/storage/bag/circuits/mini/transfer
+ name = "transfer circuit box"
+ desc = "Useful for moving data representing something arbitrary to another arbitrary virtual place."
+ icon_state = "box_transfer"
+
+/obj/item/weapon/storage/bag/circuits/mini/transfer/New()
+ ..()
+ for(var/obj/item/integrated_circuit/transfer/IC in all_integrated_circuits)
+ if(IC.spawn_flags & IC_SPAWN_DEFAULT)
+ for(var/i = 1 to 3)
+ new IC.type(src)
+ make_exact_fit()
+
+
+/obj/item/weapon/storage/bag/circuits/mini/converter
+ name = "converter circuit box"
+ desc = "Transform one piece of data to another type of data with these."
+ icon_state = "box_converter"
+
+/obj/item/weapon/storage/bag/circuits/mini/converter/New()
+ ..()
+ for(var/obj/item/integrated_circuit/converter/IC in all_integrated_circuits)
+ if(IC.spawn_flags & IC_SPAWN_DEFAULT)
+ for(var/i = 1 to 3)
+ new IC.type(src)
+ make_exact_fit()
\ No newline at end of file
diff --git a/code/modules/integrated_electronics/passive/passive.dm b/code/modules/integrated_electronics/passive/passive.dm
new file mode 100644
index 0000000000000..02f03d48d7c40
--- /dev/null
+++ b/code/modules/integrated_electronics/passive/passive.dm
@@ -0,0 +1,7 @@
+// 'Passive' components do not have any pins, and instead contribute in some form to the assembly holding them.
+/obj/item/integrated_circuit/passive
+ inputs = list()
+ outputs = list()
+ activators = list()
+ power_draw_idle = 0
+ power_draw_per_use = 0
\ No newline at end of file
diff --git a/code/modules/integrated_electronics/passive/power.dm b/code/modules/integrated_electronics/passive/power.dm
new file mode 100644
index 0000000000000..788003b0f13f1
--- /dev/null
+++ b/code/modules/integrated_electronics/passive/power.dm
@@ -0,0 +1,166 @@
+
+/obj/item/integrated_circuit/passive/power
+ name = "power thingy"
+ desc = "Does power stuff."
+ complexity = 5
+ origin_tech = list(TECH_POWER = 2, TECH_ENGINEERING = 2, TECH_DATA = 2)
+ category_text = "Power - Passive"
+
+/obj/item/integrated_circuit/passive/power/proc/make_energy()
+ return
+
+// For calculators.
+/obj/item/integrated_circuit/passive/power/solar_cell
+ name = "tiny photovoltaic cell"
+ desc = "It's a very tiny solar cell, generally used in calculators."
+ extended_desc = "The cell generates 1W of energy per second in optimal lighting conditions. Less light will result in less power being generated."
+ icon_state = "solar_cell"
+ complexity = 8
+ origin_tech = list(TECH_POWER = 3, TECH_ENGINEERING = 3, TECH_DATA = 2)
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+ var/max_power = 1
+
+/obj/item/integrated_circuit/passive/power/solar_cell/make_energy()
+ var/turf/T = get_turf(src)
+ var/light_amount = T ? T.get_lumcount() : 0
+ var/adjusted_power = max(max_power * light_amount, 0)
+ adjusted_power = round(adjusted_power, 0.1)
+ if(adjusted_power)
+ if(assembly)
+ assembly.give_power(adjusted_power)
+
+/obj/item/integrated_circuit/passive/power/starter
+ name = "starter"
+ desc = "This tiny circuit will send a pulse right after device is turned on, or when power is restored."
+ icon_state = "led"
+ complexity = 1
+ activators = list("pulse out" = IC_PINTYPE_PULSE_OUT)
+ origin_tech = list(TECH_POWER = 3, TECH_ENGINEERING = 3, TECH_DATA = 2)
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+ var/is_charge=0
+
+/obj/item/integrated_circuit/passive/power/starter/make_energy()
+ if(assembly.battery)
+ if(assembly.battery.charge)
+ if(!is_charge)
+ activate_pin(1)
+ is_charge=1
+ else
+ is_charge=0
+ else
+ is_charge=0
+ return FALSE
+
+// For implants.
+/obj/item/integrated_circuit/passive/power/metabolic_siphon
+ name = "metabolic siphon"
+ desc = "A complicated piece of technology which converts bodily nutriments of a host into electricity."
+ extended_desc = "The siphon generates 10W of energy, so long as the siphon exists inside a biological entity. The entity will feel an increased \
+ appetite and will need to eat more often due to this. This device will fail if used inside synthetic entities."
+ icon_state = "setup_implant"
+ complexity = 10
+ origin_tech = list(TECH_POWER = 4, TECH_ENGINEERING = 4, TECH_DATA = 4, TECH_BIO = 5)
+ spawn_flags = IC_SPAWN_RESEARCH
+
+/obj/item/integrated_circuit/passive/power/metabolic_siphon/proc/test_validity(var/mob/living/carbon/human/host)
+ if(!host || host.isSynthetic() || host.stat == DEAD || host.nutrition <= 10)
+ return FALSE // Robots and dead people don't have a metabolism.
+ return TRUE
+
+/obj/item/integrated_circuit/passive/power/metabolic_siphon/make_energy()
+ var/mob/living/carbon/human/host = null
+ if(assembly && istype(assembly, /obj/item/device/electronic_assembly/implant))
+ var/obj/item/device/electronic_assembly/implant/implant_assembly = assembly
+ if(implant_assembly.implant.imp_in)
+ host = implant_assembly.implant.imp_in
+ if(host && test_validity(host))
+ assembly.give_power(10)
+ host.nutrition = max(host.nutrition - HUNGER_FACTOR, 0)
+
+/obj/item/integrated_circuit/passive/power/metabolic_siphon/synthetic
+ name = "internal energy siphon"
+ desc = "A small circuit designed to be connected to an internal power wire inside a synthetic entity."
+ extended_desc = "The siphon generates 10W of energy, so long as the siphon exists inside a synthetic entity. The entity need to recharge \
+ more often due to this. This device will fail if used inside organic entities."
+ icon_state = "setup_implant"
+ complexity = 10
+ origin_tech = list(TECH_POWER = 3, TECH_ENGINEERING = 4, TECH_DATA = 3)
+ spawn_flags = IC_SPAWN_RESEARCH
+
+/obj/item/integrated_circuit/passive/power/metabolic_siphon/synthetic/test_validity(var/mob/living/carbon/human/host)
+ if(!host || !host.isSynthetic() || host.stat == DEAD || host.nutrition <= 10)
+ return FALSE // This time we don't want a metabolism.
+ return TRUE
+
+// For fat machines that need fat power, like drones.
+/obj/item/integrated_circuit/passive/power/relay
+ name = "tesla power relay"
+ desc = "A seemingly enigmatic device which connects to nearby APCs wirelessly and draws power from them."
+ w_class = 3
+ extended_desc = "The siphon generates 250W of energy, so long as an APC is in the same room, with a cell that has energy. It will always drain \
+ from the 'equipment' power channel."
+ icon_state = "power_relay"
+ complexity = 7
+ origin_tech = list(TECH_POWER = 3, TECH_ENGINEERING = 3, TECH_DATA = 2)
+ spawn_flags = IC_SPAWN_RESEARCH
+ var/power_amount = 250
+//fuel cell
+
+/obj/item/integrated_circuit/passive/power/chemical_cell
+ name = "fuel cell"
+ desc = "Produces electricity from chemicals."
+ icon_state = "chemical_cell"
+ extended_desc = "This is effectively an internal beaker. It will consume and produce power from plasma, slime jelly, welding fuel, carbon,\
+ ethanol, nutriments and blood, in order of decreasing efficiency. It will consume fuel only if the battery can take more energy."
+ flags = OPENCONTAINER
+ complexity = 4
+ inputs = list()
+ outputs = list("volume used" = IC_PINTYPE_NUMBER,"self reference" = IC_PINTYPE_REF)
+ activators = list()
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+ origin_tech = list(TECH_ENGINEERING = 2, TECH_DATA = 2, TECH_BIO = 2)
+ var/volume = 60
+ var/list/fuel = list("plasma" = 50000, "slimejelly" = 25000, "fuel" = 15000, "carbon" = 10000, "ethanol"= 10000, "nutriment" =8000, "blood" = 5000)
+
+/obj/item/integrated_circuit/passive/power/chemical_cell/New()
+ ..()
+ create_reagents(volume)
+
+/obj/item/integrated_circuit/passive/power/chemical_cell/interact(mob/user)
+ set_pin_data(IC_OUTPUT, 2, weakref(src))
+ push_data()
+ ..()
+
+/obj/item/integrated_circuit/passive/power/chemical_cell/on_reagent_change()
+ set_pin_data(IC_OUTPUT, 1, reagents.total_volume)
+ push_data()
+
+/obj/item/integrated_circuit/passive/power/chemical_cell/make_energy()
+ if(assembly)
+ for(var/I in fuel)
+ if((assembly.battery.maxcharge-assembly.battery.charge) / CELLRATE > fuel[I])
+ if(reagents.remove_reagent(I, 1))
+ assembly.give_power(fuel[I])
+
+
+// For really fat machines.
+/obj/item/integrated_circuit/passive/power/relay/large
+ name = "large tesla power relay"
+ desc = "A seemingly enigmatic device which connects to nearby APCs wirelessly and draws power from them, now in industiral size!"
+ w_class = 4
+ extended_desc = "The siphon generates 2 kW of energy, so long as an APC is in the same room, with a cell that has energy. It will always drain \
+ from the 'equipment' power channel."
+ icon_state = "power_relay"
+ complexity = 15
+ origin_tech = list(TECH_POWER = 6, TECH_ENGINEERING = 5, TECH_DATA = 4)
+ spawn_flags = IC_SPAWN_RESEARCH
+ power_amount = 2000
+
+/obj/item/integrated_circuit/passive/power/relay/make_energy()
+ if(!assembly)
+ return
+ var/area/A = get_area(src)
+ if(A)
+ if(A.powered(EQUIP) && assembly.give_power(power_amount))
+ A.use_power(power_amount, EQUIP)
+ // give_power() handles CELLRATE on its own.
diff --git a/code/modules/integrated_electronics/subtypes/arithmetic.dm b/code/modules/integrated_electronics/subtypes/arithmetic.dm
new file mode 100644
index 0000000000000..192d317baff39
--- /dev/null
+++ b/code/modules/integrated_electronics/subtypes/arithmetic.dm
@@ -0,0 +1,314 @@
+//These circuits do simple math.
+/obj/item/integrated_circuit/arithmetic
+ complexity = 1
+ inputs = list(
+ "A" = IC_PINTYPE_NUMBER,
+ "B" = IC_PINTYPE_NUMBER,
+ "C" = IC_PINTYPE_NUMBER,
+ "D" = IC_PINTYPE_NUMBER,
+ "E" = IC_PINTYPE_NUMBER,
+ "F" = IC_PINTYPE_NUMBER,
+ "G" = IC_PINTYPE_NUMBER,
+ "H" = IC_PINTYPE_NUMBER
+ )
+ outputs = list("result" = IC_PINTYPE_NUMBER)
+ activators = list("compute" = IC_PINTYPE_PULSE_IN, "on computed" = IC_PINTYPE_PULSE_OUT)
+ category_text = "Arithmetic"
+ power_draw_per_use = 5 // Math is pretty cheap.
+
+// +Adding+ //
+
+/obj/item/integrated_circuit/arithmetic/addition
+ name = "addition circuit"
+ desc = "This circuit can add numbers together."
+ extended_desc = "The order that the calculation goes is;
\
+ result = ((((A + B) + C) + D) ... ) and so on, until all pins have been added. \
+ Null pins are ignored."
+ icon_state = "addition"
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+
+/obj/item/integrated_circuit/arithmetic/addition/do_work()
+ var/result = 0
+ for(var/datum/integrated_io/I in inputs)
+ I.pull_data()
+ if(isnum(I.data))
+ result = result + I.data
+
+ set_pin_data(IC_OUTPUT, 1, result)
+ push_data()
+ activate_pin(2)
+
+// -Subtracting- //
+
+/obj/item/integrated_circuit/arithmetic/subtraction
+ name = "subtraction circuit"
+ desc = "This circuit can subtract numbers."
+ extended_desc = "The order that the calculation goes is;
\
+ result = ((((A - B) - C) - D) ... ) and so on, until all pins have been subtracted. \
+ Null pins are ignored. Pin A must be a number or the circuit will not function."
+ icon_state = "subtraction"
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+
+/obj/item/integrated_circuit/arithmetic/subtraction/do_work()
+ var/datum/integrated_io/A = inputs[1]
+ if(!isnum(A.data))
+ return
+ var/result = A.data
+
+ for(var/datum/integrated_io/I in inputs)
+ if(I == A)
+ continue
+ I.pull_data()
+ if(isnum(I.data))
+ result = result - I.data
+
+ set_pin_data(IC_OUTPUT, 1, result)
+ push_data()
+ activate_pin(2)
+
+// *Multiply* //
+
+/obj/item/integrated_circuit/arithmetic/multiplication
+ name = "multiplication circuit"
+ desc = "This circuit can multiply numbers."
+ extended_desc = "The order that the calculation goes is;
\
+ result = ((((A * B) * C) * D) ... ) and so on, until all pins have been multiplied. \
+ Null pins are ignored. Pin A must be a number or the circuit will not function."
+ icon_state = "multiplication"
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+
+
+/obj/item/integrated_circuit/arithmetic/multiplication/do_work()
+ var/datum/integrated_io/A = inputs[1]
+ if(!isnum(A.data))
+ return
+ var/result = A.data
+ for(var/datum/integrated_io/I in inputs)
+ if(I == A)
+ continue
+ I.pull_data()
+ if(isnum(I.data))
+ result = result * I.data
+
+ set_pin_data(IC_OUTPUT, 1, result)
+ push_data()
+ activate_pin(2)
+
+// /Division/ //
+
+/obj/item/integrated_circuit/arithmetic/division
+ name = "division circuit"
+ desc = "This circuit can divide numbers, just don't think about trying to divide by zero!"
+ extended_desc = "The order that the calculation goes is;
\
+ result = ((((A / B) / C) / D) ... ) and so on, until all pins have been divided. \
+ Null pins, and pins containing 0, are ignored. Pin A must be a number or the circuit will not function."
+ icon_state = "division"
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+
+/obj/item/integrated_circuit/arithmetic/division/do_work()
+ var/datum/integrated_io/A = inputs[1]
+ if(!isnum(A.data))
+ return
+ var/result = A.data
+
+ for(var/datum/integrated_io/I in inputs)
+ if(I == A)
+ continue
+ I.pull_data()
+ if(isnum(I.data) && I.data != 0) //No runtimes here.
+ result = result / I.data
+
+ set_pin_data(IC_OUTPUT, 1, result)
+ push_data()
+ activate_pin(2)
+
+//^ Exponent ^//
+
+/obj/item/integrated_circuit/arithmetic/exponent
+ name = "exponent circuit"
+ desc = "Outputs A to the power of B."
+ icon_state = "exponent"
+ inputs = list("A" = IC_PINTYPE_NUMBER, "B" = IC_PINTYPE_NUMBER)
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+
+/obj/item/integrated_circuit/arithmetic/exponent/do_work()
+ var/result = 0
+ var/datum/integrated_io/A = inputs[1]
+ var/datum/integrated_io/B = inputs[2]
+ if(isnum(A.data) && isnum(B.data))
+ result = A.data ** B.data
+
+ set_pin_data(IC_OUTPUT, 1, result)
+ push_data()
+ activate_pin(2)
+
+// +-Sign-+ //
+
+/obj/item/integrated_circuit/arithmetic/sign
+ name = "sign circuit"
+ desc = "This will say if a number is positive, negative, or zero."
+ extended_desc = "Will output 1, -1, or 0, depending on if A is a postive number, a negative number, or zero, respectively."
+ icon_state = "sign"
+ inputs = list("A" = IC_PINTYPE_NUMBER)
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+
+/obj/item/integrated_circuit/arithmetic/sign/do_work()
+ var/result = 0
+ var/datum/integrated_io/A = inputs[1]
+ if(isnum(A.data))
+ if(A.data > 0)
+ result = 1
+ else if (A.data < 0)
+ result = -1
+ else
+ result = 0
+
+ set_pin_data(IC_OUTPUT, 1, result)
+ push_data()
+ activate_pin(2)
+
+// Round //
+
+/obj/item/integrated_circuit/arithmetic/round
+ name = "round circuit"
+ desc = "Rounds A to the nearest B multiple of A."
+ extended_desc = "If B is not given a number, it will output the floor of A instead."
+ icon_state = "round"
+ inputs = list("A" = IC_PINTYPE_NUMBER, "B" = IC_PINTYPE_NUMBER)
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+
+/obj/item/integrated_circuit/arithmetic/round/do_work()
+ var/result = 0
+ var/datum/integrated_io/A = inputs[1]
+ var/datum/integrated_io/B = inputs[2]
+ if(isnum(A.data))
+ if(isnum(B.data) && B.data != 0)
+ result = round(A.data, B.data)
+ else
+ result = round(A.data)
+
+ set_pin_data(IC_OUTPUT, 1, result)
+ push_data()
+ activate_pin(2)
+
+
+// Absolute //
+
+/obj/item/integrated_circuit/arithmetic/absolute
+ name = "absolute circuit"
+ desc = "This outputs a non-negative version of the number you put in. This may also be thought of as its distance from zero."
+ icon_state = "absolute"
+ inputs = list("A" = IC_PINTYPE_NUMBER)
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+
+/obj/item/integrated_circuit/arithmetic/absolute/do_work()
+ var/result = 0
+ for(var/datum/integrated_io/I in inputs)
+ I.pull_data()
+ if(isnum(I.data))
+ result = abs(I.data)
+
+ set_pin_data(IC_OUTPUT, 1, result)
+ push_data()
+ activate_pin(2)
+
+// Averaging //
+
+/obj/item/integrated_circuit/arithmetic/average
+ name = "average circuit"
+ desc = "This circuit is of average quality, however it will compute the average for numbers you give it."
+ extended_desc = "Note that null pins are ignored, where as a pin containing 0 is included in the averaging calculation."
+ icon_state = "average"
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+
+/obj/item/integrated_circuit/arithmetic/average/do_work()
+ var/result = 0
+ var/inputs_used = 0
+ for(var/datum/integrated_io/I in inputs)
+ I.pull_data()
+ if(isnum(I.data))
+ inputs_used++
+ result = result + I.data
+
+ if(inputs_used)
+ result = result / inputs_used
+
+ set_pin_data(IC_OUTPUT, 1, result)
+ push_data()
+ activate_pin(2)
+
+// Pi, because why the hell not? //
+/obj/item/integrated_circuit/arithmetic/pi
+ name = "pi constant circuit"
+ desc = "Not recommended for cooking. Outputs '3.14159' when it receives a pulse."
+ icon_state = "pi"
+ inputs = list()
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+
+/obj/item/integrated_circuit/arithmetic/pi/do_work()
+ set_pin_data(IC_OUTPUT, 1, 3.14159)
+ push_data()
+ activate_pin(2)
+
+// Random //
+/obj/item/integrated_circuit/arithmetic/random
+ name = "random number generator circuit"
+ desc = "This gives a random (integer) number between values A and B inclusive."
+ extended_desc = "'Inclusive' means that the upper bound is included in the range of numbers, e.g. L = 1 and H = 3 will allow \
+ for outputs of 1, 2, or 3. H being the higher number is not strictly required."
+ icon_state = "random"
+ inputs = list("L" = IC_PINTYPE_NUMBER,"H" = IC_PINTYPE_NUMBER)
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+
+/obj/item/integrated_circuit/arithmetic/random/do_work()
+ var/result = 0
+ var/L = get_pin_data(IC_INPUT, 1)
+ var/H = get_pin_data(IC_INPUT, 2)
+
+ if(isnum(L) && isnum(H))
+ result = rand(L, H)
+
+ set_pin_data(IC_OUTPUT, 1, result)
+ push_data()
+ activate_pin(2)
+
+// Square Root //
+
+/obj/item/integrated_circuit/arithmetic/square_root
+ name = "square root circuit"
+ desc = "This outputs the square root of a number you put in."
+ icon_state = "square_root"
+ inputs = list("A" = IC_PINTYPE_NUMBER)
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+
+/obj/item/integrated_circuit/arithmetic/square_root/do_work()
+ var/result = 0
+ for(var/datum/integrated_io/I in inputs)
+ I.pull_data()
+ if(isnum(I.data))
+ result = sqrt(I.data)
+
+ set_pin_data(IC_OUTPUT, 1, result)
+ push_data()
+ activate_pin(2)
+
+// % Modulo % //
+
+/obj/item/integrated_circuit/arithmetic/modulo
+ name = "modulo circuit"
+ desc = "Gets the remainder of A / B."
+ icon_state = "modulo"
+ inputs = list("A" = IC_PINTYPE_NUMBER, "B" = IC_PINTYPE_NUMBER)
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+
+/obj/item/integrated_circuit/arithmetic/modulo/do_work()
+ var/result = 0
+ var/A = get_pin_data(IC_INPUT, 1)
+ var/B = get_pin_data(IC_INPUT, 2)
+ if(isnum(A) && isnum(B) && B != 0)
+ result = A % B
+
+ set_pin_data(IC_OUTPUT, 1, result)
+ push_data()
+ activate_pin(2)
+
diff --git a/code/modules/integrated_electronics/subtypes/built_in.dm b/code/modules/integrated_electronics/subtypes/built_in.dm
new file mode 100644
index 0000000000000..05053eec4cb06
--- /dev/null
+++ b/code/modules/integrated_electronics/subtypes/built_in.dm
@@ -0,0 +1,28 @@
+/obj/item/integrated_circuit/built_in
+ name = "integrated circuit"
+ desc = "It's a tiny chip! This one doesn't seem to do much, however."
+ icon = 'icons/obj/electronic_assemblies.dmi'
+ icon_state = "template"
+ size = -1
+ w_class = 1
+ removable = FALSE // Determines if a circuit is removable from the assembly.
+
+/obj/item/integrated_circuit/built_in/device_input
+ name = "assembly input"
+ desc = "A built in chip for handling pulses from attached assembly items."
+ complexity = 0 //This acts as a limitation on building machines, more resource-intensive components cost more 'space'.
+ activators = list("on pulsed" = IC_PINTYPE_PULSE_OUT)
+
+/obj/item/integrated_circuit/built_in/device_input/do_work()
+ activate_pin(1)
+
+/obj/item/integrated_circuit/built_in/device_output
+ name = "assembly out"
+ desc = "A built in chip for pulsing attached assembly items."
+ complexity = 0 //This acts as a limitation on building machines, more resource-intensive components cost more 'space'.
+ activators = list("pulse attached" = IC_PINTYPE_PULSE_IN)
+
+/obj/item/integrated_circuit/built_in/device_output/do_work()
+ if(istype(assembly, /obj/item/device/electronic_assembly/device))
+ var/obj/item/device/electronic_assembly/device/device = assembly
+ device.holder.pulse()
\ No newline at end of file
diff --git a/code/modules/integrated_electronics/subtypes/converters.dm b/code/modules/integrated_electronics/subtypes/converters.dm
new file mode 100644
index 0000000000000..b9f5c912d4c14
--- /dev/null
+++ b/code/modules/integrated_electronics/subtypes/converters.dm
@@ -0,0 +1,322 @@
+//These circuits convert one variable to another.
+/obj/item/integrated_circuit/converter
+ complexity = 2
+ inputs = list("input")
+ outputs = list("output")
+ activators = list("convert" = IC_PINTYPE_PULSE_IN, "on convert" = IC_PINTYPE_PULSE_OUT)
+ category_text = "Converter"
+ power_draw_per_use = 10
+
+/obj/item/integrated_circuit/converter/num2text
+ name = "number to string"
+ desc = "This circuit can convert a number variable into a string."
+ extended_desc = "Because of game limitations null/false variables will output a '0' string."
+ icon_state = "num-string"
+ inputs = list("input" = IC_PINTYPE_NUMBER)
+ outputs = list("output" = IC_PINTYPE_STRING)
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+
+/obj/item/integrated_circuit/converter/num2text/do_work()
+ var/result = null
+ pull_data()
+ var/incoming = get_pin_data(IC_INPUT, 1)
+ if(!isnull(incoming))
+ result = num2text(incoming)
+ else if(!incoming)
+ result = "0"
+
+ set_pin_data(IC_OUTPUT, 1, result)
+ push_data()
+ activate_pin(2)
+
+/obj/item/integrated_circuit/converter/text2num
+ name = "string to number"
+ desc = "This circuit can convert a string variable into a number."
+ icon_state = "string-num"
+ inputs = list("input" = IC_PINTYPE_STRING)
+ outputs = list("output" = IC_PINTYPE_NUMBER)
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+
+/obj/item/integrated_circuit/converter/text2num/do_work()
+ var/result = null
+ pull_data()
+ var/incoming = get_pin_data(IC_INPUT, 1)
+ if(!isnull(incoming))
+ result = text2num(incoming)
+
+ set_pin_data(IC_OUTPUT, 1, result)
+ push_data()
+ activate_pin(2)
+
+/obj/item/integrated_circuit/converter/ref2text
+ name = "reference to string"
+ desc = "This circuit can convert a reference to something else to a string, specifically the name of that reference."
+ icon_state = "ref-string"
+ inputs = list("input" = IC_PINTYPE_REF)
+ outputs = list("output" = IC_PINTYPE_STRING)
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+
+/obj/item/integrated_circuit/converter/ref2text/do_work()
+ var/result = null
+ pull_data()
+ var/atom/A = get_pin_data(IC_INPUT, 1)
+ if(A && istype(A))
+ result = A.name
+
+ set_pin_data(IC_OUTPUT, 1, result)
+ push_data()
+ activate_pin(2)
+
+/obj/item/integrated_circuit/converter/refcode
+ name = "reference encoder"
+ desc = "This circuit can encode a reference into a string, which can then be read by an EPV2 circuit."
+ icon_state = "ref-string"
+ inputs = list("input" = IC_PINTYPE_REF)
+ outputs = list("output" = IC_PINTYPE_STRING)
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+
+/obj/item/integrated_circuit/converter/refcode/do_work()
+ var/result = null
+ pull_data()
+ var/atom/A = get_pin_data(IC_INPUT, 1)
+ if(A && istype(A))
+ result = "\ref[A]"
+
+ set_pin_data(IC_OUTPUT, 1, result)
+ push_data()
+ activate_pin(2)
+
+/obj/item/integrated_circuit/converter/refdecode
+ name = "reference decoder"
+ desc = "This circuit can convert an encoded reference to actual reference."
+ icon_state = "ref-string"
+ inputs = list("input" = IC_PINTYPE_STRING)
+ outputs = list("output" = IC_PINTYPE_REF)
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+
+
+/obj/item/integrated_circuit/converter/refdecode/do_work()
+ pull_data()
+ set_pin_data(IC_OUTPUT, 1, weakref(locate(get_pin_data(IC_INPUT, 1))))
+ push_data()
+ activate_pin(2)
+
+/obj/item/integrated_circuit/converter/lowercase
+ name = "lowercase string converter"
+ desc = "this will cause a string to come out in all lowercase."
+ icon_state = "lowercase"
+ inputs = list("input" = IC_PINTYPE_STRING)
+ outputs = list("output" = IC_PINTYPE_STRING)
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+
+/obj/item/integrated_circuit/converter/lowercase/do_work()
+ var/result = null
+ pull_data()
+ var/incoming = get_pin_data(IC_INPUT, 1)
+ if(!isnull(incoming))
+ result = lowertext(incoming)
+
+ set_pin_data(IC_OUTPUT, 1, result)
+ push_data()
+ activate_pin(2)
+
+/obj/item/integrated_circuit/converter/uppercase
+ name = "uppercase string converter"
+ desc = "THIS WILL CAUSE A STRING TO COME OUT IN ALL UPPERCASE."
+ icon_state = "uppercase"
+ inputs = list("input" = IC_PINTYPE_STRING)
+ outputs = list("output" = IC_PINTYPE_STRING)
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+
+/obj/item/integrated_circuit/converter/uppercase/do_work()
+ var/result = null
+ pull_data()
+ var/incoming = get_pin_data(IC_INPUT, 1)
+ if(!isnull(incoming))
+ result = uppertext(incoming)
+
+ set_pin_data(IC_OUTPUT, 1, result)
+ push_data()
+ activate_pin(2)
+
+/obj/item/integrated_circuit/converter/concatenator
+ name = "concatenator"
+ desc = "This joins many strings together to get one big string."
+ complexity = 4
+ inputs = list(
+ "A" = IC_PINTYPE_STRING,
+ "B" = IC_PINTYPE_STRING,
+ "C" = IC_PINTYPE_STRING,
+ "D" = IC_PINTYPE_STRING,
+ "E" = IC_PINTYPE_STRING,
+ "F" = IC_PINTYPE_STRING,
+ "G" = IC_PINTYPE_STRING,
+ "H" = IC_PINTYPE_STRING
+ )
+ outputs = list("result" = IC_PINTYPE_STRING)
+ activators = list("concatenate" = IC_PINTYPE_PULSE_IN, "on concatenated" = IC_PINTYPE_PULSE_OUT)
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+
+/obj/item/integrated_circuit/converter/concatenator/do_work()
+ var/result = null
+ for(var/datum/integrated_io/I in inputs)
+ I.pull_data()
+ if(!isnull(I.data))
+ result = result + I.data
+
+ set_pin_data(IC_OUTPUT, 1, result)
+ push_data()
+ activate_pin(2)
+
+/obj/item/integrated_circuit/converter/separator
+ name = "separator"
+ desc = "This splits as single string into two at the relative split point."
+ extended_desc = "This circuits splits a given string into two, based on the string, and the index value. \
+ The index splits the string after the given index, including spaces. So 'a person' with an index of '3' \
+ will split into 'a p' and 'erson'."
+ complexity = 4
+ inputs = list(
+ "string to split" = IC_PINTYPE_STRING,
+ "index" = IC_PINTYPE_NUMBER,
+ )
+ outputs = list(
+ "before split" = IC_PINTYPE_STRING,
+ "after split" = IC_PINTYPE_STRING
+ )
+ activators = list("separate" = IC_PINTYPE_PULSE_IN, "on separated" = IC_PINTYPE_PULSE_OUT)
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+
+
+/obj/item/integrated_circuit/converter/separator/do_work()
+ var/text = get_pin_data(IC_INPUT, 1)
+ var/index = get_pin_data(IC_INPUT, 2)
+
+ var/split = min(index+1, length(text))
+
+ var/before_text = copytext(text, 1, split)
+ var/after_text = copytext(text, split, 0)
+
+ set_pin_data(IC_OUTPUT, 1, before_text)
+ set_pin_data(IC_OUTPUT, 2, after_text)
+ push_data()
+
+ activate_pin(2)
+
+/obj/item/integrated_circuit/converter/findstring
+ name = "find text"
+ desc = "This gives position of sample in the string. Or returns 0."
+ extended_desc = "The first pin is the string to be examined. The second pin is the sample to be found. \
+ For example, 'eat this burger',' ' will give you position 4. This circuit isn't case sensitive."
+ complexity = 4
+ inputs = list(
+ "string" = IC_PINTYPE_STRING,
+ "sample" = IC_PINTYPE_STRING,
+ )
+ outputs = list(
+ "position" = IC_PINTYPE_NUMBER
+ )
+ activators = list("search" = IC_PINTYPE_PULSE_IN, "after search" = IC_PINTYPE_PULSE_OUT)
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+
+
+
+/obj/item/integrated_circuit/converter/findstring/do_work()
+
+ set_pin_data(IC_OUTPUT, 1, findtext(get_pin_data(IC_INPUT, 1),get_pin_data(IC_INPUT, 2)) )
+ push_data()
+
+ activate_pin(2)
+
+/obj/item/integrated_circuit/converter/exploders
+ name = "string exploder"
+ desc = "This splits a single string into a list of strings."
+ extended_desc = "This circuit splits a given string into a list of strings based on the string and given delimiter. \
+ For example, 'eat this burger',' ' will be converted to list('eat','this','burger')."
+ complexity = 4
+ inputs = list(
+ "string to split" = IC_PINTYPE_STRING,
+ "delimiter" = IC_PINTYPE_STRING,
+ )
+ outputs = list(
+ "list" = IC_PINTYPE_LIST
+ )
+ activators = list("separate" = IC_PINTYPE_PULSE_IN, "on separated" = IC_PINTYPE_PULSE_OUT)
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+
+
+
+/obj/item/integrated_circuit/converter/exploders/do_work()
+ var/strin = get_pin_data(IC_INPUT, 1)
+ var/sample = get_pin_data(IC_INPUT, 2)
+ set_pin_data(IC_OUTPUT, 1, splittext( strin ,sample ))
+ push_data()
+
+ activate_pin(2)
+
+/obj/item/integrated_circuit/converter/radians2degrees
+ name = "radians to degrees converter"
+ desc = "Converts radians to degrees."
+ inputs = list("radian" = IC_PINTYPE_NUMBER)
+ outputs = list("degrees" = IC_PINTYPE_NUMBER)
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+
+/obj/item/integrated_circuit/converter/radians2degrees/do_work()
+ var/result = null
+ pull_data()
+ var/incoming = get_pin_data(IC_INPUT, 1)
+ if(!isnull(incoming))
+ result = ToDegrees(incoming)
+
+ set_pin_data(IC_OUTPUT, 1, result)
+ push_data()
+ activate_pin(2)
+
+/obj/item/integrated_circuit/converter/degrees2radians
+ name = "degrees to radians converter"
+ desc = "Converts degrees to radians."
+ inputs = list("degrees" = IC_PINTYPE_NUMBER)
+ outputs = list("radians" = IC_PINTYPE_NUMBER)
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+
+/obj/item/integrated_circuit/converter/degrees2radians/do_work()
+ var/result = null
+ pull_data()
+ var/incoming = get_pin_data(IC_INPUT, 1)
+ if(!isnull(incoming))
+ result = ToRadians(incoming)
+
+ set_pin_data(IC_OUTPUT, 1, result)
+ push_data()
+ activate_pin(2)
+
+
+/obj/item/integrated_circuit/converter/abs_to_rel_coords
+ name = "abs to rel coordinate converter"
+ desc = "Easily convert absolute coordinates to relative coordinates with this."
+ complexity = 4
+ inputs = list(
+ "X1" = IC_PINTYPE_NUMBER,
+ "Y1" = IC_PINTYPE_NUMBER,
+ "X2" = IC_PINTYPE_NUMBER,
+ "Y2" = IC_PINTYPE_NUMBER
+ )
+ outputs = list(
+ "X" = IC_PINTYPE_NUMBER,
+ "Y" = IC_PINTYPE_NUMBER
+ )
+ activators = list("compute rel coordinates" = IC_PINTYPE_PULSE_IN, "on convert" = IC_PINTYPE_PULSE_OUT)
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+
+/obj/item/integrated_circuit/converter/abs_to_rel_coords/do_work()
+ var/x1 = get_pin_data(IC_INPUT, 1)
+ var/y1 = get_pin_data(IC_INPUT, 2)
+
+ var/x2 = get_pin_data(IC_INPUT, 3)
+ var/y2 = get_pin_data(IC_INPUT, 4)
+
+ if(!isnull(x1) && !isnull(y1) && !isnull(x2) && !isnull(y2))
+ set_pin_data(IC_OUTPUT, 1, x1 - x2)
+ set_pin_data(IC_OUTPUT, 2, y1 - y2)
+
+ push_data()
+ activate_pin(2)
\ No newline at end of file
diff --git a/code/modules/integrated_electronics/subtypes/data_transfer.dm b/code/modules/integrated_electronics/subtypes/data_transfer.dm
new file mode 100644
index 0000000000000..697a2be2593e3
--- /dev/null
+++ b/code/modules/integrated_electronics/subtypes/data_transfer.dm
@@ -0,0 +1,146 @@
+/obj/item/integrated_circuit/transfer
+ category_text = "Data Transfer"
+ power_draw_per_use = 2
+
+/obj/item/integrated_circuit/transfer/multiplexer
+ name = "two multiplexer"
+ desc = "This is what those in the business tend to refer to as a 'mux' or data selector. It moves data from one of the selected inputs to the output."
+ extended_desc = "The first input pin is used to select which of the other input pins which has its data moved to the output. \
+ If the input selection is outside the valid range then no output is given."
+ complexity = 2
+ icon_state = "mux2"
+ inputs = list("input selection" = IC_PINTYPE_NUMBER)
+ outputs = list("output" = IC_PINTYPE_ANY)
+ activators = list("select" = IC_PINTYPE_PULSE_IN, "on select" = IC_PINTYPE_PULSE_OUT)
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+ power_draw_per_use = 4
+ var/number_of_inputs = 2
+
+/obj/item/integrated_circuit/transfer/multiplexer/New()
+ for(var/i = 1 to number_of_inputs)
+ inputs["input [i]"] = IC_PINTYPE_ANY // This is just a string since pins don't get built until ..() is called.
+// inputs += "input [i]"
+ complexity = number_of_inputs
+ ..()
+ desc += " It has [number_of_inputs] input pins."
+ extended_desc += " This multiplexer has a range from 1 to [inputs.len - 1]."
+
+/obj/item/integrated_circuit/transfer/multiplexer/do_work()
+ var/input_index = get_pin_data(IC_INPUT, 1)
+
+ if(!isnull(input_index) && (input_index >= 1 && input_index < inputs.len))
+ set_pin_data(IC_OUTPUT, 1,get_pin_data(IC_INPUT, input_index + 1))
+ push_data()
+ activate_pin(2)
+
+/obj/item/integrated_circuit/transfer/multiplexer/medium
+ name = "four multiplexer"
+ number_of_inputs = 4
+ icon_state = "mux4"
+
+/obj/item/integrated_circuit/transfer/multiplexer/large
+ name = "eight multiplexer"
+ number_of_inputs = 8
+ w_class = 2
+ icon_state = "mux8"
+
+/obj/item/integrated_circuit/transfer/multiplexer/huge
+ name = "sixteen multiplexer"
+ icon_state = "mux16"
+ w_class = 2
+ number_of_inputs = 16
+
+/obj/item/integrated_circuit/transfer/demultiplexer
+ name = "two demultiplexer"
+ desc = "This is what those in the business tend to refer to as a 'demux'. It moves data from the input to one of the selected outputs."
+ extended_desc = "The first input pin is used to select which of the output pins is given the data from the second input pin. \
+ If the output selection is outside the valid range then no output is given."
+ complexity = 2
+ icon_state = "dmux2"
+ inputs = list("output selection" = IC_PINTYPE_NUMBER, "input" = IC_PINTYPE_ANY)
+ outputs = list()
+ activators = list("select" = IC_PINTYPE_PULSE_IN, "on select" = IC_PINTYPE_PULSE_OUT)
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+ power_draw_per_use = 4
+ var/number_of_outputs = 2
+
+/obj/item/integrated_circuit/transfer/demultiplexer/New()
+ for(var/i = 1 to number_of_outputs)
+ // outputs += "output [i]"
+ outputs["output [i]"] = IC_PINTYPE_ANY
+ complexity = number_of_outputs
+
+ ..()
+ desc += " It has [number_of_outputs] output pins."
+ extended_desc += " This demultiplexer has a range from 1 to [outputs.len]."
+
+/obj/item/integrated_circuit/transfer/demultiplexer/do_work()
+ var/output_index = get_pin_data(IC_INPUT, 1)
+ for(var/i = 1 to outputs.len)
+ set_pin_data(IC_OUTPUT, i, i == output_index ? get_pin_data(IC_INPUT, 2) : null)
+
+ activate_pin(2)
+
+/obj/item/integrated_circuit/transfer/demultiplexer/medium
+ name = "four demultiplexer"
+ icon_state = "dmux4"
+ number_of_outputs = 4
+
+/obj/item/integrated_circuit/transfer/demultiplexer/large
+ name = "eight demultiplexer"
+ icon_state = "dmux8"
+ w_class = 2
+ number_of_outputs = 8
+
+/obj/item/integrated_circuit/transfer/demultiplexer/huge
+ name = "sixteen demultiplexer"
+ icon_state = "dmux16"
+ w_class = 2
+ number_of_outputs = 16
+
+/obj/item/integrated_circuit/transfer/pulsedemultiplexer
+ name = "two pulse demultiplexer"
+ desc = "Selector switch to choose the pin to be activated by number."
+ extended_desc = "The first input pin is used to select which of the pulse out pins will be activated after activation of the circuit. \
+ If the output selection is outside the valid range then no output is given."
+ complexity = 2
+ icon_state = "dmux2"
+ inputs = list("output selection" = IC_PINTYPE_NUMBER)
+ outputs = list()
+ activators = list("select" = IC_PINTYPE_PULSE_IN)
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+ power_draw_per_use = 4
+ var/number_of_outputs = 2
+
+/obj/item/integrated_circuit/transfer/pulsedemultiplexer/New()
+ for(var/i = 1 to number_of_outputs)
+ // outputs += "output [i]"
+ activators["output [i]"] = IC_PINTYPE_PULSE_OUT
+ complexity = number_of_outputs
+
+ ..()
+ desc += " It has [number_of_outputs] output pins."
+ extended_desc += " This pulse demultiplexer has a range from 1 to [activators.len - 1]."
+
+/obj/item/integrated_circuit/transfer/pulsedemultiplexer/do_work()
+ var/output_index = get_pin_data(IC_INPUT, 1)
+
+ if(output_index == Clamp(output_index, 1, number_of_outputs))
+ activate_pin(round(output_index + 1 ,1))
+
+/obj/item/integrated_circuit/transfer/pulsedemultiplexer/medium
+ name = "four pulse demultiplexer"
+ icon_state = "dmux4"
+ number_of_outputs = 4
+
+/obj/item/integrated_circuit/transfer/pulsedemultiplexer/large
+ name = "eight pulse demultiplexer"
+ icon_state = "dmux8"
+ w_class = 2
+ number_of_outputs = 8
+
+/obj/item/integrated_circuit/transfer/pulsedemultiplexer/huge
+ name = "sixteen pulse demultiplexer"
+ icon_state = "dmux16"
+ w_class = 2
+ number_of_outputs = 16
\ No newline at end of file
diff --git a/code/modules/integrated_electronics/subtypes/input.dm b/code/modules/integrated_electronics/subtypes/input.dm
new file mode 100644
index 0000000000000..583c50c4a3a60
--- /dev/null
+++ b/code/modules/integrated_electronics/subtypes/input.dm
@@ -0,0 +1,857 @@
+/obj/item/integrated_circuit/input
+ var/can_be_asked_input = 0
+ category_text = "Input"
+ power_draw_per_use = 5
+
+/obj/item/integrated_circuit/input/proc/ask_for_input(mob/user)
+ return
+
+/obj/item/integrated_circuit/input/button
+ name = "button"
+ desc = "This tiny button must do something, right?"
+ icon_state = "button"
+ complexity = 1
+ can_be_asked_input = 1
+ inputs = list()
+ outputs = list()
+ activators = list("on pressed" = IC_PINTYPE_PULSE_IN)
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+
+
+
+/obj/item/integrated_circuit/input/button/ask_for_input(mob/user) //Bit misleading name for this specific use.
+ to_chat(user, "You press the button labeled '[src.name]'.")
+ activate_pin(1)
+
+/obj/item/integrated_circuit/input/toggle_button
+ name = "toggle button"
+ desc = "It toggles on, off, on, off..."
+ icon_state = "toggle_button"
+ complexity = 1
+ can_be_asked_input = 1
+ inputs = list()
+ outputs = list("on" = IC_PINTYPE_BOOLEAN)
+ activators = list("on toggle" = IC_PINTYPE_PULSE_IN)
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+
+/obj/item/integrated_circuit/input/toggle_button/ask_for_input(mob/user) // Ditto.
+ set_pin_data(IC_OUTPUT, 1, !get_pin_data(IC_OUTPUT, 1))
+ push_data()
+ activate_pin(1)
+ to_chat(user, "You toggle the button labeled '[src.name]' [get_pin_data(IC_OUTPUT, 1) ? "on" : "off"].")
+
+/obj/item/integrated_circuit/input/numberpad
+ name = "number pad"
+ desc = "This small number pad allows someone to input a number into the system."
+ icon_state = "numberpad"
+ complexity = 2
+ can_be_asked_input = 1
+ inputs = list()
+ outputs = list("number entered" = IC_PINTYPE_NUMBER)
+ activators = list("on entered" = IC_PINTYPE_PULSE_IN)
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+ power_draw_per_use = 4
+
+/obj/item/integrated_circuit/input/numberpad/ask_for_input(mob/user)
+ var/new_input = input(user, "Enter a number, please.","Number pad") as null|num
+ if(isnum(new_input) && CanInteract(user, src))
+ set_pin_data(IC_OUTPUT, 1, new_input)
+ push_data()
+ activate_pin(1)
+
+/obj/item/integrated_circuit/input/textpad
+ name = "text pad"
+ desc = "This small text pad allows someone to input a string into the system."
+ icon_state = "textpad"
+ complexity = 2
+ can_be_asked_input = 1
+ inputs = list()
+ outputs = list("string entered" = IC_PINTYPE_STRING)
+ activators = list("on entered" = IC_PINTYPE_PULSE_IN)
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+ power_draw_per_use = 4
+
+/obj/item/integrated_circuit/input/textpad/ask_for_input(mob/user)
+ var/new_input = input(user, "Enter some words, please.","Number pad") as null|text
+ if(istext(new_input) && CanInteract(user, src))
+ set_pin_data(IC_OUTPUT, 1, new_input)
+ push_data()
+ activate_pin(1)
+
+/obj/item/integrated_circuit/input/med_scanner
+ name = "integrated medical analyser"
+ desc = "A very small version of the common medical analyser. This allows the machine to know how healthy someone is."
+ icon_state = "medscan"
+ complexity = 4
+ inputs = list("\[ target")
+ outputs = list(
+ "total health %" = IC_PINTYPE_NUMBER,
+ "total missing health" = IC_PINTYPE_NUMBER
+ )
+ activators = list("scan" = IC_PINTYPE_PULSE_IN, "on scanned" = IC_PINTYPE_PULSE_OUT)
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+ origin_tech = list(TECH_ENGINEERING = 2, TECH_DATA = 2, TECH_BIO = 2)
+ power_draw_per_use = 40
+
+/obj/item/integrated_circuit/input/med_scanner/do_work()
+ var/mob/living/carbon/human/H = get_pin_data_as_type(IC_INPUT, 1, /mob/living/carbon/human)
+ if(!istype(H)) //Invalid input
+ return
+ if(H.Adjacent(get_turf(src))) // Like normal analysers, it can't be used at range.
+ var/total_health = round(H.health/H.getMaxHealth(), 0.01)*100
+ var/missing_health = H.getMaxHealth() - H.health
+
+ set_pin_data(IC_OUTPUT, 1, total_health)
+ set_pin_data(IC_OUTPUT, 2, missing_health)
+
+ push_data()
+ activate_pin(2)
+
+
+
+/obj/item/integrated_circuit/input/adv_med_scanner
+
+ name = "integrated advanced medical analyzer"
+ desc = "A very small version of the medibot's medical analyzer. This allows the machine to know how healthy someone is. \
+
+ This type is much more precise, allowing the machine to know much more about the target than a normal analyzer."
+ icon_state = "medscan_adv"
+ complexity = 12
+ inputs = list("\][ target")
+ outputs = list(
+ "total health %" = IC_PINTYPE_NUMBER,
+ "total missing health" = IC_PINTYPE_NUMBER,
+ "brute damage" = IC_PINTYPE_NUMBER,
+ "burn damage" = IC_PINTYPE_NUMBER,
+ "tox damage" = IC_PINTYPE_NUMBER,
+ "oxy damage" = IC_PINTYPE_NUMBER,
+ "clone damage" = IC_PINTYPE_NUMBER
+ )
+ activators = list("scan" = IC_PINTYPE_PULSE_IN, "on scanned" = IC_PINTYPE_PULSE_OUT)
+ spawn_flags = IC_SPAWN_RESEARCH
+ origin_tech = list(TECH_ENGINEERING = 3, TECH_DATA = 3, TECH_BIO = 4)
+ power_draw_per_use = 80
+
+/obj/item/integrated_circuit/input/adv_med_scanner/do_work()
+ var/mob/living/carbon/human/H = get_pin_data_as_type(IC_INPUT, 1, /mob/living/carbon/human)
+ if(!istype(H)) //Invalid input
+ return
+
+ if(H in view(get_turf(H))) // Like medbot's analyzer it can be used in range..
+
+ var/total_health = round(H.health/H.getMaxHealth(), 0.01)*100
+ var/missing_health = H.getMaxHealth() - H.health
+
+ set_pin_data(IC_OUTPUT, 1, total_health)
+ set_pin_data(IC_OUTPUT, 2, missing_health)
+ set_pin_data(IC_OUTPUT, 3, H.getBruteLoss())
+ set_pin_data(IC_OUTPUT, 4, H.getFireLoss())
+ set_pin_data(IC_OUTPUT, 5, H.getToxLoss())
+ set_pin_data(IC_OUTPUT, 6, H.getOxyLoss())
+ set_pin_data(IC_OUTPUT, 7, H.getCloneLoss())
+
+ push_data()
+ activate_pin(2)
+
+/obj/item/integrated_circuit/input/examiner
+ name = "examiner"
+ desc = "It's a little machine vision system. It can return the name, description, distance,\
+ relative coordinates, total amount of reagents, and maximum amount of reagents of the referenced object."
+ icon_state = "video_camera"
+ complexity = 6
+ inputs = list("\][ target" = IC_PINTYPE_REF)
+ outputs = list(
+ "name" = IC_PINTYPE_STRING,
+ "description" = IC_PINTYPE_STRING,
+ "X" = IC_PINTYPE_NUMBER,
+ "Y" = IC_PINTYPE_NUMBER,
+ "distance" = IC_PINTYPE_NUMBER,
+ "max reagents" = IC_PINTYPE_NUMBER,
+ "amount of reagents" = IC_PINTYPE_NUMBER,
+ )
+ activators = list("scan" = IC_PINTYPE_PULSE_IN, "on scanned" = IC_PINTYPE_PULSE_OUT, "not scanned" = IC_PINTYPE_PULSE_OUT)
+ spawn_flags = IC_SPAWN_RESEARCH
+ origin_tech = list(TECH_ENGINEERING = 3, TECH_DATA = 3, TECH_BIO = 4)
+ power_draw_per_use = 80
+
+/obj/item/integrated_circuit/input/examiner/do_work()
+ var/atom/movable/H = get_pin_data_as_type(IC_INPUT, 1, /atom/movable)
+ var/turf/T = get_turf(src)
+ if(!istype(H)) //Invalid input
+ return
+
+ if(H in view(T)) // This is a camera. It can't examine thngs,that it can't see.
+
+
+ set_pin_data(IC_OUTPUT, 1, H.name)
+ set_pin_data(IC_OUTPUT, 2, H.desc)
+ set_pin_data(IC_OUTPUT, 3, H.x-T.x)
+ set_pin_data(IC_OUTPUT, 4, H.y-T.y)
+ set_pin_data(IC_OUTPUT, 5, sqrt((H.x-T.x)*(H.x-T.x)+ (H.y-T.y)*(H.y-T.y)))
+ var/mr = 0
+ var/tr = 0
+ if(H.reagents)
+ mr = H.reagents.maximum_volume
+ tr = H.reagents.total_volume
+ set_pin_data(IC_OUTPUT, 6, mr)
+ set_pin_data(IC_OUTPUT, 7, tr)
+ push_data()
+ activate_pin(2)
+ else
+ activate_pin(3)
+
+/obj/item/integrated_circuit/input/local_locator
+ name = "local locator"
+ desc = "This is needed for certain devices that demand a reference for a target to act upon. This type only locates something \
+ that is holding the machine containing it."
+ inputs = list()
+ outputs = list("located ref")
+ activators = list("locate" = IC_PINTYPE_PULSE_IN)
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+ power_draw_per_use = 20
+
+/obj/item/integrated_circuit/input/local_locator/do_work()
+ var/datum/integrated_io/O = outputs[1]
+ O.data = null
+ if(assembly)
+ if(istype(assembly.loc, /mob/living)) // Now check if someone's holding us.
+ O.data = weakref(assembly.loc)
+
+ O.push_data()
+
+/obj/item/integrated_circuit/input/adjacent_locator
+ name = "adjacent locator"
+ desc = "This is needed for certain devices that demand a reference for a target to act upon. This type only locates something \
+ that is standing a meter away from the machine."
+ extended_desc = "The first pin requires a ref to a kind of object that you want the locator to acquire. This means that it will \
+ give refs to nearby objects that are similar. If more than one valid object is found nearby, it will choose one of them at \
+ random."
+ inputs = list("desired type ref")
+ outputs = list("located ref")
+ activators = list("locate" = IC_PINTYPE_PULSE_IN,"found" = IC_PINTYPE_PULSE_OUT,
+ "not found" = IC_PINTYPE_PULSE_OUT)
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+ power_draw_per_use = 30
+
+/obj/item/integrated_circuit/input/adjacent_locator/do_work()
+ var/datum/integrated_io/I = inputs[1]
+ var/datum/integrated_io/O = outputs[1]
+ O.data = null
+
+ if(!isweakref(I.data))
+ return
+ var/atom/A = I.data.resolve()
+ if(!A)
+ return
+ var/desired_type = A.type
+
+ var/list/nearby_things = range(1, get_turf(src))
+ var/list/valid_things = list()
+ for(var/atom/thing in nearby_things)
+ if(thing.type != desired_type)
+ continue
+ valid_things.Add(thing)
+ if(valid_things.len)
+ O.data = weakref(pick(valid_things))
+ activate_pin(2)
+ else
+ activate_pin(3)
+ O.push_data()
+
+/obj/item/integrated_circuit/input/advanced_locator
+ complexity = 6
+ name = "advanced locator"
+ desc = "This is needed for certain devices that demand a reference for a target to act upon. This type locates something \
+ that is standing in given radius of up to 8 meters"
+ extended_desc = "The first pin requires a ref to a kind of object that you want the locator to acquire. This means that it will \
+ give refs to nearby objects that are similar to given sample. If this pin is a string, the locator will search for\
+ item by matching desired text in name + description. If more than one valid object is found nearby, it will choose one of them at \
+ random. The second pin is a radius."
+ inputs = list("desired type" = IC_PINTYPE_ANY, "radius" = IC_PINTYPE_NUMBER)
+ outputs = list("located ref")
+ activators = list("locate" = IC_PINTYPE_PULSE_IN,"found" = IC_PINTYPE_PULSE_OUT,"not found" = IC_PINTYPE_PULSE_OUT)
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+ power_draw_per_use = 30
+ var/radius = 1
+
+/obj/item/integrated_circuit/input/advanced_locator/on_data_written()
+ var/rad = get_pin_data(IC_INPUT, 2)
+ if(isnum(rad))
+ rad = Clamp(rad, 0, 8)
+ radius = rad
+
+/obj/item/integrated_circuit/input/advanced_locator/do_work()
+ var/datum/integrated_io/I = inputs[1]
+ var/datum/integrated_io/O = outputs[1]
+ O.data = null
+ var/turf/T = get_turf(src)
+ var/list/nearby_things = range(radius, T) & view(T)
+ var/list/valid_things = list()
+ if(isweakref(I.data))
+ var/atom/A = I.data.resolve()
+ var/desired_type = A.type
+ if(desired_type)
+ for(var/atom/thing in nearby_things)
+ if(thing.type == desired_type)
+ valid_things.Add(thing)
+ else if(istext(I.data))
+ var/DT = I.data
+ for(var/atom/thing in nearby_things)
+ if(findtext(addtext(thing.name," ",thing.desc), DT, 1, 0) )
+ valid_things.Add(thing)
+ if(valid_things.len)
+ O.data = weakref(pick(valid_things))
+ O.push_data()
+ activate_pin(2)
+ else
+ O.push_data()
+ activate_pin(3)
+
+
+
+
+
+/obj/item/integrated_circuit/input/signaler
+ name = "integrated signaler"
+ desc = "Signals from a signaler can be received with this, allowing for remote control. Additionally, it can send signals as well."
+ extended_desc = "When a signal is received from another signaler, the 'on signal received' activator pin will be pulsed. \
+ The two input pins are to configure the integrated signaler's settings. Note that the frequency should not have a decimal in it. \
+ Meaning the default frequency is expressed as 1457, not 145.7. To send a signal, pulse the 'send signal' activator pin."
+ icon_state = "signal"
+ complexity = 4
+ inputs = list("frequency" = IC_PINTYPE_NUMBER,"code" = IC_PINTYPE_NUMBER)
+ outputs = list()
+ activators = list(
+ "send signal" = IC_PINTYPE_PULSE_IN,
+ "on signal sent" = IC_PINTYPE_PULSE_OUT,
+ "on signal received" = IC_PINTYPE_PULSE_OUT)
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+ origin_tech = list(TECH_ENGINEERING = 2, TECH_DATA = 2, TECH_MAGNET = 2)
+ power_draw_idle = 5
+ power_draw_per_use = 40
+
+ var/frequency = 1457
+ var/code = 30
+ var/datum/radio_frequency/radio_connection
+
+/obj/item/integrated_circuit/input/signaler/initialize()
+ ..()
+ set_frequency(frequency)
+ // Set the pins so when someone sees them, they won't show as null
+ set_pin_data(IC_INPUT, 1, frequency)
+ set_pin_data(IC_INPUT, 2, code)
+
+/obj/item/integrated_circuit/input/signaler/Destroy()
+ if(radio_controller)
+ radio_controller.remove_object(src,frequency)
+ frequency = 0
+ . = ..()
+
+/obj/item/integrated_circuit/input/signaler/on_data_written()
+ var/new_freq = get_pin_data(IC_INPUT, 1)
+ var/new_code = get_pin_data(IC_INPUT, 2)
+ if(isnum(new_freq) && new_freq > 0)
+ set_frequency(new_freq)
+ if(isnum(new_code))
+ code = new_code
+
+
+/obj/item/integrated_circuit/input/signaler/do_work() // Sends a signal.
+ if(!radio_connection)
+ return
+
+ var/datum/signal/signal = new()
+ signal.source = src
+ signal.encryption = code
+ signal.data["message"] = "ACTIVATE"
+ radio_connection.post_signal(src, signal)
+ activate_pin(2)
+
+/obj/item/integrated_circuit/input/signaler/proc/set_frequency(new_frequency)
+ if(!frequency)
+ return
+ if(!radio_controller)
+ sleep(20)
+ if(!radio_controller)
+ return
+ radio_controller.remove_object(src, frequency)
+ frequency = new_frequency
+ radio_connection = radio_controller.add_object(src, frequency, RADIO_CHAT)
+
+/obj/item/integrated_circuit/input/signaler/receive_signal(datum/signal/signal)
+ var/new_code = get_pin_data(IC_INPUT, 2)
+ var/code = 0
+
+ if(isnum(new_code))
+ code = new_code
+ if(!signal)
+ return 0
+ if(signal.encryption != code)
+ return 0
+ if(signal.source == src) // Don't trigger ourselves.
+ return 0
+
+ activate_pin(3)
+
+ for(var/mob/O in hearers(1, get_turf(src)))
+ O.show_message(text("\icon[] *beep* *beep*", src), 3, "*beep* *beep*", 2)
+
+/obj/item/integrated_circuit/input/EPv2
+ name = "\improper EPv2 circuit"
+ desc = "Enables the sending and receiving of messages on the Exonet with the EPv2 protocol."
+ extended_desc = "An EPv2 address is a string with the format of XXXX:XXXX:XXXX:XXXX. Data can be send or received using the \
+ second pin on each side, with additonal data reserved for the third pin. When a message is received, the second activaiton pin \
+ will pulse whatever's connected to it. Pulsing the first activation pin will send a message."
+ icon_state = "signal"
+ complexity = 4
+ inputs = list(
+ "target EPv2 address" = IC_PINTYPE_STRING,
+ "data to send" = IC_PINTYPE_STRING,
+ "secondary text" = IC_PINTYPE_STRING
+ )
+ outputs = list(
+ "address received" = IC_PINTYPE_STRING,
+ "data received" = IC_PINTYPE_STRING,
+ "secondary text received" = IC_PINTYPE_STRING
+ )
+ activators = list("send data" = IC_PINTYPE_PULSE_IN, "on data received" = IC_PINTYPE_PULSE_OUT)
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+ origin_tech = list(TECH_ENGINEERING = 2, TECH_DATA = 2, TECH_MAGNET = 2, TECH_BLUESPACE = 2)
+ power_draw_per_use = 50
+ var/datum/exonet_protocol/exonet = null
+
+/obj/item/integrated_circuit/input/EPv2/New()
+ ..()
+ exonet = new(src)
+ exonet.make_address("EPv2_circuit-\ref[src]")
+ desc += "]
This circuit's EPv2 address is: [exonet.address]"
+
+/obj/item/integrated_circuit/input/EPv2/Destroy()
+ if(exonet)
+ exonet.remove_address()
+ qdel(exonet)
+ exonet = null
+ return ..()
+
+/obj/item/integrated_circuit/input/EPv2/do_work()
+ var/target_address = get_pin_data(IC_INPUT, 1)
+ var/message = get_pin_data(IC_INPUT, 2)
+ var/text = get_pin_data(IC_INPUT, 3)
+
+ if(target_address && istext(target_address))
+ exonet.send_message(target_address, message, text)
+
+/obj/item/integrated_circuit/input/receive_exonet_message(var/atom/origin_atom, var/origin_address, var/message, var/text)
+ set_pin_data(IC_OUTPUT, 1, origin_address)
+ set_pin_data(IC_OUTPUT, 2, message)
+ set_pin_data(IC_OUTPUT, 3, text)
+
+ push_data()
+ activate_pin(2)
+
+//This circuit gives information on where the machine is.
+/obj/item/integrated_circuit/input/gps
+ name = "global positioning system"
+ desc = "This allows you to easily know the position of a machine containing this device."
+ extended_desc = "The GPS's coordinates it gives is absolute, not relative."
+ icon_state = "gps"
+ complexity = 4
+ inputs = list()
+ outputs = list("X"= IC_PINTYPE_NUMBER, "Y" = IC_PINTYPE_NUMBER)
+ activators = list("get coordinates" = IC_PINTYPE_PULSE_IN, "on get coordinates" = IC_PINTYPE_PULSE_OUT)
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+ power_draw_per_use = 30
+
+/obj/item/integrated_circuit/input/gps/do_work()
+ var/turf/T = get_turf(src)
+
+ set_pin_data(IC_OUTPUT, 1, null)
+ set_pin_data(IC_OUTPUT, 2, null)
+ if(!T)
+ return
+
+ set_pin_data(IC_OUTPUT, 1, T.x)
+ set_pin_data(IC_OUTPUT, 2, T.y)
+
+ push_data()
+ activate_pin(2)
+
+
+/obj/item/integrated_circuit/input/microphone
+ name = "microphone"
+ desc = "Useful for spying on people or for voice activated machines."
+ extended_desc = "This will automatically translate most languages it hears to Galactic Common. \
+ The first activation pin is always pulsed when the circuit hears someone talk, while the second one \
+ is only triggered if it hears someone speaking a language other than Galactic Common."
+ icon_state = "recorder"
+ flags = HEAR
+ complexity = 8
+ inputs = list()
+ outputs = list(
+ "speaker" = IC_PINTYPE_STRING,
+ "message" = IC_PINTYPE_STRING
+ )
+ activators = list("on message received" = IC_PINTYPE_PULSE_IN, "on translation" = IC_PINTYPE_PULSE_OUT)
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+ power_draw_per_use = 15
+/*
+/obj/item/integrated_circuit/input/microphone/New()
+ ..()
+ listening_objects |= src
+
+/obj/item/integrated_circuit/input/microphone/Destroy()
+ listening_objects -= src
+ return ..()
+*/
+/obj/item/integrated_circuit/input/microphone/Hear(message, atom/movable/speaker, message_langs, raw_message, radio_freq, list/spans)
+ var/datum/integrated_io/V = outputs[1]
+ var/datum/integrated_io/O = outputs[2]
+ var/datum/integrated_io/A = activators[1]
+ if(speaker == src)
+ return
+ //msg = lang_treat(msg, M, message_langs, raw_message, spans)
+ V.data = speaker.GetVoice()
+ O.data = raw_message
+ A.push_data()
+
+ for(var/datum/integrated_io/out in outputs)
+ out.push_data()
+
+ A.push_data()
+
+
+
+/obj/item/integrated_circuit/input/sensor
+ name = "sensor"
+ desc = "Scans and obtains a reference for any objects or persons near you. All you need to do is shove the machine in their face."
+ extended_desc = "If 'ignore storage' pin is set to true, the sensor will disregard scanning various storage containers such as backpacks."
+ icon_state = "recorder"
+ complexity = 12
+ inputs = list("ignore storage" = IC_PINTYPE_BOOLEAN)
+ outputs = list("scanned" = IC_PINTYPE_REF)
+ activators = list("on scanned" = IC_PINTYPE_PULSE_OUT)
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+ power_draw_per_use = 120
+
+/obj/item/integrated_circuit/input/sensor/proc/scan(var/atom/A)
+ var/ignore_bags = get_pin_data(IC_INPUT, 1)
+ if(ignore_bags)
+ if(istype(A, /obj/item/weapon/storage))
+ return FALSE
+
+ set_pin_data(IC_OUTPUT, 1, weakref(A))
+ push_data()
+ activate_pin(1)
+ return TRUE
+
+/obj/item/integrated_circuit/input/internalbm
+ name = "internal battery monitor"
+ desc = "This monitors the charge level of an internal battery."
+ icon_state = "internalbm"
+ extended_desc = "This circuit will give you values of charge, max charge and percentage of the internal battery on demand."
+ w_class = 1
+ complexity = 1
+ inputs = list()
+ outputs = list(
+ "cell charge" = IC_PINTYPE_NUMBER,
+ "max charge" = IC_PINTYPE_NUMBER,
+ "percentage" = IC_PINTYPE_NUMBER,
+ "refference to assembly" = IC_PINTYPE_REF
+ )
+ activators = list("read" = IC_PINTYPE_PULSE_IN, "on read" = IC_PINTYPE_PULSE_OUT)
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+ origin_tech = list(TECH_ENGINEERING = 4, TECH_DATA = 4, TECH_POWER = 4, TECH_MAGNET = 3)
+ power_draw_per_use = 1
+
+/obj/item/integrated_circuit/input/internalbm/do_work()
+ set_pin_data(IC_OUTPUT, 1, null)
+ set_pin_data(IC_OUTPUT, 2, null)
+ set_pin_data(IC_OUTPUT, 3, null)
+ set_pin_data(IC_OUTPUT, 4, weakref(assembly))
+ if(assembly)
+ if(assembly.battery)
+
+ set_pin_data(IC_OUTPUT, 1, assembly.battery.charge)
+ set_pin_data(IC_OUTPUT, 2, assembly.battery.maxcharge)
+ set_pin_data(IC_OUTPUT, 3, 100*assembly.battery.charge/assembly.battery.maxcharge)
+ push_data()
+ activate_pin(2)
+
+/obj/item/integrated_circuit/input/externalbm
+ name = "external battery monitor"
+ desc = "This can help watch the battery level of any device in range."
+ icon_state = "externalbm"
+ extended_desc = "This circuit will give you values of charge, max charge and percentage of any device or battery in view"
+ w_class = 1
+ complexity = 2
+ inputs = list("target" = IC_PINTYPE_REF)
+ outputs = list(
+ "cell charge" = IC_PINTYPE_NUMBER,
+ "max charge" = IC_PINTYPE_NUMBER,
+ "percentage" = IC_PINTYPE_NUMBER
+ )
+ activators = list("read" = IC_PINTYPE_PULSE_IN, "on read" = IC_PINTYPE_PULSE_OUT)
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+ origin_tech = list(TECH_ENGINEERING = 4, TECH_DATA = 4, TECH_POWER = 4, TECH_MAGNET = 3)
+ power_draw_per_use = 1
+
+/obj/item/integrated_circuit/input/externalbm/do_work()
+
+ var/atom/movable/AM = get_pin_data_as_type(IC_INPUT, 1, /atom/movable)
+ set_pin_data(IC_OUTPUT, 1, null)
+ set_pin_data(IC_OUTPUT, 2, null)
+ set_pin_data(IC_OUTPUT, 3, null)
+ if(AM)
+
+
+ var/obj/item/weapon/stock_parts/cell/cell = null
+ if(istype(AM, /obj/item/weapon/stock_parts/cell)) // Is this already a cell?
+ cell = AM
+ else // If not, maybe there's a cell inside it?
+ for(var/obj/item/weapon/stock_parts/cell/C in AM.contents)
+ if(C) // Find one cell to charge.
+ cell = C
+ break
+ if(cell)
+
+ var/turf/A = get_turf(src)
+ if(AM in view(A))
+ push_data()
+ set_pin_data(IC_OUTPUT, 1, cell.charge)
+ set_pin_data(IC_OUTPUT, 2, cell.maxcharge)
+ set_pin_data(IC_OUTPUT, 3, cell.percent())
+ push_data()
+ activate_pin(2)
+
+/obj/item/integrated_circuit/input/atmo_scanner
+ name = "integrated atmospheric analyser"
+ desc = "The same atmospheric analysis module that is integrated into every PDA. \
+ This allows the machine to know the composition, temperature and pressure of the surrounding atmosphere."
+ icon_state = "medscan_adv"
+ complexity = 9
+ inputs = list()
+ outputs = list(
+ "pressure" = IC_PINTYPE_NUMBER,
+ "temperature" = IC_PINTYPE_NUMBER,
+ "oxygen" = IC_PINTYPE_NUMBER,
+ "nitrogen" = IC_PINTYPE_NUMBER,
+ "carbon dioxide" = IC_PINTYPE_NUMBER,
+ "plasma" = IC_PINTYPE_NUMBER,
+ "other" = IC_PINTYPE_NUMBER
+ )
+ activators = list("scan" = IC_PINTYPE_PULSE_IN, "on scanned" = IC_PINTYPE_PULSE_OUT)
+ spawn_flags = IC_SPAWN_RESEARCH
+ origin_tech = list(TECH_ENGINEERING = 3, TECH_DATA = 3)
+ power_draw_per_use = 60
+
+/obj/item/integrated_circuit/input/atmo_scanner/do_work()
+ var/turf/T = get_turf(src)
+ if(!istype(T)) //Invalid input
+ return
+ var/datum/gas_mixture/environment = T.return_air()
+
+ var/pressure = environment.return_pressure()
+ var/total_moles = environment.total_moles()
+
+ if (total_moles)
+ var/o2_level = environment.oxygen/total_moles
+ var/n2_level = environment.nitrogen/total_moles
+ var/co2_level = environment.carbon_dioxide/total_moles
+ var/plasma_level = environment.toxins/total_moles
+ var/unknown_level = 1-(o2_level+n2_level+co2_level+plasma_level)
+ set_pin_data(IC_OUTPUT, 1, pressure)
+ set_pin_data(IC_OUTPUT, 2, round(environment.temperature-T0C,0.1))
+ set_pin_data(IC_OUTPUT, 3, round(o2_level*100,0.1))
+ set_pin_data(IC_OUTPUT, 4, round(n2_level*100,0.1))
+ set_pin_data(IC_OUTPUT, 5, round(co2_level*100,0.1))
+ set_pin_data(IC_OUTPUT, 6, round(plasma_level*100,0.01))
+ set_pin_data(IC_OUTPUT, 7, round(unknown_level, 0.01))
+ else
+ set_pin_data(IC_OUTPUT, 1, 0)
+ set_pin_data(IC_OUTPUT, 2, -273.15)
+ set_pin_data(IC_OUTPUT, 3, 0)
+ set_pin_data(IC_OUTPUT, 4, 0)
+ set_pin_data(IC_OUTPUT, 5, 0)
+ set_pin_data(IC_OUTPUT, 6, 0)
+ set_pin_data(IC_OUTPUT, 7, 0)
+ push_data()
+ activate_pin(2)
+
+/obj/item/integrated_circuit/input/pressure_sensor
+ name = "integrated pressure sensor"
+ desc = "A tiny pressure sensor module similar to that found in a PDA atmosphere analyser."
+ icon_state = "medscan_adv"
+ complexity = 3
+ inputs = list()
+ outputs = list(
+ "pressure" = IC_PINTYPE_NUMBER
+ )
+ activators = list("scan" = IC_PINTYPE_PULSE_IN, "on scanned" = IC_PINTYPE_PULSE_OUT)
+ spawn_flags = IC_SPAWN_RESEARCH
+ origin_tech = list(TECH_ENGINEERING = 3, TECH_DATA = 3)
+ power_draw_per_use = 20
+
+/obj/item/integrated_circuit/input/pressure_sensor/do_work()
+ var/turf/T = get_turf(src)
+ if(!istype(T)) //Invalid input
+ return
+ var/datum/gas_mixture/environment = T.return_air()
+
+ var/pressure = environment.return_pressure()
+ var/total_moles = environment.total_moles()
+
+ if (total_moles)
+ set_pin_data(IC_OUTPUT, 1, pressure)
+ else
+ set_pin_data(IC_OUTPUT, 1, 0)
+ push_data()
+ activate_pin(2)
+
+/obj/item/integrated_circuit/input/temperature_sensor
+ name = "integrated temperature sensor"
+ desc = "A tiny temperature sensor module similar to that found in a PDA atmosphere analyser."
+ icon_state = "medscan_adv"
+ complexity = 3
+ inputs = list()
+ outputs = list(
+ "temperature" = IC_PINTYPE_NUMBER
+ )
+ activators = list("scan" = IC_PINTYPE_PULSE_IN, "on scanned" = IC_PINTYPE_PULSE_OUT)
+ spawn_flags = IC_SPAWN_RESEARCH
+ origin_tech = list(TECH_ENGINEERING = 3, TECH_DATA = 3)
+ power_draw_per_use = 20
+
+/obj/item/integrated_circuit/input/temperature_sensor/do_work()
+ var/turf/T = get_turf(src)
+ if(!istype(T)) //Invalid input
+ return
+ var/datum/gas_mixture/environment = T.return_air()
+
+ var/total_moles = environment.total_moles()
+
+ if (total_moles)
+ set_pin_data(IC_OUTPUT, 1, round(environment.temperature-T0C,0.1))
+ else
+ set_pin_data(IC_OUTPUT, 1, -273.15)
+ push_data()
+ activate_pin(2)
+
+/obj/item/integrated_circuit/input/oxygen_sensor
+ name = "integrated oxygen sensor"
+ desc = "A tiny oxygen sensor module similar to that found in a PDA atmosphere analyser."
+ icon_state = "medscan_adv"
+ complexity = 3
+ inputs = list()
+ outputs = list(
+ "oxygen" = IC_PINTYPE_NUMBER
+ )
+ activators = list("scan" = IC_PINTYPE_PULSE_IN, "on scanned" = IC_PINTYPE_PULSE_OUT)
+ spawn_flags = IC_SPAWN_RESEARCH
+ origin_tech = list(TECH_ENGINEERING = 3, TECH_DATA = 3)
+ power_draw_per_use = 20
+
+/obj/item/integrated_circuit/input/oxygen_sensor/do_work()
+ var/turf/T = get_turf(src)
+ if(!istype(T)) //Invalid input
+ return
+ var/datum/gas_mixture/environment = T.return_air()
+
+ var/total_moles = environment.total_moles()
+
+ if (total_moles)
+ var/o2_level = environment.oxygen/total_moles
+ set_pin_data(IC_OUTPUT, 1, round(o2_level*100,0.1))
+ else
+ set_pin_data(IC_OUTPUT, 1, 0)
+ push_data()
+ activate_pin(2)
+
+/obj/item/integrated_circuit/input/co2_sensor
+ name = "integrated co2 sensor"
+ desc = "A tiny carbon dioxide sensor module similar to that found in a PDA atmosphere analyser."
+ icon_state = "medscan_adv"
+ complexity = 3
+ inputs = list()
+ outputs = list(
+ "co2" = IC_PINTYPE_NUMBER
+ )
+ activators = list("scan" = IC_PINTYPE_PULSE_IN, "on scanned" = IC_PINTYPE_PULSE_OUT)
+ spawn_flags = IC_SPAWN_RESEARCH
+ origin_tech = list(TECH_ENGINEERING = 3, TECH_DATA = 3)
+ power_draw_per_use = 20
+
+/obj/item/integrated_circuit/input/co2_sensor/do_work()
+ var/turf/T = get_turf(src)
+ if(!istype(T)) //Invalid input
+ return
+ var/datum/gas_mixture/environment = T.return_air()
+
+ var/total_moles = environment.total_moles()
+
+ if (total_moles)
+ var/co2_level = environment.carbon_dioxide/total_moles
+ set_pin_data(IC_OUTPUT, 1, round(co2_level*100,0.1))
+ else
+ set_pin_data(IC_OUTPUT, 1, 0)
+ push_data()
+ activate_pin(2)
+
+/obj/item/integrated_circuit/input/nitrogen_sensor
+ name = "integrated nitrogen sensor"
+ desc = "A tiny nitrogen sensor module similar to that found in a PDA atmosphere analyser."
+ icon_state = "medscan_adv"
+ complexity = 3
+ inputs = list()
+ outputs = list(
+ "nitrogen" = IC_PINTYPE_NUMBER
+ )
+ activators = list("scan" = IC_PINTYPE_PULSE_IN, "on scanned" = IC_PINTYPE_PULSE_OUT)
+ spawn_flags = IC_SPAWN_RESEARCH
+ origin_tech = list(TECH_ENGINEERING = 3, TECH_DATA = 3)
+ power_draw_per_use = 20
+
+/obj/item/integrated_circuit/input/nitrogen_sensor/do_work()
+ var/turf/T = get_turf(src)
+ if(!istype(T)) //Invalid input
+ return
+ var/datum/gas_mixture/environment = T.return_air()
+
+ var/total_moles = environment.total_moles()
+
+ if (total_moles)
+ var/n2_level = environment.nitrogen/total_moles
+ set_pin_data(IC_OUTPUT, 1, round(n2_level*100,0.1))
+ else
+ set_pin_data(IC_OUTPUT, 1, 0)
+ push_data()
+ activate_pin(2)
+
+/obj/item/integrated_circuit/input/plasma_sensor
+ name = "integrated plasma sensor"
+ desc = "A tiny plasma gas sensor module similar to that found in a PDA atmosphere analyser."
+ icon_state = "medscan_adv"
+ complexity = 3
+ inputs = list()
+ outputs = list(
+ "plasma" = IC_PINTYPE_NUMBER
+ )
+ activators = list("scan" = IC_PINTYPE_PULSE_IN, "on scanned" = IC_PINTYPE_PULSE_OUT)
+ spawn_flags = IC_SPAWN_RESEARCH
+ origin_tech = list(TECH_ENGINEERING = 3, TECH_DATA = 3)
+ power_draw_per_use = 20
+
+/obj/item/integrated_circuit/input/plasma_sensor/do_work()
+ var/turf/T = get_turf(src)
+ if(!istype(T)) //Invalid input
+ return
+ var/datum/gas_mixture/environment = T.return_air()
+
+ var/total_moles = environment.total_moles()
+
+ if (total_moles)
+ var/plasma_level = environment.toxins/total_moles
+ set_pin_data(IC_OUTPUT, 1, round(plasma_level*100,0.1))
+ else
+ set_pin_data(IC_OUTPUT, 1, 0)
+ push_data()
+ activate_pin(2)
diff --git a/code/modules/integrated_electronics/subtypes/lists.dm b/code/modules/integrated_electronics/subtypes/lists.dm
new file mode 100644
index 0000000000000..f2c9e52ccc1d9
--- /dev/null
+++ b/code/modules/integrated_electronics/subtypes/lists.dm
@@ -0,0 +1,200 @@
+//These circuits do things with lists, and use special list pins for stability.
+/obj/item/integrated_circuit/list
+ complexity = 1
+ inputs = list(
+ "input" = IC_PINTYPE_LIST
+ )
+ outputs = list("result" = IC_PINTYPE_STRING)
+ activators = list("compute" = IC_PINTYPE_PULSE_IN, "on computed" = IC_PINTYPE_PULSE_OUT)
+ category_text = "Lists"
+ power_draw_per_use = 20
+
+/obj/item/integrated_circuit/list/pick
+ name = "pick circuit"
+ desc = "This circuit will randomly 'pick' an element from a list that is inputted."
+ extended_desc = "Will output null if the list is empty. Input list is unmodified."
+ icon_state = "addition"
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+
+/obj/item/integrated_circuit/list/pick/do_work()
+ var/result = null
+ var/list/input_list = get_pin_data(IC_INPUT, 1) // List pins guarantee that there is a list inside, even if just an empty one.
+ if(input_list.len)
+ result = pick(input_list)
+
+ set_pin_data(IC_OUTPUT, 1, result)
+ push_data()
+ activate_pin(2)
+
+
+/obj/item/integrated_circuit/list/append
+ name = "append circuit"
+ desc = "This circuit will add an element to a list."
+ extended_desc = "The new element will always be at the bottom of the list."
+ inputs = list(
+ "list to append" = IC_PINTYPE_LIST,
+ "input" = IC_PINTYPE_ANY
+ )
+ outputs = list(
+ "appended list" = IC_PINTYPE_LIST
+ )
+ icon_state = "addition"
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+
+/obj/item/integrated_circuit/list/append/do_work()
+ var/list/input_list = get_pin_data(IC_INPUT, 1)
+ var/list/output_list = list()
+ var/new_entry = get_pin_data(IC_INPUT, 2)
+ output_list = input_list.Copy()
+ output_list.Add(new_entry)
+
+ set_pin_data(IC_OUTPUT, 1, output_list)
+ push_data()
+ activate_pin(2)
+
+/obj/item/integrated_circuit/list/search
+ name = "search circuit"
+ desc = "This circuit will give index of desired element in the list."
+ extended_desc = "Search will start at 1 position and will return first matching position."
+ inputs = list(
+ "list" = IC_PINTYPE_LIST,
+ "item" = IC_PINTYPE_ANY
+ )
+ outputs = list(
+ "index" = IC_PINTYPE_NUMBER
+ )
+ icon_state = "addition"
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+
+/obj/item/integrated_circuit/list/search/do_work()
+ var/list/input_list = get_pin_data(IC_INPUT, 1)
+ var/item = get_pin_data(IC_INPUT, 2)
+ set_pin_data(IC_OUTPUT, 1, input_list.Find(item))
+ push_data()
+ activate_pin(2)
+
+/obj/item/integrated_circuit/list/at
+ name = "at circuit"
+ desc = "This circuit will pick an element from a list by index."
+ extended_desc = "If there is no element with such index, result will be null."
+ inputs = list(
+ "list" = IC_PINTYPE_LIST,
+ "index" = IC_PINTYPE_NUMBER
+ )
+ outputs = list("item" = IC_PINTYPE_ANY)
+ icon_state = "addition"
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+
+/obj/item/integrated_circuit/list/at/do_work()
+ var/list/input_list = get_pin_data(IC_INPUT, 1)
+ var/index = get_pin_data(IC_INPUT, 2)
+ var/item = input_list[index]
+ set_pin_data(IC_OUTPUT, 1, item)
+ push_data()
+ activate_pin(2)
+
+/obj/item/integrated_circuit/list/delete
+ name = "delete circuit"
+ desc = "This circuit will delete the element from a list by index."
+ extended_desc = "If there is no element with such index, result list will be unchanged."
+ inputs = list(
+ "list" = IC_PINTYPE_LIST,
+ "index" = IC_PINTYPE_NUMBER
+ )
+ outputs = list(
+ "item" = IC_PINTYPE_LIST
+ )
+ icon_state = "addition"
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+
+/obj/item/integrated_circuit/list/delete/do_work()
+ var/list/input_list = get_pin_data(IC_INPUT, 1)
+ var/list/red_list = list()
+ var/index = get_pin_data(IC_INPUT, 2)
+ var/j = 0
+ for(var/I in input_list)
+ j = j + 1
+ if(j != index)
+ red_list.Add(I)
+ set_pin_data(IC_OUTPUT, 1, red_list)
+ push_data()
+ activate_pin(2)
+
+/obj/item/integrated_circuit/list/write
+ name = "write circuit"
+ desc = "This circuit will write element in list with given index."
+ extended_desc = "If there is no element with such index, it will give the same list, as before."
+ inputs = list(
+ "list" = IC_PINTYPE_LIST,
+ "index" = IC_PINTYPE_NUMBER,
+ "item" = IC_PINTYPE_ANY
+ )
+ outputs = list(
+ "redacted list" = IC_PINTYPE_LIST
+ )
+ icon_state = "addition"
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+
+/obj/item/integrated_circuit/list/write/do_work()
+ var/list/input_list = get_pin_data(IC_INPUT, 1)
+ var/index = get_pin_data(IC_INPUT, 2)
+ var/item = get_pin_data(IC_INPUT, 3)
+ input_list[index] = item
+ set_pin_data(IC_OUTPUT, 1, input_list)
+ push_data()
+ activate_pin(2)
+
+obj/item/integrated_circuit/list/len
+ name = "len circuit"
+ desc = "This circuit will give length of the list."
+ inputs = list(
+ "list" = IC_PINTYPE_LIST,
+ )
+ outputs = list(
+ "item" = IC_PINTYPE_NUMBER
+ )
+ icon_state = "addition"
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+
+/obj/item/integrated_circuit/list/len/do_work()
+ var/list/input_list = get_pin_data(IC_INPUT, 1)
+ set_pin_data(IC_OUTPUT, 1, input_list.len)
+ push_data()
+ activate_pin(2)
+
+
+/obj/item/integrated_circuit/list/jointext
+ name = "join text circuit"
+ desc = "This circuit will add all elements of a list into one string, seperated by a character."
+ extended_desc = "Default settings will encode the entire list into a string."
+ inputs = list(
+ "list to join" = IC_PINTYPE_LIST,
+ "delimiter" = IC_PINTYPE_CHAR,
+ "start" = IC_PINTYPE_NUMBER,
+ "end" = IC_PINTYPE_NUMBER
+ )
+ inputs_default = list(
+ "2" = ",",
+ "3" = 1,
+ "4" = 0
+ )
+ outputs = list(
+ "joined text" = IC_PINTYPE_STRING
+ )
+ icon_state = "addition"
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+
+/obj/item/integrated_circuit/list/jointext/do_work()
+ var/list/input_list = get_pin_data(IC_INPUT, 1)
+ var/delimiter = get_pin_data(IC_INPUT, 2)
+ var/start = get_pin_data(IC_INPUT, 3)
+ var/end = get_pin_data(IC_INPUT, 4)
+
+ var/result = null
+
+ if(input_list.len && delimiter && !isnull(start) && !isnull(end))
+ result = jointext(input_list, delimiter, start, end)
+
+ set_pin_data(IC_OUTPUT, 1, result)
+ push_data()
+ activate_pin(2)
\ No newline at end of file
diff --git a/code/modules/integrated_electronics/subtypes/logic.dm b/code/modules/integrated_electronics/subtypes/logic.dm
new file mode 100644
index 0000000000000..82667634a1c0c
--- /dev/null
+++ b/code/modules/integrated_electronics/subtypes/logic.dm
@@ -0,0 +1,221 @@
+/obj/item/integrated_circuit/logic
+ name = "logic gate"
+ desc = "This tiny chip will decide for you!"
+ extended_desc = "Logic circuits will treat a null, 0, and a \"\" string value as FALSE and anything else as TRUE."
+ complexity = 3
+ outputs = list("result")
+ activators = list("compare" = IC_PINTYPE_PULSE_IN)
+ category_text = "Logic"
+ power_draw_per_use = 1
+
+/obj/item/integrated_circuit/logic/do_work()
+ push_data()
+
+/obj/item/integrated_circuit/logic/binary
+ inputs = list("A","B")
+ activators = list("compare" = IC_PINTYPE_PULSE_IN, "on true result" = IC_PINTYPE_PULSE_OUT, "on false result" = IC_PINTYPE_PULSE_OUT)
+
+/obj/item/integrated_circuit/logic/binary/do_work()
+ pull_data()
+ var/datum/integrated_io/A = inputs[1]
+ var/datum/integrated_io/B = inputs[2]
+ var/datum/integrated_io/O = outputs[1]
+ O.data = do_compare(A, B) ? TRUE : FALSE
+
+ if(get_pin_data(IC_OUTPUT, 1))
+ activate_pin(2)
+ else
+ activate_pin(3)
+ ..()
+
+/obj/item/integrated_circuit/logic/binary/proc/do_compare(var/datum/integrated_io/A, var/datum/integrated_io/B)
+ return FALSE
+
+/obj/item/integrated_circuit/logic/unary
+ inputs = list("A")
+ activators = list("compare" = IC_PINTYPE_PULSE_IN, "on compare" = IC_PINTYPE_PULSE_OUT)
+
+/obj/item/integrated_circuit/logic/unary/do_work()
+ pull_data()
+ var/datum/integrated_io/A = inputs[1]
+ var/datum/integrated_io/O = outputs[1]
+ O.data = do_check(A) ? TRUE : FALSE
+ ..()
+ activate_pin(2)
+
+/obj/item/integrated_circuit/logic/unary/proc/do_check(var/datum/integrated_io/A)
+ return FALSE
+
+/obj/item/integrated_circuit/logic/binary/equals
+ name = "equal gate"
+ desc = "This gate compares two values, and outputs the number one if both are the same."
+ icon_state = "equal"
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+
+/obj/item/integrated_circuit/logic/binary/equals/do_compare(var/datum/integrated_io/A, var/datum/integrated_io/B)
+ return A.data == B.data
+
+/obj/item/integrated_circuit/logic/binary/jklatch
+ name = "JK latch"
+ desc = "This gate is a synchronysed JK latch."
+ icon_state = "jklatch"
+ inputs = list("J","K")
+ outputs = list("Q","!Q")
+ activators = list("pulse in C" = IC_PINTYPE_PULSE_IN, "pulse out Q" = IC_PINTYPE_PULSE_OUT, "pulse out !Q" = IC_PINTYPE_PULSE_OUT)
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+ var/lstate=FALSE
+
+/obj/item/integrated_circuit/logic/binary/jklatch/do_work()
+ pull_data()
+ var/datum/integrated_io/A = inputs[1]
+ var/datum/integrated_io/B = inputs[2]
+ var/datum/integrated_io/O = outputs[1]
+ var/datum/integrated_io/Q = outputs[2]
+ if(A.data)
+ if(B.data)
+ lstate=!lstate
+ else
+ lstate = TRUE
+ else
+ if(B.data)
+ lstate=FALSE
+ O.data = lstate ? TRUE : FALSE
+ Q.data = !lstate ? TRUE : FALSE
+ if(get_pin_data(IC_OUTPUT, 1))
+ activate_pin(2)
+ else
+ activate_pin(3)
+ push_data()
+
+/obj/item/integrated_circuit/logic/binary/rslatch
+ name = "RS latch"
+ desc = "This gate is synchronysed a RS latch. If both R and S are true, the state will not change."
+ icon_state = "sr_nor"
+ inputs = list("S","R")
+ outputs = list("Q","!Q")
+ activators = list("pulse in C" = IC_PINTYPE_PULSE_IN, "pulse out Q" = IC_PINTYPE_PULSE_OUT, "pulse out !Q" = IC_PINTYPE_PULSE_OUT)
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+ var/lstate=FALSE
+
+/obj/item/integrated_circuit/logic/binary/rslatch/do_work()
+ pull_data()
+ var/datum/integrated_io/A = inputs[1]
+ var/datum/integrated_io/B = inputs[2]
+ var/datum/integrated_io/O = outputs[1]
+ var/datum/integrated_io/Q = outputs[2]
+ if(A.data)
+ if(!B.data)
+ lstate=TRUE
+ else
+ if(B.data)
+ lstate=FALSE
+ O.data = lstate ? TRUE : FALSE
+ Q.data = !lstate ? TRUE : FALSE
+ if(get_pin_data(IC_OUTPUT, 1))
+ activate_pin(2)
+ else
+ activate_pin(3)
+ push_data()
+
+/obj/item/integrated_circuit/logic/binary/gdlatch
+ name = "gated D latch"
+ desc = "This gate is a synchronysed gated D latch."
+ icon_state = "gated_d"
+ inputs = list("D","E")
+ outputs = list("Q","!Q")
+ activators = list("pulse in C" = IC_PINTYPE_PULSE_IN, "pulse out Q" = IC_PINTYPE_PULSE_OUT, "pulse out !Q" = IC_PINTYPE_PULSE_OUT)
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+ var/lstate=FALSE
+
+/obj/item/integrated_circuit/logic/binary/gdlatch/do_work()
+ pull_data()
+ var/datum/integrated_io/A = inputs[1]
+ var/datum/integrated_io/B = inputs[2]
+ var/datum/integrated_io/O = outputs[1]
+ var/datum/integrated_io/Q = outputs[2]
+ if(B.data)
+ if(A.data)
+ lstate=TRUE
+ else
+ lstate=FALSE
+
+ O.data = lstate ? TRUE : FALSE
+ Q.data = !lstate ? TRUE : FALSE
+ if(get_pin_data(IC_OUTPUT, 1))
+ activate_pin(2)
+ else
+ activate_pin(3)
+ push_data()
+
+/obj/item/integrated_circuit/logic/binary/not_equals
+ name = "not equal gate"
+ desc = "This gate compares two values, and outputs the number one if both are different."
+ icon_state = "not_equal"
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+
+/obj/item/integrated_circuit/logic/binary/not_equals/do_compare(var/datum/integrated_io/A, var/datum/integrated_io/B)
+ return A.data != B.data
+
+/obj/item/integrated_circuit/logic/binary/and
+ name = "and gate"
+ desc = "This gate will output 'one' if both inputs evaluate to true."
+ icon_state = "and"
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+
+/obj/item/integrated_circuit/logic/binary/and/do_compare(var/datum/integrated_io/A, var/datum/integrated_io/B)
+ return A.data && B.data
+
+/obj/item/integrated_circuit/logic/binary/or
+ name = "or gate"
+ desc = "This gate will output 'one' if one of the inputs evaluate to true."
+ icon_state = "or"
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+
+/obj/item/integrated_circuit/logic/binary/or/do_compare(var/datum/integrated_io/A, var/datum/integrated_io/B)
+ return A.data || B.data
+
+/obj/item/integrated_circuit/logic/binary/less_than
+ name = "less than gate"
+ desc = "This will output 'one' if the first input is less than the second input."
+ icon_state = "less_than"
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+
+/obj/item/integrated_circuit/logic/binary/less_than/do_compare(var/datum/integrated_io/A, var/datum/integrated_io/B)
+ return A.data < B.data
+
+/obj/item/integrated_circuit/logic/binary/less_than_or_equal
+ name = "less than or equal gate"
+ desc = "This will output 'one' if the first input is less than, or equal to the second input."
+ icon_state = "less_than_or_equal"
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+
+/obj/item/integrated_circuit/logic/binary/less_than_or_equal/do_compare(var/datum/integrated_io/A, var/datum/integrated_io/B)
+ return A.data <= B.data
+
+/obj/item/integrated_circuit/logic/binary/greater_than
+ name = "greater than gate"
+ desc = "This will output 'one' if the first input is greater than the second input."
+ icon_state = "greater_than"
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+
+/obj/item/integrated_circuit/logic/binary/greater_than/do_compare(var/datum/integrated_io/A, var/datum/integrated_io/B)
+ return A.data > B.data
+
+/obj/item/integrated_circuit/logic/binary/greater_than_or_equal
+ name = "greater_than or equal gate"
+ desc = "This will output 'one' if the first input is greater than, or equal to the second input."
+ icon_state = "greater_than_or_equal"
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+
+/obj/item/integrated_circuit/logic/binary/greater_than_or_equal/do_compare(var/datum/integrated_io/A, var/datum/integrated_io/B)
+ return A.data >= B.data
+
+/obj/item/integrated_circuit/logic/unary/not
+ name = "not gate"
+ desc = "This gate inverts what's fed into it."
+ icon_state = "not"
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+ activators = list("invert" = IC_PINTYPE_PULSE_IN, "on inverted" = IC_PINTYPE_PULSE_OUT)
+
+/obj/item/integrated_circuit/logic/unary/not/do_check(var/datum/integrated_io/A)
+ return !A.data
diff --git a/code/modules/integrated_electronics/subtypes/manipulation.dm b/code/modules/integrated_electronics/subtypes/manipulation.dm
new file mode 100644
index 0000000000000..1b28c8f4a5c5e
--- /dev/null
+++ b/code/modules/integrated_electronics/subtypes/manipulation.dm
@@ -0,0 +1,207 @@
+/obj/item/integrated_circuit/manipulation
+ category_text = "Manipulation"
+
+/obj/item/integrated_circuit/manipulation/weapon_firing
+ name = "weapon firing mechanism"
+ desc = "This somewhat complicated system allows one to slot in a gun, direct it towards a position, and remotely fire it."
+ extended_desc = "The firing mechanism can slot in most ranged weapons, ballistic and energy. \
+ The first and second inputs need to be numbers. They are coordinates for the gun to fire at, relative to the machine itself. \
+ The 'fire' activator will cause the mechanism to attempt to fire the weapon at the coordinates, if possible. Note that the \
+ normal limitations to firearms, such as ammunition requirements and firing delays, still hold true if fired by the mechanism."
+ complexity = 20
+ w_class = 3
+ size = 3
+ inputs = list(
+ "target X rel" = IC_PINTYPE_NUMBER,
+ "target Y rel" = IC_PINTYPE_NUMBER
+ )
+ outputs = list()
+ activators = list(
+ "fire" = IC_PINTYPE_PULSE_IN
+ )
+ var/obj/item/weapon/gun/installed_gun = null
+ spawn_flags = IC_SPAWN_RESEARCH
+ origin_tech = list(TECH_ENGINEERING = 3, TECH_DATA = 3, TECH_COMBAT = 4)
+ power_draw_per_use = 50 // The targeting mechanism uses this. The actual gun uses its own cell for firing if it's an energy weapon.
+
+/obj/item/integrated_circuit/manipulation/weapon_firing/Destroy()
+ qdel(installed_gun)
+ ..()
+
+/obj/item/integrated_circuit/manipulation/weapon_firing/attackby(var/obj/O, var/mob/user)
+ if(istype(O, /obj/item/weapon/gun))
+ var/obj/item/weapon/gun/gun = O
+ if(installed_gun)
+ user << "There's already a weapon installed."
+ return
+ user.remove_from_mob(gun)
+ //user.drop_from_inventory(gun)
+ installed_gun = gun
+ size += gun.w_class
+ gun.forceMove(src)
+ user << "You slide \the [gun] into the firing mechanism."
+ playsound(src.loc, 'sound/items/Crowbar.ogg', 50, 1)
+ else
+ ..()
+
+/obj/item/integrated_circuit/manipulation/weapon_firing/attack_self(var/mob/user)
+ if(installed_gun)
+ installed_gun.forceMove(get_turf(src))
+ user << "You slide \the [installed_gun] out of the firing mechanism."
+ size = initial(size)
+ playsound(src.loc, 'sound/items/Crowbar.ogg', 50, 1)
+ installed_gun = null
+ else
+ user << "There's no weapon to remove from the mechanism."
+
+/obj/item/integrated_circuit/manipulation/weapon_firing/do_work()
+ if(!installed_gun)
+ return
+
+ var/datum/integrated_io/target_x = inputs[1]
+ var/datum/integrated_io/target_y = inputs[2]
+
+ if(src.assembly)
+ if(isnum(target_x.data))
+ target_x.data = round(target_x.data)
+ if(isnum(target_y.data))
+ target_y.data = round(target_y.data)
+
+ var/turf/T = get_turf(src.assembly)
+
+ if(target_x.data == 0 && target_y.data == 0) // Don't shoot ourselves.
+ return
+
+ // We need to do this in order to enable relative coordinates, as locate() only works for absolute coordinates.
+ var/i
+ if(target_x.data > 0)
+ i = abs(target_x.data)
+ while(i > 0)
+ T = get_step(T, EAST)
+ i--
+ else
+ i = abs(target_x.data)
+ while(i > 0)
+ T = get_step(T, WEST)
+ i--
+
+ i = 0
+ if(target_y.data > 0)
+ i = abs(target_y.data)
+ while(i > 0)
+ T = get_step(T, NORTH)
+ i--
+ else if(target_y.data < 0)
+ i = abs(target_y.data)
+ while(i > 0)
+ T = get_step(T, SOUTH)
+ i--
+
+ if(!T)
+ return
+ installed_gun.Fire_userless(T)
+
+/obj/item/integrated_circuit/manipulation/locomotion
+ name = "locomotion circuit"
+ desc = "This allows a machine to move in a given direction."
+ icon_state = "locomotion"
+ extended_desc = "The circuit accepts a 'dir' number as a direction to move towards.
\
+ Pulsing the 'step towards dir' activator pin will cause the machine to move a meter in that direction, assuming it is not \
+ being held, or anchored in some way. It should be noted that the ability to move is dependant on the type of assembly that this circuit inhabits."
+ w_class = 3
+ complexity = 20
+// size = 5
+ inputs = list("direction" = IC_PINTYPE_DIR)
+ outputs = list()
+ activators = list("step towards dir" = IC_PINTYPE_PULSE_IN)
+ spawn_flags = IC_SPAWN_RESEARCH
+ power_draw_per_use = 100
+
+/obj/item/integrated_circuit/manipulation/locomotion/do_work()
+ ..()
+ var/turf/T = get_turf(src)
+ if(istype(loc, /obj/item/device/electronic_assembly))
+ var/obj/item/device/electronic_assembly/machine = loc
+ if(!machine.can_move())
+ return
+ if(machine.loc == T) // Check if we're held by someone. If the loc is the floor, we're not held.
+ var/datum/integrated_io/wanted_dir = inputs[1]
+ if(isnum(wanted_dir.data))
+ step(machine, wanted_dir.data)
+
+/obj/item/integrated_circuit/manipulation/grenade
+ name = "grenade primer"
+ desc = "This circuit comes with the ability to attach most types of grenades at prime them at will."
+ extended_desc = "Time between priming and detonation is limited to between 1 to 12 seconds but is optional. \
+ If unset, not a number, or a number less than 1 then the grenade's built-in timing will be used. \
+ Beware: Once primed there is no aborting the process!"
+ icon_state = "grenade"
+ complexity = 30
+ size = 2
+ inputs = list("detonation time" = IC_PINTYPE_NUMBER)
+ outputs = list()
+ activators = list("prime grenade" = IC_PINTYPE_PULSE_IN)
+ spawn_flags = IC_SPAWN_RESEARCH
+ origin_tech = list(TECH_ENGINEERING = 3, TECH_DATA = 3, TECH_COMBAT = 4)
+ var/obj/item/weapon/grenade/attached_grenade
+ var/pre_attached_grenade_type
+
+/obj/item/integrated_circuit/manipulation/grenade/New()
+ ..()
+ if(pre_attached_grenade_type)
+ var/grenade = new pre_attached_grenade_type(src)
+ attach_grenade(grenade)
+
+/obj/item/integrated_circuit/manipulation/grenade/Destroy()
+ if(attached_grenade && !attached_grenade.active)
+ attached_grenade.loc = loc
+ detach_grenade()
+ . =..()
+
+/obj/item/integrated_circuit/manipulation/grenade/attackby(var/obj/item/weapon/grenade/G, var/mob/user)
+ if(istype(G))
+ if(attached_grenade)
+ to_chat(user, "There is already a grenade attached!")
+ else if(user.unEquip(G, 1))
+ user.visible_message("\The [user] attaches \a [G] to \the [src]!", "You attach \the [G] to \the [src].")
+ attach_grenade(G)
+ G.forceMove(src)
+ else
+ ..()
+
+/obj/item/integrated_circuit/manipulation/grenade/attack_self(var/mob/user)
+ if(attached_grenade)
+ user.visible_message("\The [user] removes \an [attached_grenade] from \the [src]!", "You remove \the [attached_grenade] from \the [src].")
+ user.put_in_any_hand_if_possible(attached_grenade) || (attached_grenade.loc = loc)
+ detach_grenade()
+ else
+ ..()
+
+/obj/item/integrated_circuit/manipulation/grenade/do_work()
+ if(attached_grenade && !attached_grenade.active)
+ var/datum/integrated_io/detonation_time = inputs[1]
+ if(isnum(detonation_time.data) && detonation_time.data > 0)
+ attached_grenade.det_time = Clamp(detonation_time.data, 1, 12) SECONDS
+ attached_grenade.prime()
+// var/atom/holder = loc
+// log_and_message_admins("activated a grenade assembly. Last touches: Assembly: [holder.fingerprintslast] Circuit: [fingerprintslast] Grenade: [attached_grenade.fingerprintslast]")
+
+// These procs do not relocate the grenade, that's the callers responsibility
+/obj/item/integrated_circuit/manipulation/grenade/proc/attach_grenade(var/obj/item/weapon/grenade/G)
+ attached_grenade = G
+// destroyed_event.register(attached_grenade, src, /obj/item/integrated_circuit/manipulation/grenade/proc/detach_grenade)
+ size += G.w_class
+ desc += " \An [attached_grenade] is attached to it!"
+
+/obj/item/integrated_circuit/manipulation/grenade/proc/detach_grenade()
+ if(!attached_grenade)
+ return
+// destroyed_event.unregister(attached_grenade, src, /obj/item/integrated_circuit/manipulation/grenade/proc/detach_grenade)
+ attached_grenade = null
+ size = initial(size)
+ desc = initial(desc)
+
+/obj/item/integrated_circuit/manipulation/grenade/frag
+ pre_attached_grenade_type = /obj/item/weapon/grenade
+ origin_tech = list(TECH_ENGINEERING = 3, TECH_DATA = 3, TECH_COMBAT = 10)
+ spawn_flags = null // Used for world initializing, see the #defines above.
\ No newline at end of file
diff --git a/code/modules/integrated_electronics/subtypes/memory.dm b/code/modules/integrated_electronics/subtypes/memory.dm
new file mode 100644
index 0000000000000..0d9e2962453e2
--- /dev/null
+++ b/code/modules/integrated_electronics/subtypes/memory.dm
@@ -0,0 +1,125 @@
+/obj/item/integrated_circuit/memory
+ name = "memory chip"
+ desc = "This tiny chip can store one piece of data."
+ icon_state = "memory1"
+ complexity = 1
+ inputs = list()
+ outputs = list()
+ activators = list("set" = IC_PINTYPE_PULSE_IN, "on set" = IC_PINTYPE_PULSE_OUT)
+ category_text = "Memory"
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+ power_draw_per_use = 1
+ var/number_of_pins = 1
+
+/obj/item/integrated_circuit/memory/New()
+ for(var/i = 1 to number_of_pins)
+ inputs["input [i]"] = IC_PINTYPE_ANY // This is just a string since pins don't get built until ..() is called.
+ outputs["output [i]"] = IC_PINTYPE_ANY
+ complexity = number_of_pins
+ ..()
+
+/obj/item/integrated_circuit/memory/examine(mob/user)
+ ..()
+ var/i
+ for(i = 1, i <= outputs.len, i++)
+ var/datum/integrated_io/O = outputs[i]
+ var/data = "nothing"
+ if(isweakref(O.data))
+ var/datum/d = O.data_as_type(/datum)
+ if(d)
+ data = "[d]"
+ else if(!isnull(O.data))
+ data = O.data
+ user << "\The [src] has [data] saved to address [i]."
+
+/obj/item/integrated_circuit/memory/do_work()
+ for(var/i = 1 to inputs.len)
+ var/datum/integrated_io/I = inputs[i]
+ var/datum/integrated_io/O = outputs[i]
+ O.data = I.data
+ O.push_data()
+ activate_pin(2)
+
+/obj/item/integrated_circuit/memory/tiny
+ name = "small memory circuit"
+ desc = "This circuit can store two pieces of data."
+ icon_state = "memory2"
+ power_draw_per_use = 2
+ number_of_pins = 2
+
+/obj/item/integrated_circuit/memory/medium
+ name = "medium memory circuit"
+ desc = "This circuit can store four pieces of data."
+ icon_state = "memory4"
+ power_draw_per_use = 2
+ number_of_pins = 4
+
+/obj/item/integrated_circuit/memory/large
+ name = "large memory circuit"
+ desc = "This big circuit can hold eight pieces of data."
+ icon_state = "memory8"
+ origin_tech = list(TECH_ENGINEERING = 3, TECH_DATA = 3)
+ power_draw_per_use = 4
+ number_of_pins = 8
+
+/obj/item/integrated_circuit/memory/huge
+ name = "large memory stick"
+ desc = "This stick of memory can hold up up to sixteen pieces of data."
+ icon_state = "memory16"
+ w_class = 3
+ spawn_flags = IC_SPAWN_RESEARCH
+ origin_tech = list(TECH_ENGINEERING = 4, TECH_DATA = 4)
+ power_draw_per_use = 8
+ number_of_pins = 16
+
+/obj/item/integrated_circuit/memory/constant
+ name = "constant chip"
+ desc = "This tiny chip can store one piece of data, which cannot be overwritten without disassembly."
+ icon_state = "memory"
+ complexity = 1
+ inputs = list()
+ outputs = list("output pin" = IC_PINTYPE_ANY)
+ activators = list("push data" = IC_PINTYPE_PULSE_IN)
+ var/accepting_refs = 0
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+
+/obj/item/integrated_circuit/memory/constant/do_work()
+ var/datum/integrated_io/O = outputs[1]
+ O.push_data()
+
+/obj/item/integrated_circuit/memory/constant/attack_self(mob/user)
+ var/datum/integrated_io/O = outputs[1]
+ var/type_to_use = input("Please choose a type to use.","[src] type setting") as null|anything in list("string","number","ref", "null")
+ //if(!CanInteract(user,src))
+ // return
+
+ var/new_data = null
+ switch(type_to_use)
+ if("string")
+ accepting_refs = 0
+ new_data = input("Now type in a string.","[src] string writing") as null|text
+ if(istext(new_data))
+ O.data = new_data
+ user << "You set \the [src]'s memory to [O.display_data()]."
+ if("number")
+ accepting_refs = 0
+ new_data = input("Now type in a number.","[src] number writing") as null|num
+ if(isnum(new_data))
+ O.data = new_data
+ user << "You set \the [src]'s memory to [O.display_data()]."
+ if("ref")
+ accepting_refs = 1
+ user << "You turn \the [src]'s ref scanner on. Slide it across \
+ an object for a ref of that object to save it in memory."
+ if("null")
+ O.data = null
+ user << "You set \the [src]'s memory to absolutely nothing."
+
+/obj/item/integrated_circuit/memory/constant/afterattack(atom/target, mob/living/user, proximity)
+ if(accepting_refs && proximity)
+ var/datum/integrated_io/O = outputs[1]
+ O.data = weakref(target)
+ visible_message("[user] slides \a [src]'s over \the [target].")
+ user << "You set \the [src]'s memory to a reference to [O.display_data()]. The ref scanner is \
+ now off."
+ accepting_refs = 0
\ No newline at end of file
diff --git a/code/modules/integrated_electronics/subtypes/output.dm b/code/modules/integrated_electronics/subtypes/output.dm
new file mode 100644
index 0000000000000..4e9da97d2ad2c
--- /dev/null
+++ b/code/modules/integrated_electronics/subtypes/output.dm
@@ -0,0 +1,339 @@
+/obj/item/integrated_circuit/output
+ category_text = "Output"
+
+/obj/item/integrated_circuit/output/screen
+ name = "small screen"
+ desc = "This small screen can display a single piece of data, when the machine is examined closely."
+ icon_state = "screen"
+ inputs = list("displayed data" = IC_PINTYPE_ANY)
+ outputs = list()
+ activators = list("load data" = IC_PINTYPE_PULSE_IN)
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+ power_draw_per_use = 10
+ var/stuff_to_display = null
+
+
+/obj/item/integrated_circuit/output/screen/disconnect_all()
+ ..()
+ stuff_to_display = null
+
+/obj/item/integrated_circuit/output/screen/any_examine(mob/user)
+ to_chat(user, "There is a little screen labeled '[name]', which displays [!isnull(stuff_to_display) ? "'[stuff_to_display]'" : "nothing"].")
+
+/obj/item/integrated_circuit/output/screen/do_work()
+ var/datum/integrated_io/I = inputs[1]
+ if(isweakref(I.data))
+ var/datum/d = I.data_as_type(/datum)
+ if(d)
+ stuff_to_display = "[d]"
+ else
+ stuff_to_display = I.data
+
+/obj/item/integrated_circuit/output/screen/medium
+ name = "screen"
+ desc = "This screen allows for people holding the device to see a piece of data."
+ icon_state = "screen_medium"
+ power_draw_per_use = 20
+
+/obj/item/integrated_circuit/output/screen/medium/do_work()
+ ..()
+ var/list/nearby_things = range(0, get_turf(src))
+ for(var/mob/M in nearby_things)
+ var/obj/O = assembly ? assembly : src
+ to_chat(M, "\icon[O] [stuff_to_display]")
+
+/obj/item/integrated_circuit/output/screen/large
+ name = "large screen"
+ desc = "This screen allows for people able to see the device to see a piece of data."
+ icon_state = "screen_large"
+ power_draw_per_use = 40
+
+/obj/item/integrated_circuit/output/screen/large/do_work()
+ ..()
+ var/obj/O = assembly ? loc : assembly
+ O.visible_message("\icon[O] [stuff_to_display]")
+
+/obj/item/integrated_circuit/output/light
+ name = "light"
+ desc = "This light can turn on and off on command."
+ icon_state = "light"
+ complexity = 4
+ inputs = list()
+ outputs = list()
+ activators = list("toggle light" = IC_PINTYPE_PULSE_IN)
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+ var/light_toggled = 0
+ var/light_brightness = 3
+ var/light_rgb = "#FFFFFF"
+ power_draw_idle = 0 // Adjusted based on brightness.
+
+/obj/item/integrated_circuit/output/light/do_work()
+ light_toggled = !light_toggled
+ update_lighting()
+
+/obj/item/integrated_circuit/output/light/proc/update_lighting()
+ if(light_toggled)
+ if(assembly)
+ assembly.set_light(l_range = light_brightness, l_power = light_brightness, l_color = light_rgb)
+ else
+ if(assembly)
+ assembly.set_light(0)
+ power_draw_idle = light_toggled ? light_brightness * 2 : 0
+
+/obj/item/integrated_circuit/output/light/advanced/update_lighting()
+ var/new_color = get_pin_data(IC_INPUT, 1)
+ var/brightness = get_pin_data(IC_INPUT, 2)
+
+ if(new_color && isnum(brightness))
+ brightness = Clamp(brightness, 0, 6)
+ light_rgb = new_color
+ light_brightness = brightness
+
+ ..()
+
+/obj/item/integrated_circuit/output/light/power_fail() // Turns off the flashlight if there's no power left.
+ light_toggled = FALSE
+ update_lighting()
+
+/obj/item/integrated_circuit/output/light/advanced
+ name = "advanced light"
+ desc = "This light can turn on and off on command, in any color, and in various brightness levels."
+ icon_state = "light_adv"
+ complexity = 8
+ inputs = list(
+ "color" = IC_PINTYPE_COLOR,
+ "brightness" = IC_PINTYPE_NUMBER
+ )
+ outputs = list()
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+ origin_tech = list(TECH_ENGINEERING = 3, TECH_DATA = 3)
+
+/obj/item/integrated_circuit/output/light/advanced/on_data_written()
+ update_lighting()
+
+/obj/item/integrated_circuit/output/sound
+ name = "speaker circuit"
+ desc = "A miniature speaker is attached to this component."
+ icon_state = "speaker"
+ complexity = 8
+ cooldown_per_use = 4 SECONDS
+ inputs = list(
+ "sound ID" = IC_PINTYPE_STRING,
+ "volume" = IC_PINTYPE_NUMBER,
+ "frequency" = IC_PINTYPE_BOOLEAN
+ )
+ outputs = list()
+ activators = list("play sound" = IC_PINTYPE_PULSE_IN)
+ power_draw_per_use = 20
+ var/list/sounds = list()
+
+/obj/item/integrated_circuit/output/text_to_speech
+ name = "text-to-speech circuit"
+ desc = "A miniature speaker is attached to this component."
+ extended_desc = "This unit is more advanced than the plain speaker circuit, able to transpose any valid text to speech."
+ icon_state = "speaker"
+ complexity = 12
+ cooldown_per_use = 4 SECONDS
+ inputs = list("text" = IC_PINTYPE_STRING)
+ outputs = list()
+ activators = list("to speech" = IC_PINTYPE_PULSE_IN)
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+ power_draw_per_use = 60
+
+/obj/item/integrated_circuit/output/text_to_speech/do_work()
+ text = get_pin_data(IC_INPUT, 1)
+ if(!isnull(text))
+ var/obj/O = assembly ? loc : assembly
+ audible_message("\icon[O] \The [O.name] states, \"[text]\"")
+
+/obj/item/integrated_circuit/output/sound/New()
+ ..()
+ extended_desc = list()
+ extended_desc += "The first input pin determines which sound is used. The choices are; "
+ extended_desc += jointext(sounds, ", ")
+ extended_desc += ". The second pin determines the volume of sound that is played"
+ extended_desc += ", and the third determines if the frequency of the sound will vary with each activation."
+ extended_desc = jointext(extended_desc, null)
+
+/obj/item/integrated_circuit/output/sound/do_work()
+ var/ID = get_pin_data(IC_INPUT, 1)
+ var/vol = get_pin_data(IC_INPUT, 2)
+ var/freq = get_pin_data(IC_INPUT, 3)
+ if(!isnull(ID) && !isnull(vol))
+ var/selected_sound = sounds[ID]
+ if(!selected_sound)
+ return
+ vol = Clamp(vol, 0, 100)
+ playsound(get_turf(src), selected_sound, vol, freq, -1)
+
+/obj/item/integrated_circuit/output/sound/beeper
+ name = "beeper circuit"
+ desc = "A miniature speaker is attached to this component. This is often used in the construction of motherboards, which use \
+ the speaker to tell the user if something goes very wrong when booting up. It can also do other similar synthetic sounds such \
+ as buzzing, pinging, chiming, and more."
+ sounds = list(
+ "beep" = 'sound/machines/twobeep.ogg',
+ "chime" = 'sound/machines/chime.ogg',
+ "buzz sigh" = 'sound/machines/buzz-sigh.ogg',
+ "buzz twice" = 'sound/machines/buzz-two.ogg',
+ "ping" = 'sound/machines/ping.ogg',
+ "synth yes" = 'sound/machines/synth_yes.ogg',
+ "synth no" = 'sound/machines/synth_no.ogg',
+ "warning buzz" = 'sound/machines/warning-buzzer.ogg'
+ )
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+
+/obj/item/integrated_circuit/output/sound/beepsky
+ name = "securitron sound circuit"
+ desc = "A miniature speaker is attached to this component. Considered by some to be the essential component for a securitron."
+ sounds = list(
+ "creep" = 'sound/voice/bcreep.ogg',
+ "criminal" = 'sound/voice/bcriminal.ogg',
+ "freeze" = 'sound/voice/bfreeze.ogg',
+ "god" = 'sound/voice/bgod.ogg',
+ "i am the law" = 'sound/voice/biamthelaw.ogg',
+ "insult" = 'sound/voice/binsult.ogg',
+ "radio" = 'sound/voice/bradio.ogg',
+ "secure day" = 'sound/voice/bsecureday.ogg',
+ )
+ spawn_flags = IC_SPAWN_RESEARCH
+ origin_tech = list(TECH_ENGINEERING = 2, TECH_DATA = 2, TECH_ILLEGAL = 1)
+
+/obj/item/integrated_circuit/output/sound/medbot
+ name = "medbot sound circuit"
+ desc = "A miniature speaker is attached to this component, used to annoy patients while they get pricked by a medbot."
+ sounds = list(
+ "surgeon" = 'sound/voice/medbot/msurgeon.ogg',
+ "radar" = 'sound/voice/medbot/mradar.ogg',
+ "feel better" = 'sound/voice/medbot/mfeelbetter.ogg',
+ "patched up" = 'sound/voice/medbot/mpatchedup.ogg',
+ "injured" = 'sound/voice/medbot/minjured.ogg',
+ "insult" = 'sound/voice/medbot/minsult.ogg',
+ "coming" = 'sound/voice/medbot/mcoming.ogg',
+ "help" = 'sound/voice/medbot/mhelp.ogg',
+ "live" = 'sound/voice/medbot/mlive.ogg',
+ "lost" = 'sound/voice/medbot/mlost.ogg',
+ "flies" = 'sound/voice/medbot/mflies.ogg',
+ "catch" = 'sound/voice/medbot/mcatch.ogg',
+ "delicious" = 'sound/voice/medbot/mdelicious.ogg',
+ "apple" = 'sound/voice/medbot/mapple.ogg',
+ "no" = 'sound/voice/medbot/mno.ogg',
+ )
+ spawn_flags = IC_SPAWN_RESEARCH
+ origin_tech = list(TECH_ENGINEERING = 2, TECH_DATA = 2, TECH_BIO = 1)
+
+/obj/item/integrated_circuit/output/video_camera
+ name = "video camera circuit"
+ desc = "This small camera allows a remote viewer to see what it sees."
+ extended_desc = "The camera is linked to the Research camera network."
+ icon_state = "video_camera"
+ w_class = 2
+ complexity = 10
+ inputs = list(
+ "camera name" = IC_PINTYPE_STRING,
+ "camera active" = IC_PINTYPE_BOOLEAN
+ )
+ inputs_default = list("1" = "video camera circuit")
+ outputs = list()
+ activators = list()
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+ power_draw_idle = 5 // Raises to 80 when on.
+ var/obj/machinery/camera/camera
+
+/obj/item/integrated_circuit/output/video_camera/New()
+ ..()
+ camera = new(src)
+ on_data_written()
+
+/obj/item/integrated_circuit/output/video_camera/Destroy()
+ qdel(camera)
+ return ..()
+
+/obj/item/integrated_circuit/output/video_camera/proc/set_camera_status(var/status)
+ if(camera)
+ camera.status = status
+ power_draw_idle = camera.status ? 80 : 5
+ if(camera.status) // Ensure that there's actually power.
+ if(!draw_idle_power())
+ power_fail()
+
+/obj/item/integrated_circuit/output/video_camera/on_data_written()
+ if(camera)
+ var/cam_name = get_pin_data(IC_INPUT, 1)
+ var/cam_active = get_pin_data(IC_INPUT, 2)
+ if(!isnull(cam_name))
+ camera.c_tag = cam_name
+ set_camera_status(cam_active)
+
+/obj/item/integrated_circuit/output/video_camera/power_fail()
+ if(camera)
+ set_camera_status(0)
+ set_pin_data(IC_INPUT, 2, FALSE)
+
+/obj/item/integrated_circuit/output/led
+ name = "light-emitting diode"
+ desc = "This a LED that is lit whenever there is TRUE-equivalent data on its input."
+ extended_desc = "TRUE-equivalent values are: Non-empty strings, non-zero numbers, and valid refs."
+ complexity = 0.1
+ icon_state = "led"
+ inputs = list("lit" = IC_PINTYPE_BOOLEAN)
+ outputs = list()
+ activators = list()
+ power_draw_idle = 0 // Raises to 1 when lit.
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+ var/led_color
+
+/obj/item/integrated_circuit/output/led/on_data_written()
+ power_draw_idle = get_pin_data(IC_INPUT, 1) ? 1 : 0
+
+/obj/item/integrated_circuit/output/led/power_fail()
+ set_pin_data(IC_INPUT, 1, FALSE)
+
+/obj/item/integrated_circuit/output/led/any_examine(mob/user)
+ var/text_output = list()
+ var/initial_name = initial(name)
+
+ // Doing all this work just to have a color-blind friendly output.
+ text_output += "There is "
+ if(name == initial_name)
+ text_output += "\an [name]"
+ else
+ text_output += "\an ["\improper[initial_name]"] labeled '[name]'"
+ text_output += " which is currently [get_pin_data(IC_INPUT, 1) ? "lit ¤" : "unlit."]"
+ to_chat(user,jointext(text_output,null))
+
+/obj/item/integrated_circuit/output/led/red
+ name = "red LED"
+ led_color = COLOR_RED
+
+/obj/item/integrated_circuit/output/led/orange
+ name = "orange LED"
+ led_color = COLOR_ORANGE
+
+/obj/item/integrated_circuit/output/led/yellow
+ name = "yellow LED"
+ led_color = COLOR_YELLOW
+
+/obj/item/integrated_circuit/output/led/green
+ name = "green LED"
+ led_color = COLOR_GREEN
+
+/obj/item/integrated_circuit/output/led/blue
+ name = "blue LED"
+ led_color = COLOR_BLUE
+
+/obj/item/integrated_circuit/output/led/purple
+ name = "purple LED"
+ led_color = COLOR_PURPLE
+
+/obj/item/integrated_circuit/output/led/cyan
+ name = "cyan LED"
+ led_color = COLOR_CYAN
+
+/obj/item/integrated_circuit/output/led/white
+ name = "white LED"
+ led_color = COLOR_WHITE
+
+/obj/item/integrated_circuit/output/led/pink
+ name = "pink LED"
+ led_color = COLOR_PINK
diff --git a/code/modules/integrated_electronics/subtypes/power.dm b/code/modules/integrated_electronics/subtypes/power.dm
new file mode 100644
index 0000000000000..339448a12b9a7
--- /dev/null
+++ b/code/modules/integrated_electronics/subtypes/power.dm
@@ -0,0 +1,91 @@
+/obj/item/integrated_circuit/power/
+ category_text = "Power - Active"
+
+/obj/item/integrated_circuit/power/transmitter
+ name = "power transmission circuit"
+ desc = "This can wirelessly transmit electricity from an assembly's battery towards a nearby machine."
+ icon_state = "power_transmitter"
+ extended_desc = "This circuit transmits 5 kJ of electricity every time the activator pin is pulsed. The input pin must be \
+ a reference to a machine to send electricity to. This can be a battery, or anything containing a battery. The machine can exist \
+ inside the assembly, or adjacent to it. The power is sourced from the assembly's power cell. If the target is outside of the assembly, \
+ some power is lost due to ineffiency."
+ w_class = 2
+ complexity = 16
+ inputs = list("target" = IC_PINTYPE_REF)
+ outputs = list(
+ "target cell charge" = IC_PINTYPE_NUMBER,
+ "target cell max charge" = IC_PINTYPE_NUMBER,
+ "target cell percentage" = IC_PINTYPE_NUMBER
+ )
+ activators = list("transmit" = IC_PINTYPE_PULSE_IN, "on transmit" = IC_PINTYPE_PULSE_OUT)
+ spawn_flags = IC_SPAWN_RESEARCH
+ origin_tech = list(TECH_ENGINEERING = 4, TECH_DATA = 4, TECH_POWER = 4, TECH_MAGNETS = 3)
+ power_draw_per_use = 500 // Inefficency has to come from somewhere.
+ var/amount_to_move = 5000
+
+/obj/item/integrated_circuit/power/transmitter/large
+ name = "large power transmission circuit"
+ desc = "This can wirelessly transmit a lot of electricity from an assembly's battery towards a nearby machine. Warning: Do not operate in flammable enviroments."
+ extended_desc = "This circuit transmits 20 kJ of electricity every time the activator pin is pulsed. The input pin must be \
+ a reference to a machine to send electricity to. This can be a battery, or anything containing a battery. The machine can exist \
+ inside the assembly, or adjacent to it. The power is sourced from the assembly's power cell. If the target is outside of the assembly, \
+ some power is lost due to ineffiency."
+ w_class = 4
+ complexity = 32
+ origin_tech = list(TECH_ENGINEERING = 4, TECH_DATA = 4, TECH_POWER = 6, TECH_MAGNETS = 5)
+ power_draw_per_use = 2000
+ amount_to_move = 20000
+
+/obj/item/integrated_circuit/power/transmitter/do_work()
+
+ var/atom/movable/AM = get_pin_data_as_type(IC_INPUT, 1, /atom/movable)
+ if(AM)
+ if(!assembly)
+ return FALSE // Pointless to do everything else if there's no battery to draw from.
+
+ var/obj/item/weapon/stock_parts/cell/cell = null
+ if(istype(AM, /obj/item/weapon/stock_parts/cell)) // Is this already a cell?
+ cell = AM
+ else // If not, maybe there's a cell inside it?
+ for(var/obj/item/weapon/stock_parts/cell/C in AM.contents)
+ if(C) // Find one cell to charge.
+ cell = C
+ break
+ if(cell)
+ var/transfer_amount = amount_to_move
+ var/turf/A = get_turf(src)
+ var/turf/B = get_turf(AM)
+ if(A.Adjacent(B))
+ if(AM.loc != assembly)
+ transfer_amount *= 0.8 // Losses due to distance.
+
+ if(cell.charge >= cell.maxcharge)
+ return FALSE
+
+ if(transfer_amount && assembly.draw_power(amount_to_move)) // CELLRATE is already handled in draw_power()
+ cell.give(transfer_amount * CELLRATE)
+ cell.update_icon()
+
+ set_pin_data(IC_OUTPUT, 1, cell.charge)
+ set_pin_data(IC_OUTPUT, 2, cell.maxcharge)
+ set_pin_data(IC_OUTPUT, 3, cell.percent())
+ activate_pin(2)
+ push_data()
+ return TRUE
+ else
+ set_pin_data(IC_OUTPUT, 1, null)
+ set_pin_data(IC_OUTPUT, 2, null)
+ set_pin_data(IC_OUTPUT, 3, null)
+ activate_pin(2)
+ push_data()
+ return FALSE
+ return FALSE
+
+/obj/item/integrated_circuit/power/transmitter/large/do_work()
+ if(..()) // If the above code succeeds, do this below.
+ if(prob(2))
+ var/datum/effect/effect/system/spark_spread/sparks = new /datum/effect/effect/system/spark_spread()
+ sparks.set_up(3, 0, get_turf(src))
+ sparks.start()
+ visible_message("\The [assembly] makes some sparks!")
+ qdel(sparks)
\ No newline at end of file
diff --git a/code/modules/integrated_electronics/subtypes/reagents.dm b/code/modules/integrated_electronics/subtypes/reagents.dm
new file mode 100644
index 0000000000000..aa12f7dcceea0
--- /dev/null
+++ b/code/modules/integrated_electronics/subtypes/reagents.dm
@@ -0,0 +1,355 @@
+/obj/item/integrated_circuit/reagent
+ category_text = "Reagent"
+ var/volume = 0
+ unacidable = 1
+ origin_tech = list(TECH_ENGINEERING = 2, TECH_DATA = 2, TECH_BIO = 2)
+
+/obj/item/integrated_circuit/reagent/New()
+ ..()
+ if(volume)
+ create_reagents(volume)
+
+/obj/item/integrated_circuit/reagent/smoke
+ name = "smoke generator"
+ desc = "Unlike most electronics, creating smoke is completely intentional."
+ icon_state = "smoke"
+ extended_desc = "This smoke generator creates clouds of smoke on command. It can also hold liquids inside, which will go \
+ into the smoke clouds when activated. The reagents are consumed when smoke is made."
+ flags = OPENCONTAINER
+ complexity = 20
+ cooldown_per_use = 30 SECONDS
+ inputs = list()
+ outputs = list("volume used" = IC_PINTYPE_NUMBER,"self reference" = IC_PINTYPE_REF)
+ activators = list("create smoke" = IC_PINTYPE_PULSE_IN,"on smoked" = IC_PINTYPE_PULSE_OUT)
+ spawn_flags = IC_SPAWN_RESEARCH
+ origin_tech = list(TECH_ENGINEERING = 3, TECH_DATA = 3, TECH_BIO = 3)
+ volume = 100
+ power_draw_per_use = 20
+
+/obj/item/integrated_circuit/reagent/smoke/on_reagent_change()
+ set_pin_data(IC_OUTPUT, 1, reagents.total_volume)
+ push_data()
+
+
+/obj/item/integrated_circuit/reagent/smoke/interact(mob/user)
+ set_pin_data(IC_OUTPUT, 2, weakref(src))
+ push_data()
+ ..()
+
+/obj/item/integrated_circuit/reagent/smoke/do_work()
+ playsound(src.loc, 'sound/effects/smoke.ogg', 50, 1, -3)
+ var/datum/effect/effect/system/smoke_spread/chem/smoke_system = new()
+ smoke_system.set_up(reagents, 10, 0, get_turf(src))
+ spawn(0)
+ for(var/i = 1 to 8)
+ smoke_system.start()
+ reagents.clear_reagents()
+ activate_pin(2)
+
+/obj/item/integrated_circuit/reagent/injector
+ name = "integrated hypo-injector"
+ desc = "This scary looking thing is able to pump liquids into whatever it's pointed at."
+ icon_state = "injector"
+ extended_desc = "This autoinjector can push reagents into another container or someone else outside of the machine. The target \
+ must be adjacent to the machine, and if it is a person, they cannot be wearing thick clothing."
+ flags = OPENCONTAINER
+ complexity = 20
+ cooldown_per_use = 6 SECONDS
+ inputs = list("target" = IC_PINTYPE_REF, "injection amount" = IC_PINTYPE_NUMBER)
+ inputs_default = list("2" = 5)
+ outputs = list("volume used" = IC_PINTYPE_NUMBER,"self reference" = IC_PINTYPE_REF)
+ activators = list("inject" = IC_PINTYPE_PULSE_IN, "on injected" = IC_PINTYPE_PULSE_OUT, "on fail" = IC_PINTYPE_PULSE_OUT)
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+ volume = 30
+ power_draw_per_use = 15
+ var/direc = 1
+ var/transfer_amount = 10
+
+/obj/item/integrated_circuit/reagent/injector/interact(mob/user)
+ set_pin_data(IC_OUTPUT, 2, weakref(src))
+ push_data()
+ ..()
+
+
+/obj/item/integrated_circuit/reagent/injector/on_reagent_change()
+ set_pin_data(IC_OUTPUT, 1, reagents.total_volume)
+ push_data()
+
+/obj/item/integrated_circuit/reagent/injector/on_data_written()
+ var/new_amount = get_pin_data(IC_INPUT, 2)
+ if(new_amount < 0)
+ new_amount = -new_amount
+ direc = 0
+ else
+ direc = 1
+ if(isnum(new_amount))
+ new_amount = Clamp(new_amount, 0, volume)
+ transfer_amount = new_amount
+
+
+/obj/item/integrated_circuit/reagent/injector/do_work()
+ set waitfor = 0 // Don't sleep in a proc that is called by a processor without this set, otherwise it'll delay the entire thing
+ var/atom/movable/AM = get_pin_data_as_type(IC_INPUT, 1, /atom/movable)
+ if(!istype(AM)) //Invalid input
+ activate_pin(3)
+ return
+
+ if(direc == 1)
+
+ if(!istype(AM)) //Invalid input
+ activate_pin(3)
+ return
+ if(!reagents.total_volume) // Empty
+ activate_pin(3)
+ return
+ if(AM.can_be_injected_by(src))
+ if(isliving(AM))
+ var/mob/living/L = AM
+ var/turf/T = get_turf(AM)
+ T.visible_message("[src] is trying to inject [L]!")
+ sleep(3 SECONDS)
+ if(!L.can_be_injected_by(src))
+ activate_pin(3)
+ return
+ var/contained = reagents.get_reagents()
+ var/trans = reagents.trans_to(L, transfer_amount)
+ message_admins("[src] injected \the [L] with [trans]u of [contained].")
+ to_chat(AM, "You feel a tiny prick!")
+ visible_message("[src] injects [L]!")
+ else
+ reagents.trans_to(AM, transfer_amount)
+ else
+
+ if(reagents.total_volume >= volume) // Full
+ activate_pin(3)
+ return
+ var/obj/target = AM
+ if(!target.reagents)
+ activate_pin(3)
+ return
+ var/turf/TS = get_turf(src)
+ var/turf/TT = get_turf(AM)
+ if(!TS.Adjacent(TT))
+ activate_pin(3)
+ return
+ var/tramount = Clamp(min(transfer_amount, reagents.maximum_volume - reagents.total_volume), 0, reagents.maximum_volume)
+ if(ismob(target))//Blood!
+ if(istype(target, /mob/living/carbon))
+ var/mob/living/carbon/T = target
+ if(!T.dna)
+ if(T.reagents.trans_to(src, tramount))
+ activate_pin(2)
+ else
+ activate_pin(3)
+ return
+ if(NOCLONE in T.mutations) //target done been et, no more blood in him
+ if(T.reagents.trans_to(src, tramount))
+ activate_pin(2)
+ else
+ activate_pin(3)
+ return
+ return
+ var/datum/reagent/B
+ B = T.take_blood(src,tramount)
+ if (B)
+ reagents.reagent_list |= B
+ reagents.update_total()
+ on_reagent_change()
+ reagents.handle_reactions()
+ B = null
+ visible_message( "Machine takes a blood sample from [target].")
+ else
+ activate_pin(3)
+ return
+
+ else //if not mob
+ if(!target.reagents.total_volume)
+ visible_message( "[target] is empty.")
+ activate_pin(3)
+ return
+ target.reagents.trans_to(src, tramount)
+ activate_pin(2)
+
+
+
+
+/obj/item/integrated_circuit/reagent/pump
+ name = "reagent pump"
+ desc = "Moves liquids safely inside a machine, or even nearby it."
+ icon_state = "reagent_pump"
+ extended_desc = "This is a pump, which will move liquids from the source ref to the target ref. The third pin determines \
+ how much liquid is moved per pulse, between 0 and 50. The pump can move reagents to any open container inside the machine, or \
+ outside the machine if it is next to the machine. Note that this cannot be used on entities."
+ flags = OPENCONTAINER
+ complexity = 8
+ inputs = list("source" = IC_PINTYPE_REF, "target" = IC_PINTYPE_REF, "injection amount" = IC_PINTYPE_NUMBER)
+ inputs_default = list("3" = 5)
+ outputs = list()
+ activators = list("transfer reagents" = IC_PINTYPE_PULSE_IN, "on transfer" = IC_PINTYPE_PULSE_OUT)
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+ origin_tech = list(TECH_ENGINEERING = 2, TECH_DATA = 2, TECH_BIO = 2)
+ var/transfer_amount = 10
+ var/direc = 1
+ power_draw_per_use = 10
+
+/obj/item/integrated_circuit/reagent/pump/on_data_written()
+ var/new_amount = get_pin_data(IC_INPUT, 3)
+ if(new_amount < 0)
+ new_amount = -new_amount
+ direc = 0
+ else
+ direc = 1
+ if(isnum(new_amount))
+ new_amount = Clamp(new_amount, 0, 50)
+ transfer_amount = new_amount
+
+/obj/item/integrated_circuit/reagent/pump/do_work()
+ var/atom/movable/source = get_pin_data_as_type(IC_INPUT, 1, /atom/movable)
+ var/atom/movable/target = get_pin_data_as_type(IC_INPUT, 2, /atom/movable)
+
+ if(!istype(source) || !istype(target)) //Invalid input
+ return
+ var/turf/T = get_turf(src)
+ var/turf/TS = get_turf(source)
+ var/turf/TT = get_turf(target)
+ if(TS.Adjacent(T) && TT.Adjacent(T))
+ if(!source.reagents || !target.reagents)
+ return
+ if(ismob(source) || ismob(target))
+ return
+ if(!source.is_open_container() || !target.is_open_container())
+ return
+ if(direc)
+ if(!target.reagents.get_free_space())
+ return
+ source.reagents.trans_to(target, transfer_amount)
+ else
+ if(!source.reagents.get_free_space())
+ return
+ target.reagents.trans_to(source, transfer_amount)
+ activate_pin(2)
+
+/obj/item/integrated_circuit/reagent/storage
+ name = "reagent storage"
+ desc = "Stores liquid inside, and away from electrical components. Can store up to 60u."
+ icon_state = "reagent_storage"
+ extended_desc = "This is effectively an internal beaker."
+ flags = OPENCONTAINER
+ complexity = 4
+ inputs = list()
+ outputs = list("volume used" = IC_PINTYPE_NUMBER,"self reference" = IC_PINTYPE_REF)
+ activators = list()
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+ origin_tech = list(TECH_ENGINEERING = 2, TECH_DATA = 2, TECH_BIO = 2)
+ volume = 60
+
+
+/obj/item/integrated_circuit/reagent/storage/interact(mob/user)
+ set_pin_data(IC_OUTPUT, 2, weakref(src))
+ push_data()
+ ..()
+
+/obj/item/integrated_circuit/reagent/storage/on_reagent_change()
+ set_pin_data(IC_OUTPUT, 1, reagents.total_volume)
+ push_data()
+
+/obj/item/integrated_circuit/reagent/storage/cryo
+ name = "cryo reagent storage"
+ desc = "Stores liquid inside, and away from electrical components. Can store up to 60u. This will also suppress reactions."
+ icon_state = "reagent_storage_cryo"
+ extended_desc = "This is effectively an internal cryo beaker."
+ flags = OPENCONTAINER | NOREACT
+ complexity = 8
+ spawn_flags = IC_SPAWN_RESEARCH
+ origin_tech = list(TECH_MATERIAL = 4, TECH_ENGINEERING = 2, TECH_DATA = 2, TECH_BIO = 2)
+
+/obj/item/integrated_circuit/reagent/storage/big
+ name = "big reagent storage"
+ desc = "Stores liquid inside, and away from electrical components. Can store up to 180u."
+ icon_state = "reagent_storage_big"
+ extended_desc = "This is effectively an internal beaker."
+ flags = OPENCONTAINER
+ complexity = 16
+ volume = 180
+ spawn_flags = IC_SPAWN_RESEARCH
+ origin_tech = list(TECH_MATERIAL = 3, TECH_ENGINEERING = 2, TECH_DATA = 2, TECH_BIO = 2)
+
+/obj/item/integrated_circuit/reagent/storage/scan
+ name = "reagent scanner"
+ desc = "Stores liquid inside, and away from electrical components. Can store up to 60u. On pulse this beaker will send list of contained reagents."
+ icon_state = "reagent_scan"
+ extended_desc = "Mostly useful for reagent filter."
+ flags = OPENCONTAINER
+ complexity = 8
+ outputs = list("volume used" = IC_PINTYPE_NUMBER,"self reference" = IC_PINTYPE_REF,"list of reagents" = IC_PINTYPE_LIST)
+ activators = list("scan" = IC_PINTYPE_PULSE_IN)
+ spawn_flags = IC_SPAWN_RESEARCH
+ origin_tech = list(TECH_MATERIAL = 3, TECH_ENGINEERING = 2, TECH_DATA = 2, TECH_BIO = 2)
+
+/obj/item/integrated_circuit/reagent/storage/scan/do_work()
+ var/cont[0]
+ for(var/datum/reagent/RE in reagents.reagent_list)
+ cont += RE.id
+ set_pin_data(IC_OUTPUT, 3, cont)
+ push_data()
+
+
+/obj/item/integrated_circuit/reagent/filter
+ name = "reagent filter"
+ desc = "Filtering liquids by list of desired or unwanted reagents."
+ icon_state = "reagent_filter"
+ extended_desc = "This is a filter which will move liquids from the source ref to the target ref. \
+ It will move all reagents, except list, given in fourth pin if amount value is positive.\
+ Or it will move only desired reagents if amount is negative, The third pin determines \
+ how much reagent is moved per pulse, between 0 and 50. Amount is given for each separate reagent."
+ flags = OPENCONTAINER
+ complexity = 8
+ inputs = list("source" = IC_PINTYPE_REF, "target" = IC_PINTYPE_REF, "injection amount" = IC_PINTYPE_NUMBER, "list of reagents" = IC_PINTYPE_LIST)
+ inputs_default = list("3" = 5)
+ outputs = list()
+ activators = list("transfer reagents" = IC_PINTYPE_PULSE_IN, "on transfer" = IC_PINTYPE_PULSE_OUT)
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+ origin_tech = list(TECH_ENGINEERING = 2, TECH_DATA = 2, TECH_BIO = 2)
+ var/transfer_amount = 10
+ var/direc = 1
+ power_draw_per_use = 10
+
+/obj/item/integrated_circuit/reagent/filter/on_data_written()
+ var/new_amount = get_pin_data(IC_INPUT, 3)
+ if(new_amount < 0)
+ new_amount = -new_amount
+ direc = 0
+ else
+ direc = 1
+ if(isnum(new_amount))
+ new_amount = Clamp(new_amount, 0, 50)
+ transfer_amount = new_amount
+
+/obj/item/integrated_circuit/reagent/filter/do_work()
+ var/atom/movable/source = get_pin_data_as_type(IC_INPUT, 1, /atom/movable)
+ var/atom/movable/target = get_pin_data_as_type(IC_INPUT, 2, /atom/movable)
+ var/list/demand = get_pin_data(IC_INPUT, 4)
+ if(!istype(source) || !istype(target)) //Invalid input
+ return
+ var/turf/T = get_turf(src)
+ if(source.Adjacent(T) && target.Adjacent(T))
+ if(!source.reagents || !target.reagents)
+ return
+ if(ismob(source) || ismob(target))
+ return
+ if(!source.is_open_container() || !target.is_open_container())
+ return
+ if(!target.reagents.get_free_space())
+ return
+ for(var/datum/reagent/G in source.reagents.reagent_list)
+ if (!direc)
+ if(G.id in demand)
+ source.reagents.trans_id_to(target, G.id, transfer_amount)
+ else
+ if(!(G.id in demand))
+ source.reagents.trans_id_to(target, G.id, transfer_amount)
+ activate_pin(2)
+ push_data()
+
+
+
diff --git a/code/modules/integrated_electronics/subtypes/smart.dm b/code/modules/integrated_electronics/subtypes/smart.dm
new file mode 100644
index 0000000000000..0e16cd40794ba
--- /dev/null
+++ b/code/modules/integrated_electronics/subtypes/smart.dm
@@ -0,0 +1,34 @@
+/obj/item/integrated_circuit/smart
+ category_text = "Smart"
+
+/obj/item/integrated_circuit/smart/basic_pathfinder
+ name = "basic pathfinder"
+ desc = "This complex circuit is able to determine what direction a given target is."
+ extended_desc = "This circuit uses a miniturized, integrated camera to determine where the target is. If the machine \
+ cannot see the target, it will not be able to calculate the correct direction."
+ icon_state = "numberpad"
+ complexity = 25
+ inputs = list("target" = IC_PINTYPE_REF)
+ outputs = list("dir" = IC_PINTYPE_DIR)
+ activators = list("calculate dir" = IC_PINTYPE_PULSE_IN, "on calculated" = IC_PINTYPE_PULSE_OUT)
+ spawn_flags = IC_SPAWN_RESEARCH
+ origin_tech = list(TECH_ENGINEERING = 4, TECH_DATA = 5)
+ power_draw_per_use = 40
+
+/obj/item/integrated_circuit/smart/basic_pathfinder/do_work()
+ var/datum/integrated_io/I = inputs[1]
+ set_pin_data(IC_OUTPUT, 1, null)
+
+ if(!isweakref(I.data))
+ return
+ var/atom/A = I.data.resolve()
+ if(!A)
+ return
+ if(!(A in view(get_turf(src))))
+ push_data()
+ return // Can't see the target.
+ var/desired_dir = get_dir(get_turf(src), get_turf(A))
+
+ set_pin_data(IC_OUTPUT, 1, desired_dir)
+ push_data()
+ activate_pin(2)
\ No newline at end of file
diff --git a/code/modules/integrated_electronics/subtypes/time.dm b/code/modules/integrated_electronics/subtypes/time.dm
new file mode 100644
index 0000000000000..e5a943657d06f
--- /dev/null
+++ b/code/modules/integrated_electronics/subtypes/time.dm
@@ -0,0 +1,151 @@
+/obj/item/integrated_circuit/time
+ name = "time circuit"
+ desc = "Now you can build your own clock!"
+ complexity = 2
+ inputs = list()
+ outputs = list()
+ category_text = "Time"
+
+/obj/item/integrated_circuit/time/delay
+ name = "two-sec delay circuit"
+ desc = "This sends a pulse signal out after a delay, critical for ensuring proper control flow in a complex machine. \
+ This circuit is set to send a pulse after a delay of two seconds."
+ icon_state = "delay-20"
+ var/delay = 2 SECONDS
+ activators = list("incoming"= IC_PINTYPE_PULSE_IN,"outgoing" = IC_PINTYPE_PULSE_OUT)
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+ power_draw_per_use = 2
+
+/obj/item/integrated_circuit/time/delay/do_work()
+ set waitfor = 0 // Don't sleep in a proc that is called by a processor. It'll delay the entire thing
+
+ sleep(delay)
+ activate_pin(2)
+
+/obj/item/integrated_circuit/time/delay/five_sec
+ name = "five-sec delay circuit"
+ desc = "This sends a pulse signal out after a delay, critical for ensuring proper control flow in a complex machine. \
+ This circuit is set to send a pulse after a delay of five seconds."
+ icon_state = "delay-50"
+ delay = 5 SECONDS
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+
+/obj/item/integrated_circuit/time/delay/one_sec
+ name = "one-sec delay circuit"
+ desc = "This sends a pulse signal out after a delay, critical for ensuring proper control flow in a complex machine. \
+ This circuit is set to send a pulse after a delay of one second."
+ icon_state = "delay-10"
+ delay = 1 SECONDS
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+
+/obj/item/integrated_circuit/time/delay/half_sec
+ name = "half-sec delay circuit"
+ desc = "This sends a pulse signal out after a delay, critical for ensuring proper control flow in a complex machine. \
+ This circuit is set to send a pulse after a delay of half a second."
+ icon_state = "delay-5"
+ delay = 5
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+
+/obj/item/integrated_circuit/time/delay/tenth_sec
+ name = "tenth-sec delay circuit"
+ desc = "This sends a pulse signal out after a delay, critical for ensuring proper control flow in a complex machine. \
+ This circuit is set to send a pulse after a delay of 1/10th of a second."
+ icon_state = "delay-1"
+ delay = 1
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+
+/obj/item/integrated_circuit/time/delay/custom
+ name = "custom delay circuit"
+ desc = "This sends a pulse signal out after a delay, critical for ensuring proper control flow in a complex machine. \
+ This circuit's delay can be customized, between 1/10th of a second to one hour. The delay is updated upon receiving a pulse."
+ icon_state = "delay"
+ inputs = list("delay time" = IC_PINTYPE_NUMBER)
+ spawn_flags = IC_SPAWN_RESEARCH
+
+/obj/item/integrated_circuit/time/delay/custom/do_work()
+ var/delay_input = get_pin_data(IC_INPUT, 1)
+ if(delay_input && isnum(delay_input) )
+ var/new_delay = Clamp(delay_input, 0, 36000) //An hour.
+ delay = new_delay
+
+ ..()
+
+/obj/item/integrated_circuit/time/ticker
+ name = "ticker circuit"
+ desc = "This circuit sends an automatic pulse every four seconds."
+ icon_state = "tick-m"
+ complexity = 8
+ var/ticks_to_pulse = 4
+ var/ticks_completed = 0
+ var/is_running = FALSE
+ inputs = list("enable ticking" = IC_PINTYPE_BOOLEAN)
+ activators = list("outgoing pulse" = IC_PINTYPE_PULSE_OUT)
+ spawn_flags = IC_SPAWN_RESEARCH
+ power_draw_per_use = 4
+
+/obj/item/integrated_circuit/time/ticker/Destroy()
+ if(is_running)
+ SSobj.processing -= src
+ . = ..()
+
+/obj/item/integrated_circuit/time/ticker/on_data_written()
+ var/do_tick = get_pin_data(IC_INPUT, 1)
+ if(do_tick && !is_running)
+ is_running = TRUE
+ SSobj.processing |= src
+ else if(is_running)
+ is_running = FALSE
+ SSobj.processing -= src
+ ticks_completed = 0
+
+/obj/item/integrated_circuit/time/ticker/process()
+ var/process_ticks = SSobj.priority //process_schedule_interval("obj")
+ ticks_completed += process_ticks
+ if(ticks_completed >= ticks_to_pulse)
+ if(ticks_to_pulse >= process_ticks)
+ ticks_completed -= ticks_to_pulse
+ else
+ ticks_completed = 0
+ activate_pin(1)
+
+/obj/item/integrated_circuit/time/ticker/fast
+ name = "fast ticker"
+ desc = "This advanced circuit sends an automatic pulse every two seconds."
+ icon_state = "tick-f"
+ complexity = 12
+ ticks_to_pulse = 2
+ spawn_flags = IC_SPAWN_RESEARCH
+ power_draw_per_use = 8
+
+/obj/item/integrated_circuit/time/ticker/slow
+ name = "slow ticker"
+ desc = "This simple circuit sends an automatic pulse every six seconds."
+ icon_state = "tick-s"
+ complexity = 4
+ ticks_to_pulse = 6
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+ power_draw_per_use = 2
+
+/obj/item/integrated_circuit/time/clock
+ name = "integrated clock"
+ desc = "Tells you what the local time is, specific to your station or planet."
+ icon_state = "clock"
+ inputs = list()
+ outputs = list(
+ "time" = IC_PINTYPE_STRING,
+ "hours" = IC_PINTYPE_NUMBER,
+ "minutes" = IC_PINTYPE_NUMBER,
+ "seconds" = IC_PINTYPE_NUMBER
+ )
+ activators = list("get time" = IC_PINTYPE_PULSE_IN, "on time got" = IC_PINTYPE_PULSE_OUT)
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+ power_draw_per_use = 4
+
+/obj/item/integrated_circuit/time/clock/do_work()
+ set_pin_data(IC_OUTPUT, 1, time2text(world.time, "hh:mm:ss") )
+ set_pin_data(IC_OUTPUT, 2, text2num(time2text(world.time, "hh") ) )
+ set_pin_data(IC_OUTPUT, 3, text2num(time2text(world.time, "mm") ) )
+ set_pin_data(IC_OUTPUT, 4, text2num(time2text(world.time, "ss") ) )
+
+ push_data()
+ activate_pin(2)
\ No newline at end of file
diff --git a/code/modules/integrated_electronics/subtypes/trig.dm b/code/modules/integrated_electronics/subtypes/trig.dm
new file mode 100644
index 0000000000000..daadf52d42960
--- /dev/null
+++ b/code/modules/integrated_electronics/subtypes/trig.dm
@@ -0,0 +1,140 @@
+//These circuits do not-so-simple math.
+/obj/item/integrated_circuit/trig
+ complexity = 1
+ inputs = list(
+ "A" = IC_PINTYPE_NUMBER,
+ "B" = IC_PINTYPE_NUMBER,
+ "C" = IC_PINTYPE_NUMBER,
+ "D" = IC_PINTYPE_NUMBER,
+ "E" = IC_PINTYPE_NUMBER,
+ "F" = IC_PINTYPE_NUMBER,
+ "G" = IC_PINTYPE_NUMBER,
+ "H" = IC_PINTYPE_NUMBER
+ )
+ outputs = list("result" = IC_PINTYPE_NUMBER)
+ activators = list("compute" = IC_PINTYPE_PULSE_IN, "on computed" = IC_PINTYPE_PULSE_OUT)
+ category_text = "Trig"
+ extended_desc = "Input and output are in degrees."
+ power_draw_per_use = 1 // Still cheap math.
+
+// Sine //
+
+/obj/item/integrated_circuit/trig/sine
+ name = "sin circuit"
+ desc = "Has nothing to do with evil, unless you consider trigonometry to be evil. Outputs the sine of A."
+ icon_state = "sine"
+ inputs = list("A" = IC_PINTYPE_NUMBER)
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+
+/obj/item/integrated_circuit/trig/sine/do_work()
+ pull_data()
+ var/result = null
+ var/A = get_pin_data(IC_INPUT, 1)
+ if(!isnull(A))
+ result = sin(A)
+
+ set_pin_data(IC_OUTPUT, 1, result)
+ push_data()
+ activate_pin(2)
+
+// Cosine //
+
+/obj/item/integrated_circuit/trig/cosine
+ name = "cos circuit"
+ desc = "Outputs the cosine of A."
+ icon_state = "cosine"
+ inputs = list("A" = IC_PINTYPE_NUMBER)
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+
+/obj/item/integrated_circuit/trig/cosine/do_work()
+ pull_data()
+ var/result = null
+ var/A = get_pin_data(IC_INPUT, 1)
+ if(!isnull(A))
+ result = cos(A)
+
+ set_pin_data(IC_OUTPUT, 1, result)
+ push_data()
+ activate_pin(2)
+
+// Tangent //
+
+/obj/item/integrated_circuit/trig/tangent
+ name = "tan circuit"
+ desc = "Outputs the tangent of A. Guaranteed to not go on a tangent about its existance."
+ icon_state = "tangent"
+ inputs = list("A" = IC_PINTYPE_NUMBER)
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+
+/obj/item/integrated_circuit/trig/tangent/do_work()
+ pull_data()
+ var/result = null
+ var/A = get_pin_data(IC_INPUT, 1)
+ if(!isnull(A))
+ result = Tan(A)
+
+ set_pin_data(IC_OUTPUT, 1, result)
+ push_data()
+ activate_pin(2)
+
+// Cosecant //
+
+/obj/item/integrated_circuit/trig/cosecant
+ name = "csc circuit"
+ desc = "Outputs the cosecant of A."
+ icon_state = "cosecant"
+ inputs = list("A" = IC_PINTYPE_NUMBER)
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+
+/obj/item/integrated_circuit/trig/cosecant/do_work()
+ pull_data()
+ var/result = null
+ var/A = get_pin_data(IC_INPUT, 1)
+ if(!isnull(A))
+ result = Csc(A)
+
+ set_pin_data(IC_OUTPUT, 1, result)
+ push_data()
+ activate_pin(2)
+
+
+// Secant //
+
+/obj/item/integrated_circuit/trig/secant
+ name = "sec circuit"
+ desc = "Outputs the secant of A. Has nothing to do with the security department."
+ icon_state = "secant"
+ inputs = list("A" = IC_PINTYPE_NUMBER)
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+
+/obj/item/integrated_circuit/trig/secant/do_work()
+ pull_data()
+ var/result = null
+ var/A = get_pin_data(IC_INPUT, 1)
+ if(!isnull(A))
+ result = Sec(A)
+
+ set_pin_data(IC_OUTPUT, 1, result)
+ push_data()
+ activate_pin(2)
+
+
+// Cotangent //
+
+/obj/item/integrated_circuit/trig/cotangent
+ name = "cot circuit"
+ desc = "Outputs the cotangent of A."
+ icon_state = "cotangent"
+ inputs = list("A" = IC_PINTYPE_NUMBER)
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+
+/obj/item/integrated_circuit/trig/cotangent/do_work()
+ pull_data()
+ var/result = null
+ var/A = get_pin_data(IC_INPUT, 1)
+ if(!isnull(A))
+ result = Cot(A)
+
+ set_pin_data(IC_OUTPUT, 1, result)
+ push_data()
+ activate_pin(2)
\ No newline at end of file
diff --git a/code/modules/integrated_electronics/~defines/~defines.dm b/code/modules/integrated_electronics/~defines/~defines.dm
new file mode 100644
index 0000000000000..90aa05acf1938
--- /dev/null
+++ b/code/modules/integrated_electronics/~defines/~defines.dm
@@ -0,0 +1,36 @@
+#undef IC_INPUT
+#undef IC_OUTPUT
+#undef IC_ACTIVATOR
+
+#undef DATA_CHANNEL
+#undef PULSE_CHANNEL
+
+#undef IC_SPAWN_DEFAULT
+//#undef IC_SPAWN_RESEARCH // Research designs depend on this unfortunately.
+
+#undef IC_FORMAT_ANY
+#undef IC_FORMAT_STRING
+#undef IC_FORMAT_CHAR
+#undef IC_FORMAT_COLOR
+#undef IC_FORMAT_NUMBER
+#undef IC_FORMAT_DIR
+#undef IC_FORMAT_BOOLEAN
+#undef IC_FORMAT_REF
+#undef IC_FORMAT_LIST
+
+#undef IC_FORMAT_PULSE
+
+#undef IC_PINTYPE_ANY
+#undef IC_PINTYPE_STRING
+#undef IC_PINTYPE_CHAR
+#undef IC_PINTYPE_COLOR
+#undef IC_PINTYPE_NUMBER
+#undef IC_PINTYPE_DIR
+#undef IC_PINTYPE_BOOLEAN
+#undef IC_PINTYPE_REF
+#undef IC_PINTYPE_LIST
+
+#undef IC_PINTYPE_PULSE_IN
+#undef IC_PINTYPE_PULSE_OUT
+
+#undef IC_MAX_LIST_LENGTH
\ No newline at end of file
diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm
index c3d4115924cf9..f5e71cee29ca5 100644
--- a/code/modules/projectiles/gun.dm
+++ b/code/modules/projectiles/gun.dm
@@ -230,6 +230,78 @@
user.update_inv_r_hand(0)
return fired
+// Similar to the above proc, but does not require a user, which is ideal for things like turrets.
+/obj/item/weapon/gun/proc/Fire_userless(atom/target)
+ if(!target)
+ return
+
+ if(world.time < semicd)
+ return
+
+ var/shoot_time = (burst_size - 1)* fire_delay
+ semicd = world.time + shoot_time
+
+ var/turf/targloc = get_turf(target) //cache this in case target gets deleted during shooting, e.g. if it was a securitron that got destroyed.
+ for(var/i in 1 to burst_size)
+ var/obj/projectile = process_chamber()
+ if(!projectile)
+ src.visible_message("*click*")
+ //handle_click_empty()
+ break
+
+ if(istype(projectile, /obj/item/projectile))
+ var/obj/item/projectile/P = projectile
+
+ //Not supported, maybe later
+ /*
+ var/acc = burst_accuracy[min(i, burst_accuracy.len)]
+ var/disp = dispersion[min(i, dispersion.len)]
+
+ P.accuracy = accuracy + acc
+ P.dispersion = disp
+ */
+ P.firer = src.name
+ P.suppressed = suppressed
+
+ P.original = target
+ P.fire()
+
+ if(suppressed)
+ playsound(src, fire_sound, 10, 1)
+ else
+ playsound(src, fire_sound, 50, 1)
+
+ //if(muzzle_flash) //Not supported, maybe later
+ //set_light(muzzle_flash)
+ update_icon()
+
+ //process_accuracy(projectile, user, target, acc, disp)
+
+ // if(pointblank)
+ // process_point_blank(projectile, user, target)
+
+ // if(process_projectile(projectile, null, target, user.zone_sel.selecting, clickparams))
+ // handle_post_fire(null, target, pointblank, reflex)
+
+ // update_icon()
+
+ if(i < burst_size)
+ sleep(fire_delay)
+
+ if(!(target && target.loc))
+ target = targloc
+ //pointblank = 0
+
+ //log_and_message_admins("Fired [src].")
+
+ //admin_attack_log(usr, attacker_message="Fired [src]", admin_message="fired a gun ([src]) (MODE: [src.mode_name]) [reflex ? "by reflex" : "manually"].")
+
+ //update timing
+ semicd = world.time + fire_delay
+
+ //if(muzzle_flash)
+ //set_light(0)
+
/obj/item/weapon/gun/attack(mob/M as mob, mob/user)
if (M == user && user.zone_sel.selecting == "mouth" && !mouthshoot && ishuman(user)) //Handle gun suicide
diff --git a/code/modules/reagents/Chemistry-Reagents-Helpers.dm b/code/modules/reagents/Chemistry-Reagents-Helpers.dm
new file mode 100644
index 0000000000000..917d9f4649cc8
--- /dev/null
+++ b/code/modules/reagents/Chemistry-Reagents-Helpers.dm
@@ -0,0 +1,17 @@
+/atom/movable/proc/can_be_injected_by(var/atom/injector)
+ if(!Adjacent(get_turf(injector)))
+ return FALSE
+ if(!reagents)
+ return FALSE
+ if(!reagents.get_free_space())
+ return FALSE
+ return TRUE
+
+/obj/can_be_injected_by(var/atom/injector)
+ return is_open_container() && ..()
+
+/mob/living/can_be_injected_by(var/atom/injector)
+ return ..() && (can_inject(null, 0, "torso") || can_inject(null, 0, "groin"))
+
+/datum/reagents/proc/get_free_space() // Returns free space.
+ return maximum_volume - total_volume
\ No newline at end of file
diff --git a/code/modules/research/designs.dm b/code/modules/research/designs.dm
index dff48a32bea2c..c13d7b0c3600a 100644
--- a/code/modules/research/designs.dm
+++ b/code/modules/research/designs.dm
@@ -32,6 +32,7 @@ other types of metals and chemistry for reagents).
/datum/design //Datum for object designs, used in construction
var/name = "Name" //Name of the created object.
var/desc = "Desc" //Description of the created object.
+ var/item_name = null //An item name before it is modified by various name-modifying procs
var/id = "id" //ID of the created object for easy refernece. Alphanumeric, lower-case, no symbols
var/list/req_tech = list() //IDs of that techs the object originated from and the minimum level requirements. //Reliability modifier of the device at it's starting point.
var/reliability = 100 //Reliability of the device.
@@ -41,6 +42,33 @@ other types of metals and chemistry for reagents).
var/build_path = "" //The file path of the object that gets created
var/list/category = null //Primarily used for Mech Fabricators, but can be used for anything
+/datum/design/New()
+ ..()
+ item_name = name
+ AssembleDesignInfo()
+
+//These procs are used in subtypes for assigning names and descriptions dynamically
+/datum/design/proc/AssembleDesignInfo()
+ AssembleDesignName()
+ AssembleDesignDesc()
+ return
+
+/datum/design/proc/AssembleDesignName()
+ if(!name && build_path) //Get name from build path if posible
+ var/atom/movable/A = build_path
+ name = initial(A.name)
+ item_name = name
+ return
+
+/datum/design/proc/AssembleDesignDesc()
+ if(!desc) //Try to make up a nice description if we don't have one
+ desc = "Allows for the construction of \a [item_name]."
+ return
+
+//Returns a new instance of the item for this design
+//This is to allow additional initialization to be performed, including possibly additional contructor arguments.
+/datum/design/proc/Fabricate(var/newloc, var/fabricator)
+ return new build_path(newloc)
//A proc to calculate the reliability of a design based on tech levels and innate modifiers.
//Input: A list of /datum/tech; Output: The new reliabilty.
@@ -418,4 +446,66 @@ other types of metals and chemistry for reagents).
build_type = PROTOLATHE
materials = list(MAT_METAL = 1000, MAT_GLASS = 500, MAT_PLASMA = 1500, MAT_URANIUM = 200)
build_path = /obj/item/weapon/weldingtool/experimental
- category = list("Equipment") //fuck you tg
\ No newline at end of file
+ category = list("Equipment") //fuck you tg
+
+
+/////////////////////////////////////////
+//////Integrated Circuits////////////////
+/////////////////////////////////////////
+
+/datum/design/item/wirer
+ name = "Custom wirer tool"
+ id = "wirer"
+ req_tech = list(TECH_MATERIAL = 2, TECH_ENGINEERING = 2)
+ materials = list(MAT_METAL = 500, MAT_GLASS = 250)
+ build_type = PROTOLATHE
+ build_path = /obj/item/device/integrated_electronics/wirer
+ category = list("Electronics")
+
+/datum/design/item/debugger
+ name = "Custom circuit debugger tool"
+ id = "debugger"
+ req_tech = list(TECH_MATERIAL = 2, TECH_ENGINEERING = 2)
+ materials = list(MAT_METAL = 500, MAT_GLASS = 250)
+ build_type = PROTOLATHE
+ build_path = /obj/item/device/integrated_electronics/debugger
+ category = list("Electronics")
+
+/datum/design/circuit/integrated_circuit/AssembleDesignName()
+ ..()
+ name = "Custom circuitry ([item_name])"
+
+/datum/design/circuit/integrated_circuit/AssembleDesignDesc()
+ if(!desc)
+ desc = "Allows for the construction of \a [name] custom circuit."
+
+/datum/design/circuit/integrated_circuit/arithmetic/AssembleDesignName()
+ ..()
+ name = "Custom circuitry \[Arithmetic\] ([item_name])"
+
+/datum/design/item/custom_circuit_printer
+ name = "Portable integrated circuit printer"
+ desc = "A portable(ish) printer for modular machines."
+ id = "ic_printer"
+ req_tech = list(TECH_MATERIAL = 2, TECH_ENGINEERING = 2, TECH_DATA = 2)
+ materials = list(MAT_METAL = 10000)
+ build_type = PROTOLATHE
+ build_path = /obj/item/device/integrated_circuit_printer
+ category = list("Electronics")
+
+/datum/design/item/custom_circuit_printer_upgrade
+ name = "Integrated circuit printer upgrade - advanced designs"
+ desc = "Allows the integrated circuit printer to create advanced circuits"
+ id = "ic_printer_upgrade_adv"
+ req_tech = list(TECH_ENGINEERING = 3, TECH_DATA = 4)
+ materials = list(MAT_METAL = 2000)
+ build_type = PROTOLATHE
+ build_path = /obj/item/weapon/disk/integrated_circuit/upgrade/advanced
+ category = list("Electronics")
+
+
+/datum/design/circuit/tcom/exonet_node
+ name = "exonet node"
+ id = "tcom-exonet_node"
+ req_tech = list(TECH_DATA = 5, TECH_ENGINEERING = 5, TECH_BLUESPACE = 4)
+ build_path = /obj/item/weapon/circuitboard/telecomms/exonet_node
diff --git a/code/world.dm b/code/world.dm
index c94d8b833310c..f890481862460 100644
--- a/code/world.dm
+++ b/code/world.dm
@@ -31,7 +31,8 @@
changelog_hash = md5('html/changelog.html') //used for telling if the changelog has changed recently
make_datum_references_lists() //initialises global lists for referencing frequently used datums (so that we only ever do it once)
-
+ initialize_integrated_circuits_list()
+
load_configuration()
load_mode()
load_motd()
diff --git a/icons/obj/assemblies/electronic_setups.dmi b/icons/obj/assemblies/electronic_setups.dmi
new file mode 100644
index 0000000000000..f18018c0aec04
Binary files /dev/null and b/icons/obj/assemblies/electronic_setups.dmi differ
diff --git a/icons/obj/assemblies/electronic_tools.dmi b/icons/obj/assemblies/electronic_tools.dmi
new file mode 100644
index 0000000000000..975f2fc9a8128
Binary files /dev/null and b/icons/obj/assemblies/electronic_tools.dmi differ
diff --git a/icons/obj/electronic_assemblies.dmi b/icons/obj/electronic_assemblies.dmi
new file mode 100644
index 0000000000000..5799d85866fd8
Binary files /dev/null and b/icons/obj/electronic_assemblies.dmi differ
diff --git a/icons/obj/library.dmi b/icons/obj/library.dmi
index b416d54b9ef0c..10780cd356aa5 100644
Binary files a/icons/obj/library.dmi and b/icons/obj/library.dmi differ
diff --git a/icons/obj/stationobjs.dmi b/icons/obj/stationobjs.dmi
index f9a90f670fd56..c20a6d3113d42 100644
Binary files a/icons/obj/stationobjs.dmi and b/icons/obj/stationobjs.dmi differ
diff --git a/nano/templates/exonet_node.tmpl b/nano/templates/exonet_node.tmpl
new file mode 100644
index 0000000000000..f9d81d4e92f98
--- /dev/null
+++ b/nano/templates/exonet_node.tmpl
@@ -0,0 +1,40 @@
+
+
+Status
+
+
+ Power:
+
+
+ {{:helper.link('On', 'power', {'toggle_power' : 1}, data.on ? 'selected' : null)}}{{:helper.link('Off', 'close', {'toggle_power' : 1}, data.on ? null : 'selected', data.on ? 'redButton' : null)}}
+
+
+
+Ports
+
+
+ Incoming PDA Messages:
+
+
+ {{:helper.link('Open', 'check', {'toggle_PDA_port' : 1}, data.allowPDAs ? 'selected' : null)}}{{:helper.link('Close', 'close', {'toggle_PDA_port' : 1}, data.allowPDAs ? null : 'selected')}}
+
+
+
+
+ Incoming Communicators:
+
+
+ {{:helper.link('Open', 'check', {'toggle_communicator_port' : 1}, data.allowCommunicators ? 'selected' : null)}}{{:helper.link('Close', 'close', {'toggle_communicator_port' : 1}, data.allowCommunicators ? null : 'selected')}}
+
+
+
+
+ Incoming Newscaster Content:
+
+
+ {{:helper.link('Open', 'check', {'toggle_newscaster_port' : 1}, data.allowNewscasters ? 'selected' : null)}}{{:helper.link('Close', 'close', {'toggle_newscaster_port' : 1}, data.allowNewscasters ? null : 'selected')}}
+
+
diff --git a/sound/voice/medbot/mapple.ogg b/sound/voice/medbot/mapple.ogg
new file mode 100644
index 0000000000000..21e26742ca135
Binary files /dev/null and b/sound/voice/medbot/mapple.ogg differ
diff --git a/sound/voice/medbot/mcatch.ogg b/sound/voice/medbot/mcatch.ogg
new file mode 100644
index 0000000000000..07b8aaab75c46
Binary files /dev/null and b/sound/voice/medbot/mcatch.ogg differ
diff --git a/sound/voice/medbot/mcoming.ogg b/sound/voice/medbot/mcoming.ogg
new file mode 100644
index 0000000000000..d3eb9e467f4b3
Binary files /dev/null and b/sound/voice/medbot/mcoming.ogg differ
diff --git a/sound/voice/medbot/mdelicious.ogg b/sound/voice/medbot/mdelicious.ogg
new file mode 100644
index 0000000000000..5158538580edf
Binary files /dev/null and b/sound/voice/medbot/mdelicious.ogg differ
diff --git a/sound/voice/medbot/mfeelbetter.ogg b/sound/voice/medbot/mfeelbetter.ogg
new file mode 100644
index 0000000000000..fdbe57fd2e94a
Binary files /dev/null and b/sound/voice/medbot/mfeelbetter.ogg differ
diff --git a/sound/voice/medbot/mflies.ogg b/sound/voice/medbot/mflies.ogg
new file mode 100644
index 0000000000000..831281ebbee0f
Binary files /dev/null and b/sound/voice/medbot/mflies.ogg differ
diff --git a/sound/voice/medbot/mhelp.ogg b/sound/voice/medbot/mhelp.ogg
new file mode 100644
index 0000000000000..516d5db068d22
Binary files /dev/null and b/sound/voice/medbot/mhelp.ogg differ
diff --git a/sound/voice/medbot/minjured.ogg b/sound/voice/medbot/minjured.ogg
new file mode 100644
index 0000000000000..0e968b3980c76
Binary files /dev/null and b/sound/voice/medbot/minjured.ogg differ
diff --git a/sound/voice/medbot/minsult.ogg b/sound/voice/medbot/minsult.ogg
new file mode 100644
index 0000000000000..017292977a137
Binary files /dev/null and b/sound/voice/medbot/minsult.ogg differ
diff --git a/sound/voice/medbot/mlive.ogg b/sound/voice/medbot/mlive.ogg
new file mode 100644
index 0000000000000..ceb0dec9a348a
Binary files /dev/null and b/sound/voice/medbot/mlive.ogg differ
diff --git a/sound/voice/medbot/mlost.ogg b/sound/voice/medbot/mlost.ogg
new file mode 100644
index 0000000000000..7b332ac346af9
Binary files /dev/null and b/sound/voice/medbot/mlost.ogg differ
diff --git a/sound/voice/medbot/mno.ogg b/sound/voice/medbot/mno.ogg
new file mode 100644
index 0000000000000..030e43a010937
Binary files /dev/null and b/sound/voice/medbot/mno.ogg differ
diff --git a/sound/voice/medbot/mpatchedup.ogg b/sound/voice/medbot/mpatchedup.ogg
new file mode 100644
index 0000000000000..1314f6ee47188
Binary files /dev/null and b/sound/voice/medbot/mpatchedup.ogg differ
diff --git a/sound/voice/medbot/mradar.ogg b/sound/voice/medbot/mradar.ogg
new file mode 100644
index 0000000000000..ad347a6d891c0
Binary files /dev/null and b/sound/voice/medbot/mradar.ogg differ
diff --git a/sound/voice/medbot/msurgeon.ogg b/sound/voice/medbot/msurgeon.ogg
new file mode 100644
index 0000000000000..a300ee57ef6db
Binary files /dev/null and b/sound/voice/medbot/msurgeon.ogg differ
diff --git a/tgstation.dme b/tgstation.dme
index 5359ed0bdcb6e..238666d94447d 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -15,6 +15,7 @@
// BEGIN_INCLUDE
#include "_maps\metastation.dm"
#include "code\_compile_options.dm"
+#include "code\_macros.dm"
#include "code\hub.dm"
#include "code\world.dm"
#include "code\__DATASTRUCTURES\heap.dm"
@@ -166,6 +167,7 @@
#include "code\datums\computerfiles.dm"
#include "code\datums\datacore.dm"
#include "code\datums\datumvars.dm"
+#include "code\datums\EPv2.dm"
#include "code\datums\gas_mixture.dm"
#include "code\datums\hud.dm"
#include "code\datums\martial.dm"
@@ -178,6 +180,7 @@
#include "code\datums\spell.dm"
#include "code\datums\supplypacks.dm"
#include "code\datums\uplink_item.dm"
+#include "code\datums\weakref.dm"
#include "code\datums\diseases\_disease.dm"
#include "code\datums\diseases\_MobProcs.dm"
#include "code\datums\diseases\anxiety.dm"
@@ -422,6 +425,7 @@
#include "code\game\machinery\deployable.dm"
#include "code\game\machinery\door_control.dm"
#include "code\game\machinery\doppler_array.dm"
+#include "code\game\machinery\exonet_node.dm"
#include "code\game\machinery\flasher.dm"
#include "code\game\machinery\hologram.dm"
#include "code\game\machinery\igniter.dm"
@@ -720,6 +724,7 @@
#include "code\game\objects\items\weapons\implants\implant_storage.dm"
#include "code\game\objects\items\weapons\implants\implantcase.dm"
#include "code\game\objects\items\weapons\implants\implantchair.dm"
+#include "code\game\objects\items\weapons\implants\implantcircuits.dm"
#include "code\game\objects\items\weapons\implants\implanter.dm"
#include "code\game\objects\items\weapons\implants\implantpad.dm"
#include "code\game\objects\items\weapons\implants\implantuplink.dm"
@@ -1132,6 +1137,42 @@
#include "code\modules\hydroponics\grown\tobacco.dm"
#include "code\modules\hydroponics\grown\tomato.dm"
#include "code\modules\hydroponics\grown\towercap.dm"
+#include "code\modules\integrated_electronics\_defines.dm"
+#include "code\modules\integrated_electronics\coordinate.dm"
+#include "code\modules\integrated_electronics\core\assemblies.dm"
+#include "code\modules\integrated_electronics\core\detailer.dm"
+#include "code\modules\integrated_electronics\core\device.dm"
+#include "code\modules\integrated_electronics\core\helpers.dm"
+#include "code\modules\integrated_electronics\core\integrated_circuit.dm"
+#include "code\modules\integrated_electronics\core\pins.dm"
+#include "code\modules\integrated_electronics\core\printer.dm"
+#include "code\modules\integrated_electronics\core\tools.dm"
+#include "code\modules\integrated_electronics\core\special_pins\boolean_pin.dm"
+#include "code\modules\integrated_electronics\core\special_pins\char_pin.dm"
+#include "code\modules\integrated_electronics\core\special_pins\color_pin.dm"
+#include "code\modules\integrated_electronics\core\special_pins\dir_pin.dm"
+#include "code\modules\integrated_electronics\core\special_pins\list_pin.dm"
+#include "code\modules\integrated_electronics\core\special_pins\number_pin.dm"
+#include "code\modules\integrated_electronics\core\special_pins\ref_pin.dm"
+#include "code\modules\integrated_electronics\core\special_pins\string_pin.dm"
+#include "code\modules\integrated_electronics\passive\passive.dm"
+#include "code\modules\integrated_electronics\passive\power.dm"
+#include "code\modules\integrated_electronics\subtypes\arithmetic.dm"
+#include "code\modules\integrated_electronics\subtypes\built_in.dm"
+#include "code\modules\integrated_electronics\subtypes\converters.dm"
+#include "code\modules\integrated_electronics\subtypes\data_transfer.dm"
+#include "code\modules\integrated_electronics\subtypes\input.dm"
+#include "code\modules\integrated_electronics\subtypes\lists.dm"
+#include "code\modules\integrated_electronics\subtypes\logic.dm"
+#include "code\modules\integrated_electronics\subtypes\manipulation.dm"
+#include "code\modules\integrated_electronics\subtypes\memory.dm"
+#include "code\modules\integrated_electronics\subtypes\output.dm"
+#include "code\modules\integrated_electronics\subtypes\power.dm"
+#include "code\modules\integrated_electronics\subtypes\reagents.dm"
+#include "code\modules\integrated_electronics\subtypes\smart.dm"
+#include "code\modules\integrated_electronics\subtypes\time.dm"
+#include "code\modules\integrated_electronics\subtypes\trig.dm"
+#include "code\modules\integrated_electronics\~defines\~defines.dm"
#include "code\modules\library\lib_items.dm"
#include "code\modules\library\lib_machines.dm"
#include "code\modules\library\lib_readme.dm"
@@ -1520,6 +1561,7 @@
#include "code\modules\reagents\Chemistry-Holder.dm"
#include "code\modules\reagents\Chemistry-Machinery.dm"
#include "code\modules\reagents\Chemistry-Readme.dm"
+#include "code\modules\reagents\Chemistry-Reagents-Helpers.dm"
#include "code\modules\reagents\Chemistry-Reagents.dm"
#include "code\modules\reagents\Chemistry-Recipes.dm"
#include "code\modules\reagents\reagent_containers.dm"