diff --git a/code/__DEFINES/assemblies.dm b/code/__DEFINES/assemblies.dm new file mode 100644 index 00000000000..53409ac7254 --- /dev/null +++ b/code/__DEFINES/assemblies.dm @@ -0,0 +1,8 @@ +/// When combined in a holder, blacklists duplicate assemblies +#define ASSEMBLY_NO_DUPLICATES (1<<0) + +/// How loud do assemblies beep at +#define ASSEMBLY_BEEP_VOLUME 5 + +/// The max amount of assemblies attachable on an assembly holder +#define HOLDER_MAX_ASSEMBLIES 12 diff --git a/code/datums/components/trapdoor.dm b/code/datums/components/trapdoor.dm index 9bde925e409..154538b244b 100644 --- a/code/datums/components/trapdoor.dm +++ b/code/datums/components/trapdoor.dm @@ -224,7 +224,7 @@ var/list/stored_decals = list() -/obj/item/assembly/trapdoor/pulsed(radio, mob/pulser) +/obj/item/assembly/trapdoor/pulsed(mob/pulser) . = ..() if(linked) return @@ -307,7 +307,7 @@ return TRUE if(!internals.linked) - internals.pulsed(pulser = user) + internals.pulsed(user) // The pulse linked successfully if(internals.linked) user.balloon_alert(user, "linked") @@ -325,7 +325,7 @@ icon_state = "trapdoor_pressed" addtimer(VARSET_CALLBACK(src, icon_state, initial(icon_state)), trapdoor_cooldown_time) COOLDOWN_START(src, trapdoor_cooldown, trapdoor_cooldown_time) - internals.pulsed(pulser = user) + internals.pulsed(user) return TRUE #undef TRAPDOOR_LINKING_SEARCH_RANGE diff --git a/code/game/machinery/buttons.dm b/code/game/machinery/buttons.dm index 0b9bbab374b..81e2f292d44 100644 --- a/code/game/machinery/buttons.dm +++ b/code/game/machinery/buttons.dm @@ -182,7 +182,7 @@ icon_state = "[skin]1" if(device) - device.pulsed(pulser = user) + device.pulsed(user) SEND_GLOBAL_SIGNAL(COMSIG_GLOB_BUTTON_PRESSED,src) addtimer(CALLBACK(src, TYPE_PROC_REF(/atom/, update_appearance)), 15) diff --git a/code/modules/assembly/assembly.dm b/code/modules/assembly/assembly.dm index a5830103416..4f1dac572eb 100644 --- a/code/modules/assembly/assembly.dm +++ b/code/modules/assembly/assembly.dm @@ -1,9 +1,3 @@ -#define WIRE_RECEIVE (1<<0) -#define WIRE_PULSE (1<<1) -#define WIRE_PULSE_SPECIAL (1<<2) -#define WIRE_RADIO_RECEIVE (1<<3) -#define WIRE_RADIO_PULSE (1<<4) -#define ASSEMBLY_BEEP_VOLUME 5 /obj/item/assembly name = "assembly" @@ -24,10 +18,11 @@ * This will prevent things such as visible lasers from facing the incorrect direction when transformed by assembly_holder's update_appearance() */ var/is_position_sensitive = FALSE + /// Flags related to this assembly. See [assemblies.dm] + var/assembly_flags = NONE var/secured = TRUE var/list/attached_overlays = null var/obj/item/assembly_holder/holder = null - var/wire_type = WIRE_RECEIVE | WIRE_PULSE var/attachable = FALSE // can this be attached to wires var/datum/wires/connected = null var/next_activate = 0 //When we're next allowed to activate - for spam control @@ -75,24 +70,26 @@ return FALSE return TRUE -///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, mob/pulser) - if(wire_type & WIRE_RECEIVE) - INVOKE_ASYNC(src, PROC_REF(activate), pulser) - if(radio && (wire_type & WIRE_RADIO_RECEIVE)) - INVOKE_ASYNC(src, PROC_REF(activate), pulser) +/** + * Pulsed: This device was pulsed by another device + * + * * pulser: Who triggered the pulse + */ +/obj/item/assembly/proc/pulsed(mob/pulser) + INVOKE_ASYNC(src, PROC_REF(activate), pulser) SEND_SIGNAL(src, COMSIG_ASSEMBLY_PULSED) return TRUE -///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 && wire_type) +/** + * Pulse: This device is emitting a pulse to act on another device + */ +/obj/item/assembly/proc/pulse() + // if we have connected wires and are a pulsing assembly, pulse it + if(connected) connected.pulse_assembly(src) - return TRUE - if(holder && (wire_type & WIRE_PULSE)) - holder.process_activation(src, 1, 0) - if(holder && (wire_type & WIRE_PULSE_SPECIAL)) - holder.process_activation(src, 0, 1) + // otherwise if we're attached to a holder, process the activation of it with our flags + else if(holder) + holder.process_activation(src) return TRUE /// What the device does when turned on @@ -116,21 +113,29 @@ return . = ..() -/obj/item/assembly/attackby(obj/item/W, mob/user, params) - if(isassembly(W)) - var/obj/item/assembly/A = W - if((!A.secured) && (!secured)) - holder = new/obj/item/assembly_holder(get_turf(src)) - holder.assemble(src,A,user) - to_chat(user, span_notice("You attach and secure \the [A] to \the [src]!")) - else - to_chat(user, span_warning("Both devices must be in attachable mode to be attached together.")) +/obj/item/assembly/attackby(obj/item/attacking_item, mob/user, params) + if(isassembly(attacking_item)) + var/obj/item/assembly/new_assembly = attacking_item + // Check both our's and their's assembly flags to see if either should not duplicate + // If so, and we match types, don't create a holder - block it + if(((new_assembly.assembly_flags|assembly_flags) & ASSEMBLY_NO_DUPLICATES) && istype(new_assembly, type)) + balloon_alert(user, "can't attach another of that!") + return + if(new_assembly.secured || secured) + balloon_alert(user, "both devices not attachable!") + return + + holder = new /obj/item/assembly_holder(drop_location()) + holder.assemble(src, new_assembly, user) + holder.balloon_alert(user, "parts combined") return - if(istype(W, /obj/item/assembly_holder)) - if(!secured) - var/obj/item/assembly_holder/added_to_holder = W - added_to_holder.add_assembly(src, user) - ..() + + if(istype(attacking_item, /obj/item/assembly_holder)) + var/obj/item/assembly_holder/added_to_holder = attacking_item + added_to_holder.try_add_assembly(src, user) + return + + return ..() /obj/item/assembly/screwdriver_act(mob/living/user, obj/item/I) if(..()) diff --git a/code/modules/assembly/holder.dm b/code/modules/assembly/holder.dm index 004315f7c21..1c5ff4f448f 100644 --- a/code/modules/assembly/holder.dm +++ b/code/modules/assembly/holder.dm @@ -46,6 +46,24 @@ for(var/obj/item/assembly/assembly in assemblies) assembly.on_attach() +/obj/item/assembly_holder/proc/try_add_assembly(obj/item/assembly/attached_assembly, mob/user) + if(attached_assembly.secured) + balloon_alert(attached_assembly, "not attachable!") + return FALSE + + if(LAZYLEN(assemblies) >= HOLDER_MAX_ASSEMBLIES) + balloon_alert(user, "too many assemblies!") + return FALSE + + if(attached_assembly.assembly_flags & ASSEMBLY_NO_DUPLICATES) + if(locate(attached_assembly.type) in assemblies) + balloon_alert(user, "can't attach another of that!") + return FALSE + + add_assembly(attached_assembly, user) + balloon_alert(user, "part attached") + return TRUE + /** * Adds an assembly to the assembly holder * @@ -118,12 +136,11 @@ /obj/item/assembly_holder/attackby(obj/item/weapon, mob/user, params) if(isassembly(weapon)) - var/obj/item/assembly/attached_assembly = weapon - if(!attached_assembly.secured) - add_assembly(attached_assembly, user) - balloon_alert(user, "part added") + try_add_assembly(weapon, user) return + return ..() + /obj/item/assembly_holder/AltClick(mob/user) return ..() // This hotkey is BLACKLISTED since it's used by /datum/component/simple_rotation @@ -153,16 +170,14 @@ * send a pulse to the assembly holder, which then calls this proc that actually activates the assemblies * Arguments: * * /obj/device - the device we sent the pulse from which called this proc - * * normal - if this is a normal activation - * * special - if this is a special activation */ -/obj/item/assembly_holder/proc/process_activation(obj/device, normal = TRUE, special = TRUE) +/obj/item/assembly_holder/proc/process_activation(obj/device) if(!device) return FALSE - if(normal && LAZYLEN(assemblies) >= 2) + if(LAZYLEN(assemblies) >= 2) for(var/obj/item/assembly/assembly as anything in assemblies) if(assembly != device) - assembly.pulsed(FALSE) + assembly.pulsed() if(master) master.receive_signal() return TRUE diff --git a/code/modules/assembly/igniter.dm b/code/modules/assembly/igniter.dm index 3b27bb59e7d..783f67c35d5 100644 --- a/code/modules/assembly/igniter.dm +++ b/code/modules/assembly/igniter.dm @@ -12,6 +12,7 @@ heat = 1000 drop_sound = 'sound/items/handling/component_drop.ogg' pickup_sound = 'sound/items/handling/component_pickup.ogg' + assembly_flags = ASSEMBLY_NO_DUPLICATES /obj/item/assembly/igniter/suicide_act(mob/living/carbon/user) user.visible_message(span_suicide("[user] is trying to ignite [user.p_them()]self with \the [src]! It looks like [user.p_theyre()] trying to commit suicide!")) @@ -43,13 +44,6 @@ sparks.start() return TRUE -/obj/item/assembly/igniter/attackby(obj/item/potential_igniter, mob/user, params) - if(isigniter(potential_igniter)) - to_chat(user, "You briefly consider attaching [potential_igniter] to [src], and then conclude that's a very silly thing to do and move on.") - return - return ..() - - /obj/item/assembly/igniter/attack_self(mob/user) activate() add_fingerprint(user) diff --git a/code/modules/assembly/infrared.dm b/code/modules/assembly/infrared.dm index 9cce8adc370..75ffdfe2413 100644 --- a/code/modules/assembly/infrared.dm +++ b/code/modules/assembly/infrared.dm @@ -149,7 +149,7 @@ switchListener(location) if(!secured || !on || next_activate > world.time) return FALSE - pulse(FALSE) + pulse() audible_message("[icon2html(src, hearers(src))] *beep* *beep* *beep*", null, hearing_range) for(var/mob/hearing_mob in get_hearers_in_view(hearing_range, src)) hearing_mob.playsound_local(get_turf(src), 'sound/machines/triple_beep.ogg', ASSEMBLY_BEEP_VOLUME, TRUE) diff --git a/code/modules/assembly/mousetrap.dm b/code/modules/assembly/mousetrap.dm index a118c1bc48a..4ab9cb93820 100644 --- a/code/modules/assembly/mousetrap.dm +++ b/code/modules/assembly/mousetrap.dm @@ -87,7 +87,7 @@ var/mob/living/carbon/human/user = usr if((HAS_TRAIT(user, TRAIT_DUMB) || HAS_TRAIT(user, TRAIT_CLUMSY)) && prob(50)) to_chat(user, span_warning("Your hand slips, setting off the trigger!")) - pulse(FALSE) + pulse() update_appearance() playsound(src, 'sound/weapons/handcuffs.ogg', 30, TRUE, -3) @@ -117,7 +117,7 @@ var/mob/living/carbon/human/victim = target if(HAS_TRAIT(victim, TRAIT_PIERCEIMMUNE)) playsound(src, 'sound/effects/snap.ogg', 50, TRUE) - pulse(FALSE) + pulse() return FALSE switch(type) if("feet") @@ -148,7 +148,7 @@ visible_message(span_boldannounce("Skreeeee!")) //He's simply too large to be affected by a tiny mouse trap. playsound(src, 'sound/effects/snap.ogg', 50, TRUE) - pulse(FALSE) + pulse() /** * clumsy_check: Sets off the mousetrap if handled by a clown (with some probability) diff --git a/code/modules/assembly/proximity.dm b/code/modules/assembly/proximity.dm index 859d7c1d08d..56fdc8629bf 100644 --- a/code/modules/assembly/proximity.dm +++ b/code/modules/assembly/proximity.dm @@ -90,7 +90,7 @@ /obj/item/assembly/prox_sensor/proc/sense() if(!scanning || !secured || next_activate > world.time) return FALSE - pulse(FALSE) + pulse() audible_message("[icon2html(src, hearers(src))] *beep* *beep* *beep*", null, hearing_range) for(var/mob/hearing_mob in get_hearers_in_view(hearing_range, src)) hearing_mob.playsound_local(get_turf(src), 'sound/machines/triple_beep.ogg', ASSEMBLY_BEEP_VOLUME, TRUE) diff --git a/code/modules/assembly/signaler.dm b/code/modules/assembly/signaler.dm index 381c02de759..9dca6ece811 100644 --- a/code/modules/assembly/signaler.dm +++ b/code/modules/assembly/signaler.dm @@ -6,7 +6,6 @@ lefthand_file = 'icons/mob/inhands/items/devices_lefthand.dmi' righthand_file = 'icons/mob/inhands/items/devices_righthand.dmi' custom_materials = list(/datum/material/iron=400, /datum/material/glass=120) - wires = WIRE_RECEIVE | WIRE_PULSE | WIRE_RADIO_PULSE | WIRE_RADIO_RECEIVE attachable = TRUE drop_sound = 'sound/items/handling/component_drop.ogg' pickup_sound = 'sound/items/handling/component_pickup.ogg' @@ -156,8 +155,6 @@ return if(signal.data["code"] != code) return - if(!(src.wires & WIRE_RADIO_RECEIVE)) - return if(suicider) manual_suicide(suicider) return @@ -165,7 +162,7 @@ // If the holder is a TTV, we want to store the last received signal to incorporate it into TTV logging, else wipe it. last_receive_signal_log = istype(holder, /obj/item/transfer_valve) ? signal.logging_data : null - pulse(TRUE) + pulse() audible_message("[icon2html(src, hearers(src))] *beep* *beep* *beep*", null, hearing_range) for(var/mob/hearing_mob in get_hearers_in_view(hearing_range, src)) hearing_mob.playsound_local(get_turf(src), 'sound/machines/triple_beep.ogg', ASSEMBLY_BEEP_VOLUME, TRUE) @@ -177,34 +174,6 @@ radio_connection = SSradio.add_object(src, frequency, RADIO_SIGNALER) return -// Embedded signaller used in grenade construction. -// It's necessary because the signaler doens't have an off state. -// Generated during grenade construction. -Sayu -/obj/item/assembly/signaler/receiver - var/on = FALSE - -/obj/item/assembly/signaler/receiver/proc/toggle_safety() - on = !on - -/obj/item/assembly/signaler/receiver/activate() - toggle_safety() - return TRUE - -/obj/item/assembly/signaler/receiver/examine(mob/user) - . = ..() - . += span_notice("The radio receiver is [on?"on":"off"].") - -/obj/item/assembly/signaler/receiver/receive_signal(datum/signal/signal) - if(!on) - return - return ..(signal) - -/obj/item/assembly/signaler/anomaly/attack_self() - return - -/obj/item/assembly/signaler/crystal_anomaly/attack_self() - return - /obj/item/assembly/signaler/cyborg /obj/item/assembly/signaler/cyborg/attackby(obj/item/W, mob/user, params) @@ -228,4 +197,3 @@ if(ispAI(user)) return TRUE . = ..() - diff --git a/code/modules/assembly/timer.dm b/code/modules/assembly/timer.dm index a9f43ac380c..db85d25dc3b 100644 --- a/code/modules/assembly/timer.dm +++ b/code/modules/assembly/timer.dm @@ -55,7 +55,7 @@ /obj/item/assembly/timer/proc/timer_end() if(secured && next_activate <= world.time) - pulse(FALSE) + pulse() audible_message(span_infoplain("[icon2html(src, hearers(src))] *beep* *beep* *beep*"), null, hearing_range) for(var/mob/hearing_mob in get_hearers_in_view(hearing_range, src)) hearing_mob.playsound_local(get_turf(src), 'sound/machines/triple_beep.ogg', ASSEMBLY_BEEP_VOLUME, TRUE) diff --git a/code/modules/research/anomaly/anomaly_core.dm b/code/modules/research/anomaly/anomaly_core.dm index 87d59b59d89..ef5270075be 100644 --- a/code/modules/research/anomaly/anomaly_core.dm +++ b/code/modules/research/anomaly/anomaly_core.dm @@ -25,6 +25,9 @@ user.set_suicide(TRUE) user.gib() +/obj/item/assembly/signaler/anomaly/attack_self() + return + /obj/item/assembly/signaler/anomaly/attackby(obj/item/I, mob/user, params) if(I.tool_behaviour == TOOL_ANALYZER) to_chat(user, span_notice("Analyzing... [src]'s stabilized field is fluctuating along frequency [format_frequency(frequency)], code [code].")) diff --git a/code/modules/wiremod/shell/assembly.dm b/code/modules/wiremod/shell/assembly.dm index ed060f4b8a7..ea278fb4a64 100644 --- a/code/modules/wiremod/shell/assembly.dm +++ b/code/modules/wiremod/shell/assembly.dm @@ -64,4 +64,4 @@ return ..() /obj/item/circuit_component/assembly_output/input_received(datum/port/input/port, list/return_values) - attached_assembly.pulse(FALSE) + attached_assembly.pulse() diff --git a/tgstation.dme b/tgstation.dme index 6a2bb04776e..6e53ed4fdbb 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -39,6 +39,7 @@ #include "code\__DEFINES\apc_defines.dm" #include "code\__DEFINES\aquarium.dm" #include "code\__DEFINES\art.dm" +#include "code\__DEFINES\assemblies.dm" #include "code\__DEFINES\atom_hud.dm" #include "code\__DEFINES\basic_mobs.dm" #include "code\__DEFINES\blackmarket.dm"