From 17f597a2e8ca2fbd534cb6d7241290ba53b5e921 Mon Sep 17 00:00:00 2001 From: Y0SH1M4S73R Date: Wed, 18 Feb 2026 20:04:46 -0500 Subject: [PATCH] Adds the circuit implant (#95023) ## About The Pull Request The circuit implant is, as the name suggests, an implant that can contain an integrated circuit. It's similar in concept to the BCI, but differs from it in the following ways: - Has a capacity of 12 components, compared to the BCI's 25 - Can be inserted into basic mobs - Does not set off Body Purists' disdain for cybernetics - Cannot use the target interceptor, thought listener, or overlay components - Cannot force the user to speak/emote - the speech component simply makes the implant itself speak like it does in other shells The circuit implant can be printed from the component printer upon researching advanced shells. Only one circuit implant can be inserted into a mob at once. As a little code standardization bonus, implant case interaction has been refactored into an `item_interaction`. ## Why It's Good For The Game Implants seem like an interesting way for circuits to be utilized. Not only do they provide a body-purist-friendly way to have an internal circuit, they also give most basic mobs a way to interact with the circuit ecosystem. Additionally, them being implants implantable with an implanter provides an easier method for a circuit to be forced upon someone in a way that isn't as easy to remove as just dropping the item. --- code/datums/wires/wire_bundle_component.dm | 3 + .../machinery/computer/camera_advanced.dm | 32 +-- .../objects/items/implants/implantcase.dm | 12 +- .../research/designs/wiremod_designs.dm | 14 ++ .../research/techweb/nodes/circuit_nodes.dm | 1 + .../components/action/equpiment_action.dm | 27 +- .../components/bci/install_detector.dm | 39 +-- .../components/bci/reagent_injector.dm | 51 ++-- .../wiremod/shell/brain_computer_interface.dm | 29 +-- code/modules/wiremod/shell/implant.dm | 236 ++++++++++++++++++ tgstation.dme | 1 + 11 files changed, 345 insertions(+), 100 deletions(-) create mode 100644 code/modules/wiremod/shell/implant.dm 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"