From 074709906301e3e396179c861ca0af068c3f36ec Mon Sep 17 00:00:00 2001 From: RikuTheKiller <88713943+RikuTheKiller@users.noreply.github.com> Date: Mon, 21 Nov 2022 09:17:34 +0200 Subject: [PATCH] Adds a reagent injector component and BCI manipulators to all circuit labs (#71236) ## About The Pull Request This PR adds a reagent injector component that's exclusive to BCIs. (Requested to be integrated into BCIs by Mothblocks.) When outside of a circuit, the component itself stores the reagents. However, if it's inside of a BCI, the storage is moved to the BCI. The storage can contain up to 15u of reagents and acts like an open container. (However, it won't spill even if you throw it, it just acts like an open container code-wise, don't worry about it.) You can only have one reagent injector in a circuit. Trying to insert multiple will give you an error message. The entire dose is administered at once. (Requirement set by Mothblocks.) Please don't try to dispute any of the specific limitations in the comments as they're out of my control. They're reasonable anyways. Reagent Injector Input/Output: Inject (Input Signal) - Administers all reagents currently stored inside of the BCI into the user. Injected (Output Signal) - Triggered when reagents are injected. Not triggered if the reagent storage is empty. New BCI Input: Show Charge Meter (Number) - Toggles showing the charge meter action. (Adds some capacity for stealth.) Install Detector Outputs: (Added following a comment about having to use weird workarounds for proper loops.) Current State (Number) - Outputs 1 if the BCI is implanted and 0 if it's not. Installed (Signal) - Triggered when the BCI is implanted into it's user. Removed (Signal) - Triggered when the BCI is removed from it's user. This PR also adds BCI manipulation chambers to all currently present circuit labs. (Solution proposed by Mothblocks.) Yes I had to do some other mapping changes to allow for this. No I don't have any mapping experience, why do you ask? ## Why It's Good For The Game One small step for BCIs, one giant leap for circuit kind. (First "proper" circuit to human interaction in the entire game!) This allows for some funky stuff and also makes it less of a pain in the ass to use BCIs. What's not to love? ## Changelog :cl: add: Added a reagent injector component and BCI manipulators to all circuit labs. (+ install detector component) /:cl: Co-authored-by: Mothblocks <35135081+Mothblocks@users.noreply.github.com> --- .../map_files/Deltastation/DeltaStation2.dmm | 3 +- .../map_files/IceBoxStation/IceBoxStation.dmm | 22 ++++++-- _maps/map_files/KiloStation/KiloStation.dmm | 22 ++++++-- _maps/map_files/MetaStation/MetaStation.dmm | 8 +-- _maps/map_files/tramstation/tramstation.dmm | 7 +-- code/__DEFINES/wiremod.dm | 2 + .../research/designs/wiremod_designs.dm | 10 ++++ code/modules/research/techweb/all_nodes.dm | 2 + .../components/bci/install_detector.dm | 50 +++++++++++++++++ .../components/bci/reagent_injector.dm | 56 +++++++++++++++++++ .../wiremod/core/integrated_circuit.dm | 27 +++++++++ .../wiremod/shell/brain_computer_interface.dm | 48 +++++++++++----- tgstation.dme | 2 + 13 files changed, 223 insertions(+), 36 deletions(-) create mode 100644 code/modules/wiremod/components/bci/install_detector.dm create mode 100644 code/modules/wiremod/components/bci/reagent_injector.dm diff --git a/_maps/map_files/Deltastation/DeltaStation2.dmm b/_maps/map_files/Deltastation/DeltaStation2.dmm index 4c7bee23e7f..827a50f10f3 100644 --- a/_maps/map_files/Deltastation/DeltaStation2.dmm +++ b/_maps/map_files/Deltastation/DeltaStation2.dmm @@ -24186,8 +24186,8 @@ /area/station/maintenance/department/crew_quarters/bar) "fOV" = ( /obj/effect/turf_decal/tile/neutral/fourcorners, -/obj/item/kirbyplants/random, /obj/structure/sign/warning/firing_range/directional/west, +/obj/machinery/bci_implanter, /turf/open/floor/iron/dark, /area/station/science/circuits) "fPf" = ( @@ -57785,6 +57785,7 @@ "nZn" = ( /obj/effect/turf_decal/tile/neutral/fourcorners, /obj/structure/disposalpipe/segment, +/obj/item/kirbyplants/random, /turf/open/floor/iron/dark, /area/station/science/circuits) "nZt" = ( diff --git a/_maps/map_files/IceBoxStation/IceBoxStation.dmm b/_maps/map_files/IceBoxStation/IceBoxStation.dmm index 201b26190b2..d5a2a09e148 100644 --- a/_maps/map_files/IceBoxStation/IceBoxStation.dmm +++ b/_maps/map_files/IceBoxStation/IceBoxStation.dmm @@ -13953,9 +13953,21 @@ "egT" = ( /obj/machinery/airalarm/directional/north, /obj/structure/rack, -/obj/item/controller, -/obj/item/compact_remote, -/obj/item/compact_remote, +/obj/item/controller{ + pixel_x = -7 + }, +/obj/item/compact_remote{ + pixel_x = -7 + }, +/obj/item/compact_remote{ + pixel_x = -7 + }, +/obj/item/integrated_circuit/loaded/speech_relay{ + pixel_x = 7 + }, +/obj/item/integrated_circuit/loaded/hello_world{ + pixel_x = 7 + }, /turf/open/floor/iron/white/corner, /area/station/science/explab) "egV" = ( @@ -40279,9 +40291,7 @@ /turf/open/floor/iron/dark/textured, /area/station/security/prison) "msi" = ( -/obj/structure/rack, -/obj/item/integrated_circuit/loaded/speech_relay, -/obj/item/integrated_circuit/loaded/hello_world, +/obj/machinery/bci_implanter, /turf/open/floor/iron/white/side{ dir = 4 }, diff --git a/_maps/map_files/KiloStation/KiloStation.dmm b/_maps/map_files/KiloStation/KiloStation.dmm index c0fc35b5407..992675343c5 100644 --- a/_maps/map_files/KiloStation/KiloStation.dmm +++ b/_maps/map_files/KiloStation/KiloStation.dmm @@ -6231,13 +6231,11 @@ /turf/open/floor/iron, /area/station/hallway/primary/central) "bWv" = ( -/obj/structure/rack, -/obj/item/integrated_circuit/loaded/speech_relay, -/obj/item/integrated_circuit/loaded/hello_world, /obj/effect/turf_decal/tile/purple/anticorner/contrasted{ dir = 1 }, /obj/machinery/light_switch/directional/north, +/obj/machinery/bci_implanter, /turf/open/floor/iron/showroomfloor, /area/station/science/ordnance/office) "bWE" = ( @@ -71144,13 +71142,25 @@ /area/station/engineering/atmos) "ujZ" = ( /obj/structure/rack, -/obj/item/controller, -/obj/item/compact_remote, -/obj/item/compact_remote, +/obj/item/controller{ + pixel_x = -7 + }, +/obj/item/compact_remote{ + pixel_x = -7 + }, +/obj/item/compact_remote{ + pixel_x = -7 + }, /obj/effect/turf_decal/tile/purple/half/contrasted{ dir = 1 }, /obj/machinery/airalarm/directional/north, +/obj/item/integrated_circuit/loaded/speech_relay{ + pixel_x = 7 + }, +/obj/item/integrated_circuit/loaded/hello_world{ + pixel_x = 7 + }, /turf/open/floor/iron/showroomfloor, /area/station/science/ordnance/office) "ukc" = ( diff --git a/_maps/map_files/MetaStation/MetaStation.dmm b/_maps/map_files/MetaStation/MetaStation.dmm index 1cfb99fa695..c9b01a050eb 100644 --- a/_maps/map_files/MetaStation/MetaStation.dmm +++ b/_maps/map_files/MetaStation/MetaStation.dmm @@ -34770,11 +34770,7 @@ /turf/closed/wall/r_wall, /area/station/science/lab) "mvS" = ( -/obj/structure/table, -/obj/machinery/airalarm/directional/south, -/obj/item/storage/toolbox/electrical{ - pixel_y = 5 - }, +/obj/machinery/bci_implanter, /turf/open/floor/iron, /area/station/science/explab) "mvY" = ( @@ -65294,7 +65290,9 @@ "wVC" = ( /obj/structure/rack, /obj/item/integrated_circuit/loaded/hello_world, +/obj/item/storage/toolbox/electrical, /obj/item/integrated_circuit/loaded/speech_relay, +/obj/machinery/airalarm/directional/north, /turf/open/floor/iron/white, /area/station/science/explab) "wVO" = ( diff --git a/_maps/map_files/tramstation/tramstation.dmm b/_maps/map_files/tramstation/tramstation.dmm index be399891809..4c01777f0a2 100644 --- a/_maps/map_files/tramstation/tramstation.dmm +++ b/_maps/map_files/tramstation/tramstation.dmm @@ -18347,6 +18347,7 @@ /obj/effect/turf_decal/trimline/purple/filled/corner{ dir = 1 }, +/obj/machinery/bci_implanter, /turf/open/floor/iron/white, /area/station/science/explab) "gCH" = ( @@ -30737,11 +30738,7 @@ /area/station/science/lower) "kXw" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/door/window/right/directional/north{ - dir = 2; - name = "Research Test Chamber"; - req_access = list("science") - }, +/obj/structure/window/reinforced, /turf/open/floor/engine, /area/station/science/explab) "kXI" = ( diff --git a/code/__DEFINES/wiremod.dm b/code/__DEFINES/wiremod.dm index dcfb2c3ce0b..befbcdb8c74 100644 --- a/code/__DEFINES/wiremod.dm +++ b/code/__DEFINES/wiremod.dm @@ -119,6 +119,8 @@ #define CIRCUIT_FLAG_INSTANT (1<<4) /// This circuit component can't be loaded in module component. Saves us some headaches. #define CIRCUIT_FLAG_REFUSE_MODULE (1<<5) +/// This circuit component cannot be inserted into the same circuit multiple times. Only use this for major headaches. +#define CIRCUIT_NO_DUPLICATES (1<<6) // Datatype flags /// The datatype supports manual inputs diff --git a/code/modules/research/designs/wiremod_designs.dm b/code/modules/research/designs/wiremod_designs.dm index edf04e0d6eb..1873fa8162a 100644 --- a/code/modules/research/designs/wiremod_designs.dm +++ b/code/modules/research/designs/wiremod_designs.dm @@ -381,6 +381,16 @@ id = "comp_counter_overlay" build_path = /obj/item/circuit_component/counter_overlay +/datum/design/component/bci/reagent_injector + name = "Reagent Injector Component" + id = "comp_reagent_injector" + build_path = /obj/item/circuit_component/reagent_injector + +/datum/design/component/bci/install_detector + name = "Install Detector Component" + id = "comp_install_detector" + build_path = /obj/item/circuit_component/install_detector + /datum/design/component/foreach name = "For Each Component" id = "comp_foreach" diff --git a/code/modules/research/techweb/all_nodes.dm b/code/modules/research/techweb/all_nodes.dm index dad1a92ecd0..5b4216134d6 100644 --- a/code/modules/research/techweb/all_nodes.dm +++ b/code/modules/research/techweb/all_nodes.dm @@ -826,7 +826,9 @@ "comp_bar_overlay", "comp_bci_action", "comp_counter_overlay", + "comp_install_detector", "comp_object_overlay", + "comp_reagent_injector", "comp_target_intercept", "comp_thought_listener", "comp_vox", diff --git a/code/modules/wiremod/components/bci/install_detector.dm b/code/modules/wiremod/components/bci/install_detector.dm new file mode 100644 index 00000000000..0d39e01214e --- /dev/null +++ b/code/modules/wiremod/components/bci/install_detector.dm @@ -0,0 +1,50 @@ +/** + * # Install Detector Component + * + * Detects when a BCI is installed/removed. + * Requires a BCI shell. + */ + +/obj/item/circuit_component/install_detector + display_name = "Install Detector" + desc = "A component that detects when a BCI is installed or removed from its user." + category = "BCI" + + required_shells = list(/obj/item/organ/internal/cyberimp/bci) + + var/datum/port/output/implanted + var/datum/port/output/removed + var/datum/port/output/current_state + + var/obj/item/organ/internal/cyberimp/bci/bci + +/obj/item/circuit_component/install_detector/populate_ports() + . = ..() + current_state = add_output_port("Current State", PORT_TYPE_NUMBER) + implanted = add_output_port("Implanted", PORT_TYPE_SIGNAL) + removed = add_output_port("Removed", PORT_TYPE_SIGNAL) + +/obj/item/circuit_component/install_detector/register_shell(atom/movable/shell) + . = ..() + if(istype(shell, /obj/item/organ/internal/cyberimp/bci)) + bci = shell + RegisterSignal(shell, COMSIG_ORGAN_IMPLANTED, PROC_REF(on_organ_implanted)) + RegisterSignal(shell, COMSIG_ORGAN_REMOVED, PROC_REF(on_organ_removed)) + +/obj/item/circuit_component/install_detector/unregister_shell(atom/movable/shell) + . = ..() + bci = null + UnregisterSignal(shell, list( + COMSIG_ORGAN_IMPLANTED, + COMSIG_ORGAN_REMOVED, + )) + +/obj/item/circuit_component/install_detector/proc/on_organ_implanted(datum/source, mob/living/carbon/owner) + SIGNAL_HANDLER + current_state.set_output(TRUE) + implanted.set_output(COMPONENT_SIGNAL) + +/obj/item/circuit_component/install_detector/proc/on_organ_removed(datum/source, mob/living/carbon/owner) + SIGNAL_HANDLER + current_state.set_output(FALSE) + removed.set_output(COMPONENT_SIGNAL) diff --git a/code/modules/wiremod/components/bci/reagent_injector.dm b/code/modules/wiremod/components/bci/reagent_injector.dm new file mode 100644 index 00000000000..ef9188d7bdc --- /dev/null +++ b/code/modules/wiremod/components/bci/reagent_injector.dm @@ -0,0 +1,56 @@ +/** + * # Reagent Injector Component + * + * Injects reagents into the user. + * Requires a BCI shell. + */ + +/obj/item/circuit_component/reagent_injector + display_name = "Reagent Injector" + desc = "A component that can inject reagents from a BCI's reagent storage." + category = "BCI" + circuit_flags = CIRCUIT_NO_DUPLICATES + + required_shells = list(/obj/item/organ/internal/cyberimp/bci) + + var/datum/port/input/inject + var/datum/port/output/injected + + var/obj/item/organ/internal/cyberimp/bci/bci + +/obj/item/circuit_component/reagent_injector/Initialize(mapload) + . = ..() + create_reagents(15, OPENCONTAINER) //This is mostly used in the case of a BCI still having reagents in it when the component is removed. + +/obj/item/circuit_component/reagent_injector/populate_ports() + . = ..() + inject = add_input_port("Inject", PORT_TYPE_SIGNAL, trigger = PROC_REF(trigger_inject)) + injected = add_output_port("Injected", PORT_TYPE_SIGNAL) + +/obj/item/circuit_component/reagent_injector/proc/trigger_inject() + CIRCUIT_TRIGGER + if(!bci.owner) + return + if(bci.owner.reagents.total_volume + bci.reagents.total_volume > bci.owner.reagents.maximum_volume) + return + var/contained = bci.reagents.get_reagent_log_string() + var/units = bci.reagents.trans_to(bci.owner.reagents, bci.reagents.total_volume, methods = INJECT) + if(units) + injected.set_output(COMPONENT_SIGNAL) + log_combat(bci.owner, bci.owner, "injected", bci, "which had [contained]") + +/obj/item/circuit_component/reagent_injector/register_shell(atom/movable/shell) + . = ..() + if(istype(shell, /obj/item/organ/internal/cyberimp/bci)) + bci = shell + bci.create_reagents(15, OPENCONTAINER) + if(reagents.total_volume) + reagents.trans_to(bci, reagents.total_volume) + +/obj/item/circuit_component/reagent_injector/unregister_shell(atom/movable/shell) + . = ..() + if(bci?.reagents) + if(bci.reagents.total_volume) + bci.reagents.trans_to(src, bci.reagents.total_volume) + QDEL_NULL(bci.reagents) + bci = null diff --git a/code/modules/wiremod/core/integrated_circuit.dm b/code/modules/wiremod/core/integrated_circuit.dm index 4b3beee4296..9d4a42e5ff6 100644 --- a/code/modules/wiremod/core/integrated_circuit.dm +++ b/code/modules/wiremod/core/integrated_circuit.dm @@ -216,6 +216,28 @@ GLOBAL_LIST_EMPTY_TYPED(integrated_circuits, /obj/item/integrated_circuit) SEND_SIGNAL(src, COMSIG_CIRCUIT_SET_ON, new_value) on = new_value +/** + * Used for checking if another component of to_check's type exists in the circuit. + * Introspects through modules. + * + * Arguments: + * * to_check - The component to check. + **/ +/obj/item/integrated_circuit/proc/is_duplicate(obj/item/circuit_component/to_check) + for(var/component as anything in attached_components) + if(component == to_check) + continue + if(istype(component, to_check.type)) + return TRUE + if(istype(component, /obj/item/circuit_component/module)) + var/obj/item/circuit_component/module/module = component + for(var/module_component as anything in module.internal_circuit.attached_components) + if(module_component == to_check) + continue + if(istype(module_component, to_check.type)) + return TRUE + return FALSE + /** * Adds a component to the circuitboard * @@ -231,6 +253,11 @@ GLOBAL_LIST_EMPTY_TYPED(integrated_circuits, /obj/item/integrated_circuit) if(!to_add.add_to(src)) return FALSE + if(to_add.circuit_flags & CIRCUIT_NO_DUPLICATES) + if(is_duplicate(to_add)) + to_chat(user, span_danger("You can't insert multiple instances of this component into the same circuit!")) + return FALSE + var/success = FALSE if(user) success = user.transferItemToLoc(to_add, src) diff --git a/code/modules/wiremod/shell/brain_computer_interface.dm b/code/modules/wiremod/shell/brain_computer_interface.dm index e0e9d6639a1..c221dc00ffe 100644 --- a/code/modules/wiremod/shell/brain_computer_interface.dm +++ b/code/modules/wiremod/shell/brain_computer_interface.dm @@ -101,15 +101,19 @@ var/datum/port/input/message var/datum/port/input/send_message_signal + var/datum/port/input/show_charge_meter var/datum/port/output/user_port var/datum/weakref/user + var/obj/item/organ/internal/cyberimp/bci/bci + /obj/item/circuit_component/bci_core/populate_ports() - message = add_input_port("Message", PORT_TYPE_STRING) + message = add_input_port("Message", PORT_TYPE_STRING, trigger = null) send_message_signal = add_input_port("Send Message", PORT_TYPE_SIGNAL) + show_charge_meter = add_input_port("Show Charge Meter", PORT_TYPE_NUMBER, trigger = PROC_REF(update_charge_action)) user_port = add_output_port("User", PORT_TYPE_ATOM) @@ -117,31 +121,47 @@ QDEL_NULL(charge_action) return ..() -/obj/item/circuit_component/bci_core/register_shell(atom/movable/shell) - var/obj/item/organ/internal/cyberimp/bci/bci = shell +/obj/item/circuit_component/bci_core/proc/update_charge_action() + CIRCUIT_TRIGGER + var/mob/living/carbon/resolved_owner = user?.resolve() + if (show_charge_meter.value) + if (charge_action) + return + charge_action = new(src) + if (resolved_owner) + charge_action.Grant(resolved_owner) + bci.actions += charge_action + else + if (!charge_action) + return + if (resolved_owner) + charge_action.Remove(resolved_owner) + bci.actions -= charge_action + QDEL_NULL(charge_action) - charge_action = new(src) - bci.actions += list(charge_action) +/obj/item/circuit_component/bci_core/register_shell(atom/movable/shell) + bci = shell + + show_charge_meter.set_value(TRUE) RegisterSignal(shell, COMSIG_ORGAN_IMPLANTED, PROC_REF(on_organ_implanted)) RegisterSignal(shell, COMSIG_ORGAN_REMOVED, PROC_REF(on_organ_removed)) /obj/item/circuit_component/bci_core/unregister_shell(atom/movable/shell) - var/obj/item/organ/internal/cyberimp/bci/bci = shell + bci = shell - bci.actions -= charge_action - QDEL_NULL(charge_action) + if (charge_action) + var/mob/living/carbon/resolved_owner = user?.resolve() + if (resolved_owner) + charge_action.Remove(resolved_owner) + bci.actions -= charge_action + QDEL_NULL(charge_action) UnregisterSignal(shell, list( COMSIG_ORGAN_IMPLANTED, COMSIG_ORGAN_REMOVED, )) -/obj/item/circuit_component/bci_core/should_receive_input(datum/port/input/port) - if (!COMPONENT_TRIGGERED_BY(send_message_signal, port)) - return FALSE - return ..() - /obj/item/circuit_component/bci_core/input_received(datum/port/input/port) var/sent_message = trim(message.value) if (!sent_message) @@ -159,6 +179,8 @@ /obj/item/circuit_component/bci_core/proc/on_organ_implanted(datum/source, mob/living/carbon/owner) SIGNAL_HANDLER + update_charge_action() + user_port.set_output(owner) user = WEAKREF(owner) diff --git a/tgstation.dme b/tgstation.dme index a8d638a602a..dc15adbc9e8 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -4731,6 +4731,8 @@ #include "code\modules\wiremod\components\atom\reagentscanner.dm" #include "code\modules\wiremod\components\atom\self.dm" #include "code\modules\wiremod\components\atom\species.dm" +#include "code\modules\wiremod\components\bci\install_detector.dm" +#include "code\modules\wiremod\components\bci\reagent_injector.dm" #include "code\modules\wiremod\components\bci\thought_listener.dm" #include "code\modules\wiremod\components\bci\vox.dm" #include "code\modules\wiremod\components\bci\hud\bar_overlay.dm"