diff --git a/code/__DEFINES/dcs/signals.dm b/code/__DEFINES/dcs/signals.dm
index 84491eb549b..d738e5aa1f6 100644
--- a/code/__DEFINES/dcs/signals.dm
+++ b/code/__DEFINES/dcs/signals.dm
@@ -645,6 +645,8 @@
#define COMSIG_MINE_TRIGGERED "minegoboom"
/// Called by /obj/item/proc/worn_overlays(list/overlays, mutable_appearance/standing, isinhands, icon_file)
#define COMSIG_ITEM_GET_WORN_OVERLAYS "item_get_worn_overlays"
+/// Called by /obj/item/assembly/signaler(called_from_radio)
+#define COMSIG_ASSEMBLY_PULSED "item_assembly_pulsed"
/// Defib-specific signals
diff --git a/code/modules/assembly/assembly.dm b/code/modules/assembly/assembly.dm
index 289c56a0564..4b35f443243 100644
--- a/code/modules/assembly/assembly.dm
+++ b/code/modules/assembly/assembly.dm
@@ -20,32 +20,21 @@
var/secured = TRUE
var/list/attached_overlays = null
var/obj/item/assembly_holder/holder = null
- var/cooldown = FALSE //To prevent spam
+ var/cooldown = 0 //To prevent spam
var/wires = ASSEMBLY_WIRE_RECEIVE | ASSEMBLY_WIRE_PULSE
var/datum/wires/connected = null // currently only used by timer/signaler
-/obj/item/assembly/proc/activate() //What the device does when turned on
+
+/// Called when the holder is moved
+/obj/item/assembly/proc/holder_movement()
return
-/obj/item/assembly/proc/pulsed(radio = FALSE) //Called when another assembly acts on this one, var/radio will determine where it came from for wire calcs
+/// Called when attack_self is called
+/obj/item/assembly/interact(mob/user)
return
-/obj/item/assembly/proc/toggle_secure() //Code that has to happen when the assembly is un\secured goes here
- return
-
-/obj/item/assembly/proc/attach_assembly(obj/A, mob/user) //Called when an assembly is attacked by another
- return
-
-/obj/item/assembly/proc/process_cooldown() //Called via spawn(10) to have it count down the cooldown var
- return
-
-/obj/item/assembly/proc/holder_movement() //Called when the holder is moved
- return
-
-/obj/item/assembly/interact(mob/user) //Called when attack_self is called
- return
-
-/obj/item/assembly/process_cooldown()
+/// Called to constantly step down the countdown/cooldown
+/obj/item/assembly/proc/process_cooldown()
cooldown--
if(cooldown <= 0)
return FALSE
@@ -62,14 +51,15 @@
holder = null
return ..()
-/obj/item/assembly/pulsed(radio = FALSE)
+/// Called when another assembly acts on this one, var/radio will determine where it came from for wire calcs
+/obj/item/assembly/proc/pulsed(radio = FALSE)
if(holder && (wires & ASSEMBLY_WIRE_RECEIVE))
- activate()
+ activate(radio)
if(radio && (wires & ASSEMBLY_WIRE_RADIO_RECEIVE))
- activate()
+ activate(radio)
return TRUE
-//Called when this device attempts to act on another device, var/radio determines if it was sent via radio or direct
+/// Called when this device attempts to act on another device, var/radio determines if it was sent via radio or direct
/obj/item/assembly/proc/pulse(radio = FALSE)
if(connected && wires)
connected.pulse_assembly(src)
@@ -81,21 +71,27 @@
if(istype(loc, /obj/item/grenade)) // This is a hack. Todo: Manage this better -Sayu
var/obj/item/grenade/G = loc
G.prime() // Adios, muchachos
+ SEND_SIGNAL(src, COMSIG_ASSEMBLY_PULSED, radio)
return TRUE
-/obj/item/assembly/activate()
+/// What the device does when turned on
+/obj/item/assembly/proc/activate(radio = FALSE)
+ SHOULD_CALL_PARENT(TRUE)
if(!secured || cooldown > 0)
return FALSE
cooldown = 2
- addtimer(CALLBACK(src, PROC_REF(process_cooldown)), 10)
+ addtimer(CALLBACK(src, PROC_REF(process_cooldown)), 1 SECONDS)
+
return TRUE
-/obj/item/assembly/toggle_secure()
+/// Happens when the assembly is (un)secured
+/obj/item/assembly/proc/toggle_secure()
secured = !secured
update_icon()
return secured
-/obj/item/assembly/attach_assembly(obj/item/assembly/A, mob/user)
+/// Called when an assembly is attacked by another
+/obj/item/assembly/proc/attach_assembly(obj/item/assembly/A, mob/user)
holder = new /obj/item/assembly_holder(get_turf(src))
if(holder.attach(A, src, user))
to_chat(user, "You attach [A] to [src]!")
@@ -138,6 +134,3 @@
user.set_machine(src)
interact(user)
return TRUE
-
-/obj/item/assembly/interact(mob/user)
- return
diff --git a/code/modules/assembly/signaler.dm b/code/modules/assembly/signaler.dm
index a1551e78469..980fcc5d3ca 100644
--- a/code/modules/assembly/signaler.dm
+++ b/code/modules/assembly/signaler.dm
@@ -64,12 +64,9 @@ GLOBAL_LIST_EMPTY(remote_signalers)
// Activation pre-runner, handles cooldown and calls signal(), invoked from ui_act()
/obj/item/assembly/signaler/activate()
- if(cooldown > 0)
+ if(!..())
return
- cooldown = 2
- addtimer(CALLBACK(src, PROC_REF(process_cooldown)), 1 SECONDS)
-
signal()
/obj/item/assembly/signaler/update_icon_state()
diff --git a/code/modules/assembly/voice.dm b/code/modules/assembly/voice.dm
index 17a9e5818bb..f367dea9c49 100644
--- a/code/modules/assembly/voice.dm
+++ b/code/modules/assembly/voice.dm
@@ -43,7 +43,7 @@
pulse(0)
/obj/item/assembly/voice/activate()
- return // previously this toggled listning when not in a holder, that's a little silly. It was only called in attack_self that way.
+ return ..() // previously this toggled listning when not in a holder, that's a little silly. It was only called in attack_self that way.
/obj/item/assembly/voice/attack_self(mob/user)
diff --git a/code/modules/paperwork/desk_bell.dm b/code/modules/paperwork/desk_bell.dm
index a2077bb400b..56d2d836598 100644
--- a/code/modules/paperwork/desk_bell.dm
+++ b/code/modules/paperwork/desk_bell.dm
@@ -15,18 +15,64 @@
var/ring_cooldown_length = 0.5 SECONDS // This is here to protect against tinnitus.
/// The sound the bell makes
var/ring_sound = 'sound/machines/bell.ogg'
+ /// The remote signaller that we're gonna activate to this bell
+ var/obj/item/assembly/signaler/attached_signaler
+
+/obj/item/desk_bell/examine(mob/user)
+ . = ..()
+ if(!isnull(attached_signaler))
+ . += "There seems to be an antenna sticking out of the base."
+
+/obj/item/desk_bell/Destroy()
+ if(!isnull(attached_signaler))
+ var/turf/cur_turf = get_turf(src)
+ if(cur_turf)
+ forceMove(attached_signaler, get_turf(src))
+ else
+ qdel(attached_signaler)
+ UnregisterSignal(attached_signaler, COMSIG_ASSEMBLY_PULSED)
+ attached_signaler = null
+ return ..()
+
+/obj/item/desk_bell/attackby(obj/item/I, mob/user, params)
+ // can only attach its on your person
+ if(istype(I, /obj/item/assembly/signaler))
+ if(!in_inventory)
+ to_chat(user, "[src] needs to be in your inventory if you want to attach [I] to it!")
+ return
+ if(!isnull(attached_signaler))
+ to_chat(user, "There's already a signaller attached!")
+ return
+ var/obj/item/assembly/signaler/signal = I
+ user.unEquip(signal)
+ signal.forceMove(src)
+ attached_signaler = signal
+ if(signal.receiving)
+ RegisterSignal(attached_signaler, COMSIG_ASSEMBLY_PULSED, PROC_REF(on_signal))
+ user.visible_message(
+ "[user] attaches [signal] to [src].",
+ "You attach [signal] to [src]."
+ )
+ return ..()
+
+/obj/item/desk_bell/proc/on_signal()
+ SIGNAL_HANDLER // COMSIG_ASSEMBLY_PULSED
+ INVOKE_ASYNC(src, PROC_REF(try_ring))
+
+/obj/item/desk_bell/proc/try_ring(mob/user)
+ if(ring_cooldown > world.time || !anchored)
+ return TRUE
+ if(!ring_bell(user) && user)
+ to_chat(user, "[src] is silent. Some idiot broke it.")
+ ring_cooldown = world.time + ring_cooldown_length
+ return TRUE
/obj/item/desk_bell/attack_hand(mob/living/user)
if(in_inventory && ishuman(user))
if(!user.get_active_hand())
user.put_in_hands(src)
return TRUE
- if(ring_cooldown > world.time || !anchored)
- return TRUE
- if(!ring_bell(user))
- to_chat(user, "[src] is silent. Some idiot broke it.")
- ring_cooldown = world.time + ring_cooldown_length
- return TRUE
+ return try_ring(user)
/obj/item/desk_bell/MouseDrop(atom/over_object)
var/mob/M = usr
@@ -79,12 +125,21 @@
if(!tool.use_tool(src, user, 3 SECONDS, volume = tool.tool_volume))
return
anchored = FALSE
+ return
+ if(attached_signaler) // in inventory
+ if(!tool.use_tool(src, user, 0.5 SECONDS, volume = tool.tool_volume))
+ return TRUE
+ to_chat(user, "You remove [attached_signaler].")
+ user.put_in_hands(attached_signaler)
+ attached_signaler = null
/// Check if the clapper breaks, and if it does, break it
/obj/item/desk_bell/proc/check_clapper(mob/living/user)
if(prob(times_rang / 50) && ring_cooldown_length)
- to_chat(user, "You hear [src]'s clapper fall off of its hinge. Nice job, you broke it.")
+ audible_message("You hear [src]'s clapper fall off its hinge.")
+ if(user)
+ to_chat(user, "Nice job, you broke it.")
broken_ringer = TRUE
/// Ring the bell
@@ -95,6 +150,8 @@
// The lack of varying is intentional. The only variance occurs on the strike the bell breaks.
playsound(src, ring_sound, 70, vary = broken_ringer, extrarange = SHORT_RANGE_SOUND_EXTRARANGE)
flick("desk_bell_ring", src)
+ if(attached_signaler)
+ attached_signaler.signal()
times_rang++
return TRUE