diff --git a/code/datums/wires/wire_bundle_component.dm b/code/datums/wires/wire_bundle_component.dm
index 0fb0e91998d..490aeb91b38 100644
--- a/code/datums/wires/wire_bundle_component.dm
+++ b/code/datums/wires/wire_bundle_component.dm
@@ -21,4 +21,7 @@
proper_name = holder.name
. = ..()
+/datum/wires/wire_bundle_component/ui_host(mob/user)
+ return holder.ui_host(user)
+
#undef CAPACITY_PER_WIRE
diff --git a/code/game/machinery/computer/camera_advanced.dm b/code/game/machinery/computer/camera_advanced.dm
index 42188e251a2..9761f5ec550 100644
--- a/code/game/machinery/computer/camera_advanced.dm
+++ b/code/game/machinery/computer/camera_advanced.dm
@@ -306,18 +306,18 @@
/obj/machinery/computer/camera_advanced/proc/add_circuit_action(datum/_source, obj/item/circuit_component/equipment_action/action_comp)
SIGNAL_HANDLER
- var/datum/action/innate/camera_circuit_action/new_action = new(src, action_comp)
+ var/datum/action/innate/circuit_equipment_action/new_action = new(src, action_comp)
LAZYADD(actions, new_action)
if(current_user)
new_action.Grant(current_user)
/obj/machinery/computer/camera_advanced/proc/remove_circuit_action(datum/_source, obj/item/circuit_component/equipment_action/action_comp)
SIGNAL_HANDLER
- var/datum/action/innate/camera_circuit_action/action = action_comp.granted_to[REF(src)]
+ var/datum/action/innate/circuit_equipment_action/action = action_comp.granted_to[REF(src)]
if(!istype(action))
return
LAZYREMOVE(actions, action)
- qdel(action)
+ QDEL_LIST_ASSOC_VAL(action_comp.granted_to)
/obj/machinery/computer/camera_advanced/proc/on_port_unregister_object(datum/component/usb_port/source, atom/movable/object)
SIGNAL_HANDLER
@@ -331,32 +331,6 @@
on_port_unregister_object(port, port.physical_object)
UnregisterSignal(port, list(COMSIG_USB_PORT_REGISTER_PHYSICAL_OBJECT, COMSIG_USB_PORT_UNREGISTER_PHYSICAL_OBJECT))
-/datum/action/innate/camera_circuit_action
- name = "Action"
- button_icon = 'icons/mob/actions/actions_items.dmi'
- button_icon_state = "bci_power"
-
- var/obj/machinery/computer/camera_advanced/console
- var/obj/item/circuit_component/equipment_action/action_comp
-
-/datum/action/innate/camera_circuit_action/New(obj/machinery/computer/camera_advanced/console, obj/item/circuit_component/equipment_action/action_comp)
- . = ..()
- src.console = console
- action_comp.granted_to[REF(console)] = src
- src.action_comp = action_comp
-
-/datum/action/innate/camera_circuit_action/Destroy()
- action_comp.granted_to -= REF(console)
- action_comp = null
-
- return ..()
-
-/datum/action/innate/camera_circuit_action/Activate()
- action_comp.user.set_output(owner)
- action_comp.signal.set_output(COMPONENT_SIGNAL)
-
- return ..()
-
/// Advanced camera component
/obj/item/circuit_component/advanced_camera
diff --git a/code/game/objects/items/implants/implantcase.dm b/code/game/objects/items/implants/implantcase.dm
index d034bc47f15..d2785d564af 100644
--- a/code/game/objects/items/implants/implantcase.dm
+++ b/code/game/objects/items/implants/implantcase.dm
@@ -36,9 +36,9 @@
icon_state = "implantcase-[imp ? imp.implant_color : 0]"
return ..()
-/obj/item/implantcase/attackby(obj/item/used_item, mob/living/user, list/modifiers, list/attack_modifiers)
- if(istype(used_item, /obj/item/implanter))
- var/obj/item/implanter/used_implanter = used_item
+/obj/item/implantcase/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
+ if(istype(tool, /obj/item/implanter))
+ var/obj/item/implanter/used_implanter = tool
if(used_implanter.imp && !imp)
//implanter to case implant transfer
used_implanter.imp.forceMove(src)
@@ -55,8 +55,10 @@
reagents = null
update_appearance()
used_implanter.update_appearance()
- else
- return ..()
+ return ITEM_INTERACT_SUCCESS
+ if(imp)
+ return imp.base_item_interaction(user, tool, modifiers)
+ return ..()
/obj/item/implantcase/nameformat(input, user)
return "implant case[input? " - '[input]'" : null]"
diff --git a/code/modules/research/designs/wiremod_designs.dm b/code/modules/research/designs/wiremod_designs.dm
index 526e8d2a54a..112f3ad2512 100644
--- a/code/modules/research/designs/wiremod_designs.dm
+++ b/code/modules/research/designs/wiremod_designs.dm
@@ -725,3 +725,17 @@
category = list(
RND_CATEGORY_CIRCUITRY + RND_SUBCATEGORY_CIRCUITRY_SHELLS
)
+
+/datum/design/implant_shell
+ name = "Implant Shell Case"
+ desc = "A tiny shell that can be implanted in a living being."
+ id = "implant_shell"
+ materials = list(
+ /datum/material/iron = SMALL_MATERIAL_AMOUNT * 5,
+ /datum/material/glass = SMALL_MATERIAL_AMOUNT * 5,
+ )
+ build_path = /obj/item/implantcase/circuit
+ build_type = COMPONENT_PRINTER
+ category = list(
+ RND_CATEGORY_CIRCUITRY + RND_SUBCATEGORY_CIRCUITRY_SHELLS
+ )
diff --git a/code/modules/research/techweb/nodes/circuit_nodes.dm b/code/modules/research/techweb/nodes/circuit_nodes.dm
index 44f38afd800..2e173bf88a1 100644
--- a/code/modules/research/techweb/nodes/circuit_nodes.dm
+++ b/code/modules/research/techweb/nodes/circuit_nodes.dm
@@ -103,6 +103,7 @@
"dispenser_shell",
"door_shell",
"gun_shell",
+ "implant_shell",
"keyboard_shell",
"module_shell",
"money_bot_shell",
diff --git a/code/modules/wiremod/components/action/equpiment_action.dm b/code/modules/wiremod/components/action/equpiment_action.dm
index f313d1f4870..a38d8762ce1 100644
--- a/code/modules/wiremod/components/action/equpiment_action.dm
+++ b/code/modules/wiremod/components/action/equpiment_action.dm
@@ -86,7 +86,30 @@
update_actions()
/obj/item/circuit_component/equipment_action/proc/update_actions()
- for(var/ref in granted_to)
- var/datum/action/granted_action = granted_to[ref]
+ for(var/target in granted_to)
+ var/datum/action/granted_action = granted_to[target]
granted_action.name = button_name.value || "Action"
granted_action.button_icon_state = "bci_[replacetextEx(LOWER_TEXT(icon_options.value), " ", "_")]"
+
+/datum/action/innate/circuit_equipment_action
+ name = "Action"
+ button_icon = 'icons/mob/actions/actions_items.dmi'
+ button_icon_state = "bci_power"
+ check_flags = AB_CHECK_CONSCIOUS
+
+ var/obj/item/circuit_component/equipment_action/action_comp
+
+/datum/action/innate/circuit_equipment_action/New(datum/target, obj/item/circuit_component/equipment_action/action_comp)
+ . = ..()
+ action_comp.granted_to[REF(target)] = src
+ src.action_comp = action_comp
+
+/datum/action/innate/circuit_equipment_action/Destroy()
+ action_comp.granted_to -= REF(target)
+ action_comp = null
+
+ return ..()
+
+/datum/action/innate/circuit_equipment_action/Activate()
+ action_comp.user.set_output(owner)
+ action_comp.signal.set_output(COMPONENT_SIGNAL)
diff --git a/code/modules/wiremod/components/bci/install_detector.dm b/code/modules/wiremod/components/bci/install_detector.dm
index 123ff9d5162..39972134b84 100644
--- a/code/modules/wiremod/components/bci/install_detector.dm
+++ b/code/modules/wiremod/components/bci/install_detector.dm
@@ -1,23 +1,21 @@
/**
* # Install Detector Component
*
- * Detects when a BCI is installed/removed.
- * Requires a BCI shell.
+ * Detects when a BCI, implant, or similar such circuit is installed/removed.
+ * Requires a shell that gets inserted into a mob, such as a BCI or implant.
*/
/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"
+ desc = "A component that detects when the circuit is installed or removed from its user."
+ category = "Entity"
- required_shells = list(/obj/item/organ/cyberimp/bci)
+ required_shells = list(/obj/item/organ/cyberimp/bci, /obj/item/implant)
var/datum/port/output/implanted
var/datum/port/output/removed
var/datum/port/output/current_state
- var/obj/item/organ/cyberimp/bci/bci
-
/obj/item/circuit_component/install_detector/populate_ports()
. = ..()
current_state = add_output_port("Current State", PORT_TYPE_NUMBER)
@@ -27,24 +25,31 @@
/obj/item/circuit_component/install_detector/register_shell(atom/movable/shell)
. = ..()
if(istype(shell, /obj/item/organ/cyberimp/bci))
- bci = shell
- RegisterSignal(shell, COMSIG_ORGAN_IMPLANTED, PROC_REF(on_organ_implanted))
- RegisterSignal(shell, COMSIG_ORGAN_REMOVED, PROC_REF(on_organ_removed))
+ RegisterSignal(shell, COMSIG_ORGAN_IMPLANTED, PROC_REF(implanted))
+ RegisterSignal(shell, COMSIG_ORGAN_REMOVED, PROC_REF(removed))
+ if(istype(shell, /obj/item/implant))
+ RegisterSignal(shell, COMSIG_IMPLANT_IMPLANTED, PROC_REF(implanted))
+ RegisterSignal(shell, COMSIG_IMPLANT_REMOVED, PROC_REF(removed))
/obj/item/circuit_component/install_detector/unregister_shell(atom/movable/shell)
. = ..()
- bci = null
- UnregisterSignal(shell, list(
- COMSIG_ORGAN_IMPLANTED,
- COMSIG_ORGAN_REMOVED,
- ))
+ if(istype(shell, /obj/item/organ/cyberimp/bci))
+ UnregisterSignal(shell, list(
+ COMSIG_ORGAN_IMPLANTED,
+ COMSIG_ORGAN_REMOVED,
+ ))
+ if(istype(shell, /obj/item/implant))
+ UnregisterSignal(shell, list(
+ COMSIG_IMPLANT_IMPLANTED,
+ COMSIG_IMPLANT_REMOVED,
+ ))
-/obj/item/circuit_component/install_detector/proc/on_organ_implanted(datum/source, mob/living/carbon/owner)
+/obj/item/circuit_component/install_detector/proc/implanted(datum/source, mob/living/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)
+/obj/item/circuit_component/install_detector/proc/removed(datum/source, mob/living/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
index 73b685a39db..10194f3db3d 100644
--- a/code/modules/wiremod/components/bci/reagent_injector.dm
+++ b/code/modules/wiremod/components/bci/reagent_injector.dm
@@ -2,55 +2,66 @@
* # Reagent Injector Component
*
* Injects reagents into the user.
- * Requires a BCI shell.
+ * Requires a shell that can be inserted into a mob that reagents can be injected into.
*/
/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"
+ desc = "A component that can inject reagents from the circuit's reagent storage."
+ category = "Entity"
circuit_flags = CIRCUIT_NO_DUPLICATES
- required_shells = list(/obj/item/organ/cyberimp/bci)
+ required_shells = list(/obj/item/organ/cyberimp/bci, /obj/item/implant)
var/datum/port/input/inject
var/datum/port/output/injected
- var/obj/item/organ/cyberimp/bci/bci
+ var/obj/item/shell_item
/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.
+ create_reagents(15, OPENCONTAINER) //This is mostly used in the case of the shell 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/get_mob()
+ if(istype(shell_item, /obj/item/organ/cyberimp/bci))
+ var/obj/item/organ/cyberimp/bci/shell_bci = shell_item
+ return shell_bci.owner
+ if(istype(shell_item, /obj/item/implant))
+ var/obj/item/implant/shell_implant = shell_item
+ return shell_implant.imp_in
+
/obj/item/circuit_component/reagent_injector/proc/trigger_inject()
CIRCUIT_TRIGGER
- if(!bci.owner)
+ var/mob/living/mob_to_inject = get_mob()
+ if(!istype(mob_to_inject))
return
- if(bci.owner.reagents.total_volume + bci.reagents.total_volume > bci.owner.reagents.maximum_volume)
+ if(!mob_to_inject.reagents)
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(mob_to_inject.reagents.total_volume + shell_item.reagents.total_volume > mob_to_inject.reagents.maximum_volume)
+ return
+ var/contained = shell_item.reagents.get_reagent_log_string()
+ var/units = shell_item.reagents.trans_to(mob_to_inject.reagents, shell_item.reagents.total_volume, methods = INJECT)
if(units)
injected.set_output(COMPONENT_SIGNAL)
- log_combat(bci.owner, bci.owner, "injected", bci, "which had [contained]")
+ log_combat(mob_to_inject, mob_to_inject, "injected", shell_item, "which had [contained]")
/obj/item/circuit_component/reagent_injector/register_shell(atom/movable/shell)
. = ..()
- if(istype(shell, /obj/item/organ/cyberimp/bci))
- bci = shell
- bci.create_reagents(15, OPENCONTAINER)
+ if(is_type_in_list(shell, required_shells))
+ shell_item = shell
+ shell_item.create_reagents(15, OPENCONTAINER)
if(reagents.total_volume)
- reagents.trans_to(bci, reagents.total_volume)
+ reagents.trans_to(shell_item, 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
+ if(shell_item?.reagents)
+ if(shell_item.reagents.total_volume)
+ shell_item.reagents.trans_to(src, shell_item.reagents.total_volume)
+ QDEL_NULL(shell_item.reagents)
+ shell_item = null
diff --git a/code/modules/wiremod/shell/brain_computer_interface.dm b/code/modules/wiremod/shell/brain_computer_interface.dm
index 0b61abe7eef..ab4a7d60fbb 100644
--- a/code/modules/wiremod/shell/brain_computer_interface.dm
+++ b/code/modules/wiremod/shell/brain_computer_interface.dm
@@ -45,41 +45,16 @@
/obj/item/organ/cyberimp/bci/proc/action_comp_registered(datum/source, obj/item/circuit_component/equipment_action/action_comp)
SIGNAL_HANDLER
- LAZYADD(actions, new/datum/action/innate/bci_action(src, action_comp))
+ LAZYADD(actions, new/datum/action/innate/circuit_equipment_action(src, action_comp))
/obj/item/organ/cyberimp/bci/proc/action_comp_unregistered(datum/source, obj/item/circuit_component/equipment_action/action_comp)
SIGNAL_HANDLER
- var/datum/action/innate/bci_action/action = action_comp.granted_to[REF(src)]
+ var/datum/action/innate/circuit_equipment_action/action = action_comp.granted_to[REF(src)]
if(!istype(action))
return
LAZYREMOVE(actions, action)
QDEL_LIST_ASSOC_VAL(action_comp.granted_to)
-/datum/action/innate/bci_action
- name = "Action"
- button_icon = 'icons/mob/actions/actions_items.dmi'
- check_flags = AB_CHECK_CONSCIOUS
- button_icon_state = "bci_power"
-
- var/obj/item/organ/cyberimp/bci/bci
- var/obj/item/circuit_component/equipment_action/circuit_component
-
-/datum/action/innate/bci_action/New(obj/item/organ/cyberimp/bci/_bci, obj/item/circuit_component/equipment_action/circuit_component)
- ..()
- bci = _bci
- circuit_component.granted_to[REF(_bci)] = src
- src.circuit_component = circuit_component
-
-/datum/action/innate/bci_action/Destroy()
- circuit_component.granted_to -= REF(bci)
- circuit_component = null
-
- return ..()
-
-/datum/action/innate/bci_action/Activate()
- circuit_component.user.set_output(owner)
- circuit_component.signal.set_output(COMPONENT_SIGNAL)
-
/obj/item/circuit_component/bci_core
display_name = "BCI Core"
desc = "Controls the core operations of the BCI."
diff --git a/code/modules/wiremod/shell/implant.dm b/code/modules/wiremod/shell/implant.dm
new file mode 100644
index 00000000000..3dab241a113
--- /dev/null
+++ b/code/modules/wiremod/shell/implant.dm
@@ -0,0 +1,236 @@
+/obj/item/implant/circuit
+ name = "circuit implant"
+ actions_types = null
+
+ implant_info = "Functions as a shell for integrated circuits. Activation conditions and effects are defined by the installed circuit."
+
+ implant_lore = "The Subdermal Circuit Housing is a common implant design manufactured primarily by DIY electronics enthusiasts. \
+ Similar in concept to Brain-Computer Interfaces, these devices accept an integrated circuit, and support components that allow the \
+ user to trigger other installed components. What it gains in the ability to be implanted in non-humanoid hosts, it loses in physical \
+ capacity and support for various neural interfacing capabilities."
+
+/obj/item/implant/circuit/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/shell, list(/obj/item/circuit_component/implant_core), SHELL_CAPACITY_TINY)
+ RegisterSignal(src, COMSIG_CIRCUIT_ACTION_COMPONENT_REGISTERED, PROC_REF(action_comp_registered))
+ RegisterSignal(src, COMSIG_CIRCUIT_ACTION_COMPONENT_UNREGISTERED, PROC_REF(action_comp_unregistered))
+ RegisterSignal(src, COMSIG_IMPLANT_OTHER, PROC_REF(on_new_implant))
+
+/obj/item/implant/circuit/proc/action_comp_registered(datum/source, obj/item/circuit_component/equipment_action/action_comp)
+ SIGNAL_HANDLER
+ LAZYADD(actions, new/datum/action/innate/circuit_equipment_action(src, action_comp))
+
+/obj/item/implant/circuit/proc/action_comp_unregistered(datum/source, obj/item/circuit_component/equipment_action/action_comp)
+ SIGNAL_HANDLER
+ var/datum/action/innate/circuit_equipment_action/action = action_comp.granted_to[REF(src)]
+ if(!istype(action))
+ return
+ LAZYREMOVE(actions, action)
+ QDEL_LIST_ASSOC_VAL(action_comp.granted_to)
+
+/obj/item/implant/circuit/proc/on_new_implant(obj/item/implant/source, list/arguments, obj/item/implant/other_implant)
+ SIGNAL_HANDLER
+ if(!istype(other_implant, /obj/item/implant/circuit))
+ return
+ var/mob/living/user = arguments[2]
+ var/force = arguments[4]
+ if(!force)
+ source.balloon_alert(user, "duplicate implant present!")
+ return COMPONENT_STOP_IMPLANTING
+
+/obj/item/implant/circuit/ui_host(mob/user)
+ if(istype(loc, /obj/item/implantcase))
+ return loc
+ return ..()
+
+/obj/item/circuit_component/implant_core
+ display_name = "Implant Core"
+ desc = "Controls the core operations of the implant."
+
+ /// A reference to the action button to look at charge/get info
+ var/datum/action/innate/implant_charge_action/charge_action
+
+ 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/obj/item/implant/implant
+
+/obj/item/circuit_component/implant_core/populate_ports()
+
+ 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_USER)
+
+/obj/item/circuit_component/implant_core/Destroy()
+ QDEL_NULL(charge_action)
+ return ..()
+
+/obj/item/circuit_component/implant_core/proc/update_charge_action()
+ CIRCUIT_TRIGGER
+ if (show_charge_meter.value)
+ if (charge_action)
+ return
+ charge_action = new(src)
+ if (implant.imp_in)
+ charge_action.Grant(implant.imp_in)
+ implant.actions += charge_action
+ else
+ if (!charge_action)
+ return
+ if (implant.imp_in)
+ charge_action.Remove(implant.imp_in)
+ implant.actions -= charge_action
+ QDEL_NULL(charge_action)
+
+/obj/item/circuit_component/implant_core/register_shell(atom/movable/shell)
+ implant = shell
+
+ show_charge_meter.set_value(TRUE)
+
+ RegisterSignal(shell, COMSIG_IMPLANT_IMPLANTED, PROC_REF(on_implanted))
+ RegisterSignal(shell, COMSIG_IMPLANT_REMOVED, PROC_REF(on_removed))
+
+/obj/item/circuit_component/implant_core/unregister_shell(atom/movable/shell)
+ implant = null
+
+ if (charge_action)
+ if (implant.imp_in)
+ charge_action.Remove(implant.imp_in)
+ implant.actions -= charge_action
+ QDEL_NULL(charge_action)
+
+ UnregisterSignal(shell, list(
+ COMSIG_IMPLANT_IMPLANTED,
+ COMSIG_IMPLANT_REMOVED,
+ ))
+
+/obj/item/circuit_component/implant_core/proc/on_implanted(datum/source, mob/living/owner)
+ SIGNAL_HANDLER
+
+ update_charge_action()
+
+ user_port.set_output(owner)
+
+ RegisterSignal(owner, COMSIG_ATOM_EXAMINE, PROC_REF(on_examine))
+ RegisterSignal(owner, COMSIG_PROCESS_BORGCHARGER_OCCUPANT, PROC_REF(on_borg_charge))
+ RegisterSignal(owner, COMSIG_LIVING_ELECTROCUTE_ACT, PROC_REF(on_electrocute))
+
+/obj/item/circuit_component/implant_core/proc/on_removed(datum/source, mob/living/owner)
+ SIGNAL_HANDLER
+
+ user_port.set_output(null)
+
+ UnregisterSignal(owner, list(
+ COMSIG_ATOM_EXAMINE,
+ COMSIG_PROCESS_BORGCHARGER_OCCUPANT,
+ COMSIG_LIVING_ELECTROCUTE_ACT,
+ ))
+
+/obj/item/circuit_component/implant_core/input_received(datum/port/input/port)
+ if (!COMPONENT_TRIGGERED_BY(send_message_signal, port))
+ return
+
+ var/sent_message = trim(message.value)
+ if (!sent_message)
+ return
+
+ if (isnull(implant.imp_in))
+ return
+
+ if (implant.imp_in.stat == DEAD)
+ return
+
+ to_chat(implant.imp_in, "You hear a strange, robotic voice in your head... \"[span_robot("[html_encode(sent_message)]")]\"")
+
+/obj/item/circuit_component/implant_core/proc/on_borg_charge(datum/source, datum/callback/charge_cell, seconds_per_tick)
+ SIGNAL_HANDLER
+
+ if (isnull(parent.cell))
+ return
+
+ charge_cell.Invoke(parent.cell, seconds_per_tick)
+
+/obj/item/circuit_component/implant_core/proc/on_electrocute(datum/source, shock_damage, shock_source, siemens_coefficient, flags)
+ SIGNAL_HANDLER
+
+ if (isnull(parent.cell))
+ return
+
+ if (flags & SHOCK_ILLUSION)
+ return
+
+ parent.cell.give(shock_damage * 2)
+ to_chat(source, span_notice("You absorb some of the shock into your [parent.name]!"))
+
+/obj/item/circuit_component/implant_core/proc/on_examine(datum/source, mob/mob, list/examine_text)
+ SIGNAL_HANDLER
+
+ if (isobserver(mob))
+ examine_text += span_notice("[source.p_They()] [source.p_have()] \a [parent] implanted in [source.p_them()].")
+
+/obj/item/circuit_component/implant_core/Topic(href, list/href_list)
+ ..()
+
+ if (!isobserver(usr))
+ return
+
+ if (href_list["open_implant"])
+ parent.attack_ghost(usr)
+
+/datum/action/innate/implant_charge_action
+ name = "Check Implant Charge"
+ check_flags = NONE
+ button_icon = 'icons/obj/machines/cell_charger.dmi'
+ button_icon_state = "cell"
+
+ var/obj/item/circuit_component/implant_core/circuit_component
+
+/datum/action/innate/implant_charge_action/New(obj/item/circuit_component/implant_core/circuit_component)
+ ..()
+
+ src.circuit_component = circuit_component
+
+ build_all_button_icons()
+
+ START_PROCESSING(SSobj, src)
+
+/datum/action/innate/implant_charge_action/create_button()
+ var/atom/movable/screen/movable/action_button/button = ..()
+ button.maptext_x = 2
+ button.maptext_y = 0
+ return button
+
+/datum/action/innate/implant_charge_action/Destroy()
+ circuit_component.charge_action = null
+ circuit_component = null
+
+ STOP_PROCESSING(SSobj, src)
+
+ return ..()
+
+/datum/action/innate/implant_charge_action/Trigger(mob/clicker, trigger_flags)
+ var/obj/item/stock_parts/power_store/cell/cell = circuit_component.parent.cell
+
+ if (isnull(cell))
+ to_chat(owner, span_boldwarning("[circuit_component.parent] has no power cell."))
+ else
+ to_chat(owner, span_info("[circuit_component.parent]'s [cell.name] has [cell.percent()]% charge left."))
+ to_chat(owner, span_info("You can recharge it by using a cyborg recharging station."))
+
+/datum/action/innate/implant_charge_action/process(seconds_per_tick)
+ build_all_button_icons(UPDATE_BUTTON_STATUS)
+
+/datum/action/innate/implant_charge_action/update_button_status(atom/movable/screen/movable/action_button/button, force = FALSE)
+ . = ..()
+ var/obj/item/stock_parts/power_store/cell/cell = circuit_component.parent.cell
+ button.maptext = cell ? MAPTEXT("[cell.percent()]%") : ""
+
+/obj/item/implantcase/circuit
+ name = "implant case - 'Circuit'"
+ desc = "A glass case containing a circuit implant shell."
+ imp_type = /obj/item/implant/circuit
diff --git a/tgstation.dme b/tgstation.dme
index 8db0f8b03b9..303cf1ab8ce 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -6820,6 +6820,7 @@
#include "code\modules\wiremod\shell\dispenser.dm"
#include "code\modules\wiremod\shell\drone.dm"
#include "code\modules\wiremod\shell\gun.dm"
+#include "code\modules\wiremod\shell\implant.dm"
#include "code\modules\wiremod\shell\keyboard.dm"
#include "code\modules\wiremod\shell\module.dm"
#include "code\modules\wiremod\shell\moneybot.dm"