diff --git a/code/modules/integrated_electronics/core/assemblies.dm b/code/modules/integrated_electronics/core/assemblies.dm
index a7ed8e7c11..135461a12f 100644
--- a/code/modules/integrated_electronics/core/assemblies.dm
+++ b/code/modules/integrated_electronics/core/assemblies.dm
@@ -122,9 +122,8 @@
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)
+ battery.forceMove(drop_location())
+ playsound(src, 'sound/items/Crowbar.ogg', 50, 1)
to_chat(usr, "You pull \the [battery] out of \the [src]'s power supplier.")
battery = null
@@ -393,6 +392,17 @@
return src
+// Returns the location to be used for dropping items.
+// Same as the regular drop_location(), but with checks being run on acting_object if necessary.
+/obj/item/integrated_circuit/drop_location()
+ var/atom/movable/acting_object = get_object()
+
+ // plz no infinite loops
+ if(acting_object == src)
+ return ..()
+
+ return acting_object.drop_location()
+
/obj/item/device/electronic_assembly/medium
diff --git a/code/modules/integrated_electronics/core/integrated_circuit.dm b/code/modules/integrated_electronics/core/integrated_circuit.dm
index 748acfa5e4..069ca4245c 100644
--- a/code/modules/integrated_electronics/core/integrated_circuit.dm
+++ b/code/modules/integrated_electronics/core/integrated_circuit.dm
@@ -330,3 +330,43 @@ a creative player the means to solve many problems. Circuits are held inside an
return assembly.get_object()
else
return src // If not, the component is acting on its own.
+
+
+// Returns the location to be used for dropping items.
+// Same as the regular drop_location(), but with proc being run on assembly if there is any.
+/obj/item/integrated_circuit/drop_location()
+ // If the component is located in an assembly, let the assembly figure that one out.
+ if(assembly)
+ return assembly.drop_location()
+ else
+ return ..() // If not, the component is acting on its own.
+
+
+// Checks if the target object is reachable. Useful for various manipulators and manipulator-like objects.
+/obj/item/integrated_circuit/proc/check_target(atom/target, exclude_contents = FALSE, exclude_components = FALSE, exclude_self = FALSE)
+ if(!target)
+ return FALSE
+
+ var/atom/movable/acting_object = get_object()
+
+ if(exclude_self && target == acting_object)
+ return FALSE
+
+ if(exclude_components && assembly)
+ if(target in assembly.assembly_components)
+ return FALSE
+
+ if(target == assembly.battery)
+ return FALSE
+
+ if(target.Adjacent(acting_object) && isturf(target.loc))
+ return TRUE
+
+ if(!exclude_contents && (target in acting_object.GetAllContents()))
+ return TRUE
+
+ if(target in acting_object.loc)
+ return TRUE
+
+ return FALSE
+
diff --git a/code/modules/integrated_electronics/subtypes/manipulation.dm b/code/modules/integrated_electronics/subtypes/manipulation.dm
index 62f15d927b..cd43cfae19 100644
--- a/code/modules/integrated_electronics/subtypes/manipulation.dm
+++ b/code/modules/integrated_electronics/subtypes/manipulation.dm
@@ -309,7 +309,7 @@
var/mode = get_pin_data(IC_INPUT, 2)
if(mode == 1)
- if(AM.Adjacent(acting_object) && isturf(AM.loc))
+ if(check_target(AM, exclude_contents = TRUE))
if((contents.len < max_items) && (!max_w_class || AM.w_class <= max_w_class))
AM.forceMove(src)
if(mode == 0)
@@ -378,26 +378,20 @@
if(max_w_class && (A.w_class > max_w_class))
return
- var/atom/movable/acting_object = get_object()
- if(!(A.Adjacent(acting_object) && isturf(A.loc)) && !(A in acting_object.GetAllContents()))
+ // Is the target inside the assembly or close to it?
+ if(!check_target(A, exclude_components = TRUE))
return
- var/turf/T = get_turf(acting_object)
+ var/turf/T = get_turf(get_object())
if(!T)
return
- // No ejecting assembly components or power cells
- if(assembly)
- if((A in assembly.assembly_components) || A == assembly.battery)
- return
-
// If the item is in mob's inventory, try to remove it from there.
if(ismob(A.loc))
var/mob/living/M = A.loc
if(!M.temporarilyRemoveItemFromInventory(A))
return
-
var/x_abs = Clamp(T.x + target_x_rel, 0, world.maxx)
var/y_abs = Clamp(T.y + target_y_rel, 0, world.maxy)
var/range = round(Clamp(sqrt(target_x_rel*target_x_rel+target_y_rel*target_y_rel),0,8),1)
diff --git a/code/modules/integrated_electronics/subtypes/reagents.dm b/code/modules/integrated_electronics/subtypes/reagents.dm
index 1793869237..4fad53e746 100644
--- a/code/modules/integrated_electronics/subtypes/reagents.dm
+++ b/code/modules/integrated_electronics/subtypes/reagents.dm
@@ -1,29 +1,39 @@
/obj/item/integrated_circuit/reagent
category_text = "Reagent"
- var/volume = 0
resistance_flags = UNACIDABLE | FIRE_PROOF
origin_tech = list(TECH_ENGINEERING = 2, TECH_DATA = 2, TECH_BIO = 2)
+ var/volume = 0
-/obj/item/integrated_circuit/reagent/New()
- ..()
+/obj/item/integrated_circuit/reagent/Initialize()
+ . = ..()
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."
+
container_type = OPENCONTAINER_1
+ volume = 100
+
complexity = 20
cooldown_per_use = 1 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)
+ 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
var/smoke_radius = 5
var/notified = FALSE
@@ -35,12 +45,6 @@
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()
var/location = get_turf(src)
var/datum/effect_system/smoke_spread/chem/S = new
@@ -63,15 +67,29 @@
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. Negative given amount makes injector suck out reagents."
+
container_type = OPENCONTAINER_1
+ volume = 30
+
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)
+ 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/direction_mode = SYRINGE_INJECT
var/transfer_amount = 10
@@ -98,101 +116,109 @@
new_amount = Clamp(new_amount, 0, volume)
transfer_amount = new_amount
-/obj/item/integrated_circuit/reagent/proc/inject_tray(var/obj/machinery/hydroponics/H,var/atom/movable/SO,var/A)
- var/datum/reagents/S = new /datum/reagents() //This is a strange way, but I don't know of a better one so I can't fix it at the moment...
- S.my_atom = H
- SO.reagents.trans_to(S,A)
- H.applyChemicals(S)
- S.clear_reagents()
- qdel(S)
+// Hydroponics trays have no reagents holder and handle reagents in their own snowflakey way.
+// This is a dirty hack to make injecting reagents into them work.
+// TODO: refactor that.
+/obj/item/integrated_circuit/reagent/proc/inject_tray(obj/machinery/hydroponics/tray, atom/movable/source, amount)
+ var/datum/reagents/temp_reagents = new /datum/reagents()
+ temp_reagents.my_atom = tray
+
+ source.reagents.trans_to(temp_reagents, amount)
+ tray.applyChemicals(temp_reagents)
+
+ temp_reagents.clear_reagents()
+ qdel(temp_reagents)
/obj/item/integrated_circuit/reagent/injector/do_work()
set waitfor = FALSE // 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)||!Adjacent(AM)||busy)
+ var/atom/movable/acting_object = get_object()
+
+ if(busy || !check_target(AM))
activate_pin(3)
return
- if(istype(AM,/obj/machinery/hydroponics)&&(direction_mode==SYRINGE_INJECT)&&(reagents.total_volume))//injection into tray.
- inject_tray(AM,src,transfer_amount)
- activate_pin(2)
- return
+
if(!AM.reagents)
+ if(istype(AM, /obj/machinery/hydroponics) && direction_mode == SYRINGE_INJECT && reagents.total_volume)//injection into tray.
+ inject_tray(AM, src, transfer_amount)
+ activate_pin(2)
+ return
activate_pin(3)
return
+
if(direction_mode == SYRINGE_INJECT)
- if(!reagents.total_volume) // Empty
+ if(!reagents.total_volume || !AM.is_injectable() || AM.reagents.total_volume >= AM.reagents.maximum_volume)
activate_pin(3)
return
- if(AM.is_injectable())
- if(AM.reagents.total_volume>=AM.reagents.maximum_volume)
- activate_pin(3)
- return
- if(isliving(AM))
- var/mob/living/L = AM
- if(!L.can_inject(null, 0))
- activate_pin(3)
- return
- //Always log attemped injections for admins
- var/list/rinject = list()
- for(var/datum/reagent/R in reagents.reagent_list)
- rinject += R.name
- var/contained = english_list(rinject)
- add_logs(src, L, "attemped to inject", addition="which had [contained]") //TODO: proper logging (maybe last touched and assembled)
- L.visible_message("[src] is trying to inject [L]!", \
- "[src] is trying to inject you!")
- busy = TRUE
- if(do_atom(src, L, extra_checks=CALLBACK(L, /mob/living/proc/can_inject,null,0)))
- var/fraction = min(transfer_amount/reagents.total_volume, 1)
- reagents.reaction(L, INJECT, fraction)
- reagents.trans_to(L, transfer_amount)
- L.visible_message("[src] injects [L] with its needle!", \
- "[src] injects you with its needle!")
- else
- busy = FALSE
- activate_pin(3)
- return
- busy = FALSE
- else
- reagents.trans_to(AM, transfer_amount)
- else
- activate_pin(3)
- return
- else
- if(reagents.total_volume >= reagents.maximum_volume) // Full
- activate_pin(3)
- return
- var/tramount = Clamp(min(transfer_amount, reagents.maximum_volume - reagents.total_volume), 0, reagents.maximum_volume)
+
if(isliving(AM))
var/mob/living/L = AM
- L.visible_message("[src] is trying to take a blood sample from [L]!", \
- "[src] is trying to take a blood sample from you!")
+ if(!L.can_inject(null, 0))
+ activate_pin(3)
+ return
+
+ //Always log attemped injections for admins
+ var/list/rinject = list()
+ for(var/datum/reagent/R in reagents.reagent_list)
+ rinject += R.name
+ var/contained = english_list(rinject)
+
+ add_logs(src, L, "attemped to inject", addition="which had [contained]") //TODO: proper logging (maybe last touched and assembled)
+ L.visible_message("[acting_object] is trying to inject [L]!", \
+ "[acting_object] is trying to inject you!")
+ busy = TRUE
+ if(do_atom(src, L, extra_checks=CALLBACK(L, /mob/living/proc/can_inject,null,0)))
+ var/fraction = min(transfer_amount/reagents.total_volume, 1)
+ reagents.reaction(L, INJECT, fraction)
+ reagents.trans_to(L, transfer_amount)
+ L.visible_message("[acting_object] injects [L] with its needle!", \
+ "[acting_object] injects you with its needle!")
+ else
+ busy = FALSE
+ activate_pin(3)
+ return
+ busy = FALSE
+ else
+ reagents.trans_to(AM, transfer_amount)
+
+ else
+ if(!AM.is_drawable() || reagents.total_volume >= reagents.maximum_volume)
+ activate_pin(3)
+ return
+
+ var/tramount = Clamp(transfer_amount, 0, reagents.total_volume)
+
+ if(isliving(AM))
+ var/mob/living/L = AM
+ L.visible_message("[acting_object] is trying to take a blood sample from [L]!", \
+ "[acting_object] is trying to take a blood sample from you!")
busy = TRUE
if(do_atom(src, L, extra_checks=CALLBACK(L, /mob/living/proc/can_inject,null,0)))
if(L.transfer_blood_to(src, tramount))
- L.visible_message("[src] takes a blood sample from [L].")
+ L.visible_message("[acting_object] takes a blood sample from [L].")
else
busy = FALSE
activate_pin(3)
return
busy = FALSE
else
- if(!AM.reagents.total_volume || !AM.is_drawable())
+ if(!AM.reagents.total_volume)
activate_pin(3)
return
+
AM.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."
- container_type = OPENCONTAINER_1
+ 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."
+
complexity = 8
inputs = list("source" = IC_PINTYPE_REF, "target" = IC_PINTYPE_REF, "injection amount" = IC_PINTYPE_NUMBER)
inputs_default = list("3" = 5)
@@ -219,43 +245,52 @@
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) || !source.reagents) //Invalid input
+ // Check for invalid input.
+ if(!check_target(source) || !check_target(target))
return
- if(Adjacent(source) && Adjacent(target))
- if(istype(target,/obj/machinery/hydroponics)&&(source.reagents.total_volume))//injection into tray.
- inject_tray(target,source,transfer_amount)
- activate_pin(2)
- return
- if(!target.reagents)
- return
- if(ismob(source) || ismob(target))
- return
- if(!source.is_open_container() || !target.is_open_container())
- return
- if(direction_mode)
- if(target.reagents.maximum_volume - target.reagents.total_volume <= 0) //full
- return
- source.reagents.trans_to(target, transfer_amount)
- else
- if(source.reagents.maximum_volume - source.reagents.total_volume <= 0)
- return
- target.reagents.trans_to(source, transfer_amount)
- activate_pin(2)
+ // If the pump is pumping backwards, swap target and source.
+ if(!direction_mode)
+ var/temp_source = source
+ source = target
+ target = temp_source
+
+ if(!source.reagents)
+ return
+
+ if(!target.reagents)
+ // Hydroponics trays have no reagents holder and handle reagents in their own snowflakey way.
+ // This is a dirty hack to make injecting reagents into them work.
+ if(istype(target, /obj/machinery/hydroponics) && source.reagents.total_volume)
+ inject_tray(target, source, transfer_amount)
+ activate_pin(2)
+ return
+
+ // FALSE in those procs makes mobs invalid targets.
+ if(!source.is_drawable(FALSE) || !target.is_injectable(FALSE))
+ return
+
+ source.reagents.trans_to(target, 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."
+ 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."
+
container_type = OPENCONTAINER_1
+ volume = 60
+
complexity = 4
inputs = list()
- outputs = list("volume used" = IC_PINTYPE_NUMBER,"self reference" = IC_PINTYPE_REF)
+ 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)
@@ -269,38 +304,45 @@
/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."
+ 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."
- container_type = OPENCONTAINER_1
+
complexity = 8
spawn_flags = IC_SPAWN_RESEARCH
origin_tech = list(TECH_MATERIALS = 4, TECH_ENGINEERING = 2, TECH_DATA = 2, TECH_BIO = 2)
-/obj/item/integrated_circuit/reagent/storage/cryo/New()
+/obj/item/integrated_circuit/reagent/storage/cryo/Initialize()
. = ..()
reagents.set_reacting(FALSE)
/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."
+ 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."
- container_type = OPENCONTAINER_1
- complexity = 16
+
volume = 180
+
+ complexity = 16
spawn_flags = IC_SPAWN_RESEARCH
origin_tech = list(TECH_MATERIALS = 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."
+ 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."
- container_type = OPENCONTAINER_1
+
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)
+ 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_MATERIALS = 3, TECH_ENGINEERING = 2, TECH_DATA = 2, TECH_BIO = 2)
@@ -316,16 +358,26 @@
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. \
+ extended_desc = "This is a filter, which will move liquids from the source to the target. \
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."
- container_type = OPENCONTAINER_1
+
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)
+ 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)
+ 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
@@ -347,27 +399,28 @@
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
+
+ // Check for invalid input.
+ if(!check_target(source) || !check_target(target))
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.maximum_volume - target.reagents.total_volume <= 0)
- return
- for(var/datum/reagent/G in source.reagents.reagent_list)
- if (!direction_mode)
- 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()
+ if(!source.reagents || !target.reagents)
+ return
+ // FALSE in those procs makes mobs invalid targets.
+ if(!source.is_drawable(FALSE) || !target.is_injectable(FALSE))
+ return
+
+ if(target.reagents.maximum_volume - target.reagents.total_volume <= 0)
+ return
+
+ for(var/datum/reagent/G in source.reagents.reagent_list)
+ if(!direction_mode)
+ 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()