diff --git a/code/__DEFINES/components.dm b/code/__DEFINES/components.dm
index c882994be05..9f21654aa94 100644
--- a/code/__DEFINES/components.dm
+++ b/code/__DEFINES/components.dm
@@ -127,6 +127,27 @@
// /obj/item/clothing signals
#define COMSIG_SHOES_STEP_ACTION "shoes_step_action" //from base of obj/item/clothing/shoes/proc/step_action(): ()
+// /obj/item/implant signals
+#define COMSIG_IMPLANT_ACTIVATED "implant_activated" //from base of /obj/item/implant/proc/activate(): ()
+#define COMSIG_IMPLANT_IMPLANTING "implant_implanting" //from base of /obj/item/implant/proc/implant(): (list/args)
+ #define COMPONENT_STOP_IMPLANTING 1
+#define COMSIG_IMPLANT_OTHER "implant_other" //called on already installed implants when a new one is being added in /obj/item/implant/proc/implant(): (list/args, obj/item/implant/new_implant)
+ //#define COMPONENT_STOP_IMPLANTING 1 //The name makes sense for both
+ #define COMPONENT_DELETE_NEW_IMPLANT 2
+ #define COMPONENT_DELETE_OLD_IMPLANT 4
+#define COMSIG_IMPLANT_EXISTING_UPLINK "implant_uplink_exists" //called on implants being implanted into someone with an uplink implant: (datum/component/uplink)
+ //This uses all return values of COMSIG_IMPLANT_OTHER
+
+// /obj/item/pda signals
+#define COMSIG_PDA_CHANGE_RINGTONE "pda_change_ringtone" //called on pda when the user changes the ringtone: (mob/living/user, new_ringtone)
+ #define COMPONENT_STOP_RINGTONE_CHANGE 1
+
+// /obj/item/radio signals
+#define COMSIG_RADIO_NEW_FREQUENCY "radio_new_frequency" //called from base of /obj/item/radio/proc/set_frequency(): (list/args)
+
+// /obj/item/pen signals
+#define COMSIG_PEN_ROTATED "pen_rotated" //called after rotation in /obj/item/pen/attack_self(): (rotation, mob/living/carbon/user)
+
// /mob/living/carbon/human signals
#define COMSIG_HUMAN_MELEE_UNARMED_ATTACK "human_melee_unarmed_attack" //from mob/living/carbon/human/UnarmedAttack(): (atom/target)
#define COMSIG_HUMAN_MELEE_UNARMED_ATTACKBY "human_melee_unarmed_attackby" //from mob/living/carbon/human/UnarmedAttack(): (mob/living/carbon/human/attacker)
diff --git a/code/modules/uplink/uplink.dm b/code/datums/components/uplink.dm
similarity index 70%
rename from code/modules/uplink/uplink.dm
rename to code/datums/components/uplink.dm
index 77198ceb845..6899baeabb8 100644
--- a/code/modules/uplink/uplink.dm
+++ b/code/datums/components/uplink.dm
@@ -1,183 +1,250 @@
-GLOBAL_LIST_EMPTY(uplinks)
-
-/**
- * Uplinks
- *
- * All /obj/item(s) have a hidden_uplink var. By default it's null. Give the item one with 'new(src') (it must be in it's contents). Then add 'uses.'
- * Use whatever conditionals you want to check that the user has an uplink, and then call interact() on their uplink.
- * You might also want the uplink menu to open if active. Check if the uplink is 'active' and then interact() with it.
-**/
-/datum/component/uplink
- dupe_mode = COMPONENT_DUPE_UNIQUE
- var/name = "syndicate uplink"
- var/active = FALSE
- var/lockable = TRUE
- var/locked = TRUE
- var/allow_restricted = TRUE
- var/telecrystals
- var/selected_cat
- var/owner = null
- var/datum/game_mode/gamemode
- var/datum/uplink_purchase_log/purchase_log
- var/list/uplink_items
- var/hidden_crystals = 0
-
-/datum/component/uplink/Initialize(_owner, _lockable = TRUE, _enabled = FALSE, datum/game_mode/_gamemode, starting_tc = 20)
- if(!isitem(parent))
- return COMPONENT_INCOMPATIBLE
- GLOB.uplinks += src
- uplink_items = get_uplink_items(gamemode, TRUE, allow_restricted)
- RegisterSignal(COMSIG_PARENT_ATTACKBY, .proc/OnAttackBy)
- RegisterSignal(COMSIG_ITEM_ATTACK_SELF, .proc/interact)
- owner = _owner
- if(owner)
- LAZYINITLIST(GLOB.uplink_purchase_logs_by_key)
- if(GLOB.uplink_purchase_logs_by_key[owner])
- purchase_log = GLOB.uplink_purchase_logs_by_key[owner]
- else
- purchase_log = new(owner, src)
- lockable = _lockable
- active = _enabled
- gamemode = _gamemode
- telecrystals = starting_tc
- if(!lockable)
- active = TRUE
- locked = FALSE
-
-/datum/component/uplink/InheritComponent(datum/component/uplink/U)
- lockable |= U.lockable
- active |= U.active
- if(!gamemode)
- gamemode = U.gamemode
- telecrystals += U.telecrystals
- if(purchase_log && U.purchase_log)
- purchase_log.MergeWithAndDel(U.purchase_log)
-
-/datum/component/uplink/Destroy()
- GLOB.uplinks -= src
- gamemode = null
- return ..()
-
-/datum/component/uplink/proc/LoadTC(mob/user, obj/item/stack/telecrystal/TC, silent = FALSE)
- if(!silent)
- to_chat(user, "You slot [TC] into [parent] and charge its internal uplink.")
- var/amt = TC.amount
- telecrystals += amt
- TC.use(amt)
-
-/datum/component/uplink/proc/set_gamemode(_gamemode)
- gamemode = _gamemode
- uplink_items = get_uplink_items(gamemode, TRUE, allow_restricted)
-
-/datum/component/uplink/proc/OnAttackBy(obj/item/I, mob/user)
- if(!active)
- return //no hitting everyone/everything just to try to slot tcs in!
- if(istype(I, /obj/item/stack/telecrystal))
- LoadTC(user, I)
- for(var/category in uplink_items)
- for(var/item in uplink_items[category])
- var/datum/uplink_item/UI = uplink_items[category][item]
- var/path = UI.refund_path || UI.item
- var/cost = UI.refund_amount || UI.cost
- if(I.type == path && UI.refundable && I.check_uplink_validity())
- telecrystals += cost
- purchase_log.total_spent -= cost
- to_chat(user, "[I] refunded.")
- qdel(I)
- return
-
-/datum/component/uplink/proc/interact(mob/user)
- if(locked)
- return
- active = TRUE
- if(user)
- ui_interact(user)
- // an unlocked uplink blocks also opening the PDA or headset menu
- return COMPONENT_NO_INTERACT
-
-/datum/component/uplink/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
- datum/tgui/master_ui = null, datum/ui_state/state = GLOB.inventory_state)
- active = TRUE
- ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
- if(!ui)
- ui = new(user, src, ui_key, "uplink", name, 450, 750, master_ui, state)
- ui.set_autoupdate(FALSE) // This UI is only ever opened by one person, and never is updated outside of user input.
- ui.set_style("syndicate")
- ui.open()
-
-/datum/component/uplink/ui_data(mob/user)
- if(!user.mind)
- return
- var/list/data = list()
- data["telecrystals"] = telecrystals
- data["lockable"] = lockable
-
- data["categories"] = list()
- for(var/category in uplink_items)
- var/list/cat = list(
- "name" = category,
- "items" = (category == selected_cat ? list() : null))
- if(category == selected_cat)
- for(var/item in uplink_items[category])
- var/datum/uplink_item/I = uplink_items[category][item]
- if(I.limited_stock == 0)
- continue
- if(I.restricted_roles.len)
- var/is_inaccessible = 1
- for(var/R in I.restricted_roles)
- if(R == user.mind.assigned_role)
- is_inaccessible = 0
- if(is_inaccessible)
- continue
- cat["items"] += list(list(
- "name" = I.name,
- "cost" = I.cost,
- "desc" = I.desc,
- ))
- data["categories"] += list(cat)
- return data
-
-/datum/component/uplink/ui_act(action, params)
- if(!active)
- return
-
- switch(action)
- if("buy")
- var/item = params["item"]
-
- var/list/buyable_items = list()
- for(var/category in uplink_items)
- buyable_items += uplink_items[category]
-
- if(item in buyable_items)
- var/datum/uplink_item/I = buyable_items[item]
- MakePurchase(usr, I)
- . = TRUE
- if("lock")
- active = FALSE
- locked = TRUE
- telecrystals += hidden_crystals
- hidden_crystals = 0
- SStgui.close_uis(src)
- if("select")
- selected_cat = params["category"]
- return TRUE
-
-/datum/component/uplink/proc/MakePurchase(mob/user, datum/uplink_item/U)
- if(!istype(U))
- return
- if (!user || user.incapacitated())
- return
-
- if(telecrystals < U.cost || U.limited_stock == 0)
- return
- telecrystals -= U.cost
-
- U.purchase(user, src)
-
- if(U.limited_stock > 0)
- U.limited_stock -= 1
-
- SSblackbox.record_feedback("nested tally", "traitor_uplink_items_bought", 1, list("[initial(U.name)]", "[U.cost]"))
- return TRUE
-
+GLOBAL_LIST_EMPTY(uplinks)
+
+/**
+ * Uplinks
+ *
+ * All /obj/item(s) have a hidden_uplink var. By default it's null. Give the item one with 'new(src') (it must be in it's contents). Then add 'uses.'
+ * Use whatever conditionals you want to check that the user has an uplink, and then call interact() on their uplink.
+ * You might also want the uplink menu to open if active. Check if the uplink is 'active' and then interact() with it.
+**/
+/datum/component/uplink
+ dupe_mode = COMPONENT_DUPE_UNIQUE
+ var/name = "syndicate uplink"
+ var/active = FALSE
+ var/lockable = TRUE
+ var/locked = TRUE
+ var/allow_restricted = TRUE
+ var/telecrystals
+ var/selected_cat
+ var/owner = null
+ var/datum/game_mode/gamemode
+ var/datum/uplink_purchase_log/purchase_log
+ var/list/uplink_items
+ var/hidden_crystals = 0
+
+/datum/component/uplink/Initialize(_owner, _lockable = TRUE, _enabled = FALSE, datum/game_mode/_gamemode, starting_tc = 20)
+ if(!isitem(parent))
+ return COMPONENT_INCOMPATIBLE
+
+ RegisterSignal(COMSIG_PARENT_ATTACKBY, .proc/OnAttackBy)
+ RegisterSignal(COMSIG_ITEM_ATTACK_SELF, .proc/interact)
+ if(istype(parent, /obj/item/implant))
+ RegisterSignal(COMSIG_IMPLANT_ACTIVATED, .proc/implant_activation)
+ RegisterSignal(COMSIG_IMPLANT_IMPLANTING, .proc/implanting)
+ RegisterSignal(COMSIG_IMPLANT_OTHER, .proc/old_implant)
+ RegisterSignal(COMSIG_IMPLANT_EXISTING_UPLINK, .proc/new_implant)
+ else if(istype(parent, /obj/item/pda))
+ RegisterSignal(COMSIG_PDA_CHANGE_RINGTONE, .proc/new_ringtone)
+ else if(istype(parent, /obj/item/radio))
+ RegisterSignal(COMSIG_RADIO_NEW_FREQUENCY, .proc/new_frequency)
+ else if(istype(parent, /obj/item/pen))
+ RegisterSignal(COMSIG_PEN_ROTATED, .proc/pen_rotation)
+
+ GLOB.uplinks += src
+ uplink_items = get_uplink_items(gamemode, TRUE, allow_restricted)
+
+ if(_owner)
+ owner = _owner
+ LAZYINITLIST(GLOB.uplink_purchase_logs_by_key)
+ if(GLOB.uplink_purchase_logs_by_key[owner])
+ purchase_log = GLOB.uplink_purchase_logs_by_key[owner]
+ else
+ purchase_log = new(owner, src)
+ lockable = _lockable
+ active = _enabled
+ gamemode = _gamemode
+ telecrystals = starting_tc
+ if(!lockable)
+ active = TRUE
+ locked = FALSE
+
+/datum/component/uplink/InheritComponent(datum/component/uplink/U)
+ lockable |= U.lockable
+ active |= U.active
+ if(!gamemode)
+ gamemode = U.gamemode
+ telecrystals += U.telecrystals
+ if(purchase_log && U.purchase_log)
+ purchase_log.MergeWithAndDel(U.purchase_log)
+
+/datum/component/uplink/Destroy()
+ GLOB.uplinks -= src
+ gamemode = null
+ return ..()
+
+/datum/component/uplink/proc/LoadTC(mob/user, obj/item/stack/telecrystal/TC, silent = FALSE)
+ if(!silent)
+ to_chat(user, "You slot [TC] into [parent] and charge its internal uplink.")
+ var/amt = TC.amount
+ telecrystals += amt
+ TC.use(amt)
+
+/datum/component/uplink/proc/set_gamemode(_gamemode)
+ gamemode = _gamemode
+ uplink_items = get_uplink_items(gamemode, TRUE, allow_restricted)
+
+/datum/component/uplink/proc/OnAttackBy(obj/item/I, mob/user)
+ if(!active)
+ return //no hitting everyone/everything just to try to slot tcs in!
+ if(istype(I, /obj/item/stack/telecrystal))
+ LoadTC(user, I)
+ for(var/category in uplink_items)
+ for(var/item in uplink_items[category])
+ var/datum/uplink_item/UI = uplink_items[category][item]
+ var/path = UI.refund_path || UI.item
+ var/cost = UI.refund_amount || UI.cost
+ if(I.type == path && UI.refundable && I.check_uplink_validity())
+ telecrystals += cost
+ purchase_log.total_spent -= cost
+ to_chat(user, "[I] refunded.")
+ qdel(I)
+ return
+
+/datum/component/uplink/proc/interact(mob/user)
+ if(locked)
+ return
+ active = TRUE
+ if(user)
+ ui_interact(user)
+ // an unlocked uplink blocks also opening the PDA or headset menu
+ return COMPONENT_NO_INTERACT
+
+/datum/component/uplink/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
+ datum/tgui/master_ui = null, datum/ui_state/state = GLOB.inventory_state)
+ active = TRUE
+ ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+ if(!ui)
+ ui = new(user, src, ui_key, "uplink", name, 450, 750, master_ui, state)
+ ui.set_autoupdate(FALSE) // This UI is only ever opened by one person, and never is updated outside of user input.
+ ui.set_style("syndicate")
+ ui.open()
+
+/datum/component/uplink/ui_data(mob/user)
+ if(!user.mind)
+ return
+ var/list/data = list()
+ data["telecrystals"] = telecrystals
+ data["lockable"] = lockable
+
+ data["categories"] = list()
+ for(var/category in uplink_items)
+ var/list/cat = list(
+ "name" = category,
+ "items" = (category == selected_cat ? list() : null))
+ if(category == selected_cat)
+ for(var/item in uplink_items[category])
+ var/datum/uplink_item/I = uplink_items[category][item]
+ if(I.limited_stock == 0)
+ continue
+ if(I.restricted_roles.len)
+ var/is_inaccessible = 1
+ for(var/R in I.restricted_roles)
+ if(R == user.mind.assigned_role)
+ is_inaccessible = 0
+ if(is_inaccessible)
+ continue
+ cat["items"] += list(list(
+ "name" = I.name,
+ "cost" = I.cost,
+ "desc" = I.desc,
+ ))
+ data["categories"] += list(cat)
+ return data
+
+/datum/component/uplink/ui_act(action, params)
+ if(!active)
+ return
+
+ switch(action)
+ if("buy")
+ var/item = params["item"]
+
+ var/list/buyable_items = list()
+ for(var/category in uplink_items)
+ buyable_items += uplink_items[category]
+
+ if(item in buyable_items)
+ var/datum/uplink_item/I = buyable_items[item]
+ MakePurchase(usr, I)
+ . = TRUE
+ if("lock")
+ active = FALSE
+ locked = TRUE
+ telecrystals += hidden_crystals
+ hidden_crystals = 0
+ SStgui.close_uis(src)
+ if("select")
+ selected_cat = params["category"]
+ return TRUE
+
+/datum/component/uplink/proc/MakePurchase(mob/user, datum/uplink_item/U)
+ if(!istype(U))
+ return
+ if (!user || user.incapacitated())
+ return
+
+ if(telecrystals < U.cost || U.limited_stock == 0)
+ return
+ telecrystals -= U.cost
+
+ U.purchase(user, src)
+
+ if(U.limited_stock > 0)
+ U.limited_stock -= 1
+
+ SSblackbox.record_feedback("nested tally", "traitor_uplink_items_bought", 1, list("[initial(U.name)]", "[U.cost]"))
+ return TRUE
+
+// Implant signal responses
+
+/datum/component/uplink/proc/implant_activation()
+ var/obj/item/implant/implant = parent
+ locked = FALSE
+ interact(implant.imp_in)
+
+/datum/component/uplink/proc/implanting(list/arguments)
+ var/mob/user = arguments[2]
+ owner = "[user.key]"
+
+/datum/component/uplink/proc/old_implant(list/arguments, obj/item/implant/new_implant)
+ // It kinda has to be weird like this until implants are components
+ return SEND_SIGNAL(new_implant, COMSIG_IMPLANT_EXISTING_UPLINK, src)
+
+/datum/component/uplink/proc/new_implant(datum/component/uplink/uplink)
+ uplink.telecrystals += telecrystals
+ return COMPONENT_DELETE_NEW_IMPLANT
+
+// PDA signal responses
+
+/datum/component/uplink/proc/new_ringtone(mob/living/user, new_ring_text)
+ var/obj/item/pda/master = parent
+ if(trim(lowertext(new_ring_text)) != trim(lowertext(master.lock_code))) //why is the lock code stored on the pda?
+ return
+ locked = FALSE
+ interact(user)
+ to_chat(user, "The PDA softly beeps.")
+ user << browse(null, "window=pda")
+ master.mode = 0
+ return COMPONENT_STOP_RINGTONE_CHANGE
+
+// Radio signal responses
+
+/datum/component/uplink/proc/new_frequency(list/arguments)
+ var/obj/item/radio/master = parent
+ var/frequency = arguments[1]
+ if(frequency != master.traitor_frequency)
+ return
+ locked = FALSE
+ if(ismob(master.loc))
+ interact(master.loc)
+
+// Pen signal responses
+
+/datum/component/uplink/proc/pen_rotation(degrees, mob/living/carbon/user)
+ var/obj/item/pen/master = parent
+ if(degrees != master.traitor_unlock_degrees)
+ return
+ locked = FALSE
+ master.degrees = 0
+ interact(user)
+ to_chat(user, "Your pen makes a clicking noise, before quickly rotating back to 0 degrees!")
\ No newline at end of file
diff --git a/code/game/objects/items/devices/PDA/PDA.dm b/code/game/objects/items/devices/PDA/PDA.dm
index e514a82c5a4..2d0554b14d0 100644
--- a/code/game/objects/items/devices/PDA/PDA.dm
+++ b/code/game/objects/items/devices/PDA/PDA.dm
@@ -519,16 +519,11 @@ GLOBAL_LIST_EMPTY(PDAs)
if("Ringtone")
var/t = input(U, "Please enter new ringtone", name, ttone) as text
if(in_range(src, U) && loc == U && t)
- GET_COMPONENT(hidden_uplink, /datum/component/uplink)
- if(hidden_uplink && (trim(lowertext(t)) == trim(lowertext(lock_code))))
- hidden_uplink.locked = FALSE
- hidden_uplink.interact(U)
- to_chat(U, "The PDA softly beeps.")
+ if(SEND_SIGNAL(src, COMSIG_PDA_CHANGE_RINGTONE, U, t) & COMPONENT_STOP_RINGTONE_CHANGE)
U << browse(null, "window=pda")
- src.mode = 0
+ return
else
- t = copytext(sanitize(t), 1, 20)
- ttone = t
+ ttone = copytext(sanitize(t), 1, 20)
else
U << browse(null, "window=pda")
return
diff --git a/code/game/objects/items/devices/radio/radio.dm b/code/game/objects/items/devices/radio/radio.dm
index 9f5a570296f..6a6d34340ef 100644
--- a/code/game/objects/items/devices/radio/radio.dm
+++ b/code/game/objects/items/devices/radio/radio.dm
@@ -47,6 +47,7 @@
return BRUTELOSS
/obj/item/radio/proc/set_frequency(new_frequency)
+ SEND_SIGNAL(src, COMSIG_RADIO_NEW_FREQUENCY, args)
remove_radio(src, frequency)
frequency = add_radio(src, new_frequency)
@@ -159,13 +160,7 @@
tune = tune * 10
. = TRUE
if(.)
- frequency = sanitize_frequency(tune, freerange)
- set_frequency(frequency)
- GET_COMPONENT(hidden_uplink, /datum/component/uplink)
- if(hidden_uplink && (frequency == traitor_frequency))
- hidden_uplink.locked = FALSE
- hidden_uplink.interact(usr)
- ui.close()
+ set_frequency(sanitize_frequency(tune, freerange))
if("listen")
listening = !listening
. = TRUE
diff --git a/code/game/objects/items/implants/implant.dm b/code/game/objects/items/implants/implant.dm
index bc541181dbe..7184b956f54 100644
--- a/code/game/objects/items/implants/implant.dm
+++ b/code/game/objects/items/implants/implant.dm
@@ -18,7 +18,7 @@
return
/obj/item/implant/proc/activate()
- return
+ SEND_SIGNAL(src, COMSIG_IMPLANT_ACTIVATED)
/obj/item/implant/ui_action_click()
activate("action_button")
@@ -41,12 +41,26 @@
//return 1 if the implant injects
//return 0 if there is no room for implant / it fails
/obj/item/implant/proc/implant(mob/living/target, mob/user, silent = FALSE)
+ if(SEND_SIGNAL(src, COMSIG_IMPLANT_IMPLANTING, args) & COMPONENT_STOP_IMPLANTING)
+ return
LAZYINITLIST(target.implants)
if(!target.can_be_implanted() || !can_be_implanted_in(target))
- return 0
+ return FALSE
for(var/X in target.implants)
- if(istype(X, type))
- var/obj/item/implant/imp_e = X
+ var/obj/item/implant/imp_e = X
+ var/flags = SEND_SIGNAL(imp_e, COMSIG_IMPLANT_OTHER, args, src)
+ if(flags & COMPONENT_DELETE_NEW_IMPLANT)
+ UNSETEMPTY(target.implants)
+ qdel(src)
+ return TRUE
+ if(flags & COMPONENT_DELETE_OLD_IMPLANT)
+ qdel(imp_e)
+ continue
+ if(flags & COMPONENT_STOP_IMPLANTING)
+ UNSETEMPTY(target.implants)
+ return FALSE
+
+ if(istype(imp_e, type))
if(!allow_multiple)
if(imp_e.uses < initial(imp_e.uses)*2)
if(uses == -1)
@@ -54,9 +68,9 @@
else
imp_e.uses = min(imp_e.uses + uses, initial(imp_e.uses)*2)
qdel(src)
- return 1
+ return TRUE
else
- return 0
+ return FALSE
forceMove(target)
imp_in = target
@@ -72,7 +86,7 @@
if(user)
add_logs(user, target, "implanted", "\a [name]")
- return 1
+ return TRUE
/obj/item/implant/proc/removed(mob/living/source, silent = FALSE, special = 0)
moveToNullspace()
diff --git a/code/game/objects/items/implants/implant_abductor.dm b/code/game/objects/items/implants/implant_abductor.dm
index 3ba1e7315fe..0c50beadea1 100644
--- a/code/game/objects/items/implants/implant_abductor.dm
+++ b/code/game/objects/items/implants/implant_abductor.dm
@@ -8,6 +8,7 @@
var/cooldown = 30
/obj/item/implant/abductor/activate()
+ . = ..()
if(cooldown == initial(cooldown))
home.Retrieve(imp_in,1)
cooldown = 0
diff --git a/code/game/objects/items/implants/implant_chem.dm b/code/game/objects/items/implants/implant_chem.dm
index 88b7e032f37..b9c85c07289 100644
--- a/code/game/objects/items/implants/implant_chem.dm
+++ b/code/game/objects/items/implants/implant_chem.dm
@@ -37,6 +37,7 @@
activate(reagents.total_volume)
/obj/item/implant/chem/activate(cause)
+ . = ..()
if(!cause || !imp_in)
return 0
var/mob/living/carbon/R = imp_in
diff --git a/code/game/objects/items/implants/implant_explosive.dm b/code/game/objects/items/implants/implant_explosive.dm
index 5aa0e31d08b..e749a6c4be5 100644
--- a/code/game/objects/items/implants/implant_explosive.dm
+++ b/code/game/objects/items/implants/implant_explosive.dm
@@ -27,6 +27,7 @@
return dat
/obj/item/implant/explosive/activate(cause)
+ . = ..()
if(!cause || !imp_in || active)
return 0
if(cause == "action_button" && !popup)
diff --git a/code/game/objects/items/implants/implant_freedom.dm b/code/game/objects/items/implants/implant_freedom.dm
index 4ba04c94790..f3e66d2c08b 100644
--- a/code/game/objects/items/implants/implant_freedom.dm
+++ b/code/game/objects/items/implants/implant_freedom.dm
@@ -7,6 +7,7 @@
/obj/item/implant/freedom/activate()
+ . = ..()
uses--
to_chat(imp_in, "You feel a faint click.")
if(iscarbon(imp_in))
diff --git a/code/game/objects/items/implants/implant_krav_maga.dm b/code/game/objects/items/implants/implant_krav_maga.dm
index c2d7d442490..3a751ecd0e2 100644
--- a/code/game/objects/items/implants/implant_krav_maga.dm
+++ b/code/game/objects/items/implants/implant_krav_maga.dm
@@ -15,6 +15,7 @@
return dat
/obj/item/implant/krav_maga/activate()
+ . = ..()
var/mob/living/carbon/human/H = imp_in
if(!ishuman(H))
return
diff --git a/code/game/objects/items/implants/implant_misc.dm b/code/game/objects/items/implants/implant_misc.dm
index f17be453f47..24ee6d09666 100644
--- a/code/game/objects/items/implants/implant_misc.dm
+++ b/code/game/objects/items/implants/implant_misc.dm
@@ -31,6 +31,7 @@
return dat
/obj/item/implant/adrenalin/activate()
+ . = ..()
uses--
to_chat(imp_in, "You feel a sudden surge of energy!")
imp_in.SetStun(0)
@@ -54,6 +55,7 @@
uses = 3
/obj/item/implant/emp/activate()
+ . = ..()
uses--
empulse(imp_in, 3, 5)
if(!uses)
@@ -88,6 +90,7 @@
icon_state = "walkietalkie"
/obj/item/implant/radio/activate()
+ . = ..()
// needs to be GLOB.deep_inventory_state otherwise it won't open
radio.ui_interact(usr, "main", null, FALSE, null, GLOB.deep_inventory_state)
diff --git a/code/game/objects/items/implants/implant_storage.dm b/code/game/objects/items/implants/implant_storage.dm
index ebe1dce9c12..ec6b4d64c3e 100644
--- a/code/game/objects/items/implants/implant_storage.dm
+++ b/code/game/objects/items/implants/implant_storage.dm
@@ -6,6 +6,7 @@
var/max_slot_stacking = 4
/obj/item/implant/storage/activate()
+ . = ..()
SEND_SIGNAL(src, COMSIG_TRY_STORAGE_SHOW, imp_in, TRUE)
/obj/item/implant/storage/removed(source, silent = FALSE, special = 0)
diff --git a/code/game/objects/items/implants/implantuplink.dm b/code/game/objects/items/implants/implantuplink.dm
index 5fe603c47c3..9000fbbe343 100644
--- a/code/game/objects/items/implants/implantuplink.dm
+++ b/code/game/objects/items/implants/implantuplink.dm
@@ -11,32 +11,6 @@
. = ..()
AddComponent(/datum/component/uplink, _owner, TRUE, FALSE, null, starting_tc)
-/obj/item/implant/uplink/implant(mob/living/target, mob/user, silent = FALSE)
- GET_COMPONENT(hidden_uplink, /datum/component/uplink)
- if(hidden_uplink)
- for(var/X in target.implants)
- if(istype(X, type))
- var/obj/item/implant/imp_e = X
- GET_COMPONENT_FROM(their_hidden_uplink, /datum/component/uplink, imp_e)
- if(their_hidden_uplink)
- their_hidden_uplink.telecrystals += hidden_uplink.telecrystals
- qdel(src)
- return TRUE
- else
- qdel(imp_e) //INFERIOR AND EMPTY!
-
- if(..())
- if(hidden_uplink)
- hidden_uplink.owner = "[user.key]"
- return TRUE
- return FALSE
-
-/obj/item/implant/uplink/activate()
- GET_COMPONENT(hidden_uplink, /datum/component/uplink)
- if(hidden_uplink)
- hidden_uplink.locked = FALSE
- hidden_uplink.interact(usr)
-
/obj/item/implanter/uplink
name = "implanter (uplink)"
imp_type = /obj/item/implant/uplink
diff --git a/code/modules/paperwork/pen.dm b/code/modules/paperwork/pen.dm
index 42b3d08c0c5..d4349d3b308 100644
--- a/code/modules/paperwork/pen.dm
+++ b/code/modules/paperwork/pen.dm
@@ -104,12 +104,7 @@
if(deg && (deg > 0 && deg <= 360))
degrees = deg
to_chat(user, "You rotate the top of the pen to [degrees] degrees.")
- GET_COMPONENT(hidden_uplink, /datum/component/uplink)
- if(hidden_uplink && degrees == traitor_unlock_degrees)
- to_chat(user, "Your pen makes a clicking noise, before quickly rotating back to 0 degrees!")
- degrees = 0
- hidden_uplink.locked = FALSE
- hidden_uplink.interact(user)
+ SEND_SIGNAL(src, COMSIG_PEN_ROTATED, deg, user)
/obj/item/pen/attack(mob/living/M, mob/user,stealth)
if(!istype(M))
diff --git a/tgstation.dme b/tgstation.dme
index 7114c5831a6..5a6b488c1fd 100755
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -345,6 +345,7 @@
#include "code\datums\components\stationloving.dm"
#include "code\datums\components\swarming.dm"
#include "code\datums\components\thermite.dm"
+#include "code\datums\components\uplink.dm"
#include "code\datums\components\wearertargeting.dm"
#include "code\datums\components\wet_floor.dm"
#include "code\datums\components\decals\blood.dm"
@@ -2578,7 +2579,6 @@
#include "code\modules\tgui\states\zlevel.dm"
#include "code\modules\tooltip\tooltip.dm"
#include "code\modules\unit_tests\_unit_tests.dm"
-#include "code\modules\uplink\uplink.dm"
#include "code\modules\uplink\uplink_devices.dm"
#include "code\modules\uplink\uplink_items.dm"
#include "code\modules\uplink\uplink_purchase_log.dm"