Merge pull request #4321 from Citadel-Station-13/upstream-merge-33434

[MIRROR] Fixes integrated circuit reagent components not working while in mobs, adds new helper procs
This commit is contained in:
deathride58
2017-12-11 03:19:53 +00:00
committed by GitHub
4 changed files with 247 additions and 150 deletions
@@ -122,9 +122,8 @@
if(!battery)
to_chat(usr, "<span class='warning'>There's no power cell to remove from \the [src].</span>")
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, "<span class='notice'>You pull \the [battery] out of \the [src]'s power supplier.</span>")
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
@@ -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
@@ -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)
@@ -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("<span class='danger'>[src] is trying to inject [L]!</span>", \
"<span class='userdanger'>[src] is trying to inject you!</span>")
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("<span class='danger'>[src] injects [L] with its needle!</span>", \
"<span class='userdanger'>[src] injects you with its needle!</span>")
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("<span class='danger'>[src] is trying to take a blood sample from [L]!</span>", \
"<span class='userdanger'>[src] is trying to take a blood sample from you!</span>")
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("<span class='danger'>[acting_object] is trying to inject [L]!</span>", \
"<span class='userdanger'>[acting_object] is trying to inject you!</span>")
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("<span class='danger'>[acting_object] injects [L] with its needle!</span>", \
"<span class='userdanger'>[acting_object] injects you with its needle!</span>")
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("<span class='danger'>[acting_object] is trying to take a blood sample from [L]!</span>", \
"<span class='userdanger'>[acting_object] is trying to take a blood sample from you!</span>")
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()